Skip to content

Commit 3eeb47b

Browse files
author
Deepak kudi
committed
Reject plain Concatenate ellipsis annotations
1 parent 6f0e77b commit 3eeb47b

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

mypy/typeanal.py

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,8 @@ def __init__(
248248
self.allow_final = allow_final
249249
# Are we in a context where ParamSpec literals are allowed?
250250
self.allow_param_spec_literals = allow_param_spec_literals
251+
# Are we in a context where ParamSpec values are accepted directly?
252+
self.allow_param_spec = False
251253
# Are we in context where literal "..." specifically is allowed?
252254
self.allow_ellipsis = False
253255
# Should we report an error whenever we encounter a RawExpressionType outside
@@ -461,6 +463,22 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
461463
special = self.try_analyze_special_unbound_type(t, fullname)
462464
if special is not None:
463465
return special
466+
if node.fullname in CONCATENATE_TYPE_NAMES:
467+
# Concatenate is an operator, no need for a proper type. Some callers analyze
468+
# annotations directly via accept(), so validate direct invalid uses here.
469+
result = self.apply_concatenate_operator(t)
470+
if not (self.allow_param_spec or self.allow_param_spec_literals) and (
471+
isinstance(result, Parameters)
472+
or (
473+
isinstance(result, ParamSpecType)
474+
and result.flavor == ParamSpecFlavor.BARE
475+
and result.prefix.arg_types
476+
)
477+
):
478+
self.fail("Invalid location for Concatenate", t, code=codes.VALID_TYPE)
479+
self.note("You can use Concatenate as the first argument to Callable", t)
480+
return AnyType(TypeOfAny.from_error)
481+
return result
464482
if isinstance(node, TypeAlias):
465483
self.aliases_used.add(fullname)
466484
an_args = self.anal_array(
@@ -508,10 +526,6 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
508526
return self.analyze_type_with_type_info(node, t.args, t, t.empty_tuple_index)
509527
elif node.fullname in TYPE_ALIAS_NAMES:
510528
return AnyType(TypeOfAny.special_form)
511-
# Concatenate is an operator, no need for a proper type
512-
elif node.fullname in CONCATENATE_TYPE_NAMES:
513-
# We check the return type further up the stack for valid use locations
514-
return self.apply_concatenate_operator(t)
515529
else:
516530
return self.analyze_unbound_type_without_type_info(t, sym, defining_literal)
517531
else: # sym is None
@@ -1918,6 +1932,8 @@ def anal_type(
19181932
self.allow_final = allow_final
19191933
old_allow_ellipsis = self.allow_ellipsis
19201934
self.allow_ellipsis = allow_ellipsis
1935+
old_allow_param_spec = self.allow_param_spec
1936+
self.allow_param_spec = allow_param_spec
19211937
old_allow_unpack = self.allow_unpack
19221938
self.allow_unpack = allow_unpack
19231939
try:
@@ -1927,6 +1943,7 @@ def anal_type(
19271943
self.nesting_level -= 1
19281944
self.allow_typed_dict_special_forms = old_allow_typed_dict_special_forms
19291945
self.allow_ellipsis = old_allow_ellipsis
1946+
self.allow_param_spec = old_allow_param_spec
19301947
self.allow_unpack = old_allow_unpack
19311948
if (
19321949
not allow_param_spec

test-data/unit/check-parameter-specification.test

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ def foo2(x: P) -> P: ... # E: Invalid location for ParamSpec "P" \
2828
def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for Concatenate \
2929
# N: You can use Concatenate as the first argument to Callable
3030

31+
plain_concatenate: Concatenate[...] # E: Invalid location for Concatenate \
32+
# N: You can use Concatenate as the first argument to Callable
33+
3134
def foo4(x: List[P]) -> None: ... # E: Invalid location for ParamSpec "P" \
3235
# N: You can use ParamSpec as the first argument to Callable, e.g., "Callable[P, int]"
3336

0 commit comments

Comments
 (0)