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