Skip to content

Commit 8949bcc

Browse files
committed
Fix special form in nested
1 parent dc9fd24 commit 8949bcc

7 files changed

Lines changed: 74 additions & 128 deletions

File tree

mypy/checkexpr.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2994,7 +2994,7 @@ def infer_overload_return_type(
29942994
matches: list[CallableType] = []
29952995
return_types: list[Type] = []
29962996
inferred_types: list[Type] = []
2997-
args_contain_any = any(map(has_any_type, arg_types))
2997+
args_contain_any = any(has_any_type_for_overload(arg_type) for arg_type in arg_types)
29982998
type_maps: list[dict[Expression, Type]] = []
29992999

30003000
for typ in plausible_targets:
@@ -6597,13 +6597,29 @@ def has_any_type(t: Type, ignore_in_type_obj: bool = False) -> bool:
65976597
return t.accept(HasAnyType(ignore_in_type_obj))
65986598

65996599

6600+
def has_any_type_for_overload(t: Type, ignore_in_type_obj: bool = False) -> bool:
6601+
"""Like has_any_type, but also counts a top-level alias-to-Any.
6602+
6603+
has_any_type ignores TypeOfAny.from_alias_target so use sites don't trigger
6604+
--disallow-any-explicit, but for overload ambiguity we want an actual argument
6605+
that *is* an alias-to-Any to count (without making nested alias-to-Any count).
6606+
"""
6607+
proper_t = get_proper_type(t)
6608+
if isinstance(proper_t, AnyType) and proper_t.type_of_any == TypeOfAny.from_alias_target:
6609+
return True
6610+
return has_any_type(t, ignore_in_type_obj=ignore_in_type_obj)
6611+
6612+
66006613
class HasAnyType(types.BoolTypeQuery):
66016614
def __init__(self, ignore_in_type_obj: bool) -> None:
66026615
super().__init__(types.ANY_STRATEGY)
66036616
self.ignore_in_type_obj = ignore_in_type_obj
66046617

66056618
def visit_any(self, t: AnyType) -> bool:
6606-
return t.type_of_any != TypeOfAny.special_form # special forms are not real Any types
6619+
return t.type_of_any not in (
6620+
TypeOfAny.special_form,
6621+
TypeOfAny.from_alias_target,
6622+
)
66076623

66086624
def visit_callable_type(self, t: CallableType) -> bool:
66096625
if self.ignore_in_type_obj and t.is_type_obj():
@@ -6842,7 +6858,7 @@ def any_causes_overload_ambiguity(
68426858
# We ignore Anys in type object callables as ambiguity
68436859
# creators, since that can lead to falsely claiming ambiguity
68446860
# for overloads between Type and Callable.
6845-
if has_any_type(arg_type, ignore_in_type_obj=True):
6861+
if has_any_type_for_overload(arg_type, ignore_in_type_obj=True):
68466862
matching_formals_unfiltered = [
68476863
(item_idx, lookup[arg_idx])
68486864
for item_idx, lookup in enumerate(actual_to_formal)

mypy/semanal.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8263,13 +8263,19 @@ def remove_imported_names_from_symtable(names: SymbolTable, module: str) -> None
82638263

82648264
def make_any_non_explicit(t: Type) -> Type:
82658265
"""Replace all Any types within in with Any that has attribute 'explicit' set to False"""
8266+
# An alias whose target is exactly Any should still behave like a real Any when used.
8267+
# Explicit Any nested inside a larger alias target is kept as the historical
8268+
# non-real Any flavor to avoid making large generic aliases overload-ambiguous.
8269+
proper_t = get_proper_type(t)
8270+
if isinstance(proper_t, AnyType) and proper_t.type_of_any == TypeOfAny.explicit:
8271+
return proper_t.copy_modified(TypeOfAny.from_alias_target)
82668272
return t.accept(MakeAnyNonExplicit())
82678273

82688274

82698275
class MakeAnyNonExplicit(TrivialSyntheticTypeTranslator):
82708276
def visit_any(self, t: AnyType) -> Type:
82718277
if t.type_of_any == TypeOfAny.explicit:
8272-
return t.copy_modified(TypeOfAny.from_alias_target)
8278+
return t.copy_modified(TypeOfAny.special_form)
82738279
return t
82748280

82758281
def visit_type_alias_type(self, t: TypeAliasType) -> Type:

mypy/types.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,9 @@ class TypeOfAny:
238238
# generating constraints.
239239
suggestion_engine: Final = 9
240240
# Does this Any come from a type alias whose target is Any (e.g.
241-
# `Incomplete: TypeAlias = Any`)? It is a real Any (unlike special_form),
242-
# but tagged separately so use sites don't trigger --disallow-any-explicit.
241+
# `Incomplete: TypeAlias = Any`)? This is treated as a real Any for overload
242+
# ambiguity when it appears as an actual argument, but is otherwise ignored like
243+
# special_form to avoid triggering use-site explicit-Any errors.
243244
from_alias_target: Final = 10
244245

245246

test-data/unit/check-flags.test

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1381,6 +1381,22 @@ def h(i: int) -> None: # E: Type of decorated function contains type "Any" ("Ca
13811381
pass
13821382
[builtins fixtures/list.pyi]
13831383

1384+
[case testDisallowAnyDecoratedAliasToAny]
1385+
# flags: --disallow-any-decorated
1386+
from typing import Any, Callable, TypeVar
1387+
from typing_extensions import TypeAlias
1388+
1389+
F = TypeVar("F", bound=Callable[..., Any])
1390+
AddressFormat: TypeAlias = Any
1391+
1392+
def d(f: F) -> F:
1393+
return f
1394+
1395+
@d
1396+
def f(x: AddressFormat | None = None) -> int:
1397+
return 0
1398+
[builtins fixtures/list.pyi]
1399+
13841400
[case testDisallowAnyDecoratedReturnsCallableNoParams]
13851401
# flags: --disallow-any-decorated
13861402
from typing import Callable

test-data/unit/check-literal.test

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,6 @@ reveal_type(b) # N: Revealed type is "Any"
563563
[out]
564564

565565
[case testLiteralAliasToAnyAllowed]
566-
# Aliases to Any (e.g. typeshed's `Incomplete: TypeAlias = Any`) must not
567-
# trigger the Literal[...] cannot-be-Any error, matching the behavior of
568-
# unfollowed-import aliases.
569566
from typing import Any, Literal
570567
from typing_extensions import TypeAlias
571568

test-data/unit/check-overloading.test

Lines changed: 29 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -6857,10 +6857,9 @@ if isinstance(headers, dict):
68576857
reveal_type(headers) # N: Revealed type is "__main__.Headers | typing.Iterable[tuple[builtins.bytes, builtins.bytes]]"
68586858
[builtins fixtures/isinstancelist.pyi]
68596859

6860-
[case testOverloadAliasToAnyArgWithNoneDefault]
6861-
# An alias `Incomplete = Any` used as an overload arg must not silently match
6862-
# the `default: None = None` overload. It should be treated as Any like a bare
6863-
# Any value, triggering the overload-ambiguity fallback.
6860+
[case testOverloadAliasToAnyArg]
6861+
# An alias `Incomplete = Any` used as an overload arg should behave like bare
6862+
# Any (engage the ambiguity fallback), not silently match a None-default overload.
68646863
from typing import Any, TypeVar, overload
68656864
from typing_extensions import TypeAlias
68666865

@@ -6880,120 +6879,48 @@ a: Incomplete
68806879
b: Any
68816880
reveal_type(M().get("k", a)) # N: Revealed type is "Any"
68826881
reveal_type(M().get("k", b)) # N: Revealed type is "Any"
6883-
[builtins fixtures/tuple.pyi]
6884-
6885-
[case testOverloadAliasToAnyArgEquivalentToBareAny]
6886-
# Alias-to-Any and bare Any should produce the same overload results.
6887-
from typing import Any, overload
6888-
from typing_extensions import TypeAlias
6889-
6890-
Incomplete: TypeAlias = Any
68916882

6883+
# Unambiguous overload: alias-Any picks the matching return type, no false ambiguity.
68926884
@overload
6893-
def f(x: int) -> int: ...
6885+
def f(x: int, y: int) -> int: ...
68946886
@overload
6895-
def f(x: str) -> str: ...
6896-
def f(x): ...
6887+
def f(x: object, y: int) -> object: ...
6888+
def f(x, y): ...
68976889

6898-
a: Incomplete
6899-
b: Any
6900-
reveal_type(f(a)) # N: Revealed type is "Any"
6901-
reveal_type(f(b)) # N: Revealed type is "Any"
6890+
reveal_type(f(1, a)) # N: Revealed type is "builtins.int"
69026891
[builtins fixtures/tuple.pyi]
69036892

6904-
[case testOverloadAliasToAnyArgChainedAlias]
6905-
# A chained alias `Outer = Inner` where `Inner = Any` should still behave
6906-
# like Any, both at the inner and outer level.
6907-
from typing import Any, overload
6893+
[case testAliasToAnyDoesNotTriggerDisallowAnyExplicitAtUseSite]
6894+
# flags: --disallow-any-explicit --show-error-codes
6895+
from typing import Any
69086896
from typing_extensions import TypeAlias
69096897

6910-
Inner: TypeAlias = Any
6911-
Outer: TypeAlias = Inner
6912-
6913-
@overload
6914-
def f(x: int) -> int: ...
6915-
@overload
6916-
def f(x: str) -> str: ...
6917-
def f(x): ...
6918-
6919-
inner: Inner
6920-
outer: Outer
6921-
reveal_type(f(inner)) # N: Revealed type is "Any"
6922-
reveal_type(f(outer)) # N: Revealed type is "Any"
6923-
[builtins fixtures/tuple.pyi]
6924-
6925-
[case testOverloadAliasToAnyArgImplicitAlias]
6926-
# `MyAlias = Any` without an explicit TypeAlias annotation also gets the
6927-
# alias-Any treatment and must engage the overload-ambiguity check.
6928-
from typing import Any, overload
6929-
6930-
MyAlias = Any
6931-
6932-
@overload
6933-
def f(x: int) -> int: ...
6934-
@overload
6935-
def f(x: str) -> str: ...
6936-
def f(x): ...
6937-
6938-
a: MyAlias
6939-
reveal_type(f(a)) # N: Revealed type is "Any"
6940-
[builtins fixtures/tuple.pyi]
6941-
6942-
[case testOverloadAliasToAnyArgImported]
6943-
# Alias-to-Any imported from another module must behave the same way.
6944-
from typing import overload
6945-
from other import Incomplete
6946-
6947-
@overload
6948-
def f(x: int) -> int: ...
6949-
@overload
6950-
def f(x: str) -> str: ...
6951-
def f(x): ...
6898+
Incomplete: TypeAlias = Any # E: Explicit "Any" is not allowed [explicit-any]
69526899

6953-
a: Incomplete
6954-
reveal_type(f(a)) # N: Revealed type is "Any"
6900+
def f(x: Incomplete) -> Incomplete: # no error
6901+
return x
69556902

6956-
[file other.py]
6957-
from typing import Any
6958-
from typing_extensions import TypeAlias
6959-
Incomplete: TypeAlias = Any
6903+
a: Incomplete = 1 # no error
69606904
[builtins fixtures/tuple.pyi]
69616905

6962-
[case testOverloadAliasToAnyArgUnambiguous]
6963-
# When overloads are unambiguous, alias-to-Any should pick that overload's
6964-
# return type just like bare Any does (no false ambiguity).
6965-
from typing import Any, overload
6906+
[case testOverloadNestedAnyDoesNotTriggerAmbiguity]
6907+
# Any (explicit or alias-to-Any) nested inside a generic argument should not
6908+
# trigger the overload-ambiguity fallback — only a top-level Any actual arg counts.
6909+
from typing import Any, Generic, TypeVar, overload
69666910
from typing_extensions import TypeAlias
69676911

6912+
T = TypeVar("T")
69686913
Incomplete: TypeAlias = Any
69696914

6970-
@overload
6971-
def f(x: int, y: int, z: str) -> int: ...
6972-
@overload
6973-
def f(x: object, y: int, z: str) -> object: ...
6974-
def f(x, y, z): ...
6975-
6976-
a: Incomplete
6977-
b: Any
6978-
# Ambiguous on x: returns Any
6979-
reveal_type(f(a, 1, '')) # N: Revealed type is "Any"
6980-
reveal_type(f(b, 1, '')) # N: Revealed type is "Any"
6981-
# Unambiguous: x is concrete, only y/z are Any
6982-
reveal_type(f(1, a, '')) # N: Revealed type is "builtins.int"
6983-
reveal_type(f(1, b, '')) # N: Revealed type is "builtins.int"
6984-
[builtins fixtures/tuple.pyi]
6915+
class Array(Generic[T]): pass
69856916

6986-
[case testAliasToAnyDoesNotTriggerDisallowAnyExplicitAtUseSite]
6987-
# --disallow-any-explicit must NOT fire at use sites of an `Alias = Any`
6988-
# (the original purpose of make_any_non_explicit).
6989-
# flags: --disallow-any-explicit --show-error-codes
6990-
from typing import Any
6991-
from typing_extensions import TypeAlias
6992-
6993-
Incomplete: TypeAlias = Any # E: Explicit "Any" is not allowed [explicit-any]
6994-
6995-
def f(x: Incomplete) -> Incomplete: # no error
6996-
return x
6917+
class Series(Generic[T]):
6918+
@overload
6919+
def __add__(self: Series[bool], other: Array[Any]) -> Series[bool]: ...
6920+
@overload
6921+
def __add__(self: Series[T], other: Array[Any]) -> Series[T]: ...
6922+
def __add__(self, other): ...
69976923

6998-
a: Incomplete = 1 # no error
6924+
def f(x: Series[Any], y: Array[Incomplete]) -> None:
6925+
reveal_type(x + y) # N: Revealed type is "__main__.Series[builtins.bool]"
69996926
[builtins fixtures/tuple.pyi]

test-data/unit/check-python312.test

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2242,20 +2242,3 @@ class D[*Ts](Generic[Unpack[Us]]): # E: Generic[...] base class is redundant \
22422242
# E: Can only use one type var tuple in a class def
22432243
pass
22442244
[builtins fixtures/tuple.pyi]
2245-
2246-
[case testPEP695AliasToAnyOverloadAmbiguity]
2247-
# A PEP 695 `type Incomplete = Any` alias used as an overload arg should
2248-
# engage the overload-ambiguity fallback, just like a bare Any value.
2249-
from typing import Any, overload
2250-
2251-
type Incomplete = Any
2252-
2253-
@overload
2254-
def f(x: int) -> int: ...
2255-
@overload
2256-
def f(x: str) -> str: ...
2257-
def f(x): ...
2258-
2259-
a: Incomplete
2260-
reveal_type(f(a)) # N: Revealed type is "Any"
2261-
[builtins fixtures/tuple.pyi]

0 commit comments

Comments
 (0)