mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:44:56 +00:00
Fix problem with fallback classes (again)
This commit is contained in:
parent
3c097bfd59
commit
e1b548eaa2
5 changed files with 124 additions and 27 deletions
|
@ -277,7 +277,6 @@ reveal_type(Person._make(("Alice", 42))) # revealed: Unknown
|
||||||
|
|
||||||
person = Person("Alice", 42)
|
person = Person("Alice", 42)
|
||||||
|
|
||||||
# error: [invalid-argument-type] "Argument to bound method `_asdict` is incorrect: Expected `NamedTupleFallback`, found `Person`"
|
|
||||||
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
reveal_type(person._asdict()) # revealed: dict[str, Any]
|
||||||
reveal_type(person._replace(name="Bob")) # revealed: Person
|
reveal_type(person._replace(name="Bob")) # revealed: Person
|
||||||
```
|
```
|
||||||
|
|
|
@ -5356,12 +5356,10 @@ impl<'db> Type<'db> {
|
||||||
Some(generic_context) => (
|
Some(generic_context) => (
|
||||||
Some(class),
|
Some(class),
|
||||||
Some(generic_context),
|
Some(generic_context),
|
||||||
Type::from(class.apply_specialization(db, |_| {
|
// It is important that identity_specialization specializes the class with
|
||||||
// It is important that identity_specialization specializes the class with
|
// _inferable_ typevars, so that our specialization inference logic will
|
||||||
// _inferable_ typevars, so that our specialization inference logic will
|
// try to find a specialization for them.
|
||||||
// try to find a specialization for them.
|
Type::from(class.identity_specialization(db, Type::TypeVar)),
|
||||||
generic_context.identity_specialization(db)
|
|
||||||
})),
|
|
||||||
),
|
),
|
||||||
_ => (None, None, self),
|
_ => (None, None, self),
|
||||||
},
|
},
|
||||||
|
|
|
@ -1527,6 +1527,18 @@ impl<'db> ClassLiteral<'db> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a specialization of this class where each typevar is mapped to itself. The second
|
||||||
|
/// parameter can be `Type::TypeVar` or `Type::NonInferableTypeVar`, depending on the use case.
|
||||||
|
pub(crate) fn identity_specialization(
|
||||||
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
typevar_to_type: impl Fn(BoundTypeVarInstance<'db>) -> Type<'db>,
|
||||||
|
) -> ClassType<'db> {
|
||||||
|
self.apply_specialization(db, |generic_context| {
|
||||||
|
generic_context.identity_specialization(db, typevar_to_type)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Return an iterator over the inferred types of this class's *explicit* bases.
|
/// Return an iterator over the inferred types of this class's *explicit* bases.
|
||||||
///
|
///
|
||||||
/// Note that any class (except for `object`) that has no explicit
|
/// Note that any class (except for `object`) that has no explicit
|
||||||
|
@ -2625,7 +2637,14 @@ impl<'db> ClassLiteral<'db> {
|
||||||
.map_type(|ty|
|
.map_type(|ty|
|
||||||
ty.apply_type_mapping(
|
ty.apply_type_mapping(
|
||||||
db,
|
db,
|
||||||
&TypeMapping::ReplaceSelf {new_upper_bound: determine_upper_bound(db, self, specialization, ClassBase::is_typed_dict) }
|
&TypeMapping::ReplaceSelf {
|
||||||
|
new_upper_bound: determine_upper_bound(
|
||||||
|
db,
|
||||||
|
self,
|
||||||
|
specialization,
|
||||||
|
ClassBase::is_typed_dict
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -4174,6 +4193,90 @@ impl KnownClass {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return `true` if this class is a typeshed fallback class which is used to provide attributes and
|
||||||
|
/// methods for another type (e.g. `NamedTupleFallback` for actual `NamedTuple`s). These fallback
|
||||||
|
/// classes need special treatment in some places. For example, implicit usages of `Self` should not
|
||||||
|
/// be eagerly replaced with the fallback class itself. Instead, `Self` should eventually be treated
|
||||||
|
/// as referring to the destination type (e.g. the actual `NamedTuple`).
|
||||||
|
pub(crate) const fn is_fallback_class(self) -> bool {
|
||||||
|
match self {
|
||||||
|
KnownClass::Bool
|
||||||
|
| KnownClass::Object
|
||||||
|
| KnownClass::Bytes
|
||||||
|
| KnownClass::Bytearray
|
||||||
|
| KnownClass::Type
|
||||||
|
| KnownClass::Int
|
||||||
|
| KnownClass::Float
|
||||||
|
| KnownClass::Complex
|
||||||
|
| KnownClass::Str
|
||||||
|
| KnownClass::List
|
||||||
|
| KnownClass::Tuple
|
||||||
|
| KnownClass::Set
|
||||||
|
| KnownClass::FrozenSet
|
||||||
|
| KnownClass::Dict
|
||||||
|
| KnownClass::Slice
|
||||||
|
| KnownClass::Property
|
||||||
|
| KnownClass::BaseException
|
||||||
|
| KnownClass::Exception
|
||||||
|
| KnownClass::BaseExceptionGroup
|
||||||
|
| KnownClass::ExceptionGroup
|
||||||
|
| KnownClass::Staticmethod
|
||||||
|
| KnownClass::Classmethod
|
||||||
|
| KnownClass::Super
|
||||||
|
| KnownClass::Enum
|
||||||
|
| KnownClass::EnumType
|
||||||
|
| KnownClass::Auto
|
||||||
|
| KnownClass::Member
|
||||||
|
| KnownClass::Nonmember
|
||||||
|
| KnownClass::StrEnum
|
||||||
|
| KnownClass::ABCMeta
|
||||||
|
| KnownClass::GenericAlias
|
||||||
|
| KnownClass::ModuleType
|
||||||
|
| KnownClass::FunctionType
|
||||||
|
| KnownClass::MethodType
|
||||||
|
| KnownClass::MethodWrapperType
|
||||||
|
| KnownClass::WrapperDescriptorType
|
||||||
|
| KnownClass::UnionType
|
||||||
|
| KnownClass::GeneratorType
|
||||||
|
| KnownClass::AsyncGeneratorType
|
||||||
|
| KnownClass::CoroutineType
|
||||||
|
| KnownClass::NotImplementedType
|
||||||
|
| KnownClass::BuiltinFunctionType
|
||||||
|
| KnownClass::EllipsisType
|
||||||
|
| KnownClass::NoneType
|
||||||
|
| KnownClass::Awaitable
|
||||||
|
| KnownClass::Generator
|
||||||
|
| KnownClass::Deprecated
|
||||||
|
| KnownClass::StdlibAlias
|
||||||
|
| KnownClass::SpecialForm
|
||||||
|
| KnownClass::TypeVar
|
||||||
|
| KnownClass::ParamSpec
|
||||||
|
| KnownClass::ParamSpecArgs
|
||||||
|
| KnownClass::ParamSpecKwargs
|
||||||
|
| KnownClass::ProtocolMeta
|
||||||
|
| KnownClass::TypeVarTuple
|
||||||
|
| KnownClass::TypeAliasType
|
||||||
|
| KnownClass::NoDefaultType
|
||||||
|
| KnownClass::NewType
|
||||||
|
| KnownClass::SupportsIndex
|
||||||
|
| KnownClass::Iterable
|
||||||
|
| KnownClass::Iterator
|
||||||
|
| KnownClass::ChainMap
|
||||||
|
| KnownClass::Counter
|
||||||
|
| KnownClass::DefaultDict
|
||||||
|
| KnownClass::Deque
|
||||||
|
| KnownClass::OrderedDict
|
||||||
|
| KnownClass::VersionInfo
|
||||||
|
| KnownClass::Field
|
||||||
|
| KnownClass::KwOnly
|
||||||
|
| KnownClass::NamedTupleLike
|
||||||
|
| KnownClass::Template
|
||||||
|
| KnownClass::ConstraintSet
|
||||||
|
| KnownClass::InitVar => false,
|
||||||
|
KnownClass::NamedTupleFallback | KnownClass::TypedDictFallback => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn name(self, db: &dyn Db) -> &'static str {
|
pub(crate) fn name(self, db: &dyn Db) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
Self::Bool => "bool",
|
Self::Bool => "bool",
|
||||||
|
|
|
@ -107,28 +107,21 @@ pub(crate) fn get_self_type<'db>(
|
||||||
typevar_binding_context: Option<Definition<'db>>,
|
typevar_binding_context: Option<Definition<'db>>,
|
||||||
class: ClassLiteral<'db>,
|
class: ClassLiteral<'db>,
|
||||||
) -> Option<BoundTypeVarInstance<'db>> {
|
) -> Option<BoundTypeVarInstance<'db>> {
|
||||||
// TODO: remove duplication with Type::in_type_expression
|
|
||||||
let module = parsed_module(db, scope_id.file(db)).load(db);
|
let module = parsed_module(db, scope_id.file(db)).load(db);
|
||||||
let index = semantic_index(db, scope_id.file(db));
|
let index = semantic_index(db, scope_id.file(db));
|
||||||
|
|
||||||
let upper_bound = Type::instance(
|
|
||||||
db,
|
|
||||||
class.apply_specialization(db, |generic_context| {
|
|
||||||
let types = generic_context
|
|
||||||
.variables(db)
|
|
||||||
.iter()
|
|
||||||
.map(|typevar| Type::NonInferableTypeVar(*typevar));
|
|
||||||
|
|
||||||
generic_context.specialize(db, types.collect())
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
let class_definition = class.definition(db);
|
let class_definition = class.definition(db);
|
||||||
let typevar = TypeVarInstance::new(
|
let typevar = TypeVarInstance::new(
|
||||||
db,
|
db,
|
||||||
ast::name::Name::new_static("Self"),
|
ast::name::Name::new_static("Self"),
|
||||||
Some(class_definition),
|
Some(class_definition),
|
||||||
Some(TypeVarBoundOrConstraints::UpperBound(upper_bound).into()),
|
Some(
|
||||||
|
TypeVarBoundOrConstraints::UpperBound(Type::instance(
|
||||||
|
db,
|
||||||
|
class.identity_specialization(db, Type::NonInferableTypeVar),
|
||||||
|
))
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
// According to the [spec], we can consider `Self`
|
// According to the [spec], we can consider `Self`
|
||||||
// equivalent to an invariant type variable
|
// equivalent to an invariant type variable
|
||||||
// [spec]: https://typing.python.org/en/latest/spec/generics.html#self
|
// [spec]: https://typing.python.org/en/latest/spec/generics.html#self
|
||||||
|
@ -334,14 +327,17 @@ impl<'db> GenericContext<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a specialization of this generic context where each typevar is mapped to itself.
|
/// Returns a specialization of this generic context where each typevar is mapped to itself.
|
||||||
/// (And in particular, to an _inferable_ version of itself, since this will be used in our
|
/// The second parameter can be `Type::TypeVar` or `Type::NonInferableTypeVar`, depending on
|
||||||
/// class constructor invocation machinery to infer a specialization for the class from the
|
/// the use case.
|
||||||
/// arguments passed to its constructor.)
|
pub(crate) fn identity_specialization(
|
||||||
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
self,
|
||||||
|
db: &'db dyn Db,
|
||||||
|
typevar_to_type: impl Fn(BoundTypeVarInstance<'db>) -> Type<'db>,
|
||||||
|
) -> Specialization<'db> {
|
||||||
let types = self
|
let types = self
|
||||||
.variables(db)
|
.variables(db)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|typevar| Type::TypeVar(*typevar))
|
.map(|typevar| typevar_to_type(*typevar))
|
||||||
.collect();
|
.collect();
|
||||||
self.specialize(db, types)
|
self.specialize(db, types)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1285,6 +1285,7 @@ impl<'db> Parameters<'db> {
|
||||||
});
|
});
|
||||||
let implicit_annotation = if !method_has_self_in_generic_context
|
let implicit_annotation = if !method_has_self_in_generic_context
|
||||||
&& class.is_not_generic()
|
&& class.is_not_generic()
|
||||||
|
&& !class.known(db).is_some_and(KnownClass::is_fallback_class)
|
||||||
{
|
{
|
||||||
Type::instance(db, class)
|
Type::instance(db, class)
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue