Skip to content

Commit e2fc1f2

Browse files
authored
Fix crash when expanding invalid Unpack in a Callable alias (#17028)
Fixes #16937
1 parent 3ff6e47 commit e2fc1f2

5 files changed

+70
-22
lines changed

mypy/expandtype.py

+17-14
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
Type,
2727
TypeAliasType,
2828
TypedDictType,
29+
TypeOfAny,
2930
TypeType,
3031
TypeVarId,
3132
TypeVarLikeType,
@@ -312,24 +313,26 @@ def interpolate_args_for_unpack(self, t: CallableType, var_arg: UnpackType) -> l
312313
suffix = self.expand_types(t.arg_types[star_index + 1 :])
313314

314315
var_arg_type = get_proper_type(var_arg.type)
316+
new_unpack: Type
315317
if isinstance(var_arg_type, Instance):
316318
# we have something like Unpack[Tuple[Any, ...]]
317319
new_unpack = var_arg
318-
else:
319-
if isinstance(var_arg_type, TupleType):
320-
# We have something like Unpack[Tuple[Unpack[Ts], X1, X2]]
321-
expanded_tuple = var_arg_type.accept(self)
322-
assert isinstance(expanded_tuple, ProperType) and isinstance(
323-
expanded_tuple, TupleType
324-
)
325-
expanded_items = expanded_tuple.items
326-
fallback = var_arg_type.partial_fallback
327-
else:
328-
# We have plain Unpack[Ts]
329-
assert isinstance(var_arg_type, TypeVarTupleType), type(var_arg_type)
330-
fallback = var_arg_type.tuple_fallback
331-
expanded_items = self.expand_unpack(var_arg)
320+
elif isinstance(var_arg_type, TupleType):
321+
# We have something like Unpack[Tuple[Unpack[Ts], X1, X2]]
322+
expanded_tuple = var_arg_type.accept(self)
323+
assert isinstance(expanded_tuple, ProperType) and isinstance(expanded_tuple, TupleType)
324+
expanded_items = expanded_tuple.items
325+
fallback = var_arg_type.partial_fallback
332326
new_unpack = UnpackType(TupleType(expanded_items, fallback))
327+
elif isinstance(var_arg_type, TypeVarTupleType):
328+
# We have plain Unpack[Ts]
329+
fallback = var_arg_type.tuple_fallback
330+
expanded_items = self.expand_unpack(var_arg)
331+
new_unpack = UnpackType(TupleType(expanded_items, fallback))
332+
else:
333+
# We have invalid type in Unpack. This can happen when expanding aliases
334+
# to Callable[[*Invalid], Ret]
335+
new_unpack = AnyType(TypeOfAny.from_error, line=var_arg.line, column=var_arg.column)
333336
return prefix + [new_unpack] + suffix
334337

335338
def visit_callable_type(self, t: CallableType) -> CallableType:

test-data/unit/check-python311.test

+31
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,34 @@ myclass3 = MyClass(float, float, float) # E: No overload variant of "MyClass" m
142142
# N: def [T1, T2] __init__(Type[T1], Type[T2], /) -> MyClass[T1, T2]
143143
reveal_type(myclass3) # N: Revealed type is "Any"
144144
[builtins fixtures/tuple.pyi]
145+
146+
[case testUnpackNewSyntaxInvalidCallableAlias]
147+
from typing import Any, Callable, List, Tuple, TypeVar, Unpack
148+
149+
T = TypeVar("T")
150+
Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined
151+
152+
def good(*x: int) -> int: ...
153+
def bad(*x: int, y: int) -> int: ...
154+
155+
Alias1 = Callable[[*Ts], int] # E: Variable "__main__.Ts" is not valid as a type \
156+
# N: See https://mypy.readthedocs.io/en/stable/common_issues.html#variables-vs-type-aliases
157+
x1: Alias1[int] # E: Bad number of arguments for type alias, expected 0, given 1
158+
reveal_type(x1) # N: Revealed type is "def (*Any) -> builtins.int"
159+
x1 = good
160+
x1 = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]")
161+
162+
Alias2 = Callable[[*T], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple)
163+
x2: Alias2[int]
164+
reveal_type(x2) # N: Revealed type is "def (*Any) -> builtins.int"
165+
166+
Unknown = Any
167+
Alias3 = Callable[[*Unknown], int]
168+
x3: Alias3[int] # E: Bad number of arguments for type alias, expected 0, given 1
169+
reveal_type(x3) # N: Revealed type is "def (*Any) -> builtins.int"
170+
171+
IntList = List[int]
172+
Alias4 = Callable[[*IntList], int] # E: "List[int]" cannot be unpacked (must be tuple or TypeVarTuple)
173+
x4: Alias4[int] # E: Bad number of arguments for type alias, expected 0, given 1
174+
reveal_type(x4) # N: Revealed type is "def (*Unpack[builtins.tuple[Any, ...]]) -> builtins.int"
175+
[builtins fixtures/tuple.pyi]

test-data/unit/check-python312.test

+3-4
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,9 @@ BadAlias1 = TypeAliasType("BadAlias1", tuple[*Ts]) # E: TypeVarTuple "Ts" is no
7676
ba1: BadAlias1[int] # E: Bad number of arguments for type alias, expected 0, given 1
7777
reveal_type(ba1) # N: Revealed type is "builtins.tuple[Any, ...]"
7878

79-
# TODO this should report errors on the two following lines
80-
#BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str])
81-
#ba2: BadAlias2[int]
82-
#reveal_type(ba2)
79+
BadAlias2 = TypeAliasType("BadAlias2", Callable[[*Ts], str]) # E: TypeVarTuple "Ts" is not included in type_params
80+
ba2: BadAlias2[int] # E: Bad number of arguments for type alias, expected 0, given 1
81+
reveal_type(ba2) # N: Revealed type is "def (*Any) -> builtins.str"
8382

8483
[builtins fixtures/tuple.pyi]
8584
[typing fixtures/typing-full.pyi]

test-data/unit/check-type-aliases.test

+3-4
Original file line numberDiff line numberDiff line change
@@ -1190,10 +1190,9 @@ Ta9 = TypeAliasType("Ta9", Callable[P, T]) # E: ParamSpec "P" is not included i
11901190
unbound_ps_alias3: Ta9[int, str] # E: Bad number of arguments for type alias, expected 0, given 2
11911191
reveal_type(unbound_ps_alias3) # N: Revealed type is "def [P] (*Any, **Any) -> Any"
11921192

1193-
# TODO this should report errors on the two following lines
1194-
#Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str])
1195-
#unbound_tvt_alias2: Ta10[int]
1196-
#reveal_type(unbound_tvt_alias2)
1193+
Ta10 = TypeAliasType("Ta10", Callable[[Unpack[Ts]], str]) # E: TypeVarTuple "Ts" is not included in type_params
1194+
unbound_tvt_alias2: Ta10[int] # E: Bad number of arguments for type alias, expected 0, given 1
1195+
reveal_type(unbound_tvt_alias2) # N: Revealed type is "def (*Any) -> builtins.str"
11971196

11981197
class A(Generic[T]):
11991198
Ta11 = TypeAliasType("Ta11", Dict[str, T], type_params=(T,)) # E: Can't use bound type variable "T" to define generic alias \

test-data/unit/check-typevar-tuple.test

+16
Original file line numberDiff line numberDiff line change
@@ -2278,6 +2278,22 @@ higher_order(bad3) # E: Argument 1 to "higher_order" has incompatible type "Cal
22782278
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]"
22792279
[builtins fixtures/tuple.pyi]
22802280

2281+
[case testAliasToCallableWithUnpackInvalid]
2282+
from typing import Any, Callable, List, Tuple, TypeVar, Unpack
2283+
2284+
T = TypeVar("T")
2285+
Ts = TypeVarTuple("Ts") # E: Name "TypeVarTuple" is not defined
2286+
2287+
def good(*x: int) -> int: ...
2288+
def bad(*x: int, y: int) -> int: ...
2289+
2290+
Alias = Callable[[Unpack[T]], int] # E: "T" cannot be unpacked (must be tuple or TypeVarTuple)
2291+
x: Alias[int]
2292+
reveal_type(x) # N: Revealed type is "def (*Any) -> builtins.int"
2293+
x = good
2294+
x = bad # E: Incompatible types in assignment (expression has type "Callable[[VarArg(int), NamedArg(int, 'y')], int]", variable has type "Callable[[VarArg(Any)], int]")
2295+
[builtins fixtures/tuple.pyi]
2296+
22812297
[case testTypeVarTupleInvariant]
22822298
from typing import Generic, Tuple
22832299
from typing_extensions import Unpack, TypeVarTuple

0 commit comments

Comments
 (0)