diff --git a/mypy/checker.py b/mypy/checker.py index ae93b8558add..3c331a26063a 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -4870,6 +4870,12 @@ def enter_partial_types(self, *, is_function: bool = False, and not permissive): var.type = NoneType() else: + if is_class: + # Special case: possibly super-type defines the type for us? + parent_type = self.get_defined_in_base_class(var) + if parent_type is not None: + var.type = parent_type + self.partial_reported.add(var) if var not in self.partial_reported and not permissive: self.msg.need_annotation_for_var(var, context, self.options.python_version) self.partial_reported.add(var) @@ -4926,6 +4932,14 @@ def is_defined_in_base_class(self, var: Var) -> bool: return True return False + def get_defined_in_base_class(self, var: Var) -> Optional[Type]: + if var.info: + for base in var.info.mro[1:]: + found = base.get(var.name) + if found is not None: + return found.type + return None + def find_partial_types(self, var: Var) -> Optional[Dict[Var, Context]]: """Look for an active partial type scope containing variable. diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index ee58d98d0675..6e4ab5fc36ff 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -972,6 +972,53 @@ x = C.x [out] main:2: error: Need type annotation for "x" (hint: "x: List[] = ...") +[case testAccessingClassAttributeWithTypeInferenceWithSuperType] +from typing import List +class P: + x: List[int] +class C(P): + x = [] +reveal_type(C.x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testAccessingClassAttributeWithTypeInferenceWithMixinType] +from typing import List +class P: + pass +class M: + x: List[int] +class C(P, M): + x = [] +reveal_type(C.x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testClassAttributeWithTypeInferenceInvalidType] +from typing import List +class P: + x: List[int] +class C(P): + x = ['a'] # E: List item 0 has incompatible type "str"; expected "int" +[builtins fixtures/list.pyi] + +case testAccessingClassAttributeWithTypeInferenceWithMixinTypeConflict] +from typing import List +class P: + x: List[int] +class M: + x: List[str] +class C(P, M): + x = [] # This hides subclassing issue, but that's how it is for now + +reveal_type(C.x) # N: Revealed type is "builtins.list[builtins.int]" +[builtins fixtures/list.pyi] + +[case testClassAttributeWithTypeInferenceNoParentType] +class P: + ... +class C(P): + x = [] # E: Need type annotation for "x" (hint: "x: List[] = ...") +[builtins fixtures/list.pyi] + [case testAccessingGenericClassAttribute] from typing import Generic, TypeVar T = TypeVar('T')