diff --git a/mypy/checker_shared.py b/mypy/checker_shared.py index 2a8fbdb0c9f1..0014d2c6fc88 100644 --- a/mypy/checker_shared.py +++ b/mypy/checker_shared.py @@ -272,6 +272,10 @@ def checking_await_set(self) -> Iterator[None]: def get_precise_awaitable_type(self, typ: Type, local_errors: ErrorWatcher) -> Type | None: raise NotImplementedError + @abstractmethod + def add_any_attribute_to_type(self, typ: Type, name: str) -> Type: + raise NotImplementedError + @abstractmethod def is_defined_in_stub(self, typ: Instance, /) -> bool: raise NotImplementedError diff --git a/mypy/checkpattern.py b/mypy/checkpattern.py index 48840466f0d8..21bffa8c1af6 100644 --- a/mypy/checkpattern.py +++ b/mypy/checkpattern.py @@ -671,12 +671,15 @@ def visit_class_pattern(self, o: ClassPattern) -> PatternType: has_local_errors = local_errors.has_new_errors() if has_local_errors or key_type is None: key_type = AnyType(TypeOfAny.from_error) - self.msg.fail( - message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format( - typ.str_with_options(self.options), keyword - ), - pattern, - ) + if not (type_info and type_info.fullname == "builtins.object"): + self.msg.fail( + message_registry.CLASS_PATTERN_UNKNOWN_KEYWORD.format( + typ.str_with_options(self.options), keyword + ), + pattern, + ) + elif keyword is not None: + new_type = self.chk.add_any_attribute_to_type(new_type, keyword) inner_type, inner_rest_type, inner_captures = self.accept(pattern, key_type) if is_uninhabited(inner_type): diff --git a/test-data/unit/check-python310.test b/test-data/unit/check-python310.test index f264167cb067..20dccdea39db 100644 --- a/test-data/unit/check-python310.test +++ b/test-data/unit/check-python310.test @@ -1002,15 +1002,24 @@ match m: [builtins fixtures/tuple.pyi] [case testMatchClassPatternNonexistentKeyword] +from typing import Any class A: ... m: object +n: Any match m: case A(a=j): # E: Class "__main__.A" has no attribute "a" reveal_type(m) # N: Revealed type is "__main__.A" reveal_type(j) # N: Revealed type is "Any" +match n: + # Matching against object should not emit an error for non-existing keys + case object(a=k): + reveal_type(n) # N: Revealed type is "builtins.object" + reveal_type(n.a) # N: Revealed type is "Any" + reveal_type(k) # N: Revealed type is "Any" + [case testMatchClassPatternDuplicateKeyword] class A: a: str