mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-09 13:18:18 +00:00
[ty] Reduce false positives for TypedDict
types (#19354)
This commit is contained in:
parent
f3a27406c9
commit
002f9057db
8 changed files with 34 additions and 11 deletions
|
@ -245,7 +245,7 @@ class D(TypedDict):
|
||||||
td = D(x=1, label="a")
|
td = D(x=1, label="a")
|
||||||
td["x"] = 0
|
td["x"] = 0
|
||||||
# TODO: should be Literal[0]
|
# TODO: should be Literal[0]
|
||||||
reveal_type(td["x"]) # revealed: @Todo(TypedDict)
|
reveal_type(td["x"]) # revealed: @Todo(Support for `TypedDict`)
|
||||||
|
|
||||||
# error: [unresolved-reference]
|
# error: [unresolved-reference]
|
||||||
does["not"]["exist"] = 0
|
does["not"]["exist"] = 0
|
||||||
|
|
|
@ -10,8 +10,6 @@ class Person(TypedDict):
|
||||||
name: str
|
name: str
|
||||||
age: int | None
|
age: int | None
|
||||||
|
|
||||||
# TODO: This should not be an error:
|
|
||||||
# error: [invalid-assignment]
|
|
||||||
alice: Person = {"name": "Alice", "age": 30}
|
alice: Person = {"name": "Alice", "age": 30}
|
||||||
|
|
||||||
# Alternative syntax
|
# Alternative syntax
|
||||||
|
@ -22,6 +20,6 @@ msg = Message(id=1, content="Hello")
|
||||||
# No errors for yet-unsupported features (`closed`):
|
# No errors for yet-unsupported features (`closed`):
|
||||||
OtherMessage = TypedDict("OtherMessage", {"id": int, "content": str}, closed=True)
|
OtherMessage = TypedDict("OtherMessage", {"id": int, "content": str}, closed=True)
|
||||||
|
|
||||||
reveal_type(Person.__required_keys__) # revealed: @Todo(TypedDict)
|
reveal_type(Person.__required_keys__) # revealed: @Todo(Support for `TypedDict`)
|
||||||
reveal_type(Message.__required_keys__) # revealed: @Todo(TypedDict)
|
reveal_type(Message.__required_keys__) # revealed: @Todo(Support for `TypedDict`)
|
||||||
```
|
```
|
||||||
|
|
|
@ -5880,6 +5880,9 @@ 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 classes inheriting from `TypedDict`.
|
||||||
|
/// A temporary variant to avoid false positives while we wait for full support.
|
||||||
|
TodoTypedDict,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DynamicType {
|
impl DynamicType {
|
||||||
|
@ -5911,6 +5914,13 @@ impl std::fmt::Display for DynamicType {
|
||||||
f.write_str("@Todo")
|
f.write_str("@Todo")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DynamicType::TodoTypedDict => {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
f.write_str("@Todo(Support for `TypedDict`)")
|
||||||
|
} else {
|
||||||
|
f.write_str("@Todo")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -985,7 +985,7 @@ impl<'db> Bindings<'db> {
|
||||||
},
|
},
|
||||||
|
|
||||||
Type::SpecialForm(SpecialFormType::TypedDict) => {
|
Type::SpecialForm(SpecialFormType::TypedDict) => {
|
||||||
overload.set_return_type(todo_type!("TypedDict"));
|
overload.set_return_type(todo_type!("Support for `TypedDict`"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a special case
|
// Not a special case
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::types::signatures::{CallableSignature, Parameter, Parameters, Signatu
|
||||||
use crate::types::tuple::TupleType;
|
use crate::types::tuple::TupleType;
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
BareTypeAliasType, Binding, BoundSuperError, BoundSuperType, CallableType, DataclassParams,
|
||||||
KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
|
DynamicType, KnownInstanceType, TypeAliasType, TypeMapping, TypeRelation, TypeTransformer,
|
||||||
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
|
TypeVarBoundOrConstraints, TypeVarInstance, TypeVarKind, infer_definition_types,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -415,6 +415,15 @@ impl<'db> ClassType<'db> {
|
||||||
other: Self,
|
other: Self,
|
||||||
relation: TypeRelation,
|
relation: TypeRelation,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
// TODO: remove this branch once we have proper support for TypedDicts.
|
||||||
|
if self.is_known(db, KnownClass::Dict)
|
||||||
|
&& other
|
||||||
|
.iter_mro(db)
|
||||||
|
.any(|b| matches!(b, ClassBase::Dynamic(DynamicType::TodoTypedDict)))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
self.iter_mro(db).any(|base| {
|
self.iter_mro(db).any(|base| {
|
||||||
match base {
|
match base {
|
||||||
ClassBase::Dynamic(_) => match relation {
|
ClassBase::Dynamic(_) => match relation {
|
||||||
|
|
|
@ -51,7 +51,8 @@ impl<'db> ClassBase<'db> {
|
||||||
ClassBase::Dynamic(
|
ClassBase::Dynamic(
|
||||||
DynamicType::Todo(_)
|
DynamicType::Todo(_)
|
||||||
| DynamicType::TodoPEP695ParamSpec
|
| DynamicType::TodoPEP695ParamSpec
|
||||||
| DynamicType::TodoTypeAlias,
|
| DynamicType::TodoTypeAlias
|
||||||
|
| DynamicType::TodoTypedDict,
|
||||||
) => "@Todo",
|
) => "@Todo",
|
||||||
ClassBase::Protocol => "Protocol",
|
ClassBase::Protocol => "Protocol",
|
||||||
ClassBase::Generic => "Generic",
|
ClassBase::Generic => "Generic",
|
||||||
|
@ -229,7 +230,7 @@ impl<'db> ClassBase<'db> {
|
||||||
SpecialFormType::OrderedDict => {
|
SpecialFormType::OrderedDict => {
|
||||||
Self::try_from_type(db, KnownClass::OrderedDict.to_class_literal(db))
|
Self::try_from_type(db, KnownClass::OrderedDict.to_class_literal(db))
|
||||||
}
|
}
|
||||||
SpecialFormType::TypedDict => Self::try_from_type(db, todo_type!("TypedDict")),
|
SpecialFormType::TypedDict => Some(Self::Dynamic(DynamicType::TodoTypedDict)),
|
||||||
SpecialFormType::Callable => {
|
SpecialFormType::Callable => {
|
||||||
Self::try_from_type(db, todo_type!("Support for Callable as a base class"))
|
Self::try_from_type(db, todo_type!("Support for Callable as a base class"))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6508,7 +6508,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
todo @ Type::Dynamic(
|
todo @ Type::Dynamic(
|
||||||
DynamicType::Todo(_)
|
DynamicType::Todo(_)
|
||||||
| DynamicType::TodoPEP695ParamSpec
|
| DynamicType::TodoPEP695ParamSpec
|
||||||
| DynamicType::TodoTypeAlias,
|
| DynamicType::TodoTypeAlias
|
||||||
|
| DynamicType::TodoTypedDict,
|
||||||
),
|
),
|
||||||
_,
|
_,
|
||||||
_,
|
_,
|
||||||
|
@ -6518,7 +6519,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
todo @ Type::Dynamic(
|
todo @ Type::Dynamic(
|
||||||
DynamicType::Todo(_)
|
DynamicType::Todo(_)
|
||||||
| DynamicType::TodoPEP695ParamSpec
|
| DynamicType::TodoPEP695ParamSpec
|
||||||
| DynamicType::TodoTypeAlias,
|
| DynamicType::TodoTypeAlias
|
||||||
|
| DynamicType::TodoTypedDict,
|
||||||
),
|
),
|
||||||
_,
|
_,
|
||||||
) => Some(todo),
|
) => Some(todo),
|
||||||
|
|
|
@ -253,6 +253,9 @@ fn dynamic_elements_ordering(left: DynamicType, right: DynamicType) -> Ordering
|
||||||
|
|
||||||
(DynamicType::TodoTypeAlias, _) => Ordering::Less,
|
(DynamicType::TodoTypeAlias, _) => Ordering::Less,
|
||||||
(_, DynamicType::TodoTypeAlias) => Ordering::Greater,
|
(_, DynamicType::TodoTypeAlias) => Ordering::Greater,
|
||||||
|
|
||||||
|
(DynamicType::TodoTypedDict, _) => Ordering::Less,
|
||||||
|
(_, DynamicType::TodoTypedDict) => Ordering::Greater,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue