From e2fc1f28935806ca04b18fab277217f583b51594 Mon Sep 17 00:00:00 2001 From: Ali Hamdan Date: Mon, 8 Apr 2024 20:38:03 +0200 Subject: [PATCH] Fix crash when expanding invalid Unpack in a `Callable` alias (#17028) Fixes #16937 --- mypy/expandtype.py | 31 ++++++++++++++----------- test-data/unit/check-python311.test | 31 +++++++++++++++++++++++++ test-data/unit/check-python312.test | 7 +++--- test-data/unit/check-type-aliases.test | 7 +++--- test-data/unit/check-typevar-tuple.test | 16 +++++++++++++ 5 files changed, 70 insertions(+), 22 deletions(-) diff --git a/mypy/expandtype.py b/mypy/expandtype.py index ec6a2ecfd0d2..f7fa0258f588 100644 --- a/mypy/expandtype.py +++ b/mypy/expandtype.py @@ -26,6 +26,7 @@ Type, TypeAliasType, TypedDictType, + TypeOfAny, TypeType, TypeVarId, TypeVarLikeType, @@ -312,24 +313,26 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l suffix = self.expand_types(t.arg_types[star_index + 1 :]) var_arg_type = get_proper_type(var_arg.type) + new_unpack: Type if isinstance(var_arg_type, Instance): # we have something like Unpack[Tuple[Any, ...]] new_unpack = var_arg - else: - if isinstance(var_arg_type, TupleType): - # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] - expanded_tuple = var_arg_type.accept(self) - assert isinstance(expanded_tuple, ProperType) and isinstance( - expanded_tuple, TupleType - ) - expanded_items = expanded_tuple.items - fallback = var_arg_type.partial_fallback - else: - # We have plain Unpack[Ts] - assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type) - fallback = var_arg_type.tuple_fallback - expanded_items = self.expand_unpack(var_arg) + elif isinstance(var_arg_type, TupleType): + # We have something like Unpack[Tuple[Unpack[Ts], X1, X2]] + expanded_tuple = var_arg_type.accept(self) + assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType) + expanded_items = expanded_tuple.items + fallback = var_arg_type.partial_fallback new_unpack = UnpackType(TupleType(expanded_items, fallback)) + elif isinstance(var_arg_type, TypeVarTupleType): + # We have plain Unpack[Ts] + fallback = var_arg_type.tuple_fallback + expanded_items = self.expand_unpack(var_arg) + new_unpack = UnpackType(TupleType(expanded_items, fallback)) + else: + # We have invalid type in Unpack. This can happen when expanding aliases + # to Callable[[*Invalid], Ret] + new_unpack = AnyType(TypeOfAny.from_error, line=var_arg.line, column=var_arg.column) return prefix + [new_unpack] + suffix def visit_callable_type(self, t: CallableType) -> CallableType: diff --git a/test-data/unit/check-python311.test b/test-data/unit/check-python311.test index 37dc3ca0f5b4..2d1a09ef3336 100644 --- a/test-data/unit/check-python311.test +++ b/test-data/unit/check-python311.test @@ -142,3 +142,34 @@ myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" m # N: def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2] reveal_type(myclass3) # N: Revealed type is "Any" [builtins fixtures/tuple.pyi] + +[case testUnpackNewSyntaxInvalidCallableAlias] +from typing import Any, Callable, List, Tuple, TypeVar, Unpack + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined + +def good(*x: int) -> int: ... +def bad(*x: int, y: int) -> int: ... + +Alias1 = Callable[[*Ts], int] # E: Variable "__main__.Ts" is not valid as a type \ + # N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases +x1: Alias1[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(x1) # N: Revealed type is "def (*Any) -> builtins.int" +x1 = good +x1 = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]") + +Alias2 = Callable[[*T], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple) +x2: Alias2[int] +reveal_type(x2) # N: Revealed type is "def (*Any) -> builtins.int" + +Unknown = Any +Alias3 = Callable[[*Unknown], int] +x3: Alias3[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int" + +IntList = List[int] +Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple) +x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(x4) # N: Revealed type is "def (*Unpack[builtins.tuple[Any, ...]]) -> builtins.int" +[builtins fixtures/tuple.pyi] diff --git a/test-data/unit/check-python312.test b/test-data/unit/check-python312.test index 188c51f98185..2b99a42628b1 100644 --- a/test-data/unit/check-python312.test +++ b/test-data/unit/check-python312.test @@ -76,10 +76,9 @@ BadAlias1 = TypeAliasType("BadAlias1", tuple[*Ts]) # E: TypeVarTuple "Ts" is no ba1: BadAlias1[int] # E: Bad number of arguments for type alias, expected 0, given 1 reveal_type(ba1) # N: Revealed type is "builtins.tuple[Any, ...]" -# TODO this should report errors on the two following lines -#BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) -#ba2: BadAlias2[int] -#reveal_type(ba2) +BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) # E: TypeVarTuple "Ts" is not included in type_params +ba2: BadAlias2[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str" [builtins fixtures/tuple.pyi] [typing fixtures/typing-full.pyi] diff --git a/test-data/unit/check-type-aliases.test b/test-data/unit/check-type-aliases.test index a9c57d46ad22..aebb0381d962 100644 --- a/test-data/unit/check-type-aliases.test +++ b/test-data/unit/check-type-aliases.test @@ -1190,10 +1190,9 @@ Ta9 = TypeAliasType("Ta9", Callable[P, T]) # E: ParamSpec "P" is not included i unbound_ps_alias3: Ta9[int, str] # E: Bad number of arguments for type alias, expected 0, given 2 reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> Any" -# TODO this should report errors on the two following lines -#Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) -#unbound_tvt_alias2: Ta10[int] -#reveal_type(unbound_tvt_alias2) +Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) # E: TypeVarTuple "Ts" is not included in type_params +unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expected 0, given 1 +reveal_type(unbound_tvt_alias2) # N: Revealed type is "def (*Any) -> builtins.str" class A(Generic[T]): Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \ diff --git a/test-data/unit/check-typevar-tuple.test b/test-data/unit/check-typevar-tuple.test index cc3dc4ed9f39..f704e3c5c713 100644 --- a/test-data/unit/check-typevar-tuple.test +++ b/test-data/unit/check-typevar-tuple.test @@ -2278,6 +2278,22 @@ higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Cal higher_order(bad4) # E: Argument 1 to "higher_order" has incompatible type "Callable[[KwArg(None)], None]"; expected "Callable[[int, str, VarArg(Unpack[Tuple[Unpack[Tuple[Any, ...]], int]])], Any]" [builtins fixtures/tuple.pyi] +[case testAliasToCallableWithUnpackInvalid] +from typing import Any, Callable, List, Tuple, TypeVar, Unpack + +T = TypeVar("T") +Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined + +def good(*x: int) -> int: ... +def bad(*x: int, y: int) -> int: ... + +Alias = Callable[[Unpack[T]], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple) +x: Alias[int] +reveal_type(x) # N: Revealed type is "def (*Any) -> builtins.int" +x = good +x = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]") +[builtins fixtures/tuple.pyi] + [case testTypeVarTupleInvariant] from typing import Generic, Tuple from typing_extensions import Unpack, TypeVarTuple