|
75 | 75 | BoolTypeQuery, |
76 | 76 | CallableArgument, |
77 | 77 | CallableType, |
| 78 | + CollectAliasesVisitor, |
78 | 79 | DeletedType, |
79 | 80 | EllipsisType, |
80 | 81 | ErasedType, |
@@ -275,7 +276,9 @@ def __init__( |
275 | 276 | self.prohibit_special_class_field_types = prohibit_special_class_field_types |
276 | 277 | # Allow variables typed as Type[Any] and type (useful for base classes). |
277 | 278 | self.allow_type_any = allow_type_any |
278 | | - self.allow_type_var_tuple = False |
| 279 | + # Level of nesting at which a TypeVarTuple is allowed. Note we specify exact level |
| 280 | + # to prohibit things like Unpack[list[Ts]], which are not supported. |
| 281 | + self.allow_type_var_tuple = -1 |
279 | 282 | self.allow_unpack = allow_unpack |
280 | 283 | # Set when we are analyzing a default of a type variable. |
281 | 284 | self.analyzing_tvar_def = analyzing_tvar_def |
@@ -453,7 +456,7 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool) |
453 | 456 | self.fail(msg, t, code=codes.VALID_TYPE) |
454 | 457 | return AnyType(TypeOfAny.from_error) |
455 | 458 | assert isinstance(tvar_def, TypeVarTupleType) |
456 | | - if not self.allow_type_var_tuple: |
| 459 | + if self.allow_type_var_tuple != self.nesting_level: |
457 | 460 | self.fail( |
458 | 461 | f'TypeVarTuple "{t.name}" is only valid with an unpack', |
459 | 462 | t, |
@@ -808,9 +811,9 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ |
808 | 811 | if not self.allow_unpack: |
809 | 812 | self.fail(message_registry.INVALID_UNPACK_POSITION, t, code=codes.VALID_TYPE) |
810 | 813 | return AnyType(TypeOfAny.from_error) |
811 | | - self.allow_type_var_tuple = True |
| 814 | + self.allow_type_var_tuple = self.nesting_level + 1 |
812 | 815 | result = UnpackType(self.anal_type(t.args[0]), line=t.line, column=t.column) |
813 | | - self.allow_type_var_tuple = False |
| 816 | + self.allow_type_var_tuple = -1 |
814 | 817 | return result |
815 | 818 | elif fullname in SELF_TYPE_NAMES: |
816 | 819 | if t.args: |
@@ -1161,9 +1164,9 @@ def visit_unpack_type(self, t: UnpackType) -> Type: |
1161 | 1164 | if not self.allow_unpack: |
1162 | 1165 | self.fail(message_registry.INVALID_UNPACK_POSITION, t.type, code=codes.VALID_TYPE) |
1163 | 1166 | return AnyType(TypeOfAny.from_error) |
1164 | | - self.allow_type_var_tuple = True |
| 1167 | + self.allow_type_var_tuple = self.nesting_level + 1 |
1165 | 1168 | result = UnpackType(self.anal_type(t.type), from_star_syntax=t.from_star_syntax) |
1166 | | - self.allow_type_var_tuple = False |
| 1169 | + self.allow_type_var_tuple = -1 |
1167 | 1170 | return result |
1168 | 1171 |
|
1169 | 1172 | def visit_parameters(self, t: Parameters) -> Type: |
@@ -2523,6 +2526,15 @@ def detect_diverging_alias(node: TypeAlias, target: Type) -> bool: |
2523 | 2526 | They may be handy in rare cases, e.g. to express a union of non-mixed nested lists: |
2524 | 2527 | Nested = Union[T, Nested[List[T]]] ~> Union[T, List[T], List[List[T]], ...] |
2525 | 2528 | """ |
| 2529 | + is_recursive = node._is_recursive |
| 2530 | + if is_recursive is None: |
| 2531 | + is_recursive = node in node.target.accept(CollectAliasesVisitor()) |
| 2532 | + if not is_recursive: |
| 2533 | + # Fast path: this is not a recursive alias at all. |
| 2534 | + return False |
| 2535 | + # Note we only cache positive case, caching negative case is risky, as this type alias |
| 2536 | + # (or more importantly any other alias it uses) may be not ready yet. |
| 2537 | + node._is_recursive = True |
2526 | 2538 | visitor = DivergingAliasDetector({node}) |
2527 | 2539 | _ = target.accept(visitor) |
2528 | 2540 | return visitor.diverging |
|
0 commit comments