|
24 | 24 | YieldFromExpr, NamedTupleExpr, TypedDictExpr, SetComprehension,
|
25 | 25 | DictionaryComprehension, ComplexExpr, EllipsisExpr, TypeAliasExpr,
|
26 | 26 | RefExpr, YieldExpr, BackquoteExpr, ImportFrom, ImportAll, ImportBase,
|
27 |
| - AwaitExpr, PromoteExpr, |
28 |
| - ARG_POS, |
| 27 | + AwaitExpr, PromoteExpr, Node, |
| 28 | + ARG_POS, MDEF, |
29 | 29 | CONTRAVARIANT, COVARIANT)
|
30 | 30 | from mypy import nodes
|
31 | 31 | from mypy.types import (
|
|
44 | 44 | restrict_subtype_away, is_subtype_ignoring_tvars
|
45 | 45 | )
|
46 | 46 | from mypy.maptype import map_instance_to_supertype
|
47 |
| -from mypy.semanal import fill_typevars, set_callable_name, refers_to_fullname |
| 47 | +from mypy.typevars import fill_typevars, has_no_typevars |
| 48 | +from mypy.semanal import set_callable_name, refers_to_fullname |
48 | 49 | from mypy.erasetype import erase_typevars
|
49 | 50 | from mypy.expandtype import expand_type, expand_type_by_instance
|
50 | 51 | from mypy.visitor import NodeVisitor
|
@@ -1102,6 +1103,12 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type
|
1102 | 1103 | infer_lvalue_type)
|
1103 | 1104 | else:
|
1104 | 1105 | lvalue_type, index_lvalue, inferred = self.check_lvalue(lvalue)
|
| 1106 | + |
| 1107 | + if isinstance(lvalue, NameExpr): |
| 1108 | + if self.check_compatibility_all_supers(lvalue, lvalue_type, rvalue): |
| 1109 | + # We hit an error on this line; don't check for any others |
| 1110 | + return |
| 1111 | + |
1105 | 1112 | if lvalue_type:
|
1106 | 1113 | if isinstance(lvalue_type, PartialType) and lvalue_type.type is None:
|
1107 | 1114 | # Try to infer a proper type for a variable with a partial None type.
|
@@ -1156,6 +1163,123 @@ def check_assignment(self, lvalue: Lvalue, rvalue: Expression, infer_lvalue_type
|
1156 | 1163 | self.infer_variable_type(inferred, lvalue, self.accept(rvalue),
|
1157 | 1164 | rvalue)
|
1158 | 1165 |
|
| 1166 | + def check_compatibility_all_supers(self, lvalue: NameExpr, lvalue_type: Type, |
| 1167 | + rvalue: Expression) -> bool: |
| 1168 | + lvalue_node = lvalue.node |
| 1169 | + |
| 1170 | + # Check if we are a class variable with at least one base class |
| 1171 | + if (isinstance(lvalue_node, Var) and |
| 1172 | + lvalue.kind == MDEF and |
| 1173 | + len(lvalue_node.info.bases) > 0): |
| 1174 | + |
| 1175 | + for base in lvalue_node.info.mro[1:]: |
| 1176 | + # Only check __slots__ against the 'object' |
| 1177 | + # If a base class defines a Tuple of 3 elements, a child of |
| 1178 | + # this class should not be allowed to define it as a Tuple of |
| 1179 | + # anything other than 3 elements. The exception to this rule |
| 1180 | + # is __slots__, where it is allowed for any child class to |
| 1181 | + # redefine it. |
| 1182 | + if lvalue_node.name() == "__slots__" and base.fullname() != "builtins.object": |
| 1183 | + continue |
| 1184 | + |
| 1185 | + base_type, base_node = self.lvalue_type_from_base(lvalue_node, base) |
| 1186 | + |
| 1187 | + if base_type: |
| 1188 | + if not self.check_compatibility_super(lvalue, |
| 1189 | + lvalue_type, |
| 1190 | + rvalue, |
| 1191 | + base, |
| 1192 | + base_type, |
| 1193 | + base_node): |
| 1194 | + # Only show one error per variable; even if other |
| 1195 | + # base classes are also incompatible |
| 1196 | + return True |
| 1197 | + break |
| 1198 | + return False |
| 1199 | + |
| 1200 | + def check_compatibility_super(self, lvalue: NameExpr, lvalue_type: Type, rvalue: Expression, |
| 1201 | + base: TypeInfo, base_type: Type, base_node: Node) -> bool: |
| 1202 | + lvalue_node = lvalue.node |
| 1203 | + assert isinstance(lvalue_node, Var) |
| 1204 | + |
| 1205 | + # Do not check whether the rvalue is compatible if the |
| 1206 | + # lvalue had a type defined; this is handled by other |
| 1207 | + # parts, and all we have to worry about in that case is |
| 1208 | + # that lvalue is compatible with the base class. |
| 1209 | + compare_node = None # type: Node |
| 1210 | + if lvalue_type: |
| 1211 | + compare_type = lvalue_type |
| 1212 | + compare_node = lvalue.node |
| 1213 | + else: |
| 1214 | + compare_type = self.accept(rvalue, base_type) |
| 1215 | + if isinstance(rvalue, NameExpr): |
| 1216 | + compare_node = rvalue.node |
| 1217 | + if isinstance(compare_node, Decorator): |
| 1218 | + compare_node = compare_node.func |
| 1219 | + |
| 1220 | + if compare_type: |
| 1221 | + if (isinstance(base_type, CallableType) and |
| 1222 | + isinstance(compare_type, CallableType)): |
| 1223 | + base_static = is_node_static(base_node) |
| 1224 | + compare_static = is_node_static(compare_node) |
| 1225 | + |
| 1226 | + # In case compare_static is unknown, also check |
| 1227 | + # if 'definition' is set. The most common case for |
| 1228 | + # this is with TempNode(), where we lose all |
| 1229 | + # information about the real rvalue node (but only get |
| 1230 | + # the rvalue type) |
| 1231 | + if compare_static is None and compare_type.definition: |
| 1232 | + compare_static = is_node_static(compare_type.definition) |
| 1233 | + |
| 1234 | + # Compare against False, as is_node_static can return None |
| 1235 | + if base_static is False and compare_static is False: |
| 1236 | + # Class-level function objects and classmethods become bound |
| 1237 | + # methods: the former to the instance, the latter to the |
| 1238 | + # class |
| 1239 | + base_type = bind_self(base_type, self.scope.active_class()) |
| 1240 | + compare_type = bind_self(compare_type, self.scope.active_class()) |
| 1241 | + |
| 1242 | + # If we are a static method, ensure to also tell the |
| 1243 | + # lvalue it now contains a static method |
| 1244 | + if base_static and compare_static: |
| 1245 | + lvalue_node.is_staticmethod = True |
| 1246 | + |
| 1247 | + return self.check_subtype(compare_type, base_type, lvalue, |
| 1248 | + messages.INCOMPATIBLE_TYPES_IN_ASSIGNMENT, |
| 1249 | + 'expression has type', |
| 1250 | + 'base class "%s" defined the type as' % base.name()) |
| 1251 | + return True |
| 1252 | + |
| 1253 | + def lvalue_type_from_base(self, expr_node: Var, |
| 1254 | + base: TypeInfo) -> Tuple[Optional[Type], Optional[Node]]: |
| 1255 | + """For a NameExpr that is part of a class, walk all base classes and try |
| 1256 | + to find the first class that defines a Type for the same name.""" |
| 1257 | + expr_name = expr_node.name() |
| 1258 | + base_var = base.names.get(expr_name) |
| 1259 | + |
| 1260 | + if base_var: |
| 1261 | + base_node = base_var.node |
| 1262 | + base_type = base_var.type |
| 1263 | + if isinstance(base_node, Decorator): |
| 1264 | + base_node = base_node.func |
| 1265 | + base_type = base_node.type |
| 1266 | + |
| 1267 | + if base_type: |
| 1268 | + if not has_no_typevars(base_type): |
| 1269 | + instance = cast(Instance, self.scope.active_class()) |
| 1270 | + itype = map_instance_to_supertype(instance, base) |
| 1271 | + base_type = expand_type_by_instance(base_type, itype) |
| 1272 | + |
| 1273 | + if isinstance(base_type, CallableType) and isinstance(base_node, FuncDef): |
| 1274 | + # If we are a property, return the Type of the return |
| 1275 | + # value, not the Callable |
| 1276 | + if base_node.is_property: |
| 1277 | + base_type = base_type.ret_type |
| 1278 | + |
| 1279 | + return base_type, base_node |
| 1280 | + |
| 1281 | + return None, None |
| 1282 | + |
1159 | 1283 | def check_assignment_to_multiple_lvalues(self, lvalues: List[Lvalue], rvalue: Expression,
|
1160 | 1284 | context: Context,
|
1161 | 1285 | infer_lvalue_type: bool = True) -> None:
|
@@ -2875,6 +2999,18 @@ def is_valid_inferred_type_component(typ: Type) -> bool:
|
2875 | 2999 | return True
|
2876 | 3000 |
|
2877 | 3001 |
|
| 3002 | +def is_node_static(node: Node) -> Optional[bool]: |
| 3003 | + """Find out if a node describes a static function method.""" |
| 3004 | + |
| 3005 | + if isinstance(node, FuncDef): |
| 3006 | + return node.is_static |
| 3007 | + |
| 3008 | + if isinstance(node, Var): |
| 3009 | + return node.is_staticmethod |
| 3010 | + |
| 3011 | + return None |
| 3012 | + |
| 3013 | + |
2878 | 3014 | class Scope:
|
2879 | 3015 | # We keep two stacks combined, to maintain the relative order
|
2880 | 3016 | stack = None # type: List[Union[Type, FuncItem, MypyFile]]
|
|
0 commit comments