mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-17 13:57:25 +00:00
[ty] Make infer_method_information
less confusing (#20740)
## Summary `infer_method_information` was previously calling `ClassLiteral::to_class_type`, which uses the default-specialization of a generic class. This specialized `ClassType` was later only used if the class was non-generic, making the specialization irrelevant. The implementation was still a bit confusing, so this PR proposes a way to avoid turning the class literal into a `ClassType`.
This commit is contained in:
parent
15af4c0a34
commit
f90d6466e0
1 changed files with 27 additions and 10 deletions
|
@ -26,7 +26,7 @@ use crate::types::function::FunctionType;
|
|||
use crate::types::generics::{GenericContext, typing_self, walk_generic_context};
|
||||
use crate::types::infer::nearest_enclosing_class;
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassType,
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, ClassLiteral,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsEquivalentVisitor, KnownClass,
|
||||
MaterializationKind, NormalizedVisitor, TypeMapping, TypeRelation, VarianceInferable,
|
||||
todo_type,
|
||||
|
@ -37,7 +37,8 @@ use ruff_python_ast::{self as ast, name::Name};
|
|||
#[derive(Clone, Copy, Debug)]
|
||||
struct MethodInformation<'db> {
|
||||
method: FunctionType<'db>,
|
||||
class: ClassType<'db>,
|
||||
class_literal: ClassLiteral<'db>,
|
||||
class_is_generic: bool,
|
||||
}
|
||||
|
||||
fn infer_method_information<'db>(
|
||||
|
@ -57,12 +58,22 @@ fn infer_method_information<'db>(
|
|||
.into_function_literal()?;
|
||||
|
||||
let class_def = index.expect_single_definition(class_node);
|
||||
let class_literal = infer_definition_types(db, class_def)
|
||||
let (class_literal, class_is_generic) = match infer_definition_types(db, class_def)
|
||||
.declaration_type(class_def)
|
||||
.inner_type();
|
||||
let class = class_literal.to_class_type(db)?;
|
||||
.inner_type()
|
||||
{
|
||||
Type::ClassLiteral(class_literal) => {
|
||||
(class_literal, class_literal.generic_context(db).is_some())
|
||||
}
|
||||
Type::GenericAlias(alias) => (alias.origin(db), true),
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
Some(MethodInformation { method, class })
|
||||
Some(MethodInformation {
|
||||
method,
|
||||
class_literal,
|
||||
class_is_generic,
|
||||
})
|
||||
}
|
||||
|
||||
/// The signature of a single callable. If the callable is overloaded, there is a separate
|
||||
|
@ -1217,7 +1228,11 @@ impl<'db> Parameters<'db> {
|
|||
.is_some_and(|f| f.method.is_staticmethod(db) || f.method.is_classmethod(db));
|
||||
|
||||
let inferred_annotation = |arg: &ParameterWithDefault| {
|
||||
if let Some(MethodInformation { method, class }) = method_info
|
||||
if let Some(MethodInformation {
|
||||
method,
|
||||
class_literal,
|
||||
class_is_generic,
|
||||
}) = method_info
|
||||
&& !is_static_or_classmethod
|
||||
&& arg.parameter.annotation().is_none()
|
||||
&& parameters.index(arg.name().id()) == Some(0)
|
||||
|
@ -1230,8 +1245,10 @@ impl<'db> Parameters<'db> {
|
|||
});
|
||||
|
||||
if method_has_self_in_generic_context
|
||||
|| class.is_generic()
|
||||
|| class.known(db).is_some_and(KnownClass::is_fallback_class)
|
||||
|| class_is_generic
|
||||
|| class_literal
|
||||
.known(db)
|
||||
.is_some_and(KnownClass::is_fallback_class)
|
||||
{
|
||||
let scope_id = definition.scope(db);
|
||||
let typevar_binding_context = Some(definition);
|
||||
|
@ -1246,7 +1263,7 @@ impl<'db> Parameters<'db> {
|
|||
// For methods of non-generic classes that are not otherwise generic (e.g. return `Self` or
|
||||
// have additional type parameters), the implicit `Self` type of the `self` parameter would
|
||||
// be the only type variable, so we can just use the class directly.
|
||||
Some(Type::instance(db, class))
|
||||
Some(class_literal.to_non_generic_instance(db))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue