mirror of https://github.com/astral-sh/ruff
always defer parameter and return type inference
This commit is contained in:
parent
564a8dc6b2
commit
5a0b99ffba
|
|
@ -2241,7 +2241,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
name,
|
||||
type_params,
|
||||
parameters,
|
||||
returns,
|
||||
returns: _,
|
||||
body: _,
|
||||
decorator_list,
|
||||
} = function;
|
||||
|
|
@ -2288,21 +2288,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
|||
self.infer_expression(default, TypeContext::default());
|
||||
}
|
||||
|
||||
// If there are type params, parameters and returns are evaluated in that scope, that is, in
|
||||
// `infer_function_type_params`, rather than here.
|
||||
// If there are type params, parameters and returns are evaluated in that scope. Otherwise,
|
||||
// we always defer the inference of the parameters and returns. That ensures that we do not
|
||||
// add any spurious salsa cycles when applying decorators below. (Applying a decorator
|
||||
// requires getting the signature of this function definition, which in turn requires
|
||||
// (lazily) inferring the parameter and return types.)
|
||||
if type_params.is_none() {
|
||||
if self.defer_annotations() {
|
||||
self.deferred.insert(definition, self.multi_inference_state);
|
||||
} else {
|
||||
let previous_typevar_binding_context =
|
||||
self.typevar_binding_context.replace(definition);
|
||||
self.infer_return_type_annotation(
|
||||
returns.as_deref(),
|
||||
DeferredExpressionState::None,
|
||||
);
|
||||
self.infer_parameters(parameters);
|
||||
self.typevar_binding_context = previous_typevar_binding_context;
|
||||
}
|
||||
self.deferred.insert(definition, self.multi_inference_state);
|
||||
}
|
||||
|
||||
let known_function =
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@ use smallvec::{SmallVec, smallvec_inline};
|
|||
|
||||
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
||||
use crate::semantic_index::definition::Definition;
|
||||
use crate::semantic_index::semantic_index;
|
||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||
use crate::types::generics::{GenericContext, InferableTypeVars, walk_generic_context};
|
||||
use crate::types::infer::{infer_deferred_types, infer_scope_types};
|
||||
use crate::types::{
|
||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, CallableTypeKind,
|
||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||
|
|
@ -28,6 +30,33 @@ use crate::types::{
|
|||
use crate::{Db, FxOrderSet};
|
||||
use ruff_python_ast::{self as ast, name::Name};
|
||||
|
||||
/// Infer the type of a parameter or return annotation in a function signature.
|
||||
///
|
||||
/// This is very similar to
|
||||
/// [`definition_expression_type`][crate::types::definition_expression_type], but knows that
|
||||
/// `TypeInferenceBuilder` will always infer the parameters and return of a function in its PEP-695
|
||||
/// typevar scope, if there is one; otherwise they will be inferred in the function definition
|
||||
/// scope, but will always be deferred. (This prevents spurious salsa cycles when we need the
|
||||
/// signature of the function while in the middle of inferring its definition scope — for instance,
|
||||
/// when applying decorators.)
|
||||
fn function_signature_expression_type<'db>(
|
||||
db: &'db dyn Db,
|
||||
definition: Definition<'db>,
|
||||
expression: &ast::Expr,
|
||||
) -> Type<'db> {
|
||||
let file = definition.file(db);
|
||||
let index = semantic_index(db, file);
|
||||
let file_scope = index.expression_scope_id(expression);
|
||||
let scope = file_scope.to_scope_id(db, file);
|
||||
if scope == definition.scope(db) {
|
||||
// expression is in the function definition scope, but always deferred
|
||||
infer_deferred_types(db, definition).expression_type(expression)
|
||||
} else {
|
||||
// expression is in the PEP-695 type params sub-scope
|
||||
infer_scope_types(db, scope).expression_type(expression)
|
||||
}
|
||||
}
|
||||
|
||||
/// The signature of a single callable. If the callable is overloaded, there is a separate
|
||||
/// [`Signature`] for each overload.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||
|
|
@ -527,7 +556,7 @@ impl<'db> Signature<'db> {
|
|||
let return_ty = function_node
|
||||
.returns
|
||||
.as_ref()
|
||||
.map(|returns| definition_expression_type(db, definition, returns.as_ref()));
|
||||
.map(|returns| function_signature_expression_type(db, definition, returns.as_ref()));
|
||||
let legacy_generic_context =
|
||||
GenericContext::from_function_params(db, definition, ¶meters, return_ty);
|
||||
let full_generic_context = GenericContext::merge_pep695_and_legacy(
|
||||
|
|
@ -2089,7 +2118,7 @@ impl<'db> Parameter<'db> {
|
|||
Self {
|
||||
annotated_type: parameter
|
||||
.annotation()
|
||||
.map(|annotation| definition_expression_type(db, definition, annotation)),
|
||||
.map(|annotation| function_signature_expression_type(db, definition, annotation)),
|
||||
kind,
|
||||
form: ParameterForm::Value,
|
||||
inferred_annotation: false,
|
||||
|
|
|
|||
Loading…
Reference in New Issue