more replacement

This commit is contained in:
Jack O'Connor
2025-08-18 16:01:24 -07:00
parent 5bbeb56feb
commit e7b093a371
11 changed files with 92 additions and 82 deletions

View File

@@ -1403,7 +1403,7 @@ mod implicit_globals {
fn module_type_symbols<'db>(db: &'db dyn Db) -> smallvec::SmallVec<[ast::name::Name; 8]> {
let Some(module_type) = KnownClass::ModuleType
.to_class_singleton(db)
.into_class_literal()
.into_class_singleton()
else {
// The most likely way we get here is if a user specified a `--custom-typeshed-dir`
// without a `types.pyi` stub in the `stdlib/` directory

View File

@@ -474,7 +474,7 @@ mod tests {
let model = SemanticModel::new(&db, foo);
let ty = class.inferred_type(&model);
assert!(ty.is_class_literal());
assert!(ty.is_class_singleton());
Ok(())
}
@@ -495,7 +495,7 @@ mod tests {
let model = SemanticModel::new(&db, bar);
let ty = alias.inferred_type(&model);
assert!(ty.is_class_literal());
assert!(ty.is_class_singleton());
Ok(())
}

View File

@@ -62,7 +62,7 @@ use crate::types::tuple::TupleSpec;
use crate::unpack::EvaluationMode;
pub use crate::util::diagnostics::add_inferred_python_version_hint_to_diagnostic;
use crate::{Db, FxOrderMap, FxOrderSet, Module, Program};
pub(crate) use class::{ClassLiteral, ClassSingletonType, ClassType, GenericAlias, KnownClass};
pub(crate) use class::{ClassSingletonType, ClassType, GenericAlias, KnownClass};
use instance::Protocol;
pub use instance::{NominalInstanceType, ProtocolInstanceType};
pub use special_form::SpecialFormType;
@@ -842,7 +842,7 @@ impl<'db> Type<'db> {
}
}
pub(crate) const fn into_class_literal(self) -> Option<ClassLiteral<'db>> {
pub(crate) const fn into_class_singleton(self) -> Option<ClassSingletonType<'db>> {
match self {
Type::ClassSingleton(class_type) => Some(class_type),
_ => None,
@@ -850,9 +850,9 @@ impl<'db> Type<'db> {
}
#[track_caller]
pub(crate) fn expect_class_literal(self) -> ClassLiteral<'db> {
self.into_class_literal()
.expect("Expected a Type::ClassLiteral variant")
pub(crate) fn expect_class_singleton(self) -> ClassSingletonType<'db> {
self.into_class_singleton()
.expect("Expected a Type::ClassSingleton variant")
}
pub(crate) const fn is_subclass_of(&self) -> bool {
@@ -860,7 +860,7 @@ impl<'db> Type<'db> {
}
#[cfg(test)]
pub(crate) const fn is_class_literal(&self) -> bool {
pub(crate) const fn is_class_singleton(&self) -> bool {
matches!(self, Type::ClassSingleton(..))
}
@@ -889,12 +889,12 @@ impl<'db> Type<'db> {
}
}
/// Turn a class literal (`Type::ClassLiteral` or `Type::GenericAlias`) into a `ClassType`.
/// Turn a class literal (`Type::ClassSingleton` or `Type::GenericAlias`) into a `ClassType`.
/// Since a `ClassType` must be specialized, apply the default specialization to any
/// unspecialized generic class literal.
pub(crate) fn to_class_type(self, db: &'db dyn Db) -> Option<ClassType<'db>> {
match self {
Type::ClassSingleton(class_literal) => Some(class_literal.default_specialization(db)),
Type::ClassSingleton(singleton) => Some(singleton.default_specialization(db)),
Type::GenericAlias(alias) => Some(ClassType::Generic(alias)),
_ => None,
}
@@ -1122,7 +1122,7 @@ impl<'db> Type<'db> {
/// any `T` of this type.
///
/// This is true for fully static types, but also for some types that may not be fully static.
/// For example, a `ClassLiteral` may inherit `Any`, but its subtyping is still reflexive.
/// For example, a `ClassSingleton` may inherit `Any`, but its subtyping is still reflexive.
///
/// This method may have false negatives, but it should not have false positives. It should be
/// a cheap shallow check, not an exhaustive recursive check.
@@ -1195,8 +1195,8 @@ impl<'db> Type<'db> {
None
}
}
Type::ClassSingleton(class_literal) => {
Some(ClassType::NonGeneric(class_literal).into_callable(db))
Type::ClassSingleton(singleton) => {
Some(ClassType::NonGeneric(singleton).into_callable(db))
}
Type::GenericAlias(alias) => Some(ClassType::Generic(alias).into_callable(db)),
@@ -1812,8 +1812,8 @@ impl<'db> Type<'db> {
return false;
}
let class_literal = instance.class(db).class_singleton(db).0;
is_single_member_enum(db, class_literal)
let singleton = instance.class(db).class_singleton(db).0;
is_single_member_enum(db, singleton)
}
_ => false,
}
@@ -2365,7 +2365,7 @@ impl<'db> Type<'db> {
Type::TypeVar(_) => false,
// We eagerly transform `SubclassOf` to `ClassLiteral` for final types, so `SubclassOf` is never a singleton.
// We eagerly transform `SubclassOf` to `ClassSingleton` for final types, so `SubclassOf` is never a singleton.
Type::SubclassOf(..) => false,
Type::BoundSuper(..) => false,
Type::BooleanLiteral(_)
@@ -5330,7 +5330,7 @@ impl<'db> Type<'db> {
/// If we see a value of this type used as a type expression, what type does it name?
///
/// For example, the builtin `int` as a value expression is of type
/// `Type::ClassLiteral(builtins.int)`, that is, it is the `int` class itself. As a type
/// `Type::ClassSingleton(ClassSingletonType::Literal(builtins.int))`, that is, it is the `int` class itself. As a type
/// expression, it names the type `Type::NominalInstance(builtins.int)`, that is, all objects whose
/// `__class__` is `int`.
///
@@ -6132,8 +6132,8 @@ impl<'db> Type<'db> {
Some(TypeDefinition::Function(function.definition(db)))
}
Self::ModuleLiteral(module) => Some(TypeDefinition::Module(module.module(db))),
Self::ClassSingleton(class_literal) => {
Some(TypeDefinition::Class(class_literal.definition(db)))
Self::ClassSingleton(singleton) => {
Some(TypeDefinition::Class(singleton.definition(db)))
}
Self::GenericAlias(alias) => Some(TypeDefinition::Class(alias.definition(db))),
Self::NominalInstance(instance) => {
@@ -6257,7 +6257,7 @@ impl<'db> Type<'db> {
}
}
pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option<ClassLiteral<'db>> {
pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option<ClassSingleton<'db>> {
match self {
Type::GenericAlias(generic) => Some(generic.origin(db)),
Type::NominalInstance(instance) => {
@@ -9015,7 +9015,7 @@ impl<'db> TypeAliasType<'db> {
#[derive(Debug, Clone, PartialEq, Eq, salsa::Update, get_size2::GetSize)]
pub(super) struct MetaclassCandidate<'db> {
metaclass: ClassType<'db>,
explicit_metaclass_of: ClassLiteral<'db>,
explicit_metaclass_of: ClassSingleton<'db>,
}
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
@@ -9497,7 +9497,7 @@ impl<'db> BytesLiteralType<'db> {
#[derive(PartialOrd, Ord)]
pub struct EnumLiteralType<'db> {
/// A reference to the enum class this literal belongs to
enum_class: ClassLiteral<'db>,
enum_class: ClassSingleton<'db>,
/// The name of the enum member
#[returns(ref)]
name: Name,
@@ -9523,8 +9523,8 @@ pub struct TypedDictType<'db> {
impl<'db> TypedDictType<'db> {
pub(crate) fn items(self, db: &'db dyn Db) -> FxOrderMap<Name, Field<'db>> {
let (class_literal, specialization) = self.defining_class.class_singleton(db);
class_literal.fields(db, specialization, CodeGeneratorKind::TypedDict)
let (singleton, specialization) = self.defining_class.class_singleton(db);
singleton.fields(db, specialization, CodeGeneratorKind::TypedDict)
}
pub(crate) fn apply_type_mapping_impl<'a>(
@@ -9646,8 +9646,8 @@ impl<'db> SuperOwnerKind<'db> {
fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option<Self> {
match ty {
Type::Dynamic(dynamic) => Some(SuperOwnerKind::Dynamic(dynamic)),
Type::ClassSingleton(class_literal) => Some(SuperOwnerKind::Class(
class_literal.apply_optional_specialization(db, None),
Type::ClassSingleton(singleton) => Some(SuperOwnerKind::Class(
singleton.apply_optional_specialization(db, None),
)),
Type::NominalInstance(instance) => Some(SuperOwnerKind::Instance(instance)),
Type::BooleanLiteral(_) => {
@@ -9862,7 +9862,7 @@ impl<'db> BoundSuperType<'db> {
SuperOwnerKind::Instance(instance) => instance.class(db),
};
let (class_literal, _) = class.class_singleton(db);
let (singleton, _) = class.class_singleton(db);
// TODO properly support super() with generic types
// * requires a fix for https://github.com/astral-sh/ruff/issues/17432
// * also requires understanding how we should handle cases like this:
@@ -9873,9 +9873,9 @@ impl<'db> BoundSuperType<'db> {
// super(B, b_int)
// super(B[int], b_unknown)
// ```
match class_literal.generic_context(db) {
match singleton.generic_context(db) {
Some(_) => Place::bound(todo_type!("super in generic class")).into(),
None => class_literal.class_member_from_mro(
None => singleton.class_member_from_mro(
db,
name,
policy,

View File

@@ -1175,7 +1175,7 @@ mod tests {
.ignore_possibly_unbound()
.unwrap();
let literals = enum_member_literals(&db, safe_uuid_class.expect_class_literal(), None)
let literals = enum_member_literals(&db, safe_uuid_class.expect_class_singleton(), None)
.unwrap()
.collect::<Vec<_>>();
assert_eq!(literals.len(), 3);

View File

@@ -760,7 +760,7 @@ impl<'db> Bindings<'db> {
Some(KnownFunction::IsProtocol) => {
if let [Some(ty)] = overload.parameter_types() {
overload.set_return_type(Type::BooleanLiteral(
ty.into_class_literal()
ty.into_class_singleton()
.is_some_and(|class| class.is_protocol(db)),
));
}

View File

@@ -554,8 +554,8 @@ impl<'db> ClassType<'db> {
/// Return the metaclass of this class, or `type[Unknown]` if the metaclass cannot be inferred.
pub(super) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
let (class_literal, specialization) = self.class_singleton(db);
class_literal
let (singleton, specialization) = self.class_singleton(db);
singleton
.metaclass(db)
.apply_optional_specialization(db, specialization)
}
@@ -642,8 +642,8 @@ impl<'db> ClassType<'db> {
name: &str,
policy: MemberLookupPolicy,
) -> PlaceAndQualifiers<'db> {
let (class_literal, specialization) = self.class_singleton(db);
class_literal.class_member_inner(db, specialization, name, policy)
let (singleton, specialization) = self.class_singleton(db);
singleton.class_member_inner(db, specialization, name, policy)
}
/// Returns the inferred type of the class member named `name`. Only bound members
@@ -674,10 +674,10 @@ impl<'db> ClassType<'db> {
Signature::new(parameters, Some(return_annotation))
}
let (class_literal, specialization) = self.class_singleton(db);
let (singleton, specialization) = self.class_singleton(db);
let fallback_member_lookup = || {
class_literal
singleton
.own_class_member(db, inherited_generic_context, specialization, name)
.map_type(|ty| ty.apply_optional_specialization(db, specialization))
};
@@ -694,7 +694,7 @@ impl<'db> ClassType<'db> {
};
match name {
"__len__" if class_literal.is_tuple(db) => {
"__len__" if singleton.is_tuple(db) => {
let return_type = specialization
.and_then(|spec| spec.tuple(db))
.and_then(|tuple| tuple.len().into_fixed_length())
@@ -705,7 +705,7 @@ impl<'db> ClassType<'db> {
synthesize_simple_tuple_method(return_type)
}
"__bool__" if class_literal.is_tuple(db) => {
"__bool__" if singleton.is_tuple(db) => {
let return_type = specialization
.and_then(|spec| spec.tuple(db))
.map(|tuple| tuple.truthiness().into_type(db))
@@ -714,7 +714,7 @@ impl<'db> ClassType<'db> {
synthesize_simple_tuple_method(return_type)
}
"__getitem__" if class_literal.is_tuple(db) => {
"__getitem__" if singleton.is_tuple(db) => {
specialization
.and_then(|spec| spec.tuple(db))
.map(|tuple| {
@@ -876,7 +876,7 @@ impl<'db> ClassType<'db> {
// @overload
// def __new__[T](cls: type[tuple[T, ...]], iterable: tuple[T, ...]) -> tuple[T, ...]: ...
// ```
"__new__" if class_literal.is_tuple(db) => {
"__new__" if singleton.is_tuple(db) => {
let mut iterable_parameter =
Parameter::positional_only(Some(Name::new_static("iterable")));
@@ -948,13 +948,13 @@ impl<'db> ClassType<'db> {
///
/// See [`Type::instance_member`] for more details.
pub(super) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
let (class_literal, specialization) = self.class_singleton(db);
let (singleton, specialization) = self.class_singleton(db);
if class_literal.is_typed_dict(db) {
if singleton.is_typed_dict(db) {
return Place::Unbound.into();
}
class_literal
singleton
.instance_member(db, specialization, name)
.map_type(|ty| ty.apply_optional_specialization(db, specialization))
}
@@ -962,8 +962,8 @@ impl<'db> ClassType<'db> {
/// A helper function for `instance_member` that looks up the `name` attribute only on
/// this class, not on its superclasses.
fn own_instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> {
let (class_literal, specialization) = self.class_singleton(db);
class_literal
let (singleton, specialization) = self.class_singleton(db);
singleton
.own_instance_member(db, name)
.map_type(|ty| ty.apply_optional_specialization(db, specialization))
}
@@ -1260,6 +1260,13 @@ impl<'db> ClassSingletonType<'db> {
Self::NewType(new_type) => new_type.iter_mro(db, specialization),
}
}
pub(super) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
match self {
Self::Literal(literal) => literal.metaclass(db),
Self::NewType(new_type) => new_type.metaclass(db),
}
}
}
impl<'db> From<ClassSingletonType<'db>> for Type<'db> {
@@ -1547,7 +1554,7 @@ impl<'db> ClassLiteral<'db> {
/// Determine if this is an abstract class.
pub(super) fn is_abstract(self, db: &'db dyn Db) -> bool {
self.metaclass(db)
.into_class_literal()
.into_class_singleton()
.is_some_and(|metaclass| metaclass.is_known(db, KnownClass::ABCMeta))
}
@@ -1776,33 +1783,33 @@ impl<'db> ClassLiteral<'db> {
continue;
};
if metaclass.is_subclass_of(db, candidate.metaclass) {
let (base_class_literal, _) = base_class.class_singleton(db);
let (base_class_singleton, _) = base_class.class_singleton(db);
candidate = MetaclassCandidate {
metaclass,
explicit_metaclass_of: base_class_literal,
explicit_metaclass_of: base_class_singleton,
};
continue;
}
if candidate.metaclass.is_subclass_of(db, metaclass) {
continue;
}
let (base_class_literal, _) = base_class.class_singleton(db);
let (base_class_singleton, _) = base_class.class_singleton(db);
return Err(MetaclassError {
kind: MetaclassErrorKind::Conflict {
candidate1: candidate,
candidate2: MetaclassCandidate {
metaclass,
explicit_metaclass_of: base_class_literal,
explicit_metaclass_of: base_class_singleton,
},
candidate1_is_base_class: explicit_metaclass.is_none(),
},
});
}
let (metaclass_literal, _) = candidate.metaclass.class_singleton(db);
let (metaclass_singleton, _) = candidate.metaclass.class_singleton(db);
Ok((
candidate.metaclass.into(),
metaclass_literal.dataclass_transformer_params(db),
metaclass_singleton.dataclass_transformer_params(db),
))
}
@@ -2174,7 +2181,7 @@ impl<'db> ClassLiteral<'db> {
(CodeGeneratorKind::NamedTuple, name) if name != "__init__" => {
KnownClass::NamedTupleFallback
.to_class_singleton(db)
.into_class_literal()?
.into_class_singleton()?
.own_class_member(db, self.generic_context(db), None, name)
.place
.ignore_possibly_unbound()
@@ -2357,9 +2364,9 @@ impl<'db> ClassLiteral<'db> {
.iter_mro(db, specialization)
.filter_map(|superclass| {
if let Some(class) = superclass.into_class() {
let (class_literal, specialization) = class.class_singleton(db);
if field_policy.matches(db, class_literal) {
Some((class_literal, specialization))
let (singleton, specialization) = class.class_singleton(db);
if field_policy.matches(db, singleton) {
Some((singleton, specialization))
} else {
None
}
@@ -3015,20 +3022,20 @@ impl<'db> ClassLiteral<'db> {
) -> bool {
let mut result = false;
for explicit_base in class.explicit_bases(db) {
let explicit_base_class_literal = match explicit_base {
Type::ClassSingleton(class_literal) => *class_literal,
let explicit_base_class_singleton = match explicit_base {
Type::ClassSingleton(singleton) => *singleton,
Type::GenericAlias(generic_alias) => generic_alias.origin(db),
_ => continue,
};
if !classes_on_stack.insert(explicit_base_class_literal) {
if !classes_on_stack.insert(explicit_base_class_singleton) {
return true;
}
if visited_classes.insert(explicit_base_class_literal) {
if visited_classes.insert(explicit_base_class_singleton) {
// If we find a cycle, keep searching to check if we can reach the starting class.
result |= is_cyclically_defined_recursive(
db,
explicit_base_class_literal,
explicit_base_class_singleton,
classes_on_stack,
visited_classes,
);
@@ -3117,12 +3124,16 @@ impl<'db> NewTypeClass<'db> {
db: &'db dyn Db,
specialization: Option<Specialization<'db>>,
) -> MroIterator<'db> {
self.parent(db).iter_mro(specialization)
self.parent(db).iter_mro_specialized(db, specialization)
}
pub(super) fn is_final(self, db: &'db dyn Db) -> bool {
self.parent(db).is_final(db)
}
pub(super) fn metaclass(self, db: &'db dyn Db) -> Type<'db> {
self.parent(db).metaclass(db)
}
}
impl<'db> get_size2::GetSize for NewTypeClass<'_> {}
@@ -3952,10 +3963,10 @@ impl KnownClass {
db: &'db dyn Db,
specialization: impl IntoIterator<Item = Type<'db>>,
) -> Option<ClassType<'db>> {
let Type::ClassSingleton(class_literal) = self.to_class_singleton(db) else {
let Type::ClassSingleton(singleton) = self.to_class_singleton(db) else {
return None;
};
let generic_context = class_literal.generic_context(db)?;
let generic_context = singleton.generic_context(db)?;
let types = specialization.into_iter().collect::<Box<[_]>>();
if types.len() != generic_context.len(db) {
@@ -3969,10 +3980,10 @@ impl KnownClass {
self.display(db)
);
}
return Some(class_literal.default_specialization(db));
return Some(singleton.default_specialization(db));
}
Some(class_literal.apply_specialization(db, |_| generic_context.specialize(db, types)))
Some(singleton.apply_specialization(db, |_| generic_context.specialize(db, types)))
}
/// Lookup a [`KnownClass`] in typeshed and return a [`Type`]
@@ -4039,9 +4050,8 @@ impl KnownClass {
match lookup_error {
KnownClassLookupError::ClassPossiblyUnbound {
class_singleton: class_literal,
..
} => Ok(class_literal),
class_singleton, ..
} => Ok(class_singleton),
KnownClassLookupError::ClassNotFound { .. }
| KnownClassLookupError::SymbolNotAClass { .. } => Err(()),
}

View File

@@ -1428,7 +1428,7 @@ impl KnownFunction {
return;
};
let Some(protocol_class) = param_type
.into_class_literal()
.into_class_singleton()
.and_then(|class| class.into_protocol_class(db))
else {
report_bad_argument_to_protocol_interface(

View File

@@ -35,7 +35,7 @@ fn enclosing_generic_contexts<'db>(
NodeWithScopeKind::Class(class) => {
let definition = index.expect_single_definition(class.node(module));
binding_type(db, definition)
.into_class_literal()?
.into_class_singleton()?
.generic_context(db)
}
NodeWithScopeKind::Function(function) => {

View File

@@ -384,7 +384,7 @@ pub(crate) fn nearest_enclosing_class<'db>(
infer_definition_types(db, definition)
.declaration_type(definition)
.inner_type()
.into_class_literal()
.into_class_singleton()
})
}
@@ -1083,7 +1083,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// Filter out class literals that result from imports
if let DefinitionKind::Class(class) = definition.kind(self.db()) {
ty.inner_type()
.into_class_literal()
.into_class_singleton()
.map(|class_literal| (class_literal, class.node(self.module())))
} else {
None
@@ -1524,7 +1524,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.index
.expect_single_definition(class_node_ref.node(self.module())),
)
.expect_class_literal();
.expect_class_singleton();
if class.is_protocol(self.db())
|| (class.is_abstract(self.db())
@@ -2361,7 +2361,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let class_stmt = class_scope.node().as_class(self.module())?;
let class_definition = self.index.expect_single_definition(class_stmt);
binding_type(self.db(), class_definition).into_class_literal()
binding_type(self.db(), class_definition).into_class_singleton()
}
/// If the current scope is a (non-lambda) function, return that function's AST node.
@@ -9055,7 +9055,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// TODO: properly handle old-style generics; get rid of this temporary hack
if !value_ty
.into_class_literal()
.into_class_singleton()
.is_some_and(|class| class.iter_mro(db, None).contains(&ClassBase::Generic))
{
report_non_subscriptable(context, value_node.into(), value_ty, "__class_getitem__");

View File

@@ -789,7 +789,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> {
let callable_type = inference.expression_type(&**callable);
if callable_type
.into_class_literal()
.into_class_singleton()
.is_some_and(|c| c.is_known(self.db, KnownClass::Type))
{
let place = self.expect_place(&target);

View File

@@ -145,7 +145,7 @@ impl Ty {
known_module_symbol(db, KnownModule::Uuid, "SafeUUID")
.place
.expect_type()
.expect_class_literal(),
.expect_class_singleton(),
Name::new(name),
)),
Ty::SingleMemberEnumLiteral => {
@@ -209,7 +209,7 @@ impl Ty {
builtins_symbol(db, s)
.place
.expect_type()
.expect_class_literal()
.expect_class_singleton()
.default_specialization(db),
),
Ty::SubclassOfAbcClass(s) => SubclassOfType::from(
@@ -217,7 +217,7 @@ impl Ty {
known_module_symbol(db, KnownModule::Abc, s)
.place
.expect_type()
.expect_class_literal()
.expect_class_singleton()
.default_specialization(db),
),
Ty::AlwaysTruthy => Type::AlwaysTruthy,