Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disallow TypeVar constraints parameterized by type variables #18186

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
17 changes: 13 additions & 4 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2369,8 +2369,11 @@ def analyze_unbound_tvar_impl(
assert isinstance(sym.node, TypeVarExpr)
return t.name, sym.node

def find_type_var_likes(self, t: Type) -> TypeVarLikeList:
def find_type_var_likes(
self, t: Type, *, include_bound_tvars: bool = False
) -> TypeVarLikeList:
visitor = FindTypeVarVisitor(self, self.tvar_scope)
visitor.include_bound_tvars = include_bound_tvars
t.accept(visitor)
return visitor.type_var_likes

Expand Down Expand Up @@ -5036,9 +5039,15 @@ def analyze_value_types(self, items: list[Expression]) -> list[Type]:
result: list[Type] = []
for node in items:
try:
analyzed = self.anal_type(
self.expr_to_unanalyzed_type(node), allow_placeholder=True
)
unanalyzed_type = self.expr_to_unanalyzed_type(node)
if self.find_type_var_likes(unanalyzed_type, include_bound_tvars=True):
self.fail(
"TypeVar constraint type cannot be parametrized by type variables", node
)
result.append(AnyType(TypeOfAny.from_error))
continue

analyzed = self.anal_type(unanalyzed_type, allow_placeholder=True)
if analyzed is None:
# Type variables are special: we need to place them in the symbol table
# soon, even if some value is not ready yet, see process_typevar_parameters()
Expand Down
3 changes: 2 additions & 1 deletion mypy/typeanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2558,6 +2558,7 @@ def __init__(self, api: SemanticAnalyzerCoreInterface, scope: TypeVarLikeScope)
self.has_self_type = False
self.seen_aliases: set[TypeAliasType] | None = None
self.include_callables = True
self.include_bound_tvars = False

def _seems_like_callable(self, type: UnboundType) -> bool:
if not type.args:
Expand All @@ -2583,7 +2584,7 @@ def visit_unbound_type(self, t: UnboundType) -> None:
if (
node
and isinstance(node.node, TypeVarLikeExpr)
and self.scope.get_binding(node) is None
and (self.scope.get_binding(node) is None or self.include_bound_tvars)
):
if (name, node.node) not in self.type_var_likes:
self.type_var_likes.append((name, node.node))
Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/semanal-errors.test
Original file line number Diff line number Diff line change
Expand Up @@ -1060,6 +1060,19 @@ S = TypeVar('S', covariant=True, contravariant=True) \
# E: TypeVar cannot be both covariant and contravariant
[builtins fixtures/bool.pyi]

[case testInvalidTypevarArgumentsGenericConstraint]
from typing import Generic, List, TypeVar

T = TypeVar("T")

Bad1 = TypeVar("Bad1", int, T) # E: TypeVar constraint type cannot be parametrized by type variables
Bad2 = TypeVar("Bad2", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables
Bad3 = TypeVar("Bad3", int, List[List[T]]) # E: TypeVar constraint type cannot be parametrized by type variables
def f(x: T) -> None:
Bad4 = TypeVar("Bad4", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables
class C(Generic[T]):
Bad5 = TypeVar("Bad5", int, List[T]) # E: TypeVar constraint type cannot be parametrized by type variables

[case testInvalidTypevarValues]
from typing import TypeVar
b = TypeVar('b', *[int]) # E: Unexpected argument to "TypeVar()"
Expand Down
Loading