From f90d6466e00d9ec7d7ef8f6e3bbf3fb2e2d0b0c2 Mon Sep 17 00:00:00 2001 From: David Peter Date: Tue, 7 Oct 2025 12:12:23 +0200 Subject: [PATCH] [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`. --- .../src/types/signatures.rs | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/crates/ty_python_semantic/src/types/signatures.rs b/crates/ty_python_semantic/src/types/signatures.rs index d6f3ee7374..4601f75541 100644 --- a/crates/ty_python_semantic/src/types/signatures.rs +++ b/crates/ty_python_semantic/src/types/signatures.rs @@ -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