mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 13:51:16 +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)
|
||||
|
||||
# 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._replace(name="Bob")) # revealed: Person
|
||||
```
|
||||
|
|
|
@ -5356,12 +5356,10 @@ impl<'db> Type<'db> {
|
|||
Some(generic_context) => (
|
||||
Some(class),
|
||||
Some(generic_context),
|
||||
Type::from(class.apply_specialization(db, |_| {
|
||||
// It is important that identity_specialization specializes the class with
|
||||
// _inferable_ typevars, so that our specialization inference logic will
|
||||
// try to find a specialization for them.
|
||||
generic_context.identity_specialization(db)
|
||||
})),
|
||||
// It is important that identity_specialization specializes the class with
|
||||
// _inferable_ typevars, so that our specialization inference logic will
|
||||
// try to find a specialization for them.
|
||||
Type::from(class.identity_specialization(db, Type::TypeVar)),
|
||||
),
|
||||
_ => (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.
|
||||
///
|
||||
/// Note that any class (except for `object`) that has no explicit
|
||||
|
@ -2625,7 +2637,14 @@ impl<'db> ClassLiteral<'db> {
|
|||
.map_type(|ty|
|
||||
ty.apply_type_mapping(
|
||||
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 {
|
||||
match self {
|
||||
Self::Bool => "bool",
|
||||
|
|
|
@ -107,28 +107,21 @@ pub(crate) fn get_self_type<'db>(
|
|||
typevar_binding_context: Option<Definition<'db>>,
|
||||
class: ClassLiteral<'db>,
|
||||
) -> Option<BoundTypeVarInstance<'db>> {
|
||||
// TODO: remove duplication with Type::in_type_expression
|
||||
let module = parsed_module(db, scope_id.file(db)).load(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 typevar = TypeVarInstance::new(
|
||||
db,
|
||||
ast::name::Name::new_static("Self"),
|
||||
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`
|
||||
// equivalent to an invariant type variable
|
||||
// [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.
|
||||
/// (And in particular, to an _inferable_ version of itself, since this will be used in our
|
||||
/// class constructor invocation machinery to infer a specialization for the class from the
|
||||
/// arguments passed to its constructor.)
|
||||
pub(crate) fn identity_specialization(self, db: &'db dyn Db) -> Specialization<'db> {
|
||||
/// 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>,
|
||||
) -> Specialization<'db> {
|
||||
let types = self
|
||||
.variables(db)
|
||||
.iter()
|
||||
.map(|typevar| Type::TypeVar(*typevar))
|
||||
.map(|typevar| typevar_to_type(*typevar))
|
||||
.collect();
|
||||
self.specialize(db, types)
|
||||
}
|
||||
|
|
|
@ -1285,6 +1285,7 @@ impl<'db> Parameters<'db> {
|
|||
});
|
||||
let implicit_annotation = if !method_has_self_in_generic_context
|
||||
&& class.is_not_generic()
|
||||
&& !class.known(db).is_some_and(KnownClass::is_fallback_class)
|
||||
{
|
||||
Type::instance(db, class)
|
||||
} else {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue