@@ -1605,7 +1605,7 @@ def check_multiple_inheritance(self, typ: TypeInfo) -> None:
1605
1605
if name in base2 .names and base2 not in base .mro :
1606
1606
self .check_compatibility (name , base , base2 , typ )
1607
1607
1608
- def _determine_type_of_class_member (self , sym : SymbolTableNode ) -> Optional [Type ]:
1608
+ def determine_type_of_class_member (self , sym : SymbolTableNode ) -> Optional [Type ]:
1609
1609
if sym .type is not None :
1610
1610
return sym .type
1611
1611
if isinstance (sym .node , FuncBase ):
@@ -1629,18 +1629,41 @@ def check_compatibility(self, name: str, base1: TypeInfo,
1629
1629
first = base1 [name ]
1630
1630
second = base2 [name ]
1631
1631
if isinstance (first .node , TypeInfo ) and isinstance (second .node , TypeInfo ):
1632
- # allow nested classes with the same name
1632
+ # Checking compatibility of two nested classes with the same name is disabled.
1633
+ # It creates a lot of false positives in frameworks like Django, DRF and others,
1634
+ # where nested classes is used to define custom properties for the outer class, like
1635
+ # class MyModel:
1636
+ # class Meta:
1637
+ # abstract = True
1638
+ #
1639
+ # This is technically unsafe, it create false-negatives in cases like
1640
+ # class Mixin1:
1641
+ # class Meta:
1642
+ # def get() -> int: pass
1643
+ # class Mixin2:
1644
+ # class Meta:
1645
+ # def get() -> str: pass
1646
+ # class A(Mixin2, Mixin1):
1647
+ # pass
1648
+ # var: Mixin1 = A(); var.Meta.get() # return type will be "str"
1633
1649
return
1634
- first_type = self ._determine_type_of_class_member (first )
1635
- second_type = self ._determine_type_of_class_member (second )
1650
+ first_type = self .determine_type_of_class_member (first )
1651
+ second_type = self .determine_type_of_class_member (second )
1636
1652
1637
1653
# TODO: What if some classes are generic?
1638
1654
if (isinstance (first_type , FunctionLike ) and
1639
1655
isinstance (second_type , FunctionLike )):
1640
- # Method override
1641
- first_sig = bind_self (first_type )
1642
- second_sig = bind_self (second_type )
1643
- ok = is_subtype (first_sig , second_sig , ignore_pos_arg_names = True )
1656
+ if ((isinstance (first_type , CallableType )
1657
+ and first_type .fallback .type .fullname () == 'builtins.type' )
1658
+ and (isinstance (second_type , CallableType )
1659
+ and second_type .fallback .type .fullname () == 'builtins.type' )):
1660
+ # Both members are classes (not necessary nested), check if compatible
1661
+ ok = is_subtype (first_type .ret_type , second_type .ret_type )
1662
+ else :
1663
+ # Method override
1664
+ first_sig = bind_self (first_type )
1665
+ second_sig = bind_self (second_type )
1666
+ ok = is_subtype (first_sig , second_sig , ignore_pos_arg_names = True )
1644
1667
elif first_type and second_type :
1645
1668
ok = is_equivalent (first_type , second_type )
1646
1669
else :
0 commit comments