Compare commits

...

2 Commits

Author SHA1 Message Date
David Peter
e609da0c72 Remove it from ClassType 2025-11-26 14:55:59 +01:00
David Peter
7818186fcc GenericAliasInstance 2025-11-26 14:19:10 +01:00
17 changed files with 233 additions and 167 deletions

View File

@@ -45,6 +45,7 @@ use crate::semantic_index::{imported_modules, place_table, semantic_index};
use crate::suppression::check_suppressions;
use crate::types::bound_super::BoundSuperType;
use crate::types::call::{Binding, Bindings, CallArguments, CallableBinding};
use crate::types::class::GenericAliasInstance;
pub(crate) use crate::types::class_base::ClassBase;
use crate::types::constraints::{
ConstraintSet, IteratorConstraintsExtension, OptionConstraintsExtension,
@@ -759,7 +760,7 @@ pub enum Type<'db> {
/// A specific class object
ClassLiteral(ClassLiteral<'db>),
/// A specialization of a generic class
GenericAlias(GenericAlias<'db>),
GenericAlias(GenericAliasInstance<'db>),
/// The set of all class objects that are subclasses of the given class (C), spelled `type[C]`.
SubclassOf(SubclassOfType<'db>),
/// The set of Python objects with the given class in their __class__'s method resolution order.
@@ -906,7 +907,7 @@ impl<'db> Type<'db> {
matches!(self, Type::GenericAlias(_))
}
pub(crate) fn as_generic_alias(&self) -> Option<GenericAlias<'db>> {
pub(crate) fn as_generic_alias(&self) -> Option<GenericAliasInstance<'db>> {
match self {
Type::GenericAlias(alias) => Some(*alias),
_ => None,
@@ -1159,7 +1160,7 @@ impl<'db> Type<'db> {
pub(crate) fn to_class_type(self, db: &'db dyn Db) -> Option<ClassType<'db>> {
match self {
Type::ClassLiteral(class_literal) => Some(class_literal.default_specialization(db)),
Type::GenericAlias(alias) => Some(ClassType::Generic(alias)),
Type::GenericAlias(instance) => Some(ClassType::Generic(instance.alias(db))),
_ => None,
}
}
@@ -1283,8 +1284,8 @@ impl<'db> Type<'db> {
Self::BytesLiteral(BytesLiteralType::new(db, bytes))
}
pub(crate) fn typed_dict(defining_class: impl Into<ClassType<'db>>) -> Self {
Self::TypedDict(TypedDictType::new(defining_class.into()))
pub(crate) fn typed_dict(defining_class: ClassType<'db>) -> Self {
Self::TypedDict(TypedDictType::new(defining_class))
}
#[must_use]
@@ -1569,7 +1570,9 @@ impl<'db> Type<'db> {
Some(class_literal.default_specialization(db).into_callable(db))
}
Type::GenericAlias(alias) => Some(ClassType::Generic(alias).into_callable(db)),
Type::GenericAlias(instance) => {
Some(ClassType::Generic(instance.alias(db)).into_callable(db))
}
Type::NewTypeInstance(newtype) => {
Type::instance(db, newtype.base_class_type(db)).try_upcast_to_callable(db)
@@ -2416,20 +2419,22 @@ impl<'db> Type<'db> {
)
})
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability())),
(Type::GenericAlias(alias), Type::SubclassOf(target_subclass_ty)) => target_subclass_ty
.subclass_of()
.into_class()
.map(|subclass_of_class| {
ClassType::Generic(alias).has_relation_to_impl(
db,
subclass_of_class,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
})
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability())),
(Type::GenericAlias(instance), Type::SubclassOf(target_subclass_ty)) => {
target_subclass_ty
.subclass_of()
.into_class()
.map(|subclass_of_class| {
ClassType::Generic(instance.alias(db)).has_relation_to_impl(
db,
subclass_of_class,
inferable,
relation,
relation_visitor,
disjointness_visitor,
)
})
.unwrap_or_else(|| ConstraintSet::from(relation.is_assignability()))
}
// This branch asks: given two types `type[T]` and `type[S]`, is `type[T]` a subtype of `type[S]`?
(Type::SubclassOf(self_subclass_ty), Type::SubclassOf(target_subclass_ty)) => {
@@ -2456,7 +2461,7 @@ impl<'db> Type<'db> {
disjointness_visitor,
)
}
(Type::GenericAlias(alias), _) => ClassType::from(alias)
(Type::GenericAlias(instance), _) => ClassType::Generic(instance.alias(db))
.metaclass_instance_type(db)
.has_relation_to_impl(
db,
@@ -3180,7 +3185,7 @@ impl<'db> Type<'db> {
| (Type::GenericAlias(alias_b), Type::SubclassOf(subclass_of_ty)) => {
match subclass_of_ty.subclass_of() {
SubclassOfInner::Dynamic(_) => ConstraintSet::from(false),
SubclassOfInner::Class(class_a) => ClassType::from(alias_b)
SubclassOfInner::Class(class_a) => ClassType::Generic(alias_b.alias(db))
.when_subclass_of(db, class_a, inferable)
.negate(db),
}
@@ -3295,9 +3300,9 @@ impl<'db> Type<'db> {
.metaclass_instance_type(db)
.when_subtype_of(db, instance, inferable)
.negate(db),
(Type::GenericAlias(alias), instance @ Type::NominalInstance(_))
| (instance @ Type::NominalInstance(_), Type::GenericAlias(alias)) => {
ClassType::from(alias)
(Type::GenericAlias(generic), instance @ Type::NominalInstance(_))
| (instance @ Type::NominalInstance(_), Type::GenericAlias(generic)) => {
ClassType::Generic(generic.alias(db))
.metaclass_instance_type(db)
.has_relation_to_impl(
db,
@@ -3822,13 +3827,21 @@ impl<'db> Type<'db> {
}
}
Type::GenericAlias(alias) if alias.is_typed_dict(db) => {
Some(alias.origin(db).typed_dict_member(db, None, name, policy))
}
Type::GenericAlias(instance) if instance.alias(db).is_typed_dict(db) => Some(
instance
.alias(db)
.origin(db)
.typed_dict_member(db, None, name, policy),
),
Type::GenericAlias(alias) => {
let attr = Some(ClassType::from(*alias).class_member(db, name, policy));
match alias.specialization(db).materialization_kind(db) {
Type::GenericAlias(instance) => {
let attr =
Some(ClassType::Generic(instance.alias(db)).class_member(db, name, policy));
match instance
.alias(db)
.specialization(db)
.materialization_kind(db)
{
None => attr,
Some(materialization_kind) => attr.map(|attr| {
attr.materialize(
@@ -5032,14 +5045,16 @@ impl<'db> Type<'db> {
.metaclass_instance_type(db)
.try_bool_impl(db, allow_short_circuit, visitor)?
}
Type::GenericAlias(alias) => ClassType::from(*alias)
Type::GenericAlias(generic) => ClassType::Generic(generic.alias(db))
.metaclass_instance_type(db)
.try_bool_impl(db, allow_short_circuit, visitor)?,
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
SubclassOfInner::Dynamic(_) => Truthiness::Ambiguous,
SubclassOfInner::Class(class) => {
Type::from(class).try_bool_impl(db, allow_short_circuit, visitor)?
class
.into_type(db)
.try_bool_impl(db, allow_short_circuit, visitor)?
}
},
@@ -5770,7 +5785,7 @@ impl<'db> Type<'db> {
// getting the signature here. This signature can still be used in some cases (e.g.
// evaluating callable subtyping). TODO improve this definition (intersection of
// `__new__` and `__init__` signatures? and respect metaclass `__call__`).
SubclassOfInner::Class(class) => Type::from(class).bindings(db),
SubclassOfInner::Class(class) => class.into_type(db).bindings(db),
},
Type::NominalInstance(_) | Type::ProtocolInstance(_) | Type::NewTypeInstance(_) => {
@@ -5972,7 +5987,7 @@ impl<'db> Type<'db> {
match ty {
Type::NominalInstance(nominal) => nominal.tuple_spec(db),
Type::NewTypeInstance(newtype) => non_async_special_case(db, Type::instance(db, newtype.base_class_type(db))),
Type::GenericAlias(alias) if alias.origin(db).is_tuple(db) => {
Type::GenericAlias(instance) if instance.alias(db).origin(db).is_tuple(db) => {
Some(Cow::Owned(TupleSpec::homogeneous(todo_type!(
"*tuple[] annotations"
))))
@@ -6418,7 +6433,7 @@ impl<'db> Type<'db> {
// It is important that identity_specialization specializes the class with
// _inferable_ typevars, so that our specialization inference logic will
// try to find a specialization for them.
Type::from(class.identity_specialization(db)),
class.identity_specialization(db).into_type(db),
),
_ => (None, None, self),
},
@@ -6632,7 +6647,9 @@ impl<'db> Type<'db> {
match self {
Type::Dynamic(_) | Type::Never => Some(self),
Type::ClassLiteral(class) => Some(Type::instance(db, class.default_specialization(db))),
Type::GenericAlias(alias) => Some(Type::instance(db, ClassType::from(alias))),
Type::GenericAlias(instance) => {
Some(Type::instance(db, ClassType::Generic(instance.alias(db))))
}
Type::SubclassOf(subclass_of_ty) => Some(subclass_of_ty.to_instance(db)),
Type::KnownInstance(KnownInstanceType::NewType(newtype)) => {
Some(Type::NewTypeInstance(newtype))
@@ -6714,8 +6731,12 @@ impl<'db> Type<'db> {
};
Ok(ty)
}
Type::GenericAlias(alias) if alias.is_typed_dict(db) => Ok(Type::typed_dict(*alias)),
Type::GenericAlias(alias) => Ok(Type::instance(db, ClassType::from(*alias))),
Type::GenericAlias(instance) if instance.alias(db).is_typed_dict(db) => {
Ok(Type::typed_dict(ClassType::Generic(instance.alias(db))))
}
Type::GenericAlias(instance) => {
Ok(Type::instance(db, ClassType::Generic(instance.alias(db))))
}
Type::SubclassOf(_)
| Type::BooleanLiteral(_)
@@ -7073,7 +7094,7 @@ impl<'db> Type<'db> {
}
Type::ClassLiteral(class) => class.metaclass(db),
Type::GenericAlias(alias) => ClassType::from(alias).metaclass(db),
Type::GenericAlias(instance) => ClassType::Generic(instance.alias(db)).metaclass(db),
Type::SubclassOf(subclass_of_ty) => match subclass_of_ty.subclass_of() {
SubclassOfInner::Dynamic(_) => self,
SubclassOfInner::Class(class) => SubclassOfType::from(
@@ -7098,7 +7119,7 @@ impl<'db> Type<'db> {
// understand a more specific meta type in order to correctly handle `__getitem__`.
Type::TypedDict(typed_dict) => SubclassOfType::from(db, typed_dict.defining_class()),
Type::TypeAlias(alias) => alias.value_type(db).to_meta_type(db),
Type::NewTypeInstance(newtype) => Type::from(newtype.base_class_type(db)),
Type::NewTypeInstance(newtype) => newtype.base_class_type(db).into_type(db),
}
}
@@ -7116,7 +7137,7 @@ impl<'db> Type<'db> {
[KnownClass::Str.to_instance(db), Type::object()],
None,
)
.map(Type::from)
.map(|class| class.into_type(db))
// Guard against user-customized typesheds with a broken `dict` class
.unwrap_or_else(Type::unknown);
}
@@ -7874,7 +7895,7 @@ impl<'db> Type<'db> {
pub(crate) fn generic_origin(self, db: &'db dyn Db) -> Option<ClassLiteral<'db>> {
match self {
Type::GenericAlias(generic) => Some(generic.origin(db)),
Type::GenericAlias(instance) => Some(instance.origin(db)),
Type::NominalInstance(instance) => {
if let ClassType::Generic(generic) = instance.class(db) {
Some(generic.origin(db))
@@ -7934,7 +7955,7 @@ impl<'db> VarianceInferable<'db> for Type<'db> {
Type::NominalInstance(nominal_instance_type) => {
nominal_instance_type.variance_of(db, typevar)
}
Type::GenericAlias(generic_alias) => generic_alias.variance_of(db, typevar),
Type::GenericAlias(instance) => instance.alias(db).variance_of(db, typevar),
Type::Callable(callable_type) => callable_type.signatures(db).variance_of(db, typevar),
// A type variable is always covariant in itself.
Type::TypeVar(other_typevar) if other_typevar == typevar => {

View File

@@ -76,9 +76,11 @@ impl<'db> BoundSuperError<'db> {
BoundSuperError::InvalidPivotClassType { pivot_class } => {
if let Some(builder) = context.report_lint(&INVALID_SUPER_ARGUMENT, node) {
match pivot_class {
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
Type::GenericAlias(instance) => builder.into_diagnostic(format_args!(
"`types.GenericAlias` instance `{}` is not a valid class",
alias.display_with(context.db(), DisplaySettings::default()),
instance
.alias(context.db())
.display_with(context.db(), DisplaySettings::default()),
)),
_ => builder.into_diagnostic(format_args!(
"`{pivot_class}` is not a valid class",
@@ -209,13 +211,11 @@ impl<'db> SuperOwnerKind<'db> {
SuperOwnerKind::Instance(instance) => Some(instance.class(db)),
}
}
}
impl<'db> From<SuperOwnerKind<'db>> for Type<'db> {
fn from(owner: SuperOwnerKind<'db>) -> Self {
match owner {
pub fn into_type(self, db: &'db dyn Db) -> Type<'db> {
match self {
SuperOwnerKind::Dynamic(dynamic) => Type::Dynamic(dynamic),
SuperOwnerKind::Class(class) => class.into(),
SuperOwnerKind::Class(class) => class.into_type(db),
SuperOwnerKind::Instance(instance) => instance.into(),
}
}
@@ -236,8 +236,8 @@ pub(super) fn walk_bound_super_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
bound_super: BoundSuperType<'db>,
visitor: &V,
) {
visitor.visit_type(db, Type::from(bound_super.pivot_class(db)));
visitor.visit_type(db, Type::from(bound_super.owner(db)));
visitor.visit_type(db, bound_super.pivot_class(db).into_type(db));
visitor.visit_type(db, bound_super.owner(db).into_type(db));
}
impl<'db> BoundSuperType<'db> {
@@ -515,12 +515,12 @@ impl<'db> BoundSuperType<'db> {
db,
attribute,
Type::none(db),
Type::from(owner),
owner.into_type(db),
)
.0,
),
SuperOwnerKind::Instance(_) => {
let owner = Type::from(owner);
let owner = owner.into_type(db);
Some(
Type::try_call_dunder_get_on_attribute(
db,

View File

@@ -233,8 +233,6 @@ impl<'db> CodeGeneratorKind<'db> {
pub struct GenericAlias<'db> {
pub(crate) origin: ClassLiteral<'db>,
pub(crate) specialization: Specialization<'db>,
pub(crate) binding_context: Option<Definition<'db>>,
}
pub(super) fn walk_generic_alias<'db, V: super::visitor::TypeVisitor<'db> + ?Sized>(
@@ -254,7 +252,6 @@ impl<'db> GenericAlias<'db> {
db,
self.origin(db),
self.specialization(db).normalized_impl(db, visitor),
self.binding_context(db),
)
}
@@ -280,7 +277,6 @@ impl<'db> GenericAlias<'db> {
self.origin(db),
self.specialization(db)
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
self.binding_context(db),
)
}
@@ -300,9 +296,60 @@ impl<'db> GenericAlias<'db> {
}
}
impl<'db> From<GenericAlias<'db>> for Type<'db> {
fn from(alias: GenericAlias<'db>) -> Type<'db> {
Type::GenericAlias(alias)
#[salsa::interned(debug, heap_size=ruff_memory_usage::heap_size)]
#[derive(PartialOrd, Ord)]
pub struct GenericAliasInstance<'db> {
pub alias: GenericAlias<'db>,
pub binding_context: Option<Definition<'db>>,
}
impl get_size2::GetSize for GenericAliasInstance<'_> {}
impl<'db> GenericAliasInstance<'db> {
pub(super) fn normalized_impl(self, db: &'db dyn Db, visitor: &NormalizedVisitor<'db>) -> Self {
Self::new(
db,
self.alias(db).normalized_impl(db, visitor),
self.binding_context(db),
)
}
pub(crate) fn definition(self, db: &'db dyn Db) -> Definition<'db> {
self.alias(db).definition(db)
}
pub(crate) fn origin(self, db: &'db dyn Db) -> ClassLiteral<'db> {
self.alias(db).origin(db)
}
pub(crate) fn specialization(self, db: &'db dyn Db) -> Specialization<'db> {
self.alias(db).specialization(db)
}
pub(super) fn apply_type_mapping_impl<'a>(
self,
db: &'db dyn Db,
type_mapping: &TypeMapping<'a, 'db>,
tcx: TypeContext<'db>,
visitor: &ApplyTypeMappingVisitor<'db>,
) -> Self {
Self::new(
db,
self.alias(db)
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
self.binding_context(db),
)
}
pub(super) fn find_legacy_typevars_impl(
self,
db: &'db dyn Db,
binding_context: Option<Definition<'db>>,
typevars: &mut FxOrderSet<BoundTypeVarInstance<'db>>,
visitor: &FindLegacyTypeVarsVisitor<'db>,
) {
self.alias(db)
.find_legacy_typevars_impl(db, binding_context, typevars, visitor);
}
}
@@ -389,10 +436,19 @@ impl<'db> ClassType<'db> {
matches!(self, Self::Generic(_))
}
pub(super) const fn into_generic_alias(self) -> Option<GenericAlias<'db>> {
pub(super) fn into_generic_alias(self, db: &'db dyn Db) -> Option<GenericAlias<'db>> {
match self {
Self::NonGeneric(_) => None,
Self::Generic(generic) => Some(generic),
Self::Generic(alias) => Some(alias),
}
}
pub(super) fn into_type(self, db: &'db dyn Db) -> Type<'db> {
match self {
Self::NonGeneric(class) => class.into(),
Self::Generic(instance) => {
Type::GenericAlias(GenericAliasInstance::new(db, instance, None))
}
}
}
@@ -1062,7 +1118,7 @@ impl<'db> ClassType<'db> {
/// constructor signature of this class.
#[salsa::tracked(cycle_initial=into_callable_cycle_initial, heap_size=ruff_memory_usage::heap_size)]
pub(super) fn into_callable(self, db: &'db dyn Db) -> CallableTypes<'db> {
let self_ty = Type::from(self);
let self_ty = self.into_type(db);
let metaclass_dunder_call_function_symbol = self_ty
.member_lookup_with_policy(
db,
@@ -1226,26 +1282,11 @@ fn into_callable_cycle_initial<'db>(
CallableTypes::one(CallableType::bottom(db))
}
impl<'db> From<GenericAlias<'db>> for ClassType<'db> {
fn from(generic: GenericAlias<'db>) -> ClassType<'db> {
ClassType::Generic(generic)
}
}
impl<'db> From<ClassType<'db>> for Type<'db> {
fn from(class: ClassType<'db>) -> Type<'db> {
match class {
ClassType::NonGeneric(non_generic) => non_generic.into(),
ClassType::Generic(generic) => generic.into(),
}
}
}
impl<'db> VarianceInferable<'db> for ClassType<'db> {
fn variance_of(self, db: &'db dyn Db, typevar: BoundTypeVarInstance<'db>) -> TypeVarVariance {
match self {
Self::NonGeneric(class) => class.variance_of(db, typevar),
Self::Generic(generic) => generic.variance_of(db, typevar),
Self::Generic(alias) => alias.variance_of(db, typevar),
}
}
}
@@ -1550,7 +1591,7 @@ impl<'db> ClassLiteral<'db> {
}
}
ClassType::Generic(GenericAlias::new(db, self, specialization, binding_context))
ClassType::Generic(GenericAlias::new(db, self, specialization))
}
}
}
@@ -1646,7 +1687,7 @@ impl<'db> ClassLiteral<'db> {
Box::new([
definition_expression_type(db, class_definition, &class_stmt.bases()[0]),
Type::from(tuple_type.to_class_type(db, None)),
tuple_type.to_class_type(db, None).into_type(db),
])
} else {
class_stmt
@@ -1983,7 +2024,7 @@ impl<'db> ClassLiteral<'db> {
let (metaclass_literal, _) = candidate.metaclass.class_literal(db);
Ok((
candidate.metaclass.into(),
candidate.metaclass.into_type(db),
metaclass_literal.dataclass_transformer_params(db),
))
}
@@ -2061,7 +2102,7 @@ impl<'db> ClassLiteral<'db> {
// Note: calling `Type::from(superclass).member()` would be incorrect here.
// What we'd really want is a `Type::Any.own_class_member()` method,
// but adding such a method wouldn't make much sense -- it would always return `Any`!
dynamic_type_to_intersect_with.get_or_insert(Type::from(superclass));
dynamic_type_to_intersect_with.get_or_insert(superclass.into_type(db));
}
ClassBase::Class(class) => {
let known = class.known(db);
@@ -4855,7 +4896,7 @@ impl KnownClass {
"Use `Type::heterogeneous_tuple` or `Type::homogeneous_tuple` to create `tuple` instances"
);
self.to_specialized_class_type(db, specialization, None)
.and_then(|class_type| Type::from(class_type).to_instance(db))
.and_then(|class_type| class_type.into_type(db).to_instance(db))
.unwrap_or_else(Type::unknown)
}

View File

@@ -79,7 +79,9 @@ impl<'db> ClassBase<'db> {
match ty {
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))),
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
Type::GenericAlias(instance) => {
Some(Self::Class(ClassType::Generic(instance.alias(db))))
}
Type::NominalInstance(instance)
if instance.has_known_class(db, KnownClass::GenericAlias) =>
{
@@ -240,7 +242,7 @@ impl<'db> ClassBase<'db> {
fields.values().map(|field| field.declared_ty),
)?
.to_class_type(db, None)
.into(),
.into_type(db),
subclass,
)
}
@@ -383,7 +385,7 @@ impl<'db> ClassBase<'db> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.base {
ClassBase::Dynamic(dynamic) => dynamic.fmt(f),
ClassBase::Class(class) => Type::from(class).display(self.db).fmt(f),
ClassBase::Class(class) => class.into_type(self.db).display(self.db).fmt(f),
ClassBase::Protocol => f.write_str("typing.Protocol"),
ClassBase::Generic => f.write_str("typing.Generic"),
ClassBase::TypedDict => f.write_str("typing.TypedDict"),
@@ -393,19 +395,11 @@ impl<'db> ClassBase<'db> {
ClassBaseDisplay { db, base: self }
}
}
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
fn from(value: ClassType<'db>) -> Self {
ClassBase::Class(value)
}
}
impl<'db> From<ClassBase<'db>> for Type<'db> {
fn from(value: ClassBase<'db>) -> Self {
match value {
pub fn into_type(self, db: &'db dyn Db) -> Type<'db> {
match self {
ClassBase::Dynamic(dynamic) => Type::Dynamic(dynamic),
ClassBase::Class(class) => class.into(),
ClassBase::Class(class) => class.into_type(db),
ClassBase::Protocol => Type::SpecialForm(SpecialFormType::Protocol),
ClassBase::Generic => Type::SpecialForm(SpecialFormType::Generic),
ClassBase::TypedDict => Type::SpecialForm(SpecialFormType::TypedDict),
@@ -413,9 +407,9 @@ impl<'db> From<ClassBase<'db>> for Type<'db> {
}
}
impl<'db> From<&ClassBase<'db>> for Type<'db> {
fn from(value: &ClassBase<'db>) -> Self {
Self::from(*value)
impl<'db> From<ClassType<'db>> for ClassBase<'db> {
fn from(value: ClassType<'db>) -> Self {
ClassBase::Class(value)
}
}

View File

@@ -2506,9 +2506,9 @@ pub(super) fn report_possibly_missing_attribute(
"Attribute `{attribute}` may be missing on class `{}`",
class.name(db),
)),
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
Type::GenericAlias(instance) => builder.into_diagnostic(format_args!(
"Attribute `{attribute}` may be missing on class `{}`",
alias.display(db),
instance.alias(db).display(db),
)),
_ => builder.into_diagnostic(format_args!(
"Attribute `{attribute}` may be missing on object of type `{}`",

View File

@@ -416,11 +416,11 @@ impl<'db> super::visitor::TypeVisitor<'db> for AmbiguousClassCollector<'db> {
Type::ProtocolInstance(ProtocolInstanceType {
inner: Protocol::FromClass(class),
..
}) => return self.visit_type(db, Type::from(class)),
}) => return self.visit_type(db, class.into_type(db)),
_ => {}
}
if let visitor::TypeKind::NonAtomic(t) = visitor::TypeKind::from(ty) {
if let visitor::TypeKind::NonAtomic(t) = visitor::TypeKind::from_type(db, ty) {
if !self.visited_types.borrow_mut().insert(ty) {
// If we have already seen this type, we can skip it.
return;
@@ -599,7 +599,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
(ClassType::NonGeneric(class), _) => {
class.display_with(self.db, self.settings.clone()).fmt_detailed(f)
},
(ClassType::Generic(alias), _) => alias.display_with(self.db, self.settings.clone()).fmt_detailed(f),
(ClassType::Generic(instance), _) => instance.display_with(self.db, self.settings.clone()).fmt_detailed(f),
}
}
Type::ProtocolInstance(protocol) => match protocol.inner {
@@ -607,7 +607,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
ClassType::NonGeneric(class) => class
.display_with(self.db, self.settings.clone())
.fmt_detailed(f),
ClassType::Generic(alias) => alias
ClassType::Generic(instance) => instance
.display_with(self.db, self.settings.clone())
.fmt_detailed(f),
},
@@ -653,6 +653,7 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
let mut f = f.with_type(self.ty);
f.write_str("<class '")?;
generic
.alias(self.db)
.display_with(self.db, self.settings.clone())
.fmt_detailed(&mut f)?;
f.write_str("'>")
@@ -667,11 +668,11 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
.fmt_detailed(f)?;
f.write_char(']')
}
SubclassOfInner::Class(ClassType::Generic(alias)) => {
SubclassOfInner::Class(ClassType::Generic(instance)) => {
f.with_type(KnownClass::Type.to_class_literal(self.db))
.write_str("type")?;
f.write_char('[')?;
alias
instance
.display_with(self.db, self.settings.clone())
.fmt_detailed(f)?;
f.write_char(']')
@@ -861,11 +862,15 @@ impl<'db> FmtDetailed<'db> for DisplayRepresentation<'db> {
Type::BoundSuper(bound_super) => {
f.set_invalid_syntax();
f.write_str("<super: ")?;
Type::from(bound_super.pivot_class(self.db))
bound_super
.pivot_class(self.db)
.into_type(self.db)
.display_with(self.db, self.settings.singleline())
.fmt_detailed(f)?;
f.write_str(", ")?;
Type::from(bound_super.owner(self.db))
bound_super
.owner(self.db)
.into_type(self.db)
.display_with(self.db, self.settings.singleline())
.fmt_detailed(f)?;
f.write_str(">")

View File

@@ -154,7 +154,7 @@ pub(crate) fn enum_metadata<'db>(
.skip(1)
.filter_map(ClassBase::into_class)
.filter(|class| {
!Type::from(*class).is_subtype_of(
!class.into_type(db).is_subtype_of(
db,
KnownClass::Enum.to_subclass_of(db),
)

View File

@@ -1578,7 +1578,7 @@ impl KnownFunction {
let mut good_argument = true;
let classes = match param_type {
Type::ClassLiteral(class) => vec![ClassType::NonGeneric(*class)],
Type::GenericAlias(generic_alias) => vec![ClassType::Generic(*generic_alias)],
Type::GenericAlias(generic) => vec![ClassType::Generic(generic.alias(db))],
Type::Union(union) => {
let elements = union.elements(db);
let mut classes = Vec::with_capacity(elements.len());
@@ -1587,8 +1587,8 @@ impl KnownFunction {
Type::ClassLiteral(class) => {
classes.push(ClassType::NonGeneric(*class));
}
Type::GenericAlias(generic_alias) => {
classes.push(ClassType::Generic(*generic_alias));
Type::GenericAlias(generic) => {
classes.push(ClassType::Generic(generic.alias(db)));
}
_ => {
good_argument = false;

View File

@@ -1609,7 +1609,7 @@ impl<'db> SpecializationBuilder<'db> {
// Extract formal_alias if this is a generic class
let formal_alias = match formal {
Type::NominalInstance(formal_nominal) => {
formal_nominal.class(self.db).into_generic_alias()
formal_nominal.class(self.db).into_generic_alias(self.db)
}
// TODO: This will only handle classes that explicit implement a generic protocol
// by listing it as a base class. To handle classes that implicitly implement a
@@ -1618,7 +1618,7 @@ impl<'db> SpecializationBuilder<'db> {
Type::ProtocolInstance(ProtocolInstanceType {
inner: Protocol::FromClass(class),
..
}) => class.into_generic_alias(),
}) => class.into_generic_alias(self.db),
_ => None,
};

View File

@@ -150,7 +150,7 @@ impl<'db> AllMembers<'db> {
self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db));
}
Type::GenericAlias(generic_alias) if generic_alias.is_typed_dict(db) => {
Type::GenericAlias(instance) if instance.alias(db).is_typed_dict(db) => {
self.extend_with_type(db, KnownClass::TypedDictFallback.to_class_literal(db));
}

View File

@@ -686,7 +686,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
continue;
}
Type::ClassLiteral(class) => ClassType::NonGeneric(*class),
Type::GenericAlias(class) => ClassType::Generic(*class),
Type::GenericAlias(generic) => ClassType::Generic(generic.alias(self.db())),
_ => continue,
};
@@ -7502,7 +7502,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let class_type =
class_literal.apply_specialization(self.db(), |_| builder.build(generic_context), None);
Type::from(class_type).to_instance(self.db())
class_type.into_type(self.db()).to_instance(self.db())
}
/// Infer the type of the `iter` expression of the first comprehension.
@@ -8085,7 +8085,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let class = match callable_type {
Type::ClassLiteral(class) => Some(ClassType::NonGeneric(class)),
Type::GenericAlias(generic) => Some(ClassType::Generic(generic)),
Type::GenericAlias(generic) => Some(ClassType::Generic(generic.alias(self.db()))),
Type::SubclassOf(subclass) => subclass.subclass_of().into_class(),
_ => None,
};
@@ -9147,9 +9147,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
"Class `{}` has no attribute `{attr_name}`",
class.name(db),
)),
Type::GenericAlias(alias) => builder.into_diagnostic(format_args!(
Type::GenericAlias(instance) => builder.into_diagnostic(format_args!(
"Class `{}` has no attribute `{attr_name}`",
alias.display(db),
instance.alias(db).display(db),
)),
Type::FunctionLiteral(function) => builder.into_diagnostic(format_args!(
"Function `{}` has no attribute `{attr_name}`",
@@ -10813,11 +10813,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// If we have an implicit type alias like `MyList = list[T]`, and if `MyList` is being
// used in another implicit type alias like `Numbers = MyList[int]`, then we infer the
// right hand side as a value expression, and need to handle the specialization here.
if let Some(alias) = value_ty.as_generic_alias() {
if let Some(instance) = value_ty.as_generic_alias() {
let return_ty = self.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
alias.binding_context(self.db()),
// alias.binding_context(self.db()),
instance.binding_context(self.db()),
false,
);
@@ -10864,7 +10865,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let typevar_binding_context = self.typevar_binding_context;
let tuple_generic_alias = |tuple: Option<TupleType<'db>>| {
let tuple = tuple.unwrap_or_else(|| TupleType::homogeneous(db, Type::unknown()));
Type::from(tuple.to_class_type(db, typevar_binding_context))
tuple
.to_class_type(db, typevar_binding_context)
.into_type(db)
};
match value_ty {
@@ -11075,7 +11078,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
[element_ty],
self.typevar_binding_context,
)
.map(Type::from)
.map(|class_type| class_type.into_type(self.db()))
.unwrap_or_else(Type::unknown);
}
// `typing` special forms with two generic arguments
@@ -11137,7 +11140,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
[first_ty, second_ty],
self.typevar_binding_context,
)
.map(Type::from)
.map(|class_type| class_type.into_type(self.db()))
.unwrap_or_else(Type::unknown);
}
Type::KnownInstance(KnownInstanceType::UnionType(instance)) => {
@@ -11185,11 +11188,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let db = self.db();
let typevar_binding_context = self.typevar_binding_context;
let specialize = |types: &[Option<Type<'db>>]| {
Type::from(generic_class.apply_specialization(
db,
|_| generic_context.specialize_partial(db, types.iter().copied()),
typevar_binding_context,
))
generic_class
.apply_specialization(
db,
|_| generic_context.specialize_partial(db, types.iter().copied()),
typevar_binding_context,
)
.into_type(db)
};
self.infer_explicit_callable_specialization(

View File

@@ -4,6 +4,7 @@ use ruff_python_ast as ast;
use super::{DeferredExpressionState, TypeInferenceBuilder};
use crate::FxOrderSet;
use crate::semantic_index::definition::Definition;
use crate::types::class::GenericAliasInstance;
use crate::types::diagnostic::{
self, INVALID_TYPE_FORM, NON_SUBSCRIPTABLE, report_invalid_argument_number_to_special_form,
report_invalid_arguments_to_callable,
@@ -810,10 +811,9 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
} else {
// Update the binding context
match specialized {
Type::GenericAlias(alias) => Type::GenericAlias(GenericAlias::new(
Type::GenericAlias(alias) => Type::GenericAlias(GenericAliasInstance::new(
db,
alias.origin(db),
alias.specialization(db),
GenericAlias::new(db, alias.origin(db), alias.specialization(db)),
current_typevar_binding_context,
)),
Type::KnownInstance(KnownInstanceType::TypeGenericAlias(instance)) => {
@@ -1049,10 +1049,10 @@ impl<'db> TypeInferenceBuilder<'db, '_> {
}
}
}
Type::GenericAlias(alias) => self.infer_explicitly_specialized_type_alias(
Type::GenericAlias(instance) => self.infer_explicitly_specialized_type_alias(
subscript,
value_ty,
alias.binding_context(self.db()),
instance.binding_context(self.db()),
true,
),
Type::StringLiteral(_) => {

View File

@@ -207,7 +207,7 @@ pub(super) fn walk_nominal_instance_type<'db, V: super::visitor::TypeVisitor<'db
nominal: NominalInstanceType<'db>,
visitor: &V,
) {
visitor.visit_type(db, nominal.class(db).into());
visitor.visit_type(db, nominal.class(db).into_type(db));
}
impl<'db> NominalInstanceType<'db> {
@@ -279,7 +279,7 @@ impl<'db> NominalInstanceType<'db> {
}
KnownClass::Tuple => Some(
class
.into_generic_alias()
.into_generic_alias(db)
.and_then(|alias| {
Some(Cow::Borrowed(alias.specialization(db).tuple(db)?))
})

View File

@@ -144,6 +144,10 @@ impl<'db> ProtocolClass<'db> {
.apply_type_mapping_impl(db, type_mapping, tcx, visitor),
)
}
pub(super) fn into_type(self, db: &'db dyn Db) -> Type<'db> {
self.0.into_type(db)
}
}
impl<'db> Deref for ProtocolClass<'db> {
@@ -154,12 +158,6 @@ impl<'db> Deref for ProtocolClass<'db> {
}
}
impl<'db> From<ProtocolClass<'db>> for Type<'db> {
fn from(value: ProtocolClass<'db>) -> Self {
Self::from(value.0)
}
}
/// The interface of a protocol: the members of that protocol, and the types of those members.
///
/// # Ordering

View File

@@ -26,7 +26,7 @@ pub(super) fn walk_subclass_of_type<'db, V: super::visitor::TypeVisitor<'db> + ?
subclass_of: SubclassOfType<'db>,
visitor: &V,
) {
visitor.visit_type(db, Type::from(subclass_of.subclass_of));
visitor.visit_type(db, subclass_of.subclass_of.into_type(db));
}
impl<'db> SubclassOfType<'db> {
@@ -47,7 +47,7 @@ impl<'db> SubclassOfType<'db> {
SubclassOfInner::Dynamic(_) => Type::SubclassOf(Self { subclass_of }),
SubclassOfInner::Class(class) => {
if class.is_final(db) {
Type::from(class)
class.into_type(db)
} else if class.is_object(db) {
KnownClass::Type.to_instance(db)
} else {
@@ -129,7 +129,9 @@ impl<'db> SubclassOfType<'db> {
name: &str,
policy: MemberLookupPolicy,
) -> Option<PlaceAndQualifiers<'db>> {
Type::from(self.subclass_of).find_name_in_mro_with_policy(db, name, policy)
self.subclass_of
.into_type(db)
.find_name_in_mro_with_policy(db, name, policy)
}
/// Return `true` if `self` has a certain relation to `other`.
@@ -271,11 +273,18 @@ impl<'db> SubclassOfInner<'db> {
match ty {
Type::Dynamic(dynamic) => Some(Self::Dynamic(dynamic)),
Type::ClassLiteral(literal) => Some(Self::Class(literal.default_specialization(db))),
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic))),
Type::GenericAlias(generic) => Some(Self::Class(ClassType::Generic(generic.alias(db)))),
Type::SpecialForm(SpecialFormType::Any) => Some(Self::Dynamic(DynamicType::Any)),
_ => None,
}
}
pub(crate) fn into_type(self, db: &'db dyn Db) -> Type<'db> {
match self {
SubclassOfInner::Dynamic(dynamic) => Type::Dynamic(dynamic),
SubclassOfInner::Class(class) => class.into_type(db),
}
}
}
impl<'db> From<ClassType<'db>> for SubclassOfInner<'db> {
@@ -295,12 +304,3 @@ impl<'db> From<ProtocolClass<'db>> for SubclassOfInner<'db> {
SubclassOfInner::Class(*value)
}
}
impl<'db> From<SubclassOfInner<'db>> for Type<'db> {
fn from(value: SubclassOfInner<'db>) -> Self {
match value {
SubclassOfInner::Dynamic(dynamic) => Type::Dynamic(dynamic),
SubclassOfInner::Class(class) => class.into(),
}
}
}

View File

@@ -253,7 +253,7 @@ pub(crate) fn walk_typed_dict_type<'db, V: visitor::TypeVisitor<'db> + ?Sized>(
typed_dict: TypedDictType<'db>,
visitor: &V,
) {
visitor.visit_type(db, typed_dict.defining_class.into());
visitor.visit_type(db, typed_dict.defining_class.into_type(db));
}
pub(super) fn typed_dict_params_from_class_def(class_stmt: &StmtClassDef) -> TypedDictParams {

View File

@@ -144,8 +144,8 @@ pub(super) enum TypeKind<'db> {
NonAtomic(NonAtomicType<'db>),
}
impl<'db> From<Type<'db>> for TypeKind<'db> {
fn from(ty: Type<'db>) -> Self {
impl<'db> TypeKind<'db> {
pub(super) fn from_type(db: &'db dyn Db, ty: Type<'db>) -> Self {
match ty {
Type::AlwaysFalsy
| Type::AlwaysTruthy
@@ -180,7 +180,9 @@ impl<'db> From<Type<'db>> for TypeKind<'db> {
TypeKind::NonAtomic(NonAtomicType::MethodWrapper(method_wrapper))
}
Type::Callable(callable) => TypeKind::NonAtomic(NonAtomicType::Callable(callable)),
Type::GenericAlias(alias) => TypeKind::NonAtomic(NonAtomicType::GenericAlias(alias)),
Type::GenericAlias(instance) => {
TypeKind::NonAtomic(NonAtomicType::GenericAlias(instance.alias(db)))
}
Type::KnownInstance(known_instance) => {
TypeKind::NonAtomic(NonAtomicType::KnownInstance(known_instance))
}
@@ -260,7 +262,7 @@ pub(crate) fn walk_type_with_recursion_guard<'db>(
visitor: &impl TypeVisitor<'db>,
recursion_guard: &TypeCollector<'db>,
) {
match TypeKind::from(ty) {
match TypeKind::from_type(db, ty) {
TypeKind::Atomic => {}
TypeKind::NonAtomic(non_atomic_type) => {
if recursion_guard.type_was_already_seen(ty) {
@@ -349,7 +351,7 @@ fn specialization_depth(db: &dyn Db, ty: Type<'_>) -> usize {
}
fn visit_type(&self, db: &'db dyn Db, ty: Type<'db>) {
match TypeKind::from(ty) {
match TypeKind::from_type(db, ty) {
TypeKind::Atomic => {
if ty.is_divergent() {
self.max_depth.set(usize::MAX);