Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ def __init__(
self.allow_final = allow_final
# Are we in a context where ParamSpec literals are allowed?
self.allow_param_spec_literals = allow_param_spec_literals
# Are we in a context where ParamSpec values are accepted directly?
self.allow_param_spec = False
# Are we in context where literal "..." specifically is allowed?
self.allow_ellipsis = False
# Should we report an error whenever we encounter a RawExpressionType outside
Expand Down Expand Up @@ -461,6 +463,22 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
special = self.try_analyze_special_unbound_type(t, fullname)
if special is not None:
return special
if node.fullname in CONCATENATE_TYPE_NAMES:
# Concatenate is an operator, no need for a proper type. Some callers analyze
# annotations directly via accept(), so validate direct invalid uses here.
result = self.apply_concatenate_operator(t)
if not (self.allow_param_spec or self.allow_param_spec_literals) and (
isinstance(result, Parameters)
or (
isinstance(result, ParamSpecType)
and result.flavor == ParamSpecFlavor.BARE
and result.prefix.arg_types
)
):
self.fail("Invalid location for Concatenate", t, code=codes.VALID_TYPE)
self.note("You can use Concatenate as the first argument to Callable", t)
return AnyType(TypeOfAny.from_error)
return result
if isinstance(node, TypeAlias):
self.aliases_used.add(fullname)
an_args = self.anal_array(
Expand Down Expand Up @@ -508,10 +526,6 @@ def visit_unbound_type_nonoptional(self, t: UnboundType, defining_literal: bool)
return self.analyze_type_with_type_info(node, t.args, t, t.empty_tuple_index)
elif node.fullname in TYPE_ALIAS_NAMES:
return AnyType(TypeOfAny.special_form)
# Concatenate is an operator, no need for a proper type
elif node.fullname in CONCATENATE_TYPE_NAMES:
# We check the return type further up the stack for valid use locations
return self.apply_concatenate_operator(t)
else:
return self.analyze_unbound_type_without_type_info(t, sym, defining_literal)
else: # sym is None
Expand Down Expand Up @@ -1918,6 +1932,8 @@ def anal_type(
self.allow_final = allow_final
old_allow_ellipsis = self.allow_ellipsis
self.allow_ellipsis = allow_ellipsis
old_allow_param_spec = self.allow_param_spec
self.allow_param_spec = allow_param_spec
old_allow_unpack = self.allow_unpack
self.allow_unpack = allow_unpack
try:
Expand All @@ -1927,6 +1943,7 @@ def anal_type(
self.nesting_level -= 1
self.allow_typed_dict_special_forms = old_allow_typed_dict_special_forms
self.allow_ellipsis = old_allow_ellipsis
self.allow_param_spec = old_allow_param_spec
self.allow_unpack = old_allow_unpack
if (
not allow_param_spec
Expand Down
3 changes: 3 additions & 0 deletions test-data/unit/check-parameter-specification.test
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def foo2(x: P) -> P: ... # E: Invalid location for ParamSpec "P" \
def foo3(x: Concatenate[int, P]) -> int: ... # E: Invalid location for Concatenate \
# N: You can use Concatenate as the first argument to Callable

plain_concatenate: Concatenate[...] # E: Invalid location for Concatenate \
# N: You can use Concatenate as the first argument to Callable

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

Expand Down
3 changes: 2 additions & 1 deletion test-data/unit/check-tuples.test
Original file line number Diff line number Diff line change
Expand Up @@ -1835,6 +1835,7 @@ inst_tuple_aa_subclass: tuple_aa_subclass = tuple_aa_subclass((A(), A()))[:] #
[case testTuplePassedParameters]
from typing_extensions import Concatenate

def c(t: tuple[Concatenate[int, ...]]) -> None: # E: Cannot use "[int, VarArg(Any), KwArg(Any)]" for tuple, only for ParamSpec
def c(t: tuple[Concatenate[int, ...]]) -> None: # E: Invalid location for Concatenate \
# N: You can use Concatenate as the first argument to Callable
reveal_type(t) # N: Revealed type is "tuple[Any]"
[builtins fixtures/tuple.pyi]
Loading