mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +00:00
[ty] Fix more false positives related to Generic
or Protocol
being subscripted with a ParamSpec
or TypeVarTuple
(#19764)
This commit is contained in:
parent
934fd37d2b
commit
4090297a11
6 changed files with 47 additions and 4 deletions
|
@ -7,18 +7,30 @@ At its simplest, to define a generic class using the legacy syntax, you inherit
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import generic_context
|
from ty_extensions import generic_context
|
||||||
from typing import Generic, TypeVar
|
from typing_extensions import Generic, TypeVar, TypeVarTuple, ParamSpec, Unpack
|
||||||
|
|
||||||
T = TypeVar("T")
|
T = TypeVar("T")
|
||||||
S = TypeVar("S")
|
S = TypeVar("S")
|
||||||
|
P = ParamSpec("P")
|
||||||
|
Ts = TypeVarTuple("Ts")
|
||||||
|
|
||||||
class SingleTypevar(Generic[T]): ...
|
class SingleTypevar(Generic[T]): ...
|
||||||
class MultipleTypevars(Generic[T, S]): ...
|
class MultipleTypevars(Generic[T, S]): ...
|
||||||
|
class SingleParamSpec(Generic[P]): ...
|
||||||
|
class TypeVarAndParamSpec(Generic[P, T]): ...
|
||||||
|
class SingleTypeVarTuple(Generic[Unpack[Ts]]): ...
|
||||||
|
class TypeVarAndTypeVarTuple(Generic[T, Unpack[Ts]]): ...
|
||||||
|
|
||||||
# revealed: tuple[T@SingleTypevar]
|
# revealed: tuple[T@SingleTypevar]
|
||||||
reveal_type(generic_context(SingleTypevar))
|
reveal_type(generic_context(SingleTypevar))
|
||||||
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
|
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
|
||||||
reveal_type(generic_context(MultipleTypevars))
|
reveal_type(generic_context(MultipleTypevars))
|
||||||
|
|
||||||
|
# TODO: support `ParamSpec`/`TypeVarTuple` properly (these should not reveal `None`)
|
||||||
|
reveal_type(generic_context(SingleParamSpec)) # revealed: None
|
||||||
|
reveal_type(generic_context(TypeVarAndParamSpec)) # revealed: None
|
||||||
|
reveal_type(generic_context(SingleTypeVarTuple)) # revealed: None
|
||||||
|
reveal_type(generic_context(TypeVarAndTypeVarTuple)) # revealed: None
|
||||||
```
|
```
|
||||||
|
|
||||||
Inheriting from `Generic` multiple times yields a `duplicate-base` diagnostic, just like any other
|
Inheriting from `Generic` multiple times yields a `duplicate-base` diagnostic, just like any other
|
||||||
|
|
|
@ -7,19 +7,30 @@ python-version = "3.13"
|
||||||
|
|
||||||
## Defining a generic class
|
## Defining a generic class
|
||||||
|
|
||||||
At its simplest, to define a generic class using PEP 695 syntax, you add a list of typevars after
|
At its simplest, to define a generic class using PEP 695 syntax, you add a list of `TypeVar`s,
|
||||||
the class name.
|
`ParamSpec`s or `TypeVarTuple`s after the class name.
|
||||||
|
|
||||||
```py
|
```py
|
||||||
from ty_extensions import generic_context
|
from ty_extensions import generic_context
|
||||||
|
|
||||||
class SingleTypevar[T]: ...
|
class SingleTypevar[T]: ...
|
||||||
class MultipleTypevars[T, S]: ...
|
class MultipleTypevars[T, S]: ...
|
||||||
|
class SingleParamSpec[**P]: ...
|
||||||
|
class TypeVarAndParamSpec[T, **P]: ...
|
||||||
|
class SingleTypeVarTuple[*Ts]: ...
|
||||||
|
class TypeVarAndTypeVarTuple[T, *Ts]: ...
|
||||||
|
|
||||||
# revealed: tuple[T@SingleTypevar]
|
# revealed: tuple[T@SingleTypevar]
|
||||||
reveal_type(generic_context(SingleTypevar))
|
reveal_type(generic_context(SingleTypevar))
|
||||||
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
|
# revealed: tuple[T@MultipleTypevars, S@MultipleTypevars]
|
||||||
reveal_type(generic_context(MultipleTypevars))
|
reveal_type(generic_context(MultipleTypevars))
|
||||||
|
|
||||||
|
# TODO: support `ParamSpec`/`TypeVarTuple` properly
|
||||||
|
# (these should include the `ParamSpec`s and `TypeVarTuple`s in their generic contexts)
|
||||||
|
reveal_type(generic_context(SingleParamSpec)) # revealed: tuple[()]
|
||||||
|
reveal_type(generic_context(TypeVarAndParamSpec)) # revealed: tuple[T@TypeVarAndParamSpec]
|
||||||
|
reveal_type(generic_context(SingleTypeVarTuple)) # revealed: tuple[()]
|
||||||
|
reveal_type(generic_context(TypeVarAndTypeVarTuple)) # revealed: tuple[T@TypeVarAndTypeVarTuple]
|
||||||
```
|
```
|
||||||
|
|
||||||
You cannot use the same typevar more than once.
|
You cannot use the same typevar more than once.
|
||||||
|
|
|
@ -6374,6 +6374,8 @@ pub enum DynamicType {
|
||||||
/// A special Todo-variant for type aliases declared using `typing.TypeAlias`.
|
/// A special Todo-variant for type aliases declared using `typing.TypeAlias`.
|
||||||
/// A temporary variant to detect and special-case the handling of these aliases in autocomplete suggestions.
|
/// A temporary variant to detect and special-case the handling of these aliases in autocomplete suggestions.
|
||||||
TodoTypeAlias,
|
TodoTypeAlias,
|
||||||
|
/// A special Todo-variant for `Unpack[Ts]`, so that we can treat it specially in `Generic[Unpack[Ts]]`
|
||||||
|
TodoUnpack,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicType {
|
impl DynamicType {
|
||||||
|
@ -6398,6 +6400,13 @@ impl std::fmt::Display for DynamicType {
|
||||||
f.write_str("@Todo")
|
f.write_str("@Todo")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DynamicType::TodoUnpack => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
f.write_str("@Todo(typing.Unpack)")
|
||||||
|
} else {
|
||||||
|
f.write_str("@Todo")
|
||||||
|
}
|
||||||
|
}
|
||||||
DynamicType::TodoTypeAlias => {
|
DynamicType::TodoTypeAlias => {
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
f.write_str("@Todo(Support for `typing.TypeAlias`)")
|
f.write_str("@Todo(Support for `typing.TypeAlias`)")
|
||||||
|
|
|
@ -52,7 +52,8 @@ impl<'db> ClassBase<'db> {
|
||||||
ClassBase::Dynamic(
|
ClassBase::Dynamic(
|
||||||
DynamicType::Todo(_)
|
DynamicType::Todo(_)
|
||||||
| DynamicType::TodoPEP695ParamSpec
|
| DynamicType::TodoPEP695ParamSpec
|
||||||
| DynamicType::TodoTypeAlias,
|
| DynamicType::TodoTypeAlias
|
||||||
|
| DynamicType::TodoUnpack,
|
||||||
) => "@Todo",
|
) => "@Todo",
|
||||||
ClassBase::Protocol => "Protocol",
|
ClassBase::Protocol => "Protocol",
|
||||||
ClassBase::Generic => "Generic",
|
ClassBase::Generic => "Generic",
|
||||||
|
|
|
@ -7286,6 +7286,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
todo @ Type::Dynamic(
|
todo @ Type::Dynamic(
|
||||||
DynamicType::Todo(_)
|
DynamicType::Todo(_)
|
||||||
| DynamicType::TodoPEP695ParamSpec
|
| DynamicType::TodoPEP695ParamSpec
|
||||||
|
| DynamicType::TodoUnpack
|
||||||
| DynamicType::TodoTypeAlias,
|
| DynamicType::TodoTypeAlias,
|
||||||
),
|
),
|
||||||
_,
|
_,
|
||||||
|
@ -7296,6 +7297,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
todo @ Type::Dynamic(
|
todo @ Type::Dynamic(
|
||||||
DynamicType::Todo(_)
|
DynamicType::Todo(_)
|
||||||
| DynamicType::TodoPEP695ParamSpec
|
| DynamicType::TodoPEP695ParamSpec
|
||||||
|
| DynamicType::TodoUnpack
|
||||||
| DynamicType::TodoTypeAlias,
|
| DynamicType::TodoTypeAlias,
|
||||||
),
|
),
|
||||||
_,
|
_,
|
||||||
|
@ -8803,6 +8805,10 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
Some(todo_type!("doubly-specialized typing.Generic"))
|
Some(todo_type!("doubly-specialized typing.Generic"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(Type::SpecialForm(SpecialFormType::Unpack), _) => {
|
||||||
|
Some(Type::Dynamic(DynamicType::TodoUnpack))
|
||||||
|
}
|
||||||
|
|
||||||
(Type::SpecialForm(special_form), _) if special_form.class().is_special_form() => {
|
(Type::SpecialForm(special_form), _) if special_form.class().is_special_form() => {
|
||||||
Some(todo_type!("Inference of subscript on special form"))
|
Some(todo_type!("Inference of subscript on special form"))
|
||||||
}
|
}
|
||||||
|
@ -8969,6 +8975,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|typevar| match typevar {
|
.map(|typevar| match typevar {
|
||||||
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Ok(*typevar),
|
Type::KnownInstance(KnownInstanceType::TypeVar(typevar)) => Ok(*typevar),
|
||||||
|
Type::Dynamic(DynamicType::TodoUnpack) => Err(GenericContextError::NotYetSupported),
|
||||||
Type::NominalInstance(NominalInstanceType { class, .. })
|
Type::NominalInstance(NominalInstanceType { class, .. })
|
||||||
if matches!(
|
if matches!(
|
||||||
class.known(self.db()),
|
class.known(self.db()),
|
||||||
|
|
|
@ -262,6 +262,9 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
|
||||||
(DynamicType::TodoPEP695ParamSpec, _) => Ordering::Less,
|
(DynamicType::TodoPEP695ParamSpec, _) => Ordering::Less,
|
||||||
(_, DynamicType::TodoPEP695ParamSpec) => Ordering::Greater,
|
(_, DynamicType::TodoPEP695ParamSpec) => Ordering::Greater,
|
||||||
|
|
||||||
|
(DynamicType::TodoUnpack, _) => Ordering::Less,
|
||||||
|
(_, DynamicType::TodoUnpack) => Ordering::Greater,
|
||||||
|
|
||||||
(DynamicType::TodoTypeAlias, _) => Ordering::Less,
|
(DynamicType::TodoTypeAlias, _) => Ordering::Less,
|
||||||
(_, DynamicType::TodoTypeAlias) => Ordering::Greater,
|
(_, DynamicType::TodoTypeAlias) => Ordering::Greater,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue