diff --git a/mypy/types.py b/mypy/types.py index f9d5d3c23a34..c24428b3aad7 100644 --- a/mypy/types.py +++ b/mypy/types.py @@ -1915,6 +1915,7 @@ class TypeQuery(SyntheticTypeVisitor[T]): def __init__(self, strategy: Callable[[Iterable[T]], T]) -> None: self.strategy = strategy + self.seen = [] # type: List[Type] def visit_unbound_type(self, t: UnboundType) -> T: return self.query_types(t.args) @@ -1984,8 +1985,17 @@ def query_types(self, types: Iterable[Type]) -> T: """Perform a query for a list of types. Use the strategy to combine the results. + Skip types already visited types to avoid infinite recursion. + Note: types can be recursive until they are fully analyzed and "unentangled" + in patches after the semantic analysis. """ - return self.strategy(t.accept(self) for t in types) + res = [] # type: List[T] + for t in types: + if any(t is s for s in self.seen): + continue + self.seen.append(t) + res.append(t.accept(self)) + return self.strategy(res) def strip_type(typ: Type) -> Type: diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 4520fd3a5d71..2892ff8c5db9 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -1059,3 +1059,29 @@ ignore_missing_imports = True always_true = YOLO1, YOLO always_false = BLAH, BLAH1 [builtins fixtures/bool.pyi] + +[case testCheckDisallowAnyGenericsNamedTuple] +# flags: --disallow-any-generics +from typing import NamedTuple + +N = NamedTuple('N', [('x', N)]) # type: ignore +n: N +[out] + +[case testCheckDisallowAnyGenericsTypedDict] +# flags: --disallow-any-generics +from typing import Dict, Any, Optional +from mypy_extensions import TypedDict + +VarsDict = Dict[str, Any] +HostsDict = Dict[str, Optional[VarsDict]] + +GroupDataDict = TypedDict( # type: ignore + "GroupDataDict", {"children": "GroupsDict", + "vars": VarsDict, + "hosts": HostsDict}, total=False +) + +GroupsDict = Dict[str, GroupDataDict] # type: ignore +[builtins fixtures/dict.pyi] +[out] diff --git a/test-data/unit/fixtures/dict.pyi b/test-data/unit/fixtures/dict.pyi index 13530501a119..93648b274d98 100644 --- a/test-data/unit/fixtures/dict.pyi +++ b/test-data/unit/fixtures/dict.pyi @@ -46,5 +46,5 @@ class float: pass class bool: pass class ellipsis: pass -def isinstance(x: object, t: Union[type, Tuple]) -> bool: pass +def isinstance(x: object, t: Union[type, Tuple[type, ...]]) -> bool: pass class BaseException: pass