Skip to content

Commit

Permalink
Fix crash on invalid type variable with ParamSpec (#15953)
Browse files Browse the repository at this point in the history
Fixes #15948

The fix is straightforward: invalid type variable resulted in applying
type arguments packing/simplification when we shouldn't. Making the
latter more strict fixes the issue.

---------

Co-authored-by: Jelle Zijlstra <jelle.zijlstra@gmail.com>
  • Loading branch information
ilevkivskyi and JelleZijlstra committed Aug 25, 2023
1 parent 9e1f4df commit f9b1db6
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 7 deletions.
37 changes: 30 additions & 7 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -458,11 +458,30 @@ def pack_paramspec_args(self, an_args: Sequence[Type]) -> list[Type]:
# These do not support mypy_extensions VarArgs, etc. as they were already analyzed
# TODO: should these be re-analyzed to get rid of this inconsistency?
count = len(an_args)
if count > 0:
first_arg = get_proper_type(an_args[0])
if not (count == 1 and isinstance(first_arg, (Parameters, ParamSpecType, AnyType))):
return [Parameters(an_args, [ARG_POS] * count, [None] * count)]
return list(an_args)
if count == 0:
return []
if count == 1 and isinstance(get_proper_type(an_args[0]), AnyType):
# Single Any is interpreted as ..., rather that a single argument with Any type.
# I didn't find this in the PEP, but it sounds reasonable.
return list(an_args)
if any(isinstance(a, (Parameters, ParamSpecType)) for a in an_args):
if len(an_args) > 1:
first_wrong = next(
arg for arg in an_args if isinstance(arg, (Parameters, ParamSpecType))
)
self.fail(
"Nested parameter specifications are not allowed",
first_wrong,
code=codes.VALID_TYPE,
)
return [AnyType(TypeOfAny.from_error)]
return list(an_args)
first = an_args[0]
return [
Parameters(
an_args, [ARG_POS] * count, [None] * count, line=first.line, column=first.column
)
]

def cannot_resolve_type(self, t: UnboundType) -> None:
# TODO: Move error message generation to messages.py. We'd first
Expand Down Expand Up @@ -503,7 +522,11 @@ def apply_concatenate_operator(self, t: UnboundType) -> Type:
names: list[str | None] = [None] * len(args)

pre = Parameters(
args + pre.arg_types, [ARG_POS] * len(args) + pre.arg_kinds, names + pre.arg_names
args + pre.arg_types,
[ARG_POS] * len(args) + pre.arg_kinds,
names + pre.arg_names,
line=t.line,
column=t.column,
)
return ps.copy_modified(prefix=pre) if isinstance(ps, ParamSpecType) else pre

Expand Down Expand Up @@ -913,7 +936,7 @@ def visit_type_list(self, t: TypeList) -> Type:
if params:
ts, kinds, names = params
# bind these types
return Parameters(self.anal_array(ts), kinds, names)
return Parameters(self.anal_array(ts), kinds, names, line=t.line, column=t.column)
else:
return AnyType(TypeOfAny.from_error)
else:
Expand Down
23 changes: 23 additions & 0 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -1741,3 +1741,26 @@ def bar(x): ...

reveal_type(bar) # N: Revealed type is "Overload(def (x: builtins.int) -> builtins.float, def (x: builtins.str) -> builtins.str)"
[builtins fixtures/paramspec.pyi]

[case testParamSpecDecoratorOverloadNoCrashOnInvalidTypeVar]
from typing import Any, Callable, List
from typing_extensions import ParamSpec

P = ParamSpec("P")
T = 1

Alias = Callable[P, List[T]] # type: ignore
def dec(fn: Callable[P, T]) -> Alias[P, T]: ... # type: ignore
f: Any
dec(f) # No crash
[builtins fixtures/paramspec.pyi]

[case testParamSpecErrorNestedParams]
from typing import Generic
from typing_extensions import ParamSpec

P = ParamSpec("P")
class C(Generic[P]): ...
c: C[int, [int, str], str] # E: Nested parameter specifications are not allowed
reveal_type(c) # N: Revealed type is "__main__.C[Any]"
[builtins fixtures/paramspec.pyi]

0 comments on commit f9b1db6

Please sign in to comment.