diff --git a/crates/ty_python_semantic/resources/mdtest/attributes.md b/crates/ty_python_semantic/resources/mdtest/attributes.md index e228b5707c00d..1f22da612bfda 100644 --- a/crates/ty_python_semantic/resources/mdtest/attributes.md +++ b/crates/ty_python_semantic/resources/mdtest/attributes.md @@ -623,8 +623,6 @@ class C: self.c = c if False: def set_e(self, e: str) -> None: - # TODO: Should not emit this diagnostic - # error: [unresolved-attribute] self.e = e # TODO: this would ideally be `Unknown | Literal[1]` diff --git a/crates/ty_python_semantic/resources/mdtest/function/return_type.md b/crates/ty_python_semantic/resources/mdtest/function/return_type.md index 6d399ed07dd66..ebc107eeffe5e 100644 --- a/crates/ty_python_semantic/resources/mdtest/function/return_type.md +++ b/crates/ty_python_semantic/resources/mdtest/function/return_type.md @@ -187,12 +187,14 @@ elif TYPE_CHECKING: def j() -> str: ... else: - def j_() -> str: ... # error: [empty-body] + def j(): + raise NotImplementedError if False: pass elif not TYPE_CHECKING: - def k_() -> str: ... # error: [empty-body] + def k() -> str: + raise NotImplementedError else: def k() -> str: ... @@ -224,19 +226,22 @@ if typing.TYPE_CHECKING: def o() -> str: ... if not typing.TYPE_CHECKING: - def p() -> str: ... # error: [empty-body] + def p() -> str: + raise NotImplementedError if compat.sub.sub.TYPE_CHECKING: def q() -> str: ... if not compat.sub.sub.TYPE_CHECKING: - def r() -> str: ... # error: [empty-body] + def r() -> str: + raise NotImplementedError if t.TYPE_CHECKING: def s() -> str: ... if not t.TYPE_CHECKING: - def t() -> str: ... # error: [empty-body] + def t() -> str: + raise NotImplementedError ``` ## Conditional return type diff --git a/crates/ty_python_semantic/resources/mdtest/unreachable.md b/crates/ty_python_semantic/resources/mdtest/unreachable.md index 9ff84162ac750..033744b58ed6b 100644 --- a/crates/ty_python_semantic/resources/mdtest/unreachable.md +++ b/crates/ty_python_semantic/resources/mdtest/unreachable.md @@ -464,6 +464,29 @@ if False: print(x) ``` +This also applies to deferred annotations on Python 3.14+, which are resolved from the perspective +of the end of the scope, which may not be part of the unreachable section. + +```toml +[environment] +python-version = "3.14" +``` + +```py +from typing import TYPE_CHECKING + +class NonCallable: + pass + +if not TYPE_CHECKING: + def _(non_callable: NonCallable): + non_callable() + +if False: + def _(non_callable: NonCallable): + non_callable() +``` + ### Type annotations Silencing of diagnostics also works for type annotations, even if they are stringified: diff --git a/crates/ty_python_semantic/src/types/context.rs b/crates/ty_python_semantic/src/types/context.rs index a0f688981e98b..b2c470dca4ecc 100644 --- a/crates/ty_python_semantic/src/types/context.rs +++ b/crates/ty_python_semantic/src/types/context.rs @@ -442,6 +442,12 @@ impl<'db, 'ctx> LintDiagnosticGuardBuilder<'db, 'ctx> { if ctx.is_in_multi_inference() { return None; } + // Nested scopes in statically unreachable branches should not produce body diagnostics. + if !semantic_index(ctx.db, ctx.file) + .is_scope_reachable(ctx.db, ctx.scope.file_scope_id(ctx.db)) + { + return None; + } Some((severity, source)) }