mirror of https://github.com/astral-sh/ruff
catch self-referential typevars
This commit is contained in:
parent
1e33d25d1c
commit
b314119835
|
|
@ -490,8 +490,7 @@ V = TypeVar("V", default="V")
|
||||||
class D(Generic[V]):
|
class D(Generic[V]):
|
||||||
x: V
|
x: V
|
||||||
|
|
||||||
# TODO: we shouldn't leak a typevar like this in type inference
|
reveal_type(D().x) # revealed: Unknown
|
||||||
reveal_type(D().x) # revealed: V@D
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Regression
|
## Regression
|
||||||
|
|
|
||||||
|
|
@ -863,7 +863,7 @@ reveal_type(C[int]().y) # revealed: int
|
||||||
class D[T = T]:
|
class D[T = T]:
|
||||||
x: T
|
x: T
|
||||||
|
|
||||||
reveal_type(D().x) # revealed: T@D
|
reveal_type(D().x) # revealed: Unknown
|
||||||
```
|
```
|
||||||
|
|
||||||
[pep 695]: https://peps.python.org/pep-0695/
|
[pep 695]: https://peps.python.org/pep-0695/
|
||||||
|
|
|
||||||
|
|
@ -9662,6 +9662,22 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_is_self_referential(self, db: &'db dyn Db, ty: Type<'db>) -> bool {
|
||||||
|
let identity = self.identity(db);
|
||||||
|
any_over_type(
|
||||||
|
db,
|
||||||
|
ty,
|
||||||
|
&|ty| match ty {
|
||||||
|
Type::TypeVar(bound_typevar) => identity == bound_typevar.typevar(db).identity(db),
|
||||||
|
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => {
|
||||||
|
identity == typevar.identity(db)
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[salsa::tracked(
|
#[salsa::tracked(
|
||||||
cycle_initial=lazy_bound_or_constraints_cycle_initial,
|
cycle_initial=lazy_bound_or_constraints_cycle_initial,
|
||||||
heap_size=ruff_memory_usage::heap_size
|
heap_size=ruff_memory_usage::heap_size
|
||||||
|
|
@ -9683,6 +9699,11 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if self.type_is_self_referential(db, ty) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
Some(TypeVarBoundOrConstraints::UpperBound(ty))
|
Some(TypeVarBoundOrConstraints::UpperBound(ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -9721,6 +9742,15 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if ty
|
||||||
|
.elements(db)
|
||||||
|
.iter()
|
||||||
|
.any(|ty| self.type_is_self_referential(db, *ty))
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
Some(TypeVarBoundOrConstraints::Constraints(ty))
|
Some(TypeVarBoundOrConstraints::Constraints(ty))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -9728,33 +9758,31 @@ impl<'db> TypeVarInstance<'db> {
|
||||||
fn lazy_default(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
fn lazy_default(self, db: &'db dyn Db) -> Option<Type<'db>> {
|
||||||
let definition = self.definition(db)?;
|
let definition = self.definition(db)?;
|
||||||
let module = parsed_module(db, definition.file(db)).load(db);
|
let module = parsed_module(db, definition.file(db)).load(db);
|
||||||
match definition.kind(db) {
|
let ty = match definition.kind(db) {
|
||||||
// PEP 695 typevar
|
// PEP 695 typevar
|
||||||
DefinitionKind::TypeVar(typevar) => {
|
DefinitionKind::TypeVar(typevar) => {
|
||||||
let typevar_node = typevar.node(&module);
|
let typevar_node = typevar.node(&module);
|
||||||
Some(definition_expression_type(
|
definition_expression_type(db, definition, typevar_node.default.as_ref()?)
|
||||||
db,
|
|
||||||
definition,
|
|
||||||
typevar_node.default.as_ref()?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
// legacy typevar
|
// legacy typevar
|
||||||
DefinitionKind::Assignment(assignment) => {
|
DefinitionKind::Assignment(assignment) => {
|
||||||
let call_expr = assignment.value(&module).as_call_expr()?;
|
let call_expr = assignment.value(&module).as_call_expr()?;
|
||||||
let expr = &call_expr.arguments.find_keyword("default")?.value;
|
let expr = &call_expr.arguments.find_keyword("default")?.value;
|
||||||
Some(definition_expression_type(db, definition, expr))
|
definition_expression_type(db, definition, expr)
|
||||||
}
|
}
|
||||||
// PEP 695 ParamSpec
|
// PEP 695 ParamSpec
|
||||||
DefinitionKind::ParamSpec(paramspec) => {
|
DefinitionKind::ParamSpec(paramspec) => {
|
||||||
let paramspec_node = paramspec.node(&module);
|
let paramspec_node = paramspec.node(&module);
|
||||||
Some(definition_expression_type(
|
definition_expression_type(db, definition, paramspec_node.default.as_ref()?)
|
||||||
db,
|
|
||||||
definition,
|
|
||||||
paramspec_node.default.as_ref()?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => return None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.type_is_self_referential(db, ty) {
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Some(ty)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_pep695(self, db: &'db dyn Db) -> Option<BoundTypeVarInstance<'db>> {
|
pub fn bind_pep695(self, db: &'db dyn Db) -> Option<BoundTypeVarInstance<'db>> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue