[ty] Remove use of ClassBase::try_from_type from super() machinery (#19902)

This commit is contained in:
Alex Waygood 2025-08-14 22:14:31 +01:00 committed by GitHub
parent ce938fe205
commit 82350a398e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 41 additions and 16 deletions

View file

@ -331,6 +331,9 @@ instance or a subclass of the first. If either condition is violated, a `TypeErr
runtime. runtime.
```py ```py
import typing
import collections
def f(x: int): def f(x: int):
# error: [invalid-super-argument] "`int` is not a valid class" # error: [invalid-super-argument] "`int` is not a valid class"
super(x, x) super(x, x)
@ -367,6 +370,19 @@ reveal_type(super(B, A))
reveal_type(super(B, object)) reveal_type(super(B, object))
super(object, object()).__class__ super(object, object()).__class__
# Not all objects valid in a class's bases list are valid as the first argument to `super()`.
# For example, it's valid to inherit from `typing.ChainMap`, but it's not valid as the first argument to `super()`.
#
# error: [invalid-super-argument] "`typing.ChainMap` is not a valid class"
reveal_type(super(typing.ChainMap, collections.ChainMap())) # revealed: Unknown
# Meanwhile, it's not valid to inherit from unsubscripted `typing.Generic`,
# but it *is* valid as the first argument to `super()`.
reveal_type(super(typing.Generic, typing.SupportsInt)) # revealed: <super: typing.Generic, <class 'SupportsInt'>>
def _(x: type[typing.Any], y: typing.Any):
reveal_type(super(x, y)) # revealed: <super: Any, Any>
``` ```
### Instance Member Access via `super` ### Instance Member Access via `super`

View file

@ -9526,23 +9526,32 @@ impl<'db> BoundSuperType<'db> {
)); ));
} }
// TODO: having to get a class-literal just to pass it in here is silly. // We don't use `Classbase::try_from_type` here because:
// `BoundSuperType` should probably not be using `ClassBase::try_from_type` here; // - There are objects that may validly be present in a class's bases list
// this also leads to false negatives in some cases. See discussion in // but are not valid as pivot classes, e.g. `typing.ChainMap`
// <https://github.com/astral-sh/ruff/pull/19560#discussion_r2271570071>. // - There are objects that are not valid in a class's bases list
let pivot_class = ClassBase::try_from_type( // but are valid as pivot classes, e.g. unsubscripted `typing.Generic`
db, let pivot_class = match pivot_class_type {
pivot_class_type, Type::ClassLiteral(class) => ClassBase::Class(ClassType::NonGeneric(class)),
KnownClass::Object Type::GenericAlias(class) => ClassBase::Class(ClassType::Generic(class)),
.to_class_literal(db) Type::SubclassOf(subclass_of) if subclass_of.subclass_of().is_dynamic() => {
.into_class_literal() ClassBase::Dynamic(
.expect("`object` should always exist in typeshed"), subclass_of
.subclass_of()
.into_dynamic()
.expect("Checked in branch arm"),
) )
.ok_or({
BoundSuperError::InvalidPivotClassType {
pivot_class: pivot_class_type,
} }
})?; Type::SpecialForm(SpecialFormType::Protocol) => ClassBase::Protocol,
Type::SpecialForm(SpecialFormType::Generic) => ClassBase::Generic,
Type::SpecialForm(SpecialFormType::TypedDict) => ClassBase::TypedDict,
Type::Dynamic(dynamic) => ClassBase::Dynamic(dynamic),
_ => {
return Err(BoundSuperError::InvalidPivotClassType {
pivot_class: pivot_class_type,
});
}
};
let owner = SuperOwnerKind::try_from_type(db, owner_type) let owner = SuperOwnerKind::try_from_type(db, owner_type)
.and_then(|owner| { .and_then(|owner| {