diff --git a/crates/ty_ide/src/semantic_tokens.rs b/crates/ty_ide/src/semantic_tokens.rs index 749671ad1813c..99d77c9a67670 100644 --- a/crates/ty_ide/src/semantic_tokens.rs +++ b/crates/ty_ide/src/semantic_tokens.rs @@ -336,7 +336,7 @@ impl<'db> SemanticTokenVisitor<'db> { } match ty { - Type::ClassLiteral(_) => (SemanticTokenType::Class, modifiers), + Type::ClassSingleton(_) => (SemanticTokenType::Class, modifiers), Type::NonInferableTypeVar(_) | Type::TypeVar(_) => { (SemanticTokenType::TypeParameter, modifiers) } @@ -370,7 +370,7 @@ impl<'db> SemanticTokenVisitor<'db> { // Classify based on the inferred type of the attribute match ty { - Type::ClassLiteral(_) => (SemanticTokenType::Class, modifiers), + Type::ClassSingleton(_) => (SemanticTokenType::Class, modifiers), Type::FunctionLiteral(_) => { // This is a function accessed as an attribute, likely a method (SemanticTokenType::Method, modifiers) @@ -1373,10 +1373,10 @@ from typing import List class MyClass: CONSTANT = 42 - + def method(self): return \"hello\" - + @property def prop(self): return self.CONSTANT @@ -1442,7 +1442,7 @@ u = List.__name__ # __name__ should be variable " class MyClass: some_attr = \"value\" - + obj = MyClass() # Test attribute that might not have detailed semantic info x = obj.some_attr # Should fall back to variable, not property @@ -1476,10 +1476,10 @@ class MyClass: lower_case = 24 MixedCase = 12 A = 1 - + obj = MyClass() x = obj.UPPER_CASE # Should have readonly modifier -y = obj.lower_case # Should not have readonly modifier +y = obj.lower_case # Should not have readonly modifier z = obj.MixedCase # Should not have readonly modifier w = obj.A # Should not have readonly modifier (length == 1) ", @@ -1604,7 +1604,7 @@ def test_function(param: int, other: MyClass) -> Optional[List[str]]: x: int = 42 y: MyClass = MyClass() z: List[str] = [\"hello\"] - + # Type annotations should be Class tokens: # int, MyClass, Optional, List, str return None @@ -1683,7 +1683,7 @@ class MyProtocol(Protocol): # Value context - MyProtocol is still a class literal, so should be Class my_protocol_var = MyProtocol -# Type annotation context - should be Class +# Type annotation context - should be Class def test_function(param: MyProtocol) -> MyProtocol: return param ", @@ -1719,7 +1719,7 @@ def test_function(param: MyProtocol) -> MyProtocol: def func[T](x: T) -> T: return x -# Generic function with TypeVarTuple +# Generic function with TypeVarTuple def func_tuple[*Ts](args: tuple[*Ts]) -> tuple[*Ts]: return args @@ -1734,10 +1734,10 @@ class Container[T, U]: def __init__(self, value1: T, value2: U): self.value1: T = value1 self.value2: U = value2 - + def get_first(self) -> T: return self.value1 - + def get_second(self) -> U: return self.value2 @@ -1892,8 +1892,8 @@ class MyClass: fn test_implicitly_concatenated_strings() { let test = cursor_test( r#"x = "hello" "world" -y = ("multi" - "line" +y = ("multi" + "line" "string") z = 'single' "mixed" 'quotes'"#, ); @@ -1919,8 +1919,8 @@ z = 'single' "mixed" 'quotes'"#, fn test_bytes_literals() { let test = cursor_test( r#"x = b"hello" b"world" -y = (b"multi" - b"line" +y = (b"multi" + b"line" b"bytes") z = b'single' b"mixed" b'quotes'"#, ); @@ -2040,21 +2040,21 @@ y = "another_global" def outer(): x = "outer_value" z = "outer_local" - + def inner(): nonlocal x, z # These should be variable tokens global y # This should be a variable token x = "modified" y = "modified_global" z = "modified_local" - + def deeper(): nonlocal x # Variable token global y, x # Both should be variable tokens return x + y - + return deeper - + return inner "#, ); @@ -2100,11 +2100,11 @@ def outer(): def test(): global x nonlocal y - + # Multiple variables in one statement global a, b, c nonlocal d, e, f - + return x + y + a + b + c + d + e + f "#, ); diff --git a/crates/ty_python_semantic/src/place.rs b/crates/ty_python_semantic/src/place.rs index 41fc651dc113a..9ff8bcd61900a 100644 --- a/crates/ty_python_semantic/src/place.rs +++ b/crates/ty_python_semantic/src/place.rs @@ -1323,7 +1323,7 @@ mod implicit_globals { { return Place::Unbound.into(); } - let Type::ClassLiteral(module_type_class) = KnownClass::ModuleType.to_class_literal(db) + let Type::ClassSingleton(module_type_class) = KnownClass::ModuleType.to_class_singleton(db) else { return Place::Unbound.into(); }; @@ -1402,8 +1402,8 @@ mod implicit_globals { #[salsa::tracked(returns(deref), heap_size=ruff_memory_usage::heap_size)] fn module_type_symbols<'db>(db: &'db dyn Db) -> smallvec::SmallVec<[ast::name::Name; 8]> { let Some(module_type) = KnownClass::ModuleType - .to_class_literal(db) - .into_class_literal() + .to_class_singleton(db) + .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 diff --git a/crates/ty_python_semantic/src/semantic_model.rs b/crates/ty_python_semantic/src/semantic_model.rs index 1aeeb36db7778..d9ee52a026a32 100644 --- a/crates/ty_python_semantic/src/semantic_model.rs +++ b/crates/ty_python_semantic/src/semantic_model.rs @@ -215,7 +215,7 @@ impl<'db> Completion<'db> { | Type::Callable(_) => CompletionKind::Function, Type::BoundMethod(_) | Type::MethodWrapper(_) => CompletionKind::Method, Type::ModuleLiteral(_) => CompletionKind::Module, - Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::SubclassOf(_) => { + Type::ClassSingleton(_) | Type::GenericAlias(_) | Type::SubclassOf(_) => { CompletionKind::Class } // This is a little weird for "struct." I'm mostly interpreting @@ -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(()) } diff --git a/crates/ty_python_semantic/src/types.rs b/crates/ty_python_semantic/src/types.rs index 3440ee58b0590..fc925e5e81cbd 100644 --- a/crates/ty_python_semantic/src/types.rs +++ b/crates/ty_python_semantic/src/types.rs @@ -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, ClassType, GenericAlias, KnownClass}; +pub(crate) use class::{ClassSingletonType, ClassType, GenericAlias, KnownClass}; use instance::Protocol; pub use instance::{NominalInstanceType, ProtocolInstanceType}; pub use special_form::SpecialFormType; @@ -569,7 +569,7 @@ pub enum Type<'db> { /// A specific module object ModuleLiteral(ModuleLiteralType<'db>), /// A specific class object - ClassLiteral(ClassLiteral<'db>), + ClassSingleton(ClassSingletonType<'db>), /// A specialization of a generic class GenericAlias(GenericAlias<'db>), /// The set of all class objects that are subclasses of the given class (C), spelled `type[C]`. @@ -781,7 +781,7 @@ impl<'db> Type<'db> { | Type::KnownInstance(_) | Type::AlwaysFalsy | Type::AlwaysTruthy - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::BoundSuper(_) => *self, Type::PropertyInstance(property_instance) => { @@ -842,17 +842,17 @@ impl<'db> Type<'db> { } } - pub(crate) const fn into_class_literal(self) -> Option> { + pub(crate) const fn into_class_singleton(self) -> Option> { match self { - Type::ClassLiteral(class_type) => Some(class_type), + Type::ClassSingleton(class_type) => Some(class_type), _ => None, } } #[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,8 +860,8 @@ impl<'db> Type<'db> { } #[cfg(test)] - pub(crate) const fn is_class_literal(&self) -> bool { - matches!(self, Type::ClassLiteral(..)) + pub(crate) const fn is_class_singleton(&self) -> bool { + matches!(self, Type::ClassSingleton(..)) } pub(crate) fn into_enum_literal(self) -> Option> { @@ -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> { match self { - Type::ClassLiteral(class_literal) => Some(class_literal.default_specialization(db)), + Type::ClassSingleton(singleton) => Some(singleton.default_specialization(db)), Type::GenericAlias(alias) => Some(ClassType::Generic(alias)), _ => None, } @@ -1112,7 +1112,7 @@ impl<'db> Type<'db> { | Type::DataclassDecorator(_) | Type::DataclassTransformer(_) | Type::ModuleLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::SpecialForm(_) | Type::IntLiteral(_) => self, } @@ -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. @@ -1148,7 +1148,7 @@ impl<'db> Type<'db> { | Type::AlwaysTruthy | Type::PropertyInstance(_) // might inherit `Any`, but subtyping is still reflexive - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) => true, Type::Dynamic(_) | Type::NominalInstance(_) @@ -1195,8 +1195,8 @@ impl<'db> Type<'db> { None } } - Type::ClassLiteral(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)), @@ -1493,14 +1493,14 @@ impl<'db> Type<'db> { Type::StringLiteral(_) | Type::IntLiteral(_) | Type::BytesLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::FunctionLiteral(_) | Type::ModuleLiteral(_) | Type::EnumLiteral(_), Type::StringLiteral(_) | Type::IntLiteral(_) | Type::BytesLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::FunctionLiteral(_) | Type::ModuleLiteral(_) | Type::EnumLiteral(_), @@ -1613,18 +1613,20 @@ impl<'db> Type<'db> { // `Literal[]` is a subtype of `type[B]` if `C` is a subclass of `B`, // since `type[B]` describes all possible runtime subclasses of the class object `B`. - (Type::ClassLiteral(class), Type::SubclassOf(target_subclass_ty)) => target_subclass_ty - .subclass_of() - .into_class() - .map(|subclass_of_class| { - ClassType::NonGeneric(class).has_relation_to_impl( - db, - subclass_of_class, - relation, - visitor, - ) - }) - .unwrap_or(relation.is_assignability()), + (Type::ClassSingleton(class), Type::SubclassOf(target_subclass_ty)) => { + target_subclass_ty + .subclass_of() + .into_class() + .map(|subclass_of_class| { + ClassType::NonGeneric(class).has_relation_to_impl( + db, + subclass_of_class, + relation, + visitor, + ) + }) + .unwrap_or(relation.is_assignability()) + } (Type::GenericAlias(alias), Type::SubclassOf(target_subclass_ty)) => target_subclass_ty .subclass_of() .into_class() @@ -1646,7 +1648,7 @@ impl<'db> Type<'db> { // `Literal[str]` is a subtype of `type` because the `str` class object is an instance of its metaclass `type`. // `Literal[abc.ABC]` is a subtype of `abc.ABCMeta` because the `abc.ABC` class object // is an instance of its metaclass `abc.ABCMeta`. - (Type::ClassLiteral(class), _) => class + (Type::ClassSingleton(class), _) => class .metaclass_instance_type(db) .has_relation_to_impl(db, target, relation, visitor), (Type::GenericAlias(alias), _) => ClassType::from(alias) @@ -1810,8 +1812,8 @@ impl<'db> Type<'db> { return false; } - let class_literal = instance.class(db).class_literal(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, } @@ -1955,7 +1957,7 @@ impl<'db> Type<'db> { | Type::MethodWrapper(..) | Type::WrapperDescriptor(..) | Type::ModuleLiteral(..) - | Type::ClassLiteral(..) + | Type::ClassSingleton(..) | Type::GenericAlias(..) | Type::SpecialForm(..) | Type::KnownInstance(..)), @@ -1969,7 +1971,7 @@ impl<'db> Type<'db> { | Type::MethodWrapper(..) | Type::WrapperDescriptor(..) | Type::ModuleLiteral(..) - | Type::ClassLiteral(..) + | Type::ClassSingleton(..) | Type::GenericAlias(..) | Type::SpecialForm(..) | Type::KnownInstance(..)), @@ -2061,7 +2063,7 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::BytesLiteral(..) | Type::BooleanLiteral(..) - | Type::ClassLiteral(..) + | Type::ClassSingleton(..) | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) | Type::GenericAlias(..) @@ -2076,7 +2078,7 @@ impl<'db> Type<'db> { | Type::StringLiteral(..) | Type::BytesLiteral(..) | Type::BooleanLiteral(..) - | Type::ClassLiteral(..) + | Type::ClassSingleton(..) | Type::FunctionLiteral(..) | Type::ModuleLiteral(..) | Type::GenericAlias(..) @@ -2104,8 +2106,8 @@ impl<'db> Type<'db> { }) }), - (Type::SubclassOf(subclass_of_ty), Type::ClassLiteral(class_b)) - | (Type::ClassLiteral(class_b), Type::SubclassOf(subclass_of_ty)) => { + (Type::SubclassOf(subclass_of_ty), Type::ClassSingleton(class_b)) + | (Type::ClassSingleton(class_b), Type::SubclassOf(subclass_of_ty)) => { match subclass_of_ty.subclass_of() { SubclassOfInner::Dynamic(_) => false, SubclassOfInner::Class(class_a) => !class_b.is_subclass_of(db, None, class_a), @@ -2194,8 +2196,8 @@ impl<'db> Type<'db> { // A class-literal type `X` is always disjoint from an instance type `Y`, // unless the type expressing "all instances of `Z`" is a subtype of of `Y`, // where `Z` is `X`'s metaclass. - (Type::ClassLiteral(class), instance @ Type::NominalInstance(_)) - | (instance @ Type::NominalInstance(_), Type::ClassLiteral(class)) => !class + (Type::ClassSingleton(class), instance @ Type::NominalInstance(_)) + | (instance @ Type::NominalInstance(_), Type::ClassSingleton(class)) => !class .metaclass_instance_type(db) .is_subtype_of(db, instance), (Type::GenericAlias(alias), instance @ Type::NominalInstance(_)) @@ -2363,13 +2365,13 @@ 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(_) | Type::FunctionLiteral(..) | Type::WrapperDescriptor(..) - | Type::ClassLiteral(..) + | Type::ClassSingleton(..) | Type::GenericAlias(..) | Type::ModuleLiteral(..) | Type::EnumLiteral(..) => true, @@ -2439,7 +2441,7 @@ impl<'db> Type<'db> { | Type::WrapperDescriptor(_) | Type::MethodWrapper(_) | Type::ModuleLiteral(..) - | Type::ClassLiteral(..) + | Type::ClassSingleton(..) | Type::GenericAlias(..) | Type::IntLiteral(..) | Type::BooleanLiteral(..) @@ -2562,11 +2564,11 @@ impl<'db> Type<'db> { Type::Dynamic(_) | Type::Never => Some(Place::bound(self).into()), - Type::ClassLiteral(class) if class.is_typed_dict(db) => { + Type::ClassSingleton(class) if class.is_typed_dict(db) => { Some(class.typed_dict_member(db, None, name, policy)) } - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { match (class.known(db), name) { (Some(KnownClass::FunctionType), "__get__") => Some( Place::bound(Type::WrapperDescriptor( @@ -2610,7 +2612,7 @@ impl<'db> Type<'db> { // Note: `super(pivot, owner).__class__` is `builtins.super`, not the owner's class. // `BoundSuper` should look up the name in the MRO of `builtins.super`. Type::BoundSuper(_) => KnownClass::Super - .to_class_literal(db) + .to_class_singleton(db) .find_name_in_mro_with_policy(db, name, policy), // We eagerly normalize type[object], i.e. Type::SubclassOf(object) to `type`, @@ -2624,7 +2626,7 @@ impl<'db> Type<'db> { Some(Place::Unbound.into()) } else { KnownClass::Object - .to_class_literal(db) + .to_class_singleton(db) .find_name_in_mro_with_policy(db, name, policy) } } @@ -2819,7 +2821,7 @@ impl<'db> Type<'db> { // a `__dict__` that is filled with class level attributes. Modeling this is currently not // required, as `instance_member` is only called for instance-like types through `member`, // but we might want to add this in the future. - Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::SubclassOf(_) => { + Type::ClassSingleton(_) | Type::GenericAlias(_) | Type::SubclassOf(_) => { Place::Unbound.into() } @@ -3200,7 +3202,7 @@ impl<'db> Type<'db> { ) .into(), - Type::ClassLiteral(class) + Type::ClassSingleton(class) if name == "__get__" && class.is_known(db, KnownClass::FunctionType) => { Place::bound(Type::WrapperDescriptor( @@ -3208,7 +3210,7 @@ impl<'db> Type<'db> { )) .into() } - Type::ClassLiteral(class) + Type::ClassSingleton(class) if name == "__get__" && class.is_known(db, KnownClass::Property) => { Place::bound(Type::WrapperDescriptor( @@ -3216,7 +3218,7 @@ impl<'db> Type<'db> { )) .into() } - Type::ClassLiteral(class) + Type::ClassSingleton(class) if name == "__set__" && class.is_known(db, KnownClass::Property) => { Place::bound(Type::WrapperDescriptor( @@ -3402,7 +3404,7 @@ impl<'db> Type<'db> { } } - Type::ClassLiteral(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => { + Type::ClassSingleton(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => { let class_attr_plain = self.find_name_in_mro_with_policy(db, name_str,policy).expect( "Calling `find_name_in_mro` on class literals and subclass-of types should always return `Some`", ); @@ -3412,11 +3414,11 @@ impl<'db> Type<'db> { } if let Some(enum_class) = match self { - Type::ClassLiteral(literal) => Some(literal), + Type::ClassSingleton(literal) => Some(literal), Type::SubclassOf(subclass_of) => subclass_of .subclass_of() .into_class() - .map(|class| class.class_literal(db).0), + .map(|class| class.class_singleton(db).0), _ => None, } { if let Some(metadata) = enum_metadata(db, enum_class) { @@ -3633,7 +3635,7 @@ impl<'db> Type<'db> { Type::AlwaysFalsy => Truthiness::AlwaysFalse, - Type::ClassLiteral(class) => class + Type::ClassSingleton(class) => class .metaclass_instance_type(db) .try_bool_impl(db, allow_short_circuit)?, Type::GenericAlias(alias) => ClassType::from(*alias) @@ -4141,7 +4143,7 @@ impl<'db> Type<'db> { .into(), }, - Type::ClassLiteral(class) => match class.known(db) { + Type::ClassSingleton(class) => match class.known(db) { // TODO: Ideally we'd use `try_call_constructor` for all constructor calls. // Currently we don't for a few special known types, either because their // constructors are defined with overloads, or because we want to special case @@ -4257,6 +4259,20 @@ impl<'db> Type<'db> { .into() } + Some(KnownClass::NewType) => Binding::single( + self, + Signature::new( + Parameters::new([ + Parameter::positional_or_keyword(Name::new_static("name")) + .with_annotated_type(Type::LiteralString), + Parameter::positional_or_keyword(Name::new_static("tp")), + ]), + // TODO: What should this return type be? + Some(KnownClass::NewType.to_instance(db)), + ), + ) + .into(), + Some(KnownClass::Object) => { // ```py // class object: @@ -5047,7 +5063,7 @@ impl<'db> Type<'db> { let from_class_base = |base: ClassBase<'db>| { let class = base.into_class()?; if class.is_known(db, KnownClass::Generator) { - if let Some(specialization) = class.class_literal_specialized(db, None).1 { + if let Some(specialization) = class.singleton_specialized(db, None).1 { if let [_, _, return_ty] = specialization.types(db) { return Some(*return_ty); } @@ -5093,7 +5109,7 @@ impl<'db> Type<'db> { ) -> Result, ConstructorCallError<'db>> { debug_assert!(matches!( self, - Type::ClassLiteral(_) | Type::GenericAlias(_) | Type::SubclassOf(_) + Type::ClassSingleton(_) | Type::GenericAlias(_) | Type::SubclassOf(_) )); // If we are trying to construct a non-specialized generic class, we should use the @@ -5105,7 +5121,7 @@ impl<'db> Type<'db> { // do this, we instead use the _identity_ specialization, which maps each of the class's // generic typevars to itself. let (generic_origin, generic_context, self_type) = match self { - Type::ClassLiteral(class) => match class.generic_context(db) { + Type::ClassSingleton(class) => match class.generic_context(db) { Some(generic_context) => ( Some(class), Some(generic_context), @@ -5269,7 +5285,9 @@ impl<'db> Type<'db> { pub(crate) fn to_instance(self, db: &'db dyn Db) -> Option> { match self { Type::Dynamic(_) | Type::Never => Some(self), - Type::ClassLiteral(class) => Some(Type::instance(db, class.default_specialization(db))), + Type::ClassSingleton(class) => { + Some(Type::instance(db, class.default_specialization(db))) + } Type::GenericAlias(alias) => Some(Type::instance(db, ClassType::from(alias))), Type::SubclassOf(subclass_of_ty) => Some(subclass_of_ty.to_instance(db)), Type::Union(union) => union.to_instance(db), @@ -5312,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`. /// @@ -5327,7 +5345,7 @@ impl<'db> Type<'db> { match self { // Special cases for `float` and `complex` // https://typing.python.org/en/latest/spec/special-types.html#special-cases-for-float-and-complex - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { let ty = match class.known(db) { Some(KnownClass::Any) => Type::any(), Some(KnownClass::Complex) => UnionType::from_elements( @@ -5469,7 +5487,7 @@ impl<'db> Type<'db> { ], }); }; - let instance = Type::ClassLiteral(class).to_instance(db).expect( + let instance = Type::ClassSingleton(class).to_instance(db).expect( "nearest_enclosing_class must return type that can be instantiated", ); let class_definition = class.definition(db); @@ -5643,22 +5661,22 @@ impl<'db> Type<'db> { Type::NominalInstance(instance) => instance.to_meta_type(db), Type::KnownInstance(known_instance) => known_instance.to_meta_type(db), Type::SpecialForm(special_form) => special_form.to_meta_type(db), - Type::PropertyInstance(_) => KnownClass::Property.to_class_literal(db), + Type::PropertyInstance(_) => KnownClass::Property.to_class_singleton(db), Type::Union(union) => union.map(db, |ty| ty.to_meta_type(db)), - Type::BooleanLiteral(_) | Type::TypeIs(_) => KnownClass::Bool.to_class_literal(db), - Type::BytesLiteral(_) => KnownClass::Bytes.to_class_literal(db), - Type::IntLiteral(_) => KnownClass::Int.to_class_literal(db), - Type::EnumLiteral(enum_literal) => Type::ClassLiteral(enum_literal.enum_class(db)), - Type::FunctionLiteral(_) => KnownClass::FunctionType.to_class_literal(db), - Type::BoundMethod(_) => KnownClass::MethodType.to_class_literal(db), - Type::MethodWrapper(_) => KnownClass::MethodWrapperType.to_class_literal(db), - Type::WrapperDescriptor(_) => KnownClass::WrapperDescriptorType.to_class_literal(db), - Type::DataclassDecorator(_) => KnownClass::FunctionType.to_class_literal(db), + Type::BooleanLiteral(_) | Type::TypeIs(_) => KnownClass::Bool.to_class_singleton(db), + Type::BytesLiteral(_) => KnownClass::Bytes.to_class_singleton(db), + Type::IntLiteral(_) => KnownClass::Int.to_class_singleton(db), + Type::EnumLiteral(enum_literal) => Type::ClassSingleton(enum_literal.enum_class(db)), + Type::FunctionLiteral(_) => KnownClass::FunctionType.to_class_singleton(db), + Type::BoundMethod(_) => KnownClass::MethodType.to_class_singleton(db), + Type::MethodWrapper(_) => KnownClass::MethodWrapperType.to_class_singleton(db), + Type::WrapperDescriptor(_) => KnownClass::WrapperDescriptorType.to_class_singleton(db), + Type::DataclassDecorator(_) => KnownClass::FunctionType.to_class_singleton(db), Type::Callable(callable) if callable.is_function_like(db) => { - KnownClass::FunctionType.to_class_literal(db) + KnownClass::FunctionType.to_class_singleton(db) } Type::Callable(_) | Type::DataclassTransformer(_) => KnownClass::Type.to_instance(db), - Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class_literal(db), + Type::ModuleLiteral(_) => KnownClass::ModuleType.to_class_singleton(db), Type::NonInferableTypeVar(bound_typevar) => { match bound_typevar.typevar(db).bound_or_constraints(db) { None => KnownClass::Type.to_instance(db), @@ -5672,7 +5690,7 @@ impl<'db> Type<'db> { } Type::TypeVar(_) => KnownClass::Type.to_instance(db), - Type::ClassLiteral(class) => class.metaclass(db), + Type::ClassSingleton(class) => class.metaclass(db), Type::GenericAlias(alias) => ClassType::from(alias).metaclass(db), Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() { SubclassOfInner::Dynamic(_) => self, @@ -5683,7 +5701,7 @@ impl<'db> Type<'db> { ), }, - Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_literal(db), + Type::StringLiteral(_) | Type::LiteralString => KnownClass::Str.to_class_singleton(db), Type::Dynamic(dynamic) => SubclassOfType::from(db, SubclassOfInner::Dynamic(dynamic)), // TODO intersections Type::Intersection(_) => SubclassOfType::from( @@ -5692,7 +5710,7 @@ impl<'db> Type<'db> { .expect("Type::Todo should be a valid `SubclassOfInner`"), ), Type::AlwaysTruthy | Type::AlwaysFalsy => KnownClass::Type.to_instance(db), - Type::BoundSuper(_) => KnownClass::Super.to_class_literal(db), + Type::BoundSuper(_) => KnownClass::Super.to_class_singleton(db), Type::ProtocolInstance(protocol) => protocol.to_meta_type(db), Type::TypedDict(typed_dict) => SubclassOfType::from(db, typed_dict.defining_class), Type::TypeAlias(alias) => alias.value_type(db).to_meta_type(db), @@ -5919,7 +5937,7 @@ impl<'db> Type<'db> { // A non-generic class never needs to be specialized. A generic class is specialized // explicitly (via a subscript expression) or implicitly (via a call), and not because // some other generic context's specialization is applied to it. - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::BoundSuper(_) | Type::SpecialForm(_) | Type::KnownInstance(_) => self, @@ -6033,7 +6051,7 @@ impl<'db> Type<'db> { | Type::DataclassDecorator(_) | Type::DataclassTransformer(_) | Type::ModuleLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::IntLiteral(_) | Type::BooleanLiteral(_) | Type::LiteralString @@ -6114,8 +6132,8 @@ impl<'db> Type<'db> { Some(TypeDefinition::Function(function.definition(db))) } Self::ModuleLiteral(module) => Some(TypeDefinition::Module(module.module(db))), - Self::ClassLiteral(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) => { @@ -6239,7 +6257,7 @@ impl<'db> Type<'db> { } } - pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option> { + pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option> { match self { Type::GenericAlias(generic) => Some(generic.origin(db)), Type::NominalInstance(instance) => { @@ -6448,7 +6466,7 @@ impl<'db> KnownInstanceType<'db> { } fn to_meta_type(self, db: &'db dyn Db) -> Type<'db> { - self.class().to_class_literal(db) + self.class().to_class_singleton(db) } /// Return the instance type which this type is a subtype of. @@ -8997,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)] @@ -9479,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, @@ -9505,8 +9523,8 @@ pub struct TypedDictType<'db> { impl<'db> TypedDictType<'db> { pub(crate) fn items(self, db: &'db dyn Db) -> FxOrderMap> { - let (class_literal, specialization) = self.defining_class.class_literal(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>( @@ -9628,8 +9646,8 @@ impl<'db> SuperOwnerKind<'db> { fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option { match ty { Type::Dynamic(dynamic) => Some(SuperOwnerKind::Dynamic(dynamic)), - Type::ClassLiteral(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(_) => { @@ -9714,7 +9732,7 @@ impl<'db> BoundSuperType<'db> { // - There are objects that are not valid in a class's bases list // but are valid as pivot classes, e.g. unsubscripted `typing.Generic` let pivot_class = match pivot_class_type { - Type::ClassLiteral(class) => ClassBase::Class(ClassType::NonGeneric(class)), + Type::ClassSingleton(class) => ClassBase::Class(ClassType::NonGeneric(class)), Type::GenericAlias(class) => ClassBase::Class(ClassType::Generic(class)), Type::SubclassOf(subclass_of) if subclass_of.subclass_of().is_dynamic() => { ClassBase::Dynamic( @@ -9844,7 +9862,7 @@ impl<'db> BoundSuperType<'db> { SuperOwnerKind::Instance(instance) => instance.class(db), }; - let (class_literal, _) = class.class_literal(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: @@ -9855,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, diff --git a/crates/ty_python_semantic/src/types/builder.rs b/crates/ty_python_semantic/src/types/builder.rs index 0f6ba1782d3e5..d5d7081c2a66d 100644 --- a/crates/ty_python_semantic/src/types/builder.rs +++ b/crates/ty_python_semantic/src/types/builder.rs @@ -577,7 +577,7 @@ impl<'db> IntersectionBuilder<'db> { self } Type::NominalInstance(instance) - if enum_metadata(self.db, instance.class(self.db).class_literal(self.db).0) + if enum_metadata(self.db, instance.class(self.db).class_singleton(self.db).0) .is_some() => { let mut contains_enum_literal_as_negative_element = false; @@ -602,7 +602,7 @@ impl<'db> IntersectionBuilder<'db> { let db = self.db; self.add_positive(Type::Union(UnionType::new( db, - enum_member_literals(db, instance.class(db).class_literal(db).0, None) + enum_member_literals(db, instance.class(db).class_singleton(db).0, None) .expect("Calling `enum_member_literals` on an enum class") .collect::>(), ))) @@ -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::>(); assert_eq!(literals.len(), 3); diff --git a/crates/ty_python_semantic/src/types/call/arguments.rs b/crates/ty_python_semantic/src/types/call/arguments.rs index b200f3fc8af14..267d89b90be09 100644 --- a/crates/ty_python_semantic/src/types/call/arguments.rs +++ b/crates/ty_python_semantic/src/types/call/arguments.rs @@ -251,7 +251,8 @@ fn expand_type<'db>(db: &'db dyn Db, ty: Type<'db>) -> Option>> { }; } - if let Some(enum_members) = enum_member_literals(db, class.class_literal(db).0, None) { + if let Some(enum_members) = enum_member_literals(db, class.class_singleton(db).0, None) + { return Some(enum_members.collect()); } diff --git a/crates/ty_python_semantic/src/types/call/bind.rs b/crates/ty_python_semantic/src/types/call/bind.rs index e0642122253c9..97ff819203413 100644 --- a/crates/ty_python_semantic/src/types/call/bind.rs +++ b/crates/ty_python_semantic/src/types/call/bind.rs @@ -644,7 +644,7 @@ impl<'db> Bindings<'db> { // TODO: Handle generic functions, and unions/intersections of // generic types overload.set_return_type(match ty { - Type::ClassLiteral(class) => class + Type::ClassSingleton(class) => class .generic_context(db) .map(|generic_context| generic_context.as_tuple(db)) .unwrap_or_else(|| Type::none(db)), @@ -693,7 +693,7 @@ impl<'db> Bindings<'db> { Some(KnownFunction::EnumMembers) => { if let [Some(ty)] = overload.parameter_types() { let return_ty = match ty { - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { if let Some(metadata) = enums::enum_metadata(db, *class) { Type::heterogeneous_tuple( db, @@ -760,14 +760,15 @@ 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)), )); } } Some(KnownFunction::GetProtocolMembers) => { - if let [Some(Type::ClassLiteral(class))] = overload.parameter_types() { + if let [Some(Type::ClassSingleton(class))] = overload.parameter_types() + { if let Some(protocol_class) = class.into_protocol_class(db) { let member_names = protocol_class .interface(db) @@ -878,7 +879,7 @@ impl<'db> Bindings<'db> { } // `dataclass` being used as a non-decorator - if let [Some(Type::ClassLiteral(class_literal))] = + if let [Some(Type::ClassSingleton(class_literal))] = overload.parameter_types() { let params = DataclassParams::default(); @@ -1008,7 +1009,7 @@ impl<'db> Bindings<'db> { } }, - Type::ClassLiteral(class) => match class.known(db) { + Type::ClassSingleton(class) => match class.known(db) { Some(KnownClass::Bool) => match overload.parameter_types() { [Some(arg)] => overload.set_return_type(arg.bool(db).into_type(db)), [None] => overload.set_return_type(Type::BooleanLiteral(false)), @@ -1056,6 +1057,22 @@ impl<'db> Bindings<'db> { } } + Some(KnownClass::NewType) => match overload.parameter_types() { + [Some(Type::StringLiteral(name)), Some(supertype)] => { + let params = DataclassParams::default(); + overload.set_return_type(Type::from(ClassLiteral::new( + db, + ast::name::Name::new(name.value(db)), + what_goes_here, + None, + None, + None, + None, + ))); + } + _ => {} + }, + _ => {} }, @@ -2560,7 +2577,7 @@ impl<'db> CallableDescription<'db> { kind: "function", name: function.name(db), }), - Type::ClassLiteral(class_type) => Some(CallableDescription { + Type::ClassSingleton(class_type) => Some(CallableDescription { kind: "class", name: class_type.name(db), }), diff --git a/crates/ty_python_semantic/src/types/class.rs b/crates/ty_python_semantic/src/types/class.rs index 2dcb979fcd229..040a25cf7f38a 100644 --- a/crates/ty_python_semantic/src/types/class.rs +++ b/crates/ty_python_semantic/src/types/class.rs @@ -179,7 +179,7 @@ pub(crate) enum CodeGeneratorKind { } impl CodeGeneratorKind { - pub(crate) fn from_class(db: &dyn Db, class: ClassLiteral<'_>) -> Option { + pub(crate) fn from_class(db: &dyn Db, class: ClassSingletonType<'_>) -> Option { #[salsa::tracked( cycle_fn=code_generator_of_class_recover, cycle_initial=code_generator_of_class_initial, @@ -187,7 +187,7 @@ impl CodeGeneratorKind { )] fn code_generator_of_class<'db>( db: &'db dyn Db, - class: ClassLiteral<'db>, + class: ClassSingletonType<'db>, ) -> Option { if class.dataclass_params(db).is_some() || class @@ -209,7 +209,7 @@ impl CodeGeneratorKind { fn code_generator_of_class_initial( _db: &dyn Db, - _class: ClassLiteral<'_>, + _class: ClassSingletonType<'_>, ) -> Option { None } @@ -219,7 +219,7 @@ impl CodeGeneratorKind { _db: &dyn Db, _value: &Option, _count: u32, - _class: ClassLiteral<'_>, + _class: ClassSingletonType<'_>, ) -> salsa::CycleRecoveryAction> { salsa::CycleRecoveryAction::Iterate } @@ -227,7 +227,7 @@ impl CodeGeneratorKind { code_generator_of_class(db, class) } - pub(super) fn matches(self, db: &dyn Db, class: ClassLiteral<'_>) -> bool { + pub(super) fn matches(self, db: &dyn Db, class: ClassSingletonType<'_>) -> bool { CodeGeneratorKind::from_class(db, class) == Some(self) } } @@ -240,7 +240,7 @@ impl CodeGeneratorKind { #[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] #[derive(PartialOrd, Ord)] pub struct GenericAlias<'db> { - pub(crate) origin: ClassLiteral<'db>, + pub(crate) origin: ClassSingletonType<'db>, pub(crate) specialization: Specialization<'db>, } @@ -327,7 +327,7 @@ impl<'db> From> for Type<'db> { get_size2::GetSize, )] pub enum ClassType<'db> { - NonGeneric(ClassLiteral<'db>), + NonGeneric(ClassSingletonType<'db>), Generic(GenericAlias<'db>), } @@ -367,10 +367,10 @@ impl<'db> ClassType<'db> { /// Returns the class literal and specialization for this class. For a non-generic class, this /// is the class itself. For a generic alias, this is the alias's origin. - pub(crate) fn class_literal( + pub(crate) fn class_singleton( self, db: &'db dyn Db, - ) -> (ClassLiteral<'db>, Option>) { + ) -> (ClassSingletonType<'db>, Option>) { match self { Self::NonGeneric(non_generic) => (non_generic, None), Self::Generic(generic) => (generic.origin(db), Some(generic.specialization(db))), @@ -379,11 +379,11 @@ impl<'db> ClassType<'db> { /// Returns the class literal and specialization for this class, with an additional /// specialization applied if the class is generic. - pub(crate) fn class_literal_specialized( + pub(crate) fn singleton_specialized( self, db: &'db dyn Db, additional_specialization: Option>, - ) -> (ClassLiteral<'db>, Option>) { + ) -> (ClassSingletonType<'db>, Option>) { match self { Self::NonGeneric(non_generic) => (non_generic, None), Self::Generic(generic) => ( @@ -398,23 +398,28 @@ impl<'db> ClassType<'db> { } pub(crate) fn name(self, db: &'db dyn Db) -> &'db ast::name::Name { - let (class_literal, _) = self.class_literal(db); - class_literal.name(db) + let (singleton, _) = self.class_singleton(db); + singleton.name(db) } pub(crate) fn known(self, db: &'db dyn Db) -> Option { - let (class_literal, _) = self.class_literal(db); - class_literal.known(db) + let (singleton, _) = self.class_singleton(db); + singleton.known(db) } pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> { - let (class_literal, _) = self.class_literal(db); - class_literal.definition(db) + let (singleton, _) = self.class_singleton(db); + singleton.definition(db) + } + + pub(crate) fn is_typed_dict(self, db: &'db dyn Db) -> bool { + let (singleton, _) = self.class_singleton(db); + singleton.is_typed_dict(db) } /// Return `Some` if this class is known to be a [`SolidBase`], or `None` if it is not. pub(super) fn as_solid_base(self, db: &'db dyn Db) -> Option> { - self.class_literal(db).0.as_solid_base(db) + self.class_singleton(db).0.as_solid_base(db) } /// Return `true` if this class represents `known_class` @@ -462,8 +467,8 @@ impl<'db> ClassType<'db> { /// /// [method resolution order]: https://docs.python.org/3/glossary.html#term-method-resolution-order pub(super) fn iter_mro(self, db: &'db dyn Db) -> MroIterator<'db> { - let (class_literal, specialization) = self.class_literal(db); - class_literal.iter_mro(db, specialization) + let (singleton, specialization) = self.class_singleton(db); + singleton.iter_mro(db, specialization) } /// Iterate over the method resolution order ("MRO") of the class, optionally applying an @@ -473,15 +478,14 @@ impl<'db> ClassType<'db> { db: &'db dyn Db, additional_specialization: Option>, ) -> MroIterator<'db> { - let (class_literal, specialization) = - self.class_literal_specialized(db, additional_specialization); - class_literal.iter_mro(db, specialization) + let (singleton, specialization) = self.singleton_specialized(db, additional_specialization); + singleton.iter_mro(db, specialization) } /// Is this class final? pub(super) fn is_final(self, db: &'db dyn Db) -> bool { - let (class_literal, _) = self.class_literal(db); - class_literal.is_final(db) + let (singleton, _) = self.class_singleton(db); + singleton.is_final(db) } /// Return `true` if `other` is present in this class's MRO. @@ -548,10 +552,22 @@ impl<'db> ClassType<'db> { } } + pub(super) fn try_metaclass( + self, + db: &'db dyn Db, + ) -> Result<(Type<'db>, Option), MetaclassError<'db>> { + let (singleton, specialization) = self.class_singleton(db); + let (type_, dataclass_transformer_params) = singleton.try_metaclass(db)?; + Ok(( + type_.apply_optional_specialization(db, specialization), + dataclass_transformer_params, + )) + } + /// 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_literal(db); - class_literal + let (singleton, specialization) = self.class_singleton(db); + singleton .metaclass(db) .apply_optional_specialization(db, specialization) } @@ -597,7 +613,7 @@ impl<'db> ClassType<'db> { // however, since we end up with infinite recursion in that case due to the fact // that `type` is its own metaclass (and we know that `type` can coexist in an MRO // with any other arbitrary class, anyway). - let type_class = KnownClass::Type.to_class_literal(db); + let type_class = KnownClass::Type.to_class_singleton(db); let self_metaclass = self.metaclass(db); if self_metaclass == type_class { return true; @@ -638,8 +654,31 @@ impl<'db> ClassType<'db> { name: &str, policy: MemberLookupPolicy, ) -> PlaceAndQualifiers<'db> { - let (class_literal, specialization) = self.class_literal(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) + } + + pub(super) fn class_member_inner( + self, + db: &'db dyn Db, + specialization: Option>, + name: &str, + policy: MemberLookupPolicy, + ) -> PlaceAndQualifiers<'db> { + let (singleton, specialization) = self.singleton_specialized(db, specialization); + singleton.class_member_inner(db, specialization, name, policy) + } + + pub(super) fn class_member_from_mro( + self, + db: &'db dyn Db, + name: &str, + policy: MemberLookupPolicy, + mro_iter: impl Iterator>, + ) -> PlaceAndQualifiers<'db> { + // TODO: Is this correct? + let (singleton, _) = self.class_singleton(db); + singleton.class_member_from_mro(db, name, policy, mro_iter) } /// Returns the inferred type of the class member named `name`. Only bound members @@ -670,10 +709,10 @@ impl<'db> ClassType<'db> { Signature::new(parameters, Some(return_annotation)) } - let (class_literal, specialization) = self.class_literal(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)) }; @@ -690,7 +729,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()) @@ -701,7 +740,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)) @@ -710,7 +749,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| { @@ -872,7 +911,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"))); @@ -944,13 +983,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_literal(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)) } @@ -958,8 +997,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_literal(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)) } @@ -1116,6 +1155,29 @@ impl<'db> ClassType<'db> { } } } + + pub(crate) fn dataclass_params(&self, db: &'db dyn Db) -> Option { + let (singleton, _) = self.class_singleton(db); + singleton.dataclass_params(db) + } + + pub(crate) fn dataclass_transformer_params( + &self, + db: &'db dyn Db, + ) -> Option { + let (singleton, _) = self.class_singleton(db); + singleton.dataclass_transformer_params(db) + } + + pub(super) fn explicit_bases(self, db: &'db dyn Db) -> &[Type<'db>] { + let (singleton, _) = self.class_singleton(db); + singleton.explicit_bases(db) + } + + pub(super) fn own_fields(self, db: &'db dyn Db) -> FxOrderMap> { + let (singleton, specialization) = self.class_singleton(db); + singleton.own_fields(db, specialization) + } } impl<'db> From> for ClassType<'db> { @@ -1176,6 +1238,245 @@ pub(crate) struct Field<'db> { pub(crate) kw_only: Option, } +#[derive( + Copy, + Clone, + Debug, + PartialEq, + Eq, + Hash, + PartialOrd, + Ord, + salsa::Supertype, + salsa::Update, + get_size2::GetSize, +)] +pub(crate) enum ClassSingletonType<'db> { + Literal(ClassLiteral<'db>), + NewType(NewTypeClass<'db>), +} + +impl<'db> ClassSingletonType<'db> { + pub(crate) fn has_pep_695_type_params(self, db: &'db dyn Db) -> bool { + match self { + Self::Literal(literal) => literal.has_pep_695_type_params(db), + Self::NewType(_) => false, + } + } + + pub(crate) fn name(self, db: &'db dyn Db) -> &Name { + match self { + Self::Literal(literal) => literal.name(db), + Self::NewType(new_type) => new_type.name(db), + } + } + + pub(crate) fn known(self, db: &'db dyn Db) -> Option { + match self { + Self::Literal(literal) => literal.known(db), + Self::NewType(_) => None, // a NewType is never a known class + } + } + + pub(crate) fn is_known(self, db: &'db dyn Db, known_class: KnownClass) -> bool { + self.known(db) == Some(known_class) + } + + pub(crate) fn is_tuple(self, db: &'db dyn Db) -> bool { + self.is_known(db, KnownClass::Tuple) + } + + pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> { + match self { + Self::Literal(literal) => literal.definition(db), + Self::NewType(_) => todo!( + "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX: WHAT DO I DO HERE?" + ), + } + } + + pub(crate) fn is_typed_dict(self, db: &'db dyn Db) -> bool { + match self { + Self::Literal(literal) => literal.is_typed_dict(db), + Self::NewType(new_type) => new_type.is_typed_dict(db), + } + } + + pub(crate) fn is_final(self, db: &'db dyn Db) -> bool { + match self { + Self::Literal(literal) => literal.is_final(db), + Self::NewType(new_type) => new_type.is_final(db), + } + } + + pub(super) fn as_solid_base(self, db: &'db dyn Db) -> Option> { + match self { + Self::Literal(literal) => literal.as_solid_base(db), + Self::NewType(_) => None, // a NewType is never a SolidBase + } + } + + pub(super) fn iter_mro( + self, + db: &'db dyn Db, + specialization: Option>, + ) -> MroIterator<'db> { + match self { + Self::Literal(literal) => literal.iter_mro(db, specialization), + Self::NewType(new_type) => new_type.iter_mro(db, specialization), + } + } + + pub(super) fn try_metaclass( + self, + db: &'db dyn Db, + ) -> Result<(Type<'db>, Option), MetaclassError<'db>> { + match self { + Self::Literal(literal) => literal.try_metaclass(db), + Self::NewType(new_type) => new_type.try_metaclass(db), + } + } + + 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), + } + } + + pub(super) fn class_member( + self, + db: &'db dyn Db, + name: &str, + policy: MemberLookupPolicy, + ) -> PlaceAndQualifiers<'db> { + self.class_member_inner(db, None, name, policy) + } + + fn class_member_inner( + self, + db: &'db dyn Db, + specialization: Option>, + name: &str, + policy: MemberLookupPolicy, + ) -> PlaceAndQualifiers<'db> { + if name == "__mro__" { + let tuple_elements = self.iter_mro(db, specialization); + return Place::bound(Type::heterogeneous_tuple(db, tuple_elements)).into(); + } + + self.class_member_from_mro(db, name, policy, self.iter_mro(db, specialization)) + } + + pub(super) fn class_member_from_mro( + self, + db: &'db dyn Db, + name: &str, + policy: MemberLookupPolicy, + mro_iter: impl Iterator>, + ) -> PlaceAndQualifiers<'db> { + match self { + Self::Literal(literal) => literal.class_member_from_mro(db, name, policy, mro_iter), + Self::NewType(new_type) => new_type.class_member_from_mro(db, name, policy, mro_iter), + } + } + + pub(super) fn own_class_member( + self, + db: &'db dyn Db, + inherited_generic_context: Option>, + specialization: Option>, + name: &str, + ) -> PlaceAndQualifiers<'db> { + match self { + Self::Literal(literal) => { + literal.own_class_member(db, inherited_generic_context, specialization, name) + } + Self::NewType(new_type) => { + // A NewType can't be specialized. + new_type.own_class_member(db, inherited_generic_context, name) + } + } + } + + pub(super) fn instance_member( + self, + db: &'db dyn Db, + specialization: Option>, + name: &str, + ) -> PlaceAndQualifiers<'db> { + match self { + Self::Literal(literal) => literal.instance_member(db, specialization, name), + Self::NewType(new_type) => { + // A NewType can't be specialized. + new_type.instance_member(db, name) + } + } + } + + fn own_instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> { + match self { + Self::Literal(literal) => literal.own_instance_member(db, name), + Self::NewType(new_type) => new_type.own_instance_member(db, name), + } + } + + pub(crate) fn dataclass_params(&self, db: &'db dyn Db) -> Option { + match self { + Self::Literal(literal) => literal.dataclass_params(db), + Self::NewType(new_type) => new_type.dataclass_params(db), + } + } + + pub(crate) fn dataclass_transformer_params( + &self, + db: &'db dyn Db, + ) -> Option { + match self { + Self::Literal(literal) => literal.dataclass_transformer_params(db), + Self::NewType(new_type) => new_type.dataclass_transformer_params(db), + } + } + + pub(super) fn is_subclass_of( + self, + db: &'db dyn Db, + specialization: Option>, + other: ClassType<'db>, + ) -> bool { + match self { + Self::Literal(literal) => literal.is_subclass_of(db, specialization, other), + // A NewType can't be specialized. + Self::NewType(new_type) => new_type.is_subclass_of(db, other), + } + } + + pub(super) fn explicit_bases(self, db: &'db dyn Db) -> &[Type<'db>] { + match self { + Self::Literal(literal) => literal.explicit_bases(db), + Self::NewType(new_type) => new_type.explicit_bases(db), + } + } + + pub(super) fn own_fields( + self, + db: &'db dyn Db, + specialization: Option>, + ) -> FxOrderMap> { + match self { + Self::Literal(literal) => literal.own_fields(db, specialization), + // A NewType can't be specialized. + Self::NewType(new_type) => new_type.own_fields(db), + } + } +} + +impl<'db> From> for Type<'db> { + fn from(singleton: ClassSingletonType) -> Type { + Type::ClassSingleton(singleton) + } +} + /// Representation of a class definition statement in the AST: either a non-generic class, or a /// generic class that has not been specialized. /// @@ -1320,10 +1621,14 @@ impl<'db> ClassLiteral<'db> { f: impl FnOnce(GenericContext<'db>) -> Specialization<'db>, ) -> ClassType<'db> { match self.generic_context(db) { - None => ClassType::NonGeneric(self), + None => ClassType::NonGeneric(self.into()), Some(generic_context) => { let specialization = f(generic_context); - ClassType::Generic(GenericAlias::new(db, self, specialization)) + ClassType::Generic(GenericAlias::new( + db, + ClassSingletonType::Literal(self), + specialization, + )) } } } @@ -1451,7 +1756,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)) } @@ -1626,12 +1931,12 @@ impl<'db> ClassLiteral<'db> { let explicit_metaclass = self.explicit_metaclass(db, &module); let (metaclass, class_metaclass_was_from) = if let Some(metaclass) = explicit_metaclass { - (metaclass, self) + (metaclass, self.into()) } else if let Some(base_class) = base_classes.next() { - let (base_class_literal, _) = base_class.class_literal(db); - (base_class.metaclass(db), base_class_literal) + let (base_class_singleton, _) = base_class.class_singleton(db); + (base_class.metaclass(db), base_class_singleton) } else { - (KnownClass::Type.to_class_literal(db), self) + (KnownClass::Type.to_class_singleton(db), self.into()) }; let mut candidate = if let Some(metaclass_ty) = metaclass.to_class_type(db) { @@ -1680,33 +1985,33 @@ impl<'db> ClassLiteral<'db> { continue; }; if metaclass.is_subclass_of(db, candidate.metaclass) { - let (base_class_literal, _) = base_class.class_literal(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_literal(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_literal(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), )) } @@ -1797,7 +2102,7 @@ impl<'db> ClassLiteral<'db> { } ClassBase::TypedDict => { return KnownClass::TypedDictFallback - .to_class_literal(db) + .to_class_singleton(db) .find_name_in_mro_with_policy(db, name, policy) .expect("Will return Some() when called on class literal"); } @@ -1862,7 +2167,7 @@ impl<'db> ClassLiteral<'db> { .with_qualifiers(TypeQualifiers::CLASS_VAR); } - if CodeGeneratorKind::NamedTuple.matches(db, self) { + if CodeGeneratorKind::NamedTuple.matches(db, self.into()) { if let Some(field) = self.own_fields(db, specialization).get(name) { let property_getter_signature = Signature::new( Parameters::new([Parameter::positional_only(Some(Name::new_static("self")))]), @@ -1923,7 +2228,7 @@ impl<'db> ClassLiteral<'db> { let has_dataclass_param = |param| dataclass_params.is_some_and(|params| params.contains(param)); - let field_policy = CodeGeneratorKind::from_class(db, self)?; + let field_policy = CodeGeneratorKind::from_class(db, self.into())?; let instance_ty = Type::instance(db, self.apply_optional_specialization(db, specialization)); @@ -1995,7 +2300,11 @@ impl<'db> ClassLiteral<'db> { if let Some(ref mut default_ty) = default_ty { *default_ty = default_ty - .try_call_dunder_get(db, Type::none(db), Type::ClassLiteral(self)) + .try_call_dunder_get( + db, + Type::none(db), + Type::ClassSingleton(self.into()), + ) .map(|(return_ty, _)| return_ty) .unwrap_or_else(Type::unknown); } @@ -2077,8 +2386,8 @@ impl<'db> ClassLiteral<'db> { } (CodeGeneratorKind::NamedTuple, name) if name != "__init__" => { KnownClass::NamedTupleFallback - .to_class_literal(db) - .into_class_literal()? + .to_class_singleton(db) + .into_class_singleton()? .own_class_member(db, self.generic_context(db), None, name) .place .ignore_possibly_unbound() @@ -2236,7 +2545,7 @@ impl<'db> ClassLiteral<'db> { Place::bound(member).into() } else { KnownClass::TypedDictFallback - .to_class_literal(db) + .to_class_singleton(db) .find_name_in_mro_with_policy(db, name, policy) .expect("`find_name_in_mro_with_policy` will return `Some()` when called on class literal") } @@ -2261,9 +2570,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_literal(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 } @@ -2899,7 +3208,7 @@ impl<'db> ClassLiteral<'db> { } pub(super) fn to_non_generic_instance(self, db: &'db dyn Db) -> Type<'db> { - Type::instance(db, ClassType::NonGeneric(self)) + Type::instance(db, ClassType::NonGeneric(self.into())) } /// Return this class' involvement in an inheritance cycle, if any. @@ -2913,26 +3222,26 @@ impl<'db> ClassLiteral<'db> { /// Also, populates `visited_classes` with all base classes of `self`. fn is_cyclically_defined_recursive<'db>( db: &'db dyn Db, - class: ClassLiteral<'db>, - classes_on_stack: &mut IndexSet>, - visited_classes: &mut IndexSet>, + class: ClassSingletonType<'db>, + classes_on_stack: &mut IndexSet>, + visited_classes: &mut IndexSet>, ) -> bool { let mut result = false; for explicit_base in class.explicit_bases(db) { - let explicit_base_class_literal = match explicit_base { - Type::ClassLiteral(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, ); @@ -2945,9 +3254,10 @@ impl<'db> ClassLiteral<'db> { tracing::trace!("Class::inheritance_cycle: {}", self.name(db)); let visited_classes = &mut IndexSet::new(); - if !is_cyclically_defined_recursive(db, self, &mut IndexSet::new(), visited_classes) { + if !is_cyclically_defined_recursive(db, self.into(), &mut IndexSet::new(), visited_classes) + { None - } else if visited_classes.contains(&self) { + } else if visited_classes.contains(&ClassSingletonType::from(self)) { Some(InheritanceCycle::Participant) } else { Some(InheritanceCycle::Inherited) @@ -2986,13 +3296,142 @@ impl<'db> ClassLiteral<'db> { impl<'db> From> for Type<'db> { fn from(class: ClassLiteral<'db>) -> Type<'db> { - Type::ClassLiteral(class) + Type::ClassSingleton(class.into()) + } +} + +impl<'db> From> for ClassSingletonType<'db> { + fn from(class: ClassLiteral<'db>) -> ClassSingletonType<'db> { + ClassSingletonType::Literal(class) } } impl<'db> From> for ClassType<'db> { fn from(class: ClassLiteral<'db>) -> ClassType<'db> { - ClassType::NonGeneric(class) + ClassType::NonGeneric(class.into()) + } +} + +#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)] +#[derive(PartialOrd, Ord)] +pub struct NewTypeClass<'db> { + #[returns(ref)] + name: Name, + + parent: ClassType<'db>, +} + +impl<'db> NewTypeClass<'db> { + pub(super) fn is_typed_dict(self, db: &'db dyn Db) -> bool { + self.parent(db).is_typed_dict(db) + } + + pub(super) fn iter_mro( + self, + db: &'db dyn Db, + specialization: Option>, + ) -> MroIterator<'db> { + 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) + } + + pub(super) fn class_member( + self, + db: &'db dyn Db, + name: &str, + policy: MemberLookupPolicy, + ) -> PlaceAndQualifiers<'db> { + self.class_member_inner(db, None, name, policy) + } + + fn class_member_inner( + self, + db: &'db dyn Db, + specialization: Option>, + name: &str, + policy: MemberLookupPolicy, + ) -> PlaceAndQualifiers<'db> { + self.parent(db) + .class_member_inner(db, specialization, name, policy) + } + + pub(super) fn class_member_from_mro( + self, + db: &'db dyn Db, + name: &str, + policy: MemberLookupPolicy, + mro_iter: impl Iterator>, + ) -> PlaceAndQualifiers<'db> { + self.parent(db) + .class_member_from_mro(db, name, policy, mro_iter) + } + + pub(super) fn own_class_member( + self, + db: &'db dyn Db, + inherited_generic_context: Option>, + name: &str, + ) -> PlaceAndQualifiers<'db> { + self.parent(db) + .own_class_member(db, inherited_generic_context, name) + } + + pub(super) fn instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> { + self.parent(db).instance_member(db, name) + } + + fn own_instance_member(self, db: &'db dyn Db, name: &str) -> PlaceAndQualifiers<'db> { + self.parent(db).own_instance_member(db, name) + } + + pub(crate) fn dataclass_params(&self, db: &'db dyn Db) -> Option { + self.parent(db).dataclass_params(db) + } + + pub(crate) fn dataclass_transformer_params( + &self, + db: &'db dyn Db, + ) -> Option { + self.parent(db).dataclass_transformer_params(db) + } + + pub(super) fn is_subclass_of(self, db: &'db dyn Db, other: ClassType<'db>) -> bool { + let parent = self.parent(db); + if parent == other { + true + } else { + parent.is_subclass_of(db, other) + } + } + + pub(super) fn try_metaclass( + self, + db: &'db dyn Db, + ) -> Result<(Type<'db>, Option), MetaclassError<'db>> { + self.parent(db).try_metaclass(db) + } + + pub(super) fn explicit_bases(self, db: &'db dyn Db) -> &[Type<'db>] { + self.parent(db).explicit_bases(db) + } + + pub(super) fn own_fields(self, db: &'db dyn Db) -> FxOrderMap> { + self.parent(db).own_fields(db) + } +} + +impl<'db> get_size2::GetSize for NewTypeClass<'_> {} + +impl<'db> From> for ClassSingletonType<'db> { + fn from(class: NewTypeClass<'db>) -> ClassSingletonType<'db> { + ClassSingletonType::NewType(class) } } @@ -3799,7 +4238,7 @@ impl KnownClass { /// /// If the class cannot be found in typeshed, a debug-level log message will be emitted stating this. pub(crate) fn to_instance(self, db: &dyn Db) -> Type<'_> { - self.to_class_literal(db) + self.to_class_singleton(db) .to_class_type(db) .map(|class| Type::instance(db, class)) .unwrap_or_else(Type::unknown) @@ -3815,7 +4254,9 @@ impl KnownClass { db: &'db dyn Db, specialization: impl IntoIterator>, ) -> Option> { - let Type::ClassLiteral(class_literal) = self.to_class_literal(db) else { + let Type::ClassSingleton(ClassSingletonType::Literal(class_literal)) = + self.to_class_singleton(db) + else { return None; }; let generic_context = class_literal.generic_context(db)?; @@ -3857,15 +4298,17 @@ impl KnownClass { /// /// Return an error if the symbol cannot be found in the expected typeshed module, /// or if the symbol is not a class definition, or if the symbol is possibly unbound. - fn try_to_class_literal_without_logging( + fn try_to_class_singleton_without_logging( self, db: &dyn Db, - ) -> Result, KnownClassLookupError<'_>> { + ) -> Result, KnownClassLookupError<'_>> { let symbol = known_module_symbol(db, self.canonical_module(db), self.name(db)).place; match symbol { - Place::Type(Type::ClassLiteral(class_literal), Boundness::Bound) => Ok(class_literal), - Place::Type(Type::ClassLiteral(class_literal), Boundness::PossiblyUnbound) => { - Err(KnownClassLookupError::ClassPossiblyUnbound { class_literal }) + Place::Type(Type::ClassSingleton(singleton), Boundness::Bound) => Ok(singleton), + Place::Type(Type::ClassSingleton(singleton), Boundness::PossiblyUnbound) => { + Err(KnownClassLookupError::ClassPossiblyUnbound { + class_singleton: singleton, + }) } Place::Type(found_type, _) => { Err(KnownClassLookupError::SymbolNotAClass { found_type }) @@ -3877,12 +4320,12 @@ impl KnownClass { /// Lookup a [`KnownClass`] in typeshed and return a [`Type`] representing that class-literal. /// /// If the class cannot be found in typeshed, a debug-level log message will be emitted stating this. - pub(crate) fn try_to_class_literal(self, db: &dyn Db) -> Option> { + pub(crate) fn try_to_class_singleton(self, db: &dyn Db) -> Option> { // a cache of the `KnownClass`es that we have already failed to lookup in typeshed // (and therefore that we've already logged a warning for) static MESSAGES: LazyLock>> = LazyLock::new(Mutex::default); - self.try_to_class_literal_without_logging(db) + self.try_to_class_singleton_without_logging(db) .or_else(|lookup_error| { if MESSAGES.lock().unwrap().insert(self) { if matches!( @@ -3899,9 +4342,9 @@ impl KnownClass { } match lookup_error { - KnownClassLookupError::ClassPossiblyUnbound { class_literal, .. } => { - Ok(class_literal) - } + KnownClassLookupError::ClassPossiblyUnbound { + class_singleton, .. + } => Ok(class_singleton), KnownClassLookupError::ClassNotFound { .. } | KnownClassLookupError::SymbolNotAClass { .. } => Err(()), } @@ -3912,9 +4355,9 @@ impl KnownClass { /// Lookup a [`KnownClass`] in typeshed and return a [`Type`] representing that class-literal. /// /// If the class cannot be found in typeshed, a debug-level log message will be emitted stating this. - pub(crate) fn to_class_literal(self, db: &dyn Db) -> Type<'_> { - self.try_to_class_literal(db) - .map(Type::ClassLiteral) + pub(crate) fn to_class_singleton(self, db: &dyn Db) -> Type<'_> { + self.try_to_class_singleton(db) + .map(Type::ClassSingleton) .unwrap_or_else(Type::unknown) } @@ -3923,7 +4366,7 @@ impl KnownClass { /// /// If the class cannot be found in typeshed, a debug-level log message will be emitted stating this. pub(crate) fn to_subclass_of(self, db: &dyn Db) -> Type<'_> { - self.to_class_literal(db) + self.to_class_singleton(db) .to_class_type(db) .map(|class| SubclassOfType::from(db, class)) .unwrap_or_else(SubclassOfType::subclass_of_unknown) @@ -3932,7 +4375,7 @@ impl KnownClass { /// Return `true` if this symbol can be resolved to a class definition `class` in typeshed, /// *and* `class` is a subclass of `other`. pub(super) fn is_subclass_of<'db>(self, db: &'db dyn Db, other: ClassType<'db>) -> bool { - self.try_to_class_literal_without_logging(db) + self.try_to_class_singleton_without_logging(db) .is_ok_and(|class| class.is_subclass_of(db, None, other)) } @@ -4421,7 +4864,7 @@ impl KnownClass { let bound_super = BoundSuperType::build( db, - Type::ClassLiteral(enclosing_class), + Type::ClassSingleton(enclosing_class), first_param, ) .unwrap_or_else(|err| { @@ -4667,7 +5110,9 @@ pub(crate) enum KnownClassLookupError<'db> { SymbolNotAClass { found_type: Type<'db> }, /// There is a symbol by that name in the expected typeshed module, /// and it's a class definition, but it's possibly unbound. - ClassPossiblyUnbound { class_literal: ClassLiteral<'db> }, + ClassPossiblyUnbound { + class_singleton: ClassSingletonType<'db>, + }, } impl<'db> KnownClassLookupError<'db> { diff --git a/crates/ty_python_semantic/src/types/class_base.rs b/crates/ty_python_semantic/src/types/class_base.rs index e8a5fd1e41e8f..9cf972a41997b 100644 --- a/crates/ty_python_semantic/src/types/class_base.rs +++ b/crates/ty_python_semantic/src/types/class_base.rs @@ -62,7 +62,7 @@ impl<'db> ClassBase<'db> { /// Return a `ClassBase` representing the class `builtins.object` pub(super) fn object(db: &'db dyn Db) -> Self { KnownClass::Object - .to_class_literal(db) + .to_class_singleton(db) .to_class_type(db) .map_or(Self::unknown(), Self::Class) } @@ -77,7 +77,7 @@ impl<'db> ClassBase<'db> { ) -> Option { match ty { Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), - Type::ClassLiteral(literal) => { + Type::ClassSingleton(literal) => { if literal.is_known(db, KnownClass::Any) { Some(Self::Dynamic(DynamicType::Any)) } else { @@ -220,38 +220,42 @@ impl<'db> ClassBase<'db> { // TODO: Classes inheriting from `typing.Type` et al. also have `Generic` in their MRO SpecialFormType::Dict => { - Self::try_from_type(db, KnownClass::Dict.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::Dict.to_class_singleton(db), subclass) } SpecialFormType::List => { - Self::try_from_type(db, KnownClass::List.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::List.to_class_singleton(db), subclass) } SpecialFormType::Type => { - Self::try_from_type(db, KnownClass::Type.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::Type.to_class_singleton(db), subclass) } SpecialFormType::Tuple => { - Self::try_from_type(db, KnownClass::Tuple.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::Tuple.to_class_singleton(db), subclass) } SpecialFormType::Set => { - Self::try_from_type(db, KnownClass::Set.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::Set.to_class_singleton(db), subclass) } SpecialFormType::FrozenSet => { - Self::try_from_type(db, KnownClass::FrozenSet.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::FrozenSet.to_class_singleton(db), subclass) } SpecialFormType::ChainMap => { - Self::try_from_type(db, KnownClass::ChainMap.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::ChainMap.to_class_singleton(db), subclass) } SpecialFormType::Counter => { - Self::try_from_type(db, KnownClass::Counter.to_class_literal(db), subclass) - } - SpecialFormType::DefaultDict => { - Self::try_from_type(db, KnownClass::DefaultDict.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::Counter.to_class_singleton(db), subclass) } + SpecialFormType::DefaultDict => Self::try_from_type( + db, + KnownClass::DefaultDict.to_class_singleton(db), + subclass, + ), SpecialFormType::Deque => { - Self::try_from_type(db, KnownClass::Deque.to_class_literal(db), subclass) - } - SpecialFormType::OrderedDict => { - Self::try_from_type(db, KnownClass::OrderedDict.to_class_literal(db), subclass) + Self::try_from_type(db, KnownClass::Deque.to_class_singleton(db), subclass) } + SpecialFormType::OrderedDict => Self::try_from_type( + db, + KnownClass::OrderedDict.to_class_singleton(db), + subclass, + ), SpecialFormType::TypedDict => Some(Self::TypedDict), SpecialFormType::Callable => Self::try_from_type( db, @@ -302,7 +306,7 @@ impl<'db> ClassBase<'db> { pub(super) fn has_cyclic_mro(self, db: &'db dyn Db) -> bool { match self { ClassBase::Class(class) => { - let (class_literal, specialization) = class.class_literal(db); + let (class_literal, specialization) = class.class_singleton(db); class_literal .try_mro(db, specialization) .is_err_and(MroError::is_cycle) diff --git a/crates/ty_python_semantic/src/types/diagnostic.rs b/crates/ty_python_semantic/src/types/diagnostic.rs index 730ab1627d8ff..be534996978e5 100644 --- a/crates/ty_python_semantic/src/types/diagnostic.rs +++ b/crates/ty_python_semantic/src/types/diagnostic.rs @@ -1848,7 +1848,7 @@ fn report_invalid_assignment_with_message( return; }; match target_ty { - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { let mut diag = builder.into_diagnostic(format_args!( "Implicit shadowing of class `{}`", class.name(context.db()), @@ -2357,7 +2357,7 @@ pub(crate) fn report_bad_argument_to_protocol_interface( ), ); class_def_diagnostic.annotate(Annotation::primary( - class.class_literal(db).0.header_span(db), + class.class_singleton(db).0.header_span(db), )); diagnostic.sub(class_def_diagnostic); } diff --git a/crates/ty_python_semantic/src/types/display.rs b/crates/ty_python_semantic/src/types/display.rs index 3d23a8393d25a..257c129c98a30 100644 --- a/crates/ty_python_semantic/src/types/display.rs +++ b/crates/ty_python_semantic/src/types/display.rs @@ -37,7 +37,7 @@ impl Display for DisplayType<'_> { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let representation = self.ty.representation(self.db); match self.ty { - Type::ClassLiteral(literal) if literal.is_known(self.db, KnownClass::Any) => { + Type::ClassSingleton(literal) if literal.is_known(self.db, KnownClass::Any) => { write!(f, "typing.Any") } Type::IntLiteral(_) @@ -111,7 +111,7 @@ impl Display for DisplayRepresentation<'_> { Type::ModuleLiteral(module) => { write!(f, "", module.module(self.db).name(self.db)) } - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { write!(f, "", class.name(self.db)) } Type::GenericAlias(generic) => write!(f, "", generic.display(self.db)), diff --git a/crates/ty_python_semantic/src/types/enums.rs b/crates/ty_python_semantic/src/types/enums.rs index fb6f5b0c33d78..8f34d80a21263 100644 --- a/crates/ty_python_semantic/src/types/enums.rs +++ b/crates/ty_python_semantic/src/types/enums.rs @@ -66,7 +66,7 @@ pub(crate) fn enum_metadata<'db>( return None; } - if !Type::ClassLiteral(class).is_subtype_of(db, KnownClass::Enum.to_subclass_of(db)) + if !Type::ClassSingleton(class).is_subtype_of(db, KnownClass::Enum.to_subclass_of(db)) && !class .metaclass(db) .is_subtype_of(db, KnownClass::EnumType.to_subclass_of(db)) @@ -249,7 +249,7 @@ pub(crate) fn is_single_member_enum<'db>(db: &'db dyn Db, class: ClassLiteral<'d pub(crate) fn is_enum_class<'db>(db: &'db dyn Db, ty: Type<'db>) -> bool { match ty { - Type::ClassLiteral(class_literal) => enum_metadata(db, class_literal).is_some(), + Type::ClassSingleton(class_literal) => enum_metadata(db, class_literal).is_some(), _ => false, } } diff --git a/crates/ty_python_semantic/src/types/function.rs b/crates/ty_python_semantic/src/types/function.rs index b482e23e7439f..41a4f8ef480ea 100644 --- a/crates/ty_python_semantic/src/types/function.rs +++ b/crates/ty_python_semantic/src/types/function.rs @@ -132,7 +132,7 @@ impl FunctionDecorators { Some(KnownFunction::Override) => FunctionDecorators::OVERRIDE, _ => FunctionDecorators::empty(), }, - Type::ClassLiteral(class) => match class.known(db) { + Type::ClassSingleton(class) => match class.known(db) { Some(KnownClass::Classmethod) => FunctionDecorators::CLASSMETHOD, Some(KnownClass::Staticmethod) => FunctionDecorators::STATICMETHOD, _ => FunctionDecorators::empty(), @@ -997,7 +997,7 @@ fn is_instance_truthiness<'db>( always_true_if(is_instance(&KnownClass::FunctionType.to_instance(db))) } - Type::ClassLiteral(..) => always_true_if(is_instance(&KnownClass::Type.to_instance(db))), + Type::ClassSingleton(..) => always_true_if(is_instance(&KnownClass::Type.to_instance(db))), Type::TypeAlias(alias) => is_instance_truthiness(db, alias.value_type(db), class), @@ -1414,7 +1414,7 @@ impl KnownFunction { } KnownFunction::GetProtocolMembers => { - let [Some(Type::ClassLiteral(class))] = parameter_types else { + let [Some(Type::ClassSingleton(class))] = parameter_types else { return; }; if class.is_protocol(db) { @@ -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( @@ -1451,7 +1451,7 @@ impl KnownFunction { } KnownFunction::IsInstance | KnownFunction::IsSubclass => { - let [Some(first_arg), Some(Type::ClassLiteral(class))] = parameter_types else { + let [Some(first_arg), Some(Type::ClassSingleton(class))] = parameter_types else { return; }; diff --git a/crates/ty_python_semantic/src/types/generics.rs b/crates/ty_python_semantic/src/types/generics.rs index e0619e8343698..7020d08a659b9 100644 --- a/crates/ty_python_semantic/src/types/generics.rs +++ b/crates/ty_python_semantic/src/types/generics.rs @@ -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) => { diff --git a/crates/ty_python_semantic/src/types/ide_support.rs b/crates/ty_python_semantic/src/types/ide_support.rs index f96401bd2f862..5f27fff06ce82 100644 --- a/crates/ty_python_semantic/src/types/ide_support.rs +++ b/crates/ty_python_semantic/src/types/ide_support.rs @@ -95,26 +95,26 @@ impl<'db> AllMembers<'db> { ), Type::NominalInstance(instance) => { - let (class_literal, _specialization) = instance.class(db).class_literal(db); + let (class_literal, _specialization) = instance.class(db).class_singleton(db); self.extend_with_instance_members(db, ty, class_literal); } - Type::ClassLiteral(class_literal) if class_literal.is_typed_dict(db) => { - self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db)); + Type::ClassSingleton(class_literal) if class_literal.is_typed_dict(db) => { + self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_singleton(db)); } Type::GenericAlias(generic_alias) if generic_alias.is_typed_dict(db) => { - self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db)); + self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_singleton(db)); } Type::SubclassOf(subclass_of_type) if subclass_of_type.is_typed_dict(db) => { - self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db)); + self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_singleton(db)); } - Type::ClassLiteral(class_literal) => { + Type::ClassSingleton(class_literal) => { self.extend_with_class_members(db, ty, class_literal); - if let Type::ClassLiteral(meta_class_literal) = ty.to_meta_type(db) { + if let Type::ClassSingleton(meta_class_literal) = ty.to_meta_type(db) { self.extend_with_class_members(db, ty, meta_class_literal); } } @@ -126,7 +126,7 @@ impl<'db> AllMembers<'db> { Type::SubclassOf(subclass_of_type) => { if let Some(class_literal) = subclass_of_type.subclass_of().into_class() { - self.extend_with_class_members(db, ty, class_literal.class_literal(db).0); + self.extend_with_class_members(db, ty, class_literal.class_singleton(db).0); } } @@ -155,12 +155,12 @@ impl<'db> AllMembers<'db> { | Type::TypeVar(_) | Type::BoundSuper(_) | Type::TypeIs(_) => match ty.to_meta_type(db) { - Type::ClassLiteral(class_literal) => { + Type::ClassSingleton(class_literal) => { self.extend_with_class_members(db, ty, class_literal); } Type::SubclassOf(subclass_of) => { if let Some(class) = subclass_of.subclass_of().into_class() { - self.extend_with_class_members(db, ty, class.class_literal(db).0); + self.extend_with_class_members(db, ty, class.class_singleton(db).0); } } Type::GenericAlias(generic_alias) => { @@ -171,12 +171,12 @@ impl<'db> AllMembers<'db> { }, Type::TypedDict(_) => { - if let Type::ClassLiteral(class_literal) = ty.to_meta_type(db) { + if let Type::ClassSingleton(class_literal) = ty.to_meta_type(db) { self.extend_with_class_members(db, ty, class_literal); } - if let Type::ClassLiteral(class) = - KnownClass::TypedDictFallback.to_class_literal(db) + if let Type::ClassSingleton(class) = + KnownClass::TypedDictFallback.to_class_singleton(db) { self.extend_with_instance_members(db, ty, class); } @@ -222,7 +222,7 @@ impl<'db> AllMembers<'db> { { continue; } - Type::ClassLiteral(class) if class.is_protocol(db) => continue, + Type::ClassSingleton(class) if class.is_protocol(db) => continue, Type::KnownInstance( KnownInstanceType::TypeVar(_) | KnownInstanceType::TypeAliasType(_), ) => continue, @@ -279,7 +279,7 @@ impl<'db> AllMembers<'db> { for parent in class_literal .iter_mro(db, None) .filter_map(ClassBase::into_class) - .map(|class| class.class_literal(db).0) + .map(|class| class.class_singleton(db).0) { let parent_scope = parent.body_scope(db); for Member { name, .. } in all_declarations_and_bindings(db, parent_scope) { @@ -301,7 +301,7 @@ impl<'db> AllMembers<'db> { for parent in class_literal .iter_mro(db, None) .filter_map(ClassBase::into_class) - .map(|class| class.class_literal(db).0) + .map(|class| class.class_singleton(db).0) { let class_body_scope = parent.body_scope(db); let file = class_body_scope.file(db); @@ -605,13 +605,13 @@ pub fn definitions_for_attribute<'db>( // First, transform the type to its meta type, unless it's already a class-like type. let meta_type = match ty { - Type::ClassLiteral(_) | Type::SubclassOf(_) | Type::GenericAlias(_) => ty, + Type::ClassSingleton(_) | Type::SubclassOf(_) | Type::GenericAlias(_) => ty, _ => ty.to_meta_type(db), }; let class_literal = match meta_type { - Type::ClassLiteral(class_literal) => class_literal, + Type::ClassSingleton(class_literal) => class_literal, Type::SubclassOf(subclass) => match subclass.subclass_of().into_class() { - Some(cls) => cls.class_literal(db).0, + Some(cls) => cls.class_singleton(db).0, None => continue, }, _ => continue, @@ -621,7 +621,7 @@ pub fn definitions_for_attribute<'db>( 'scopes: for ancestor in class_literal .iter_mro(db, None) .filter_map(ClassBase::into_class) - .map(|cls| cls.class_literal(db).0) + .map(|cls| cls.class_singleton(db).0) { let class_scope = ancestor.body_scope(db); let class_place_table = crate::semantic_index::place_table(db, class_scope); diff --git a/crates/ty_python_semantic/src/types/infer.rs b/crates/ty_python_semantic/src/types/infer.rs index 201dcd750078a..7c7385530594a 100644 --- a/crates/ty_python_semantic/src/types/infer.rs +++ b/crates/ty_python_semantic/src/types/infer.rs @@ -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 @@ -1169,17 +1169,20 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { ); continue; } - Type::ClassLiteral(class) => ClassType::NonGeneric(*class), + Type::ClassSingleton(class) => ClassType::NonGeneric(*class), Type::GenericAlias(class) => ClassType::Generic(*class), _ => continue, }; if let Some(solid_base) = base_class.nearest_solid_base(self.db()) { - solid_bases.insert(solid_base, i, base_class.class_literal(self.db()).0); + solid_bases.insert(solid_base, i, base_class.class_singleton(self.db()).0); } if is_protocol - && !(base_class.class_literal(self.db()).0.is_protocol(self.db()) + && !(base_class + .class_singleton(self.db()) + .0 + .is_protocol(self.db()) || base_class.is_known(self.db(), KnownClass::Object)) { if let Some(builder) = self @@ -1521,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()) @@ -2358,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. @@ -4277,7 +4280,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } } - Type::ClassLiteral(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => { + Type::ClassSingleton(..) | Type::GenericAlias(..) | Type::SubclassOf(..) => { match object_ty.class_member(db, attribute.into()) { PlaceAndQualifiers { place: Place::Type(meta_attr_ty, meta_attr_boundness), @@ -6186,7 +6189,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } let class = match callable_type { - Type::ClassLiteral(class) => Some(ClassType::NonGeneric(class)), + Type::ClassSingleton(class) => Some(ClassType::NonGeneric(class)), Type::GenericAlias(generic) => Some(ClassType::Generic(generic)), Type::SubclassOf(subclass) => subclass.subclass_of().into_class(), _ => None, @@ -6202,7 +6205,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // . if !callable_type.is_subclass_of() { if let Some(protocol) = class - .class_literal(self.db()) + .class_singleton(self.db()) .0 .into_protocol_class(self.db()) { @@ -6231,6 +6234,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { | KnownClass::TypeVar | KnownClass::TypeAliasType | KnownClass::Deprecated + | KnownClass::NewType ) ) || ( // Constructor calls to `tuple` and subclasses of `tuple` are handled in `Type::Bindings`, @@ -6244,7 +6248,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // until we support the functional syntax for creating enum classes if !has_special_cased_constructor && KnownClass::Enum - .to_class_literal(self.db()) + .to_class_singleton(self.db()) .to_class_type(self.db()) .is_none_or(|enum_class| !class.is_subclass_of(self.db(), enum_class)) { @@ -6288,7 +6292,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { ); } } - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { if let Some(known_class) = class.known(self.db()) { known_class.check_call( &self.context, @@ -6489,7 +6493,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { /// Check if the given ty is `@deprecated` or not fn check_deprecated(&self, ranged: T, ty: Type) { // First handle classes - if let Type::ClassLiteral(class_literal) = ty { + if let Type::ClassSingleton(class_literal) = ty { let Some(deprecated) = class_literal.deprecated(self.db()) else { return; }; @@ -7087,7 +7091,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { if report_unresolved_attribute { let bound_on_instance = match value_type { - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { !class.instance_member(db, None, attr).place.is_unbound() } Type::SubclassOf(subclass_of @ SubclassOfType { .. }) => { @@ -7231,7 +7235,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { | Type::DataclassTransformer(_) | Type::BoundMethod(_) | Type::ModuleLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::GenericAlias(_) | Type::SubclassOf(_) | Type::NominalInstance(_) @@ -7573,7 +7577,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { | Type::DataclassDecorator(_) | Type::DataclassTransformer(_) | Type::ModuleLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::GenericAlias(_) | Type::SubclassOf(_) | Type::NominalInstance(_) @@ -7603,7 +7607,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { | Type::DataclassDecorator(_) | Type::DataclassTransformer(_) | Type::ModuleLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::GenericAlias(_) | Type::SubclassOf(_) | Type::NominalInstance(_) @@ -8635,7 +8639,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { // this callable as the `__class_getitem__` method on `type`. That probably requires // updating all of the subscript logic below to use custom callables for all of the _other_ // special cases, too. - if let Type::ClassLiteral(class) = value_ty { + if let Type::ClassSingleton(class) = value_ty { if class.is_tuple(self.db()) { return tuple_generic_alias(self.db(), self.infer_tuple_type_expression(slice)); } @@ -9032,7 +9036,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> { } } - if let Type::ClassLiteral(class) = value_ty { + if let Type::ClassSingleton(class) = value_ty { if class.is_known(db, KnownClass::Type) { return KnownClass::GenericAlias.to_instance(db); } @@ -9051,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__"); @@ -9418,7 +9422,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { Type::SpecialForm(SpecialFormType::Final) => { TypeAndQualifiers::new(Type::unknown(), TypeQualifiers::FINAL) } - Type::ClassLiteral(class) + Type::ClassSingleton(class) if class.is_known(self.db(), KnownClass::InitVar) => { if let Some(builder) = @@ -9534,7 +9538,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> { } type_and_qualifiers } - Type::ClassLiteral(class) if class.is_known(self.db(), KnownClass::InitVar) => { + Type::ClassSingleton(class) + if class.is_known(self.db(), KnownClass::InitVar) => + { let arguments = if let ast::Expr::Tuple(tuple) = slice { &*tuple.elts } else { @@ -10069,7 +10075,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { value_ty: Type<'db>, ) -> Type<'db> { match value_ty { - Type::ClassLiteral(class_literal) => match class_literal.known(self.db()) { + Type::ClassSingleton(class_literal) => match class_literal.known(self.db()) { Some(KnownClass::Tuple) => Type::tuple(self.infer_tuple_type_expression(slice)), Some(KnownClass::Type) => self.infer_subclass_of_type_expression(slice), _ => self.infer_subscript_type_expression(subscript, value_ty), @@ -10195,7 +10201,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { ast::Expr::Name(_) | ast::Expr::Attribute(_) => { let name_ty = self.infer_expression(slice); match name_ty { - Type::ClassLiteral(class_literal) => { + Type::ClassSingleton(class_literal) => { if class_literal.is_known(self.db(), KnownClass::Any) { SubclassOfType::subclass_of_any() } else { @@ -10288,7 +10294,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { self.infer_expression(&subscript.slice); Type::unknown() } - Type::ClassLiteral(literal) if literal.is_known(self.db(), KnownClass::Any) => { + Type::ClassSingleton(literal) if literal.is_known(self.db(), KnownClass::Any) => { self.infer_expression(slice); if let Some(builder) = self.context.report_lint(&INVALID_TYPE_FORM, subscript) { builder.into_diagnostic("Type `typing.Any` expected no type parameter"); @@ -10348,7 +10354,7 @@ impl<'db> TypeInferenceBuilder<'db, '_> { self.infer_type_expression(slice); value_ty } - Type::ClassLiteral(class) => { + Type::ClassSingleton(class) => { match class.generic_context(self.db()) { Some(generic_context) => { let specialized_class = self.infer_explicit_class_specialization( diff --git a/crates/ty_python_semantic/src/types/instance.rs b/crates/ty_python_semantic/src/types/instance.rs index ca837cd294279..9dbd64daf86fe 100644 --- a/crates/ty_python_semantic/src/types/instance.rs +++ b/crates/ty_python_semantic/src/types/instance.rs @@ -20,7 +20,7 @@ pub(super) use synthesized_protocol::SynthesizedProtocolType; impl<'db> Type<'db> { pub(crate) fn instance(db: &'db dyn Db, class: ClassType<'db>) -> Self { - let (class_literal, specialization) = class.class_literal(db); + let (class_literal, specialization) = class.class_singleton(db); match class_literal.known(db) { Some(KnownClass::Any) => Type::Dynamic(DynamicType::Any), @@ -213,7 +213,7 @@ impl<'db> NominalInstanceType<'db> { NominalInstanceInner::ExactTuple(_) => return None, NominalInstanceInner::NonTuple(class) => class, }; - let (class, Some(specialization)) = class.class_literal(db) else { + let (class, Some(specialization)) = class.class_singleton(db) else { return None; }; if !class.is_known(db, KnownClass::Slice) { @@ -324,7 +324,7 @@ impl<'db> NominalInstanceType<'db> { NominalInstanceInner::NonTuple(class) => class .known(db) .map(KnownClass::is_singleton) - .unwrap_or_else(|| is_single_member_enum(db, class.class_literal(db).0)), + .unwrap_or_else(|| is_single_member_enum(db, class.class_singleton(db).0)), } } @@ -335,7 +335,7 @@ impl<'db> NominalInstanceType<'db> { .known(db) .and_then(KnownClass::is_single_valued) .or_else(|| Some(self.tuple_spec(db)?.is_single_valued(db))) - .unwrap_or_else(|| is_single_member_enum(db, class.class_literal(db).0)), + .unwrap_or_else(|| is_single_member_enum(db, class.class_singleton(db).0)), } } @@ -608,7 +608,7 @@ impl<'db> Protocol<'db> { fn interface(self, db: &'db dyn Db) -> ProtocolInterface<'db> { match self { Self::FromClass(class) => class - .class_literal(db) + .class_singleton(db) .0 .into_protocol_class(db) .expect("Protocol class literal should be a protocol class") diff --git a/crates/ty_python_semantic/src/types/mro.rs b/crates/ty_python_semantic/src/types/mro.rs index 0e472270103b0..d7b4d019f3635 100644 --- a/crates/ty_python_semantic/src/types/mro.rs +++ b/crates/ty_python_semantic/src/types/mro.rs @@ -151,7 +151,7 @@ impl<'db> Mro<'db> { ) ) => { - ClassBase::try_from_type(db, *single_base, class.class_literal(db).0).map_or_else( + ClassBase::try_from_type(db, *single_base, class.class_singleton(db).0).map_or_else( || Err(MroErrorKind::InvalidBases(Box::from([(0, *single_base)]))), |single_base| { if single_base.has_cyclic_mro(db) { @@ -186,7 +186,7 @@ impl<'db> Mro<'db> { &original_bases[i + 1..], ); } else { - match ClassBase::try_from_type(db, *base, class.class_literal(db).0) { + match ClassBase::try_from_type(db, *base, class.class_singleton(db).0) { Some(valid_base) => resolved_bases.push(valid_base), None => invalid_bases.push((i, *base)), } @@ -254,7 +254,7 @@ impl<'db> Mro<'db> { // precise!). for (index, base) in original_bases.iter().enumerate() { let Some(base) = - ClassBase::try_from_type(db, *base, class.class_literal(db).0) + ClassBase::try_from_type(db, *base, class.class_singleton(db).0) else { continue; }; diff --git a/crates/ty_python_semantic/src/types/narrow.rs b/crates/ty_python_semantic/src/types/narrow.rs index d1df76dee6fa2..97189e83f80f7 100644 --- a/crates/ty_python_semantic/src/types/narrow.rs +++ b/crates/ty_python_semantic/src/types/narrow.rs @@ -183,7 +183,7 @@ impl ClassInfoConstraintFunction { match classinfo { Type::TypeAlias(alias) => self.generate_constraint(db, alias.value_type(db)), - Type::ClassLiteral(class_literal) => { + Type::ClassSingleton(class_literal) => { // At runtime (on Python 3.11+), this will return `True` for classes that actually // do inherit `typing.Any` and `False` otherwise. We could accurately model that? if class_literal.is_known(db, KnownClass::Any) { @@ -573,13 +573,18 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> { } // Treat enums as a union of their members. Type::NominalInstance(instance) - if enum_metadata(db, instance.class(db).class_literal(db).0).is_some() => + if enum_metadata(db, instance.class(db).class_singleton(db).0) + .is_some() => { UnionType::from_elements( db, - enum_member_literals(db, instance.class(db).class_literal(db).0, None) - .expect("Calling `enum_member_literals` on an enum class") - .map(|ty| filter_to_cannot_be_equal(db, ty, rhs_ty)), + enum_member_literals( + db, + instance.class(db).class_singleton(db).0, + None, + ) + .expect("Calling `enum_member_literals` on an enum class") + .map(|ty| filter_to_cannot_be_equal(db, ty, rhs_ty)), ) } _ => { @@ -757,7 +762,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> { node_index: _, }, }) if keywords.is_empty() => { - let Type::ClassLiteral(rhs_class) = rhs_ty else { + let Type::ClassSingleton(rhs_class) = rhs_ty else { continue; }; @@ -784,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); @@ -881,7 +886,7 @@ impl<'db, 'ast> NarrowingConstraintsBuilder<'db, 'ast> { }) } // for the expression `bool(E)`, we further narrow the type based on `E` - Type::ClassLiteral(class_type) + Type::ClassSingleton(class_type) if expr_call.arguments.args.len() == 1 && expr_call.arguments.keywords.is_empty() && class_type.is_known(self.db, KnownClass::Bool) => diff --git a/crates/ty_python_semantic/src/types/property_tests/type_generation.rs b/crates/ty_python_semantic/src/types/property_tests/type_generation.rs index 0a296088ff35a..4f35585732f68 100644 --- a/crates/ty_python_semantic/src/types/property_tests/type_generation.rs +++ b/crates/ty_python_semantic/src/types/property_tests/type_generation.rs @@ -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 => { @@ -153,7 +153,7 @@ impl Ty { .place .expect_type(); debug_assert!( - matches!(ty, Type::NominalInstance(instance) if is_single_member_enum(db, instance.class(db).class_literal(db).0)) + matches!(ty, Type::NominalInstance(instance) if is_single_member_enum(db, instance.class(db).class_singleton(db).0)) ); ty } @@ -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, diff --git a/crates/ty_python_semantic/src/types/protocol_class.rs b/crates/ty_python_semantic/src/types/protocol_class.rs index 77c5607c00562..8aafa452f980a 100644 --- a/crates/ty_python_semantic/src/types/protocol_class.rs +++ b/crates/ty_python_semantic/src/types/protocol_class.rs @@ -528,7 +528,7 @@ fn cached_protocol_interface<'db>( for parent_protocol in class .iter_mro(db, None) .filter_map(ClassBase::into_class) - .filter_map(|class| class.class_literal(db).0.into_protocol_class(db)) + .filter_map(|class| class.class_singleton(db).0.into_protocol_class(db)) { let parent_scope = parent_protocol.body_scope(db); let use_def_map = use_def_map(db, parent_scope); diff --git a/crates/ty_python_semantic/src/types/special_form.rs b/crates/ty_python_semantic/src/types/special_form.rs index f0dd81b150cdf..ce90437694aee 100644 --- a/crates/ty_python_semantic/src/types/special_form.rs +++ b/crates/ty_python_semantic/src/types/special_form.rs @@ -253,7 +253,7 @@ impl SpecialFormType { } pub(super) fn to_meta_type(self, db: &dyn Db) -> Type<'_> { - self.class().to_class_literal(db) + self.class().to_class_singleton(db) } /// Return true if this special form is callable at runtime. diff --git a/crates/ty_python_semantic/src/types/subclass_of.rs b/crates/ty_python_semantic/src/types/subclass_of.rs index c27e92018e1e4..d469660da8f90 100644 --- a/crates/ty_python_semantic/src/types/subclass_of.rs +++ b/crates/ty_python_semantic/src/types/subclass_of.rs @@ -211,7 +211,7 @@ impl<'db> SubclassOfType<'db> { pub(crate) fn is_typed_dict(self, db: &'db dyn Db) -> bool { self.subclass_of .into_class() - .is_some_and(|class| class.class_literal(db).0.is_typed_dict(db)) + .is_some_and(|class| class.class_singleton(db).0.is_typed_dict(db)) } } @@ -268,7 +268,7 @@ impl<'db> SubclassOfInner<'db> { pub(crate) fn try_from_type(db: &'db dyn Db, ty: Type<'db>) -> Option { match ty { Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)), - Type::ClassLiteral(literal) => Some(if literal.is_known(db, KnownClass::Any) { + Type::ClassSingleton(literal) => Some(if literal.is_known(db, KnownClass::Any) { Self::Dynamic(DynamicType::Any) } else { Self::Class(literal.default_specialization(db)) diff --git a/crates/ty_python_semantic/src/types/tuple.rs b/crates/ty_python_semantic/src/types/tuple.rs index 6058b4e49a0d9..cde8dd30ac217 100644 --- a/crates/ty_python_semantic/src/types/tuple.rs +++ b/crates/ty_python_semantic/src/types/tuple.rs @@ -201,7 +201,7 @@ impl<'db> TupleType<'db> { #[salsa::tracked(cycle_fn=to_class_type_cycle_recover, cycle_initial=to_class_type_cycle_initial, heap_size=ruff_memory_usage::heap_size)] pub(crate) fn to_class_type(self, db: &'db dyn Db) -> ClassType<'db> { let tuple_class = KnownClass::Tuple - .try_to_class_literal(db) + .try_to_class_singleton(db) .expect("Typeshed should always have a `tuple` class in `builtins.pyi`"); tuple_class.apply_specialization(db, |generic_context| { @@ -285,7 +285,7 @@ fn to_class_type_cycle_recover<'db>( fn to_class_type_cycle_initial<'db>(db: &'db dyn Db, self_: TupleType<'db>) -> ClassType<'db> { let tuple_class = KnownClass::Tuple - .try_to_class_literal(db) + .try_to_class_singleton(db) .expect("Typeshed should always have a `tuple` class in `builtins.pyi`"); tuple_class.apply_specialization(db, |generic_context| { diff --git a/crates/ty_python_semantic/src/types/type_ordering.rs b/crates/ty_python_semantic/src/types/type_ordering.rs index 21612c36148ac..3903d2e29a68e 100644 --- a/crates/ty_python_semantic/src/types/type_ordering.rs +++ b/crates/ty_python_semantic/src/types/type_ordering.rs @@ -104,9 +104,9 @@ pub(super) fn union_or_intersection_elements_ordering<'db>( (Type::ModuleLiteral(_), _) => Ordering::Less, (_, Type::ModuleLiteral(_)) => Ordering::Greater, - (Type::ClassLiteral(left), Type::ClassLiteral(right)) => left.cmp(right), - (Type::ClassLiteral(_), _) => Ordering::Less, - (_, Type::ClassLiteral(_)) => Ordering::Greater, + (Type::ClassSingleton(left), Type::ClassSingleton(right)) => left.cmp(right), + (Type::ClassSingleton(_), _) => Ordering::Less, + (_, Type::ClassSingleton(_)) => Ordering::Greater, (Type::GenericAlias(left), Type::GenericAlias(right)) => left.cmp(right), (Type::GenericAlias(_), _) => Ordering::Less, diff --git a/crates/ty_python_semantic/src/types/visitor.rs b/crates/ty_python_semantic/src/types/visitor.rs index 4b58f20bf5071..a26ed6d694f8b 100644 --- a/crates/ty_python_semantic/src/types/visitor.rs +++ b/crates/ty_python_semantic/src/types/visitor.rs @@ -142,7 +142,7 @@ impl<'db> From> for TypeKind<'db> { | Type::DataclassTransformer(_) | Type::WrapperDescriptor(_) | Type::ModuleLiteral(_) - | Type::ClassLiteral(_) + | Type::ClassSingleton(_) | Type::SpecialForm(_) | Type::Dynamic(_) => TypeKind::Atomic,