|
45 | 45 | freeze_all_type_vars, |
46 | 46 | function_type, |
47 | 47 | get_all_type_vars, |
48 | | - get_type_vars, |
49 | 48 | make_simplified_union, |
50 | 49 | supported_self_type, |
51 | 50 | tuple_fallback, |
@@ -1192,31 +1191,36 @@ def analyze_class_attribute_access( |
1192 | 1191 |
|
1193 | 1192 | if isinstance(node.node, Var): |
1194 | 1193 | assert isuper is not None |
| 1194 | + object_type = get_proper_type(mx.self_type) |
1195 | 1195 | # Check if original variable type has type variables. For example: |
1196 | 1196 | # class C(Generic[T]): |
1197 | 1197 | # x: T |
1198 | 1198 | # C.x # Error, ambiguous access |
1199 | 1199 | # C[int].x # Also an error, since C[int] is same as C at runtime |
1200 | 1200 | # Exception is Self type wrapped in ClassVar, that is safe. |
| 1201 | + prohibit_self = not node.node.is_classvar |
1201 | 1202 | def_vars = set(node.node.info.defn.type_vars) |
1202 | | - if not node.node.is_classvar and node.node.info.self_type: |
| 1203 | + if prohibit_self and node.node.info.self_type: |
1203 | 1204 | def_vars.add(node.node.info.self_type) |
1204 | | - # TODO: should we include ParamSpec etc. here (i.e. use get_all_type_vars)? |
1205 | | - typ_vars = set(get_type_vars(t)) |
1206 | | - if def_vars & typ_vars: |
1207 | | - # Exception: access on Type[...], including first argument of class methods is OK. |
1208 | | - if not isinstance(get_proper_type(mx.original_type), TypeType) or node.implicit: |
1209 | | - if node.node.is_classvar: |
1210 | | - message = message_registry.GENERIC_CLASS_VAR_ACCESS |
1211 | | - else: |
1212 | | - message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS |
1213 | | - mx.fail(message) |
| 1205 | + # Exception: access on Type[...], including first argument of class methods is OK. |
| 1206 | + prohibit_generic = not isinstance(object_type, TypeType) or node.implicit |
| 1207 | + if prohibit_generic and def_vars & set(get_all_type_vars(t)): |
| 1208 | + if node.node.is_classvar: |
| 1209 | + message = message_registry.GENERIC_CLASS_VAR_ACCESS |
| 1210 | + else: |
| 1211 | + message = message_registry.GENERIC_INSTANCE_VAR_CLASS_ACCESS |
| 1212 | + mx.fail(message) |
1214 | 1213 | t = expand_self_type_if_needed(t, mx, node.node, itype, is_class=True) |
| 1214 | + t = expand_type_by_instance(t, isuper) |
1215 | 1215 | # Erase non-mapped variables, but keep mapped ones, even if there is an error. |
1216 | 1216 | # In the above example this means that we infer following types: |
1217 | 1217 | # C.x -> Any |
1218 | 1218 | # C[int].x -> int |
1219 | | - t = erase_typevars(expand_type_by_instance(t, isuper), {tv.id for tv in def_vars}) |
| 1219 | + if prohibit_generic: |
| 1220 | + erase_vars = set(itype.type.defn.type_vars) |
| 1221 | + if prohibit_self and itype.type.self_type: |
| 1222 | + erase_vars.add(itype.type.self_type) |
| 1223 | + t = erase_typevars(t, {tv.id for tv in erase_vars}) |
1220 | 1224 |
|
1221 | 1225 | is_classmethod = (is_decorated and cast(Decorator, node.node).func.is_class) or ( |
1222 | 1226 | isinstance(node.node, SYMBOL_FUNCBASE_TYPES) and node.node.is_class |
|
0 commit comments