From 8eb437bc877211809c5e1e1523d59e3fbaf67543 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 22 Jun 2023 21:28:13 +0100 Subject: [PATCH 1/2] Correctly handle TypeOfAny.from_another_any --- mypy/checkexpr.py | 7 ++++++- mypy/test/testtypes.py | 6 ++++++ test-data/unit/check-flags.test | 12 ++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 43896171eadc8..162f070b6c559 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5363,7 +5363,12 @@ def __init__(self, ignore_in_type_obj: bool) -> None: self.ignore_in_type_obj = ignore_in_type_obj def visit_any(self, t: AnyType) -> bool: - return t.type_of_any != TypeOfAny.special_form # special forms are not real Any types + # Special forms are not real Any types (note that we don't need to recurse + # since AnyType constructor finds actual source). + return t.type_of_any != TypeOfAny.special_form and not ( + t.type_of_any == TypeOfAny.from_another_any + and t.source_any.type_of_any == TypeOfAny.special_form + ) def visit_callable_type(self, t: CallableType) -> bool: if self.ignore_in_type_obj and t.is_type_obj(): diff --git a/mypy/test/testtypes.py b/mypy/test/testtypes.py index 5f6943de3199d..664360b0c951c 100644 --- a/mypy/test/testtypes.py +++ b/mypy/test/testtypes.py @@ -6,6 +6,7 @@ from unittest import TestCase, skipUnless import mypy.expandtype +from mypy.checkexpr import has_any_type from mypy.erasetype import erase_type, remove_instance_last_known_values from mypy.expandtype import expand_type from mypy.indirection import TypeIndirectionVisitor @@ -64,6 +65,11 @@ def setUp(self) -> None: def test_any(self) -> None: assert_equal(str(AnyType(TypeOfAny.special_form)), "Any") + def test_has_any_special(self) -> None: + a = AnyType(TypeOfAny.special_form) + assert not has_any_type(a) + assert not has_any_type(AnyType(TypeOfAny.from_another_any, source_any=a)) + def test_simple_unbound_type(self) -> None: u = UnboundType("Foo") assert_equal(str(u), "Foo?") diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test index 6ec0849146c04..3c91bf3a8abc9 100644 --- a/test-data/unit/check-flags.test +++ b/test-data/unit/check-flags.test @@ -2195,3 +2195,15 @@ cb(lambda x: a) # OK fn = lambda x: a cb(fn) + +[case testDisallowAnyFromAnotherAny] +# flags: --disallow-any-expr +from typing import Tuple, TypeVar + +T = TypeVar("T") + +def f(x: T) -> Tuple[T, float]: ... + +def a() -> None: + _ = f(42) +[builtins fixtures/tuple.pyi] From bee47c627abe85383e81e068e5d409d04878cef8 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 22 Jun 2023 22:20:58 +0100 Subject: [PATCH 2/2] Fix self-check --- mypy/checkexpr.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mypy/checkexpr.py b/mypy/checkexpr.py index 162f070b6c559..f6352ae9868c5 100644 --- a/mypy/checkexpr.py +++ b/mypy/checkexpr.py @@ -5365,10 +5365,12 @@ def __init__(self, ignore_in_type_obj: bool) -> None: def visit_any(self, t: AnyType) -> bool: # Special forms are not real Any types (note that we don't need to recurse # since AnyType constructor finds actual source). - return t.type_of_any != TypeOfAny.special_form and not ( - t.type_of_any == TypeOfAny.from_another_any - and t.source_any.type_of_any == TypeOfAny.special_form - ) + if t.type_of_any == TypeOfAny.special_form: + return False + if t.type_of_any == TypeOfAny.from_another_any: + assert t.source_any is not None + return t.source_any.type_of_any != TypeOfAny.special_form + return True def visit_callable_type(self, t: CallableType) -> bool: if self.ignore_in_type_obj and t.is_type_obj():