mirror of https://github.com/astral-sh/ruff
[ty] Fix false positives for `class F(Generic[*Ts]): ...` (#21723)
This commit is contained in:
parent
116fd7c7af
commit
0e651b50b7
|
|
@ -5,6 +5,11 @@
|
|||
At its simplest, to define a generic class using the legacy syntax, you inherit from the
|
||||
`typing.Generic` special form, which is "specialized" with the generic class's type variables.
|
||||
|
||||
```toml
|
||||
[environment]
|
||||
python-version = "3.11"
|
||||
```
|
||||
|
||||
```py
|
||||
from ty_extensions import generic_context
|
||||
from typing_extensions import Generic, TypeVar, TypeVarTuple, ParamSpec, Unpack
|
||||
|
|
@ -19,7 +24,9 @@ class MultipleTypevars(Generic[T, S]): ...
|
|||
class SingleParamSpec(Generic[P]): ...
|
||||
class TypeVarAndParamSpec(Generic[P, T]): ...
|
||||
class SingleTypeVarTuple(Generic[Unpack[Ts]]): ...
|
||||
class StarredSingleTypeVarTuple(Generic[*Ts]): ...
|
||||
class TypeVarAndTypeVarTuple(Generic[T, Unpack[Ts]]): ...
|
||||
class StarredTypeVarAndTypeVarTuple(Generic[T, *Ts]): ...
|
||||
|
||||
# revealed: ty_extensions.GenericContext[T@SingleTypevar]
|
||||
reveal_type(generic_context(SingleTypevar))
|
||||
|
|
@ -34,6 +41,8 @@ reveal_type(generic_context(TypeVarAndParamSpec))
|
|||
# TODO: support `TypeVarTuple` properly (these should not reveal `None`)
|
||||
reveal_type(generic_context(SingleTypeVarTuple)) # revealed: None
|
||||
reveal_type(generic_context(TypeVarAndTypeVarTuple)) # revealed: None
|
||||
reveal_type(generic_context(StarredSingleTypeVarTuple)) # revealed: None
|
||||
reveal_type(generic_context(StarredTypeVarAndTypeVarTuple)) # revealed: None
|
||||
```
|
||||
|
||||
Inheriting from `Generic` multiple times yields a `duplicate-base` diagnostic, just like any other
|
||||
|
|
|
|||
|
|
@ -956,8 +956,13 @@ impl<'db> Type<'db> {
|
|||
self.is_instance_of(db, KnownClass::NotImplementedType)
|
||||
}
|
||||
|
||||
pub(crate) const fn is_todo(&self) -> bool {
|
||||
matches!(self, Type::Dynamic(DynamicType::Todo(_)))
|
||||
pub(crate) fn is_todo(&self) -> bool {
|
||||
self.as_dynamic().is_some_and(|dynamic| match dynamic {
|
||||
DynamicType::Any | DynamicType::Unknown | DynamicType::Divergent(_) => false,
|
||||
DynamicType::Todo(_) | DynamicType::TodoStarredExpression | DynamicType::TodoUnpack => {
|
||||
true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub const fn is_generic_alias(&self) -> bool {
|
||||
|
|
@ -8133,7 +8138,7 @@ impl<'db> Type<'db> {
|
|||
Self::AlwaysFalsy => Type::SpecialForm(SpecialFormType::AlwaysFalsy).definition(db),
|
||||
|
||||
// These types have no definition
|
||||
Self::Dynamic(DynamicType::Divergent(_) | DynamicType::Todo(_) | DynamicType::TodoUnpack)
|
||||
Self::Dynamic(DynamicType::Divergent(_) | DynamicType::Todo(_) | DynamicType::TodoUnpack | DynamicType::TodoStarredExpression)
|
||||
| Self::Callable(_)
|
||||
| Self::TypeIs(_) => None,
|
||||
}
|
||||
|
|
@ -8794,6 +8799,8 @@ pub enum DynamicType {
|
|||
Todo(TodoType),
|
||||
/// A special Todo-variant for `Unpack[Ts]`, so that we can treat it specially in `Generic[Unpack[Ts]]`
|
||||
TodoUnpack,
|
||||
/// A special Todo-variant for `*Ts`, so that we can treat it specially in `Generic[Unpack[Ts]]`
|
||||
TodoStarredExpression,
|
||||
/// A type that is determined to be divergent during recursive type inference.
|
||||
Divergent(DivergentType),
|
||||
}
|
||||
|
|
@ -8824,13 +8831,8 @@ impl std::fmt::Display for DynamicType {
|
|||
// `DynamicType::Todo`'s display should be explicit that is not a valid display of
|
||||
// any other type
|
||||
DynamicType::Todo(todo) => write!(f, "@Todo{todo}"),
|
||||
DynamicType::TodoUnpack => {
|
||||
if cfg!(debug_assertions) {
|
||||
f.write_str("@Todo(typing.Unpack)")
|
||||
} else {
|
||||
f.write_str("@Todo")
|
||||
}
|
||||
}
|
||||
DynamicType::TodoUnpack => f.write_str("@Todo(typing.Unpack)"),
|
||||
DynamicType::TodoStarredExpression => f.write_str("@Todo(StarredExpression)"),
|
||||
DynamicType::Divergent(_) => f.write_str("Divergent"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,9 @@ impl<'db> ClassBase<'db> {
|
|||
ClassBase::Class(class) => class.name(db),
|
||||
ClassBase::Dynamic(DynamicType::Any) => "Any",
|
||||
ClassBase::Dynamic(DynamicType::Unknown) => "Unknown",
|
||||
ClassBase::Dynamic(DynamicType::Todo(_) | DynamicType::TodoUnpack) => "@Todo",
|
||||
ClassBase::Dynamic(
|
||||
DynamicType::Todo(_) | DynamicType::TodoUnpack | DynamicType::TodoStarredExpression,
|
||||
) => "@Todo",
|
||||
ClassBase::Dynamic(DynamicType::Divergent(_)) => "Divergent",
|
||||
ClassBase::Protocol => "Protocol",
|
||||
ClassBase::Generic => "Generic",
|
||||
|
|
|
|||
|
|
@ -8407,7 +8407,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
});
|
||||
|
||||
// TODO
|
||||
todo_type!("starred expression")
|
||||
Type::Dynamic(DynamicType::TodoStarredExpression)
|
||||
}
|
||||
|
||||
fn infer_yield_expression(&mut self, yield_expression: &ast::ExprYield) -> Type<'db> {
|
||||
|
|
@ -9571,10 +9571,24 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
(unknown @ Type::Dynamic(DynamicType::Unknown), _, _)
|
||||
| (_, unknown @ Type::Dynamic(DynamicType::Unknown), _) => Some(unknown),
|
||||
|
||||
(todo @ Type::Dynamic(DynamicType::Todo(_) | DynamicType::TodoUnpack), _, _)
|
||||
| (_, todo @ Type::Dynamic(DynamicType::Todo(_) | DynamicType::TodoUnpack), _) => {
|
||||
Some(todo)
|
||||
}
|
||||
(
|
||||
todo @ Type::Dynamic(
|
||||
DynamicType::Todo(_)
|
||||
| DynamicType::TodoUnpack
|
||||
| DynamicType::TodoStarredExpression,
|
||||
),
|
||||
_,
|
||||
_,
|
||||
)
|
||||
| (
|
||||
_,
|
||||
todo @ Type::Dynamic(
|
||||
DynamicType::Todo(_)
|
||||
| DynamicType::TodoUnpack
|
||||
| DynamicType::TodoStarredExpression,
|
||||
),
|
||||
_,
|
||||
) => Some(todo),
|
||||
|
||||
(Type::Never, _, _) | (_, Type::Never, _) => Some(Type::Never),
|
||||
|
||||
|
|
@ -11898,7 +11912,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.db(),
|
||||
*typevar,
|
||||
&|ty| match ty {
|
||||
Type::Dynamic(DynamicType::TodoUnpack) => true,
|
||||
Type::Dynamic(
|
||||
DynamicType::TodoUnpack | DynamicType::TodoStarredExpression,
|
||||
) => true,
|
||||
Type::NominalInstance(nominal) => matches!(
|
||||
nominal.known_class(self.db()),
|
||||
Some(KnownClass::TypeVarTuple | KnownClass::ParamSpec)
|
||||
|
|
|
|||
|
|
@ -499,7 +499,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
|
|||
if starred_type.exact_tuple_instance_spec(self.db()).is_some() {
|
||||
starred_type
|
||||
} else {
|
||||
todo_type!("PEP 646")
|
||||
Type::Dynamic(DynamicType::TodoStarredExpression)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -274,6 +274,9 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
|
|||
(DynamicType::TodoUnpack, _) => Ordering::Less,
|
||||
(_, DynamicType::TodoUnpack) => Ordering::Greater,
|
||||
|
||||
(DynamicType::TodoStarredExpression, _) => Ordering::Less,
|
||||
(_, DynamicType::TodoStarredExpression) => Ordering::Greater,
|
||||
|
||||
(DynamicType::Divergent(left), DynamicType::Divergent(right)) => left.cmp(&right),
|
||||
(DynamicType::Divergent(_), _) => Ordering::Less,
|
||||
(_, DynamicType::Divergent(_)) => Ordering::Greater,
|
||||
|
|
|
|||
Loading…
Reference in New Issue