diff --git a/mypy/checker.py b/mypy/checker.py index b721c8690fc7..c4e3f8c5b239 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1602,7 +1602,10 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None: # Verify that inherited attributes are compatible. mro = typ.mro[1:] for i, base in enumerate(mro): - for name in base.names: + # Attributes defined in both the type and base are skipped. + # Normal checks for attribute compatibility should catch any problems elsewhere. + non_overridden_attrs = base.names.keys() - typ.names.keys() + for name in non_overridden_attrs: for base2 in mro[i + 1:]: # We only need to check compatibility of attributes from classes not # in a subclass relationship. For subclasses, normal (single inheritance) @@ -1867,6 +1870,9 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, lvalue_type: Optional[ # Show only one error per variable break + direct_bases = lvalue_node.info.direct_base_classes() + last_immediate_base = direct_bases[-1] if direct_bases else None + for base in lvalue_node.info.mro[1:]: # Only check __slots__ against the 'object' # If a base class defines a Tuple of 3 elements, a child of @@ -1890,7 +1896,10 @@ def check_compatibility_all_supers(self, lvalue: RefExpr, lvalue_type: Optional[ # Only show one error per variable; even if other # base classes are also incompatible return True - break + if base is last_immediate_base: + # At this point, the attribute was found to be compatible with all + # immediate parents. + break return False def check_compatibility_super(self, lvalue: RefExpr, lvalue_type: Optional[Type], diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index ccc683088cde..0dab5b687773 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -4009,7 +4009,7 @@ class B(A): main:4: error: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") main:5: error: Incompatible types in assignment (expression has type "str", base class "A" defined the type as "int") -[case testClassIgnoreType] +[case testClassIgnoreType_RedefinedAttributeAndGrandparentAttributeTypesNotIgnored] class A: x = 0 class B(A): @@ -4018,6 +4018,15 @@ class C(B): x = '' [out] +[case testClassIgnoreType_RedefinedAttributeTypeIgnoredInChildren] +class A: + x = 0 +class B(A): + x = '' # type: ignore +class C(B): + x = '' # type: ignore +[out] + [case testInvalidMetaclassStructure] class X(type): pass class Y(type): pass diff --git a/test-data/unit/check-multiple-inheritance.test b/test-data/unit/check-multiple-inheritance.test index 1bdc70c19fa1..d87b4a5c4819 100644 --- a/test-data/unit/check-multiple-inheritance.test +++ b/test-data/unit/check-multiple-inheritance.test @@ -228,7 +228,7 @@ class B(A, int): pass from typing import Callable, TypeVar T = TypeVar('T') class A(B, C): - def f(self): pass + pass class B: @dec def f(self): pass @@ -461,6 +461,121 @@ class Combo(Base2, Base1): ... [out] main:10: error: Definition of "NestedVar" in base class "Base2" is incompatible with definition in base class "Base1" +[case testMultipleInheritance_NestedVariableOverriddenWithCompatibleType] +from typing import TypeVar, Generic +T = TypeVar('T', covariant=True) +class GenericBase(Generic[T]): + pass +class Base1: + Nested: GenericBase['Base1'] +class Base2: + Nested: GenericBase['Base2'] +class A(Base1, Base2): + Nested: GenericBase['A'] +[out] + +[case testMultipleInheritance_NestedVariableOverriddenWithIncompatibleType1] +from typing import TypeVar, Generic +T = TypeVar('T', covariant=True) +class GenericBase(Generic[T]): + pass +class Base1: + Nested: GenericBase['Base1'] +class Base2: + Nested: GenericBase['Base2'] +class A(Base1, Base2): + Nested: GenericBase['Base1'] +[out] +main:10: error: Incompatible types in assignment (expression has type "GenericBase[Base1]", base class "Base2" defined the type as "GenericBase[Base2]") + +[case testMultipleInheritance_NestedVariableOverriddenWithIncompatibleType2] +from typing import TypeVar, Generic +T = TypeVar('T', covariant=True) +class GenericBase(Generic[T]): + pass +class Base1: + Nested: GenericBase['Base1'] +class Base2: + Nested: GenericBase['Base2'] +class A(Base1, Base2): + Nested: GenericBase['Base2'] +[out] +main:10: error: Incompatible types in assignment (expression has type "GenericBase[Base2]", base class "Base1" defined the type as "GenericBase[Base1]") + +[case testMultipleInheritance_NestedVariableOverriddenWithCompatibleType] +from typing import TypeVar, Generic +T = TypeVar('T', covariant=True) +class GenericBase(Generic[T]): + pass +class Base1: + Nested: GenericBase['Base1'] +class Base2: + Nested: GenericBase['Base1'] +class A(Base1, Base2): + Nested: GenericBase['Base1'] +[out] + +[case testMultipleInheritance_MethodDefinitionsCompatibleWithOverride] +from typing import TypeVar, Union +_T = TypeVar('_T') + +class Flag: + def __or__(self: _T, other: _T) -> _T: ... + +# int defines __or__ as: +# def __or__(self, n: int) -> int: ... +class IntFlag(int, Flag): + def __or__(self: _T, other: Union[int, _T]) -> _T: ... +[out] + +[case testMultipleInheritance_MethodDefinitionsIncompatibleOverride] +from typing import TypeVar, Union +_T = TypeVar('_T') + +class Flag: + def __or__(self: _T, other: _T) -> _T: ... + +class IntFlag(int, Flag): + def __or__(self: _T, other: str) -> _T: ... +[out] +main:8: error: Argument 1 of "__or__" incompatible with supertype "Flag" + +[case testMultipleInheritance_MethodDefinitionsCompatibleNoOverride] +from typing import TypeVar, Union +_T = TypeVar('_T') + +class Flag: + def __or__(self: _T, other: _T) -> _T: ... + +class IntFlag(int, Flag): + pass +[out] + +[case testMultipleInheritance_MethodsReturningSelfCompatible] +class A(object): + def x(self) -> 'A': + return self + +class B(object): + def x(self) -> 'B': + return self + +class C(A, B): + def x(self) -> 'C': + return self + +[case testMultipleInheritance_MethodsReturningSelfIncompatible] +class A(object): + def x(self) -> 'A': + return self + +class B(object): + def x(self) -> 'B': + return self + +class C(A, B): # E: Definition of "x" in base class "A" is incompatible with definition in base class "B" + pass + [case testNestedVariableRefersToSubclassOfAnotherNestedClass] class Mixin1: class Meta: