diff --git a/mypy/typeanal.py b/mypy/typeanal.py index 8d500c54364ae..6717f4f18c90b 100644 --- a/mypy/typeanal.py +++ b/mypy/typeanal.py @@ -1217,6 +1217,15 @@ def visit_callable_type( "Arguments not allowed after ParamSpec.args", t, code=codes.VALID_TYPE ) at = self.anal_type(ut, nested=nested, allow_unpack=False) + if isinstance(at, Parameters): + # A Parameters here comes from a misplaced Concatenate ending in + # an explicit ellipsis, e.g. Callable[[Concatenate[int, ...]], None]. + self.fail( + "Concatenate is only valid as the first argument to Callable", + ut, + code=codes.VALID_TYPE, + ) + at = AnyType(TypeOfAny.from_error) arg_types.append(at) if nested and arg_types: diff --git a/test-data/unit/check-parameter-specification.test b/test-data/unit/check-parameter-specification.test index f5eb4c416cda0..795af772f5fb1 100644 --- a/test-data/unit/check-parameter-specification.test +++ b/test-data/unit/check-parameter-specification.test @@ -2787,3 +2787,16 @@ g2(y1) # E: Argument 1 to "g2" has incompatible type "Y[[str]]"; expected "Y[[i g3(y1) [builtins fixtures/paramspec.pyi] [typing fixtures/typing-full.pyi] + +[case testParamSpecNoCrashOnConcatenateInCallableArg] +from typing import Callable +from typing_extensions import Concatenate, ParamSpec + +P = ParamSpec("P") + +def takes_paramspec(f: Callable[P, object]) -> bool: + return False + +def run(callback: Callable[[Concatenate[int, ...]], None]) -> None: # E: Concatenate is only valid as the first argument to Callable + takes_paramspec(callback) +[builtins fixtures/paramspec.pyi]