Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion mypy/checkpattern.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,14 +665,21 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType:
#
# Check keyword patterns
#
narrowed_type_for_members = narrowed_type
proper_narrowed_type_for_members = get_proper_type(narrowed_type_for_members)
if isinstance(proper_narrowed_type_for_members, TupleType) and (
proper_narrowed_type_for_members.partial_fallback.type.is_named_tuple
):
narrowed_type_for_members = proper_narrowed_type_for_members.partial_fallback

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This keeps the tuple form for class-pattern narrowing, but switches to the namedtuple fallback for keyword field lookup. Without that conversion, generic NamedTuple fields are effectively erased during member access inside the pattern.

can_match = True
for keyword, pattern in keyword_pairs:
key_type: Type | None = None
with self.msg.filter_errors() as local_errors:
if keyword is not None:
key_type = analyze_member_access(
keyword,
narrowed_type,
narrowed_type_for_members,
pattern,
is_lvalue=False,
is_super=False,
Expand Down
18 changes: 18 additions & 0 deletions test-data/unit/check-python310.test
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,24 @@ match m:
reveal_type(m) # N: Revealed type is "__main__.A[builtins.int]"
reveal_type(i) # N: Revealed type is "builtins.int"

[case testMatchClassPatternCaptureGenericNamedTupleAlreadyKnown]
# flags: --warn-unreachable
from typing import Generic, NamedTuple, TypeVar

T = TypeVar("T")

class A(NamedTuple, Generic[T]):
a: T

def f(m: A[int]) -> int:
match m:
case A(a=i):
return i
case _:
return "oops" # E: Statement is unreachable
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-namedtuple.pyi]

[case testMatchClassPatternCaptureFilledGenericTypeAlias]
from typing import Generic, TypeVar

Expand Down
16 changes: 16 additions & 0 deletions test-data/unit/check-python312.test
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,22 @@ e: M[bool] # E: Value of type variable "T" of "M" cannot be "bool"
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testMatchClassPatternCapturePEP695GenericNamedTupleAlreadyKnown]
# flags: --warn-unreachable
from typing import NamedTuple

class N[T](NamedTuple):
x: T

def f(n: N[int]) -> int:
match n:
case N(x=value):
return value
case _:
return "oops" # E: Statement is unreachable
[builtins fixtures/tuple.pyi]
[typing fixtures/typing-full.pyi]

[case testPEP695GenericTypedDict]
from typing import TypedDict

Expand Down
Loading