Skip to content

Commit

Permalink
Preserve block unreachablility when checking function definitions wit…
Browse files Browse the repository at this point in the history
…h constrained TypeVars (#18217)

Fixes #18210

When checking function definitions with constrained type variables (i.e.
type variables with value restrictions), mypy expands the function
definition for each possible type variable value. However, blocks in the
expanded function definitions have their `is_unreachable` reset to
`False`, which leads to spurious errors in blocks that were marked as
unreachable during semantic analysis.

This PR preserves the value of `is_unreachable` on blocks in the
expanded function definitions.
  • Loading branch information
brianschubert authored Dec 2, 2024
1 parent 9dad464 commit e666217
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 3 deletions.
4 changes: 2 additions & 2 deletions mypy/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1263,15 +1263,15 @@ class Block(Statement):

__match_args__ = ("body", "is_unreachable")

def __init__(self, body: list[Statement]) -> None:
def __init__(self, body: list[Statement], *, is_unreachable: bool = False) -> None:
super().__init__()
self.body = body
# True if we can determine that this block is not executed during semantic
# analysis. For example, this applies to blocks that are protected by
# something like "if PY3:" when using Python 2. However, some code is
# only considered unreachable during type checking and this is not true
# in those cases.
self.is_unreachable = False
self.is_unreachable = is_unreachable

def accept(self, visitor: StatementVisitor[T]) -> T:
return visitor.visit_block(self)
Expand Down
2 changes: 1 addition & 1 deletion mypy/treetransform.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ def visit_nonlocal_decl(self, node: NonlocalDecl) -> NonlocalDecl:
return NonlocalDecl(node.names.copy())

def visit_block(self, node: Block) -> Block:
return Block(self.statements(node.body))
return Block(self.statements(node.body), is_unreachable=node.is_unreachable)

def visit_decorator(self, node: Decorator) -> Decorator:
# Note that a Decorator must be transformed to a Decorator.
Expand Down
10 changes: 10 additions & 0 deletions test-data/unit/check-unreachable-code.test
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,16 @@ class Test4(Generic[T3]):

[builtins fixtures/isinstancelist.pyi]

[case testUnreachableBlockStaysUnreachableWithTypeVarConstraints]
# flags: --always-false COMPILE_TIME_FALSE
from typing import TypeVar
COMPILE_TIME_FALSE = False
T = TypeVar("T", int, str)
def foo(x: T) -> T:
if COMPILE_TIME_FALSE:
return "bad"
return x

[case testUnreachableFlagContextManagersNoSuppress]
# flags: --warn-unreachable
from contextlib import contextmanager
Expand Down

0 comments on commit e666217

Please sign in to comment.