From 6cd8e007923acef7a2899e85378bc4822472b848 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 19 Nov 2022 02:00:33 +0000 Subject: [PATCH] Fix type query for recursive aliases (#14136) See https://github.com/python/mypy/pull/14130 for context. Btw it looks like these `Any` reports are quite broken in general. Some issues I found: * Many types are reported twice (even non-recursive) * Explicit `Any` in alias r.h.s are not counted (because of reckless `res = make_any_non_explicit(res)` in semanal.py) * For generic aliases we count their r.h.s. as containing `Any` from omitted generics I tried to fix these things, but it is not trivial, so maybe we can do it later in a separate PR. --- mypy/type_visitor.py | 24 ++++++++---------------- mypy/typeanal.py | 2 +- test-data/unit/reports.test | 24 +++++++++++++++++++++++- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/mypy/type_visitor.py b/mypy/type_visitor.py index fe404cda0bec..0f5ac05e68ac 100644 --- a/mypy/type_visitor.py +++ b/mypy/type_visitor.py @@ -404,24 +404,16 @@ def visit_placeholder_type(self, t: PlaceholderType) -> T: return self.query_types(t.args) def visit_type_alias_type(self, t: TypeAliasType) -> T: + # Skip type aliases already visited types to avoid infinite recursion. + # TODO: Ideally we should fire subvisitors here (or use caching) if we care + # about duplicates. + if t in self.seen_aliases: + return self.strategy([]) + self.seen_aliases.add(t) if self.skip_alias_target: return self.query_types(t.args) return get_proper_type(t).accept(self) def query_types(self, types: Iterable[Type]) -> T: - """Perform a query for a list of types. - - Use the strategy to combine the results. - Skip type aliases already visited types to avoid infinite recursion. - """ - res: list[T] = [] - for t in types: - if isinstance(t, TypeAliasType): - # Avoid infinite recursion for recursive type aliases. - # TODO: Ideally we should fire subvisitors here (or use caching) if we care - # about duplicates. - if t in self.seen_aliases: - continue - self.seen_aliases.add(t) - res.append(t.accept(self)) - return self.strategy(res) + """Perform a query for a list of types using the strategy to combine the results.""" + return self.strategy([t.accept(self) for t in types]) diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 18a63011c5bf..0dc1717d0724 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -450,7 +450,7 @@ def try_analyze_special_unbound_type(self, t: UnboundType, fullname: str) -> Typ if fullname == "builtins.None": return NoneType() elif fullname == "typing.Any" or fullname == "builtins.Any": - return AnyType(TypeOfAny.explicit) + return AnyType(TypeOfAny.explicit, line=t.line, column=t.column) elif fullname in FINAL_TYPE_NAMES: self.fail( "Final can be only used as an outermost qualifier in a variable annotation", diff --git a/test-data/unit/reports.test b/test-data/unit/reports.test index a7ab6d754b2c..50dabb1fdea9 100644 --- a/test-data/unit/reports.test +++ b/test-data/unit/reports.test @@ -103,6 +103,28 @@ class A(object): +[case testNoCrashRecursiveAliasInReport] +# cmd: mypy --any-exprs-report report n.py + +[file n.py] +from typing import Union, List, Any, TypeVar + +Nested = List[Union[Any, Nested]] +T = TypeVar("T") +NestedGen = List[Union[T, NestedGen[T]]] + +x: Nested +y: NestedGen[int] +z: NestedGen[Any] + +[file report/any-exprs.txt] +[outfile report/types-of-anys.txt] + Name Unannotated Explicit Unimported Omitted Generics Error Special Form Implementation Artifact +----------------------------------------------------------------------------------------------------------------- + n 0 4 0 8 0 0 0 +----------------------------------------------------------------------------------------------------------------- +Total 0 4 0 8 0 0 0 + [case testTypeVarTreatedAsEmptyLine] # cmd: mypy --html-report report n.py @@ -480,7 +502,7 @@ namespace_packages = True -

folder.subfolder.something

+

folder.subfolder.something

folder/subfolder/something.py