Skip to content

Commit 5219d09

Browse files
committed
[ty] Ban TypeVar bounds or constraints from containing type variables
1 parent f02ee29 commit 5219d09

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

crates/ty_python_semantic/resources/mdtest/generics/legacy/variables.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -444,18 +444,26 @@ def constrained(x: T_constrained):
444444
A typevar's bounds and constraints cannot be generic, cyclic or otherwise:
445445

446446
```py
447-
from typing import Any, TypeVar
447+
from typing import Any, TypeVar, Generic
448448

449449
S = TypeVar("S")
450450

451-
# TODO: error
451+
# error: [invalid-legacy-type-variable] "TypeVar upper bound cannot be generic"
452452
T = TypeVar("T", bound=list[S])
453453

454-
# TODO: error
454+
# error: [invalid-legacy-type-variable] "TypeVar constraint cannot be generic"
455455
U = TypeVar("U", list["T"], str)
456456

457-
# TODO: error
457+
# error: [invalid-legacy-type-variable] "TypeVar constraint cannot be generic"
458458
V = TypeVar("V", list["V"], str)
459+
460+
# error: [invalid-legacy-type-variable] "TypeVar constraint cannot be generic"
461+
# error: [invalid-legacy-type-variable] "TypeVar constraint cannot be generic"
462+
W = TypeVar("W", list[list[list[list["V"]]]], V)
463+
464+
class Foo(Generic[S]):
465+
# error: [invalid-legacy-type-variable] "TypeVar upper bound cannot be generic"
466+
T = TypeVar("T", bound=S)
459467
```
460468

461469
However, they are lazily evaluated and can cyclically refer to their own type:

crates/ty_python_semantic/src/types/infer/builder.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6430,11 +6430,33 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
64306430
self.infer_newtype_assignment_deferred(arguments);
64316431
return;
64326432
}
6433+
let is_typevar = &|t| {
6434+
matches!(
6435+
t,
6436+
Type::KnownInstance(KnownInstanceType::TypeVar(_)) | Type::TypeVar(_)
6437+
)
6438+
};
6439+
let db = self.db();
6440+
let is_generic = |ty| any_over_type(db, ty, is_typevar, false);
64336441
for arg in arguments.args.iter().skip(1) {
6434-
self.infer_type_expression(arg);
6442+
let constraint = self.infer_type_expression(arg);
6443+
6444+
if is_generic(constraint)
6445+
&& let Some(builder) = self.context.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, arg)
6446+
{
6447+
builder.into_diagnostic("TypeVar constraint cannot be generic");
6448+
}
64356449
}
64366450
if let Some(bound) = arguments.find_keyword("bound") {
6437-
self.infer_type_expression(&bound.value);
6451+
let bound_type = self.infer_type_expression(&bound.value);
6452+
6453+
if is_generic(bound_type)
6454+
&& let Some(builder) = self
6455+
.context
6456+
.report_lint(&INVALID_LEGACY_TYPE_VARIABLE, bound)
6457+
{
6458+
builder.into_diagnostic("TypeVar upper bound cannot be generic");
6459+
}
64386460
}
64396461
if let Some(default) = arguments.find_keyword("default") {
64406462
if matches!(

0 commit comments

Comments
 (0)