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,
|
name,
|
||||||
type_params,
|
type_params,
|
||||||
parameters,
|
parameters,
|
||||||
returns,
|
returns: _,
|
||||||
body: _,
|
body: _,
|
||||||
decorator_list,
|
decorator_list,
|
||||||
} = function;
|
} = function;
|
||||||
|
|
@ -2288,21 +2288,13 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
|
||||||
self.infer_expression(default, TypeContext::default());
|
self.infer_expression(default, TypeContext::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there are type params, parameters and returns are evaluated in that scope, that is, in
|
// If there are type params, parameters and returns are evaluated in that scope. Otherwise,
|
||||||
// `infer_function_type_params`, rather than here.
|
// 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 type_params.is_none() {
|
||||||
if self.defer_annotations() {
|
self.deferred.insert(definition, self.multi_inference_state);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let known_function =
|
let known_function =
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,10 @@ use smallvec::{SmallVec, smallvec_inline};
|
||||||
|
|
||||||
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
use super::{DynamicType, Type, TypeVarVariance, definition_expression_type};
|
||||||
use crate::semantic_index::definition::Definition;
|
use crate::semantic_index::definition::Definition;
|
||||||
|
use crate::semantic_index::semantic_index;
|
||||||
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
use crate::types::constraints::{ConstraintSet, IteratorConstraintsExtension};
|
||||||
use crate::types::generics::{GenericContext, InferableTypeVars, walk_generic_context};
|
use crate::types::generics::{GenericContext, InferableTypeVars, walk_generic_context};
|
||||||
|
use crate::types::infer::{infer_deferred_types, infer_scope_types};
|
||||||
use crate::types::{
|
use crate::types::{
|
||||||
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, CallableTypeKind,
|
ApplyTypeMappingVisitor, BindingContext, BoundTypeVarInstance, CallableTypeKind,
|
||||||
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
FindLegacyTypeVarsVisitor, HasRelationToVisitor, IsDisjointVisitor, IsEquivalentVisitor,
|
||||||
|
|
@ -28,6 +30,33 @@ use crate::types::{
|
||||||
use crate::{Db, FxOrderSet};
|
use crate::{Db, FxOrderSet};
|
||||||
use ruff_python_ast::{self as ast, name::Name};
|
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
|
/// The signature of a single callable. If the callable is overloaded, there is a separate
|
||||||
/// [`Signature`] for each overload.
|
/// [`Signature`] for each overload.
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, salsa::Update, get_size2::GetSize)]
|
||||||
|
|
@ -527,7 +556,7 @@ impl<'db> Signature<'db> {
|
||||||
let return_ty = function_node
|
let return_ty = function_node
|
||||||
.returns
|
.returns
|
||||||
.as_ref()
|
.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 =
|
let legacy_generic_context =
|
||||||
GenericContext::from_function_params(db, definition, ¶meters, return_ty);
|
GenericContext::from_function_params(db, definition, ¶meters, return_ty);
|
||||||
let full_generic_context = GenericContext::merge_pep695_and_legacy(
|
let full_generic_context = GenericContext::merge_pep695_and_legacy(
|
||||||
|
|
@ -2089,7 +2118,7 @@ impl<'db> Parameter<'db> {
|
||||||
Self {
|
Self {
|
||||||
annotated_type: parameter
|
annotated_type: parameter
|
||||||
.annotation()
|
.annotation()
|
||||||
.map(|annotation| definition_expression_type(db, definition, annotation)),
|
.map(|annotation| function_signature_expression_type(db, definition, annotation)),
|
||||||
kind,
|
kind,
|
||||||
form: ParameterForm::Value,
|
form: ParameterForm::Value,
|
||||||
inferred_annotation: false,
|
inferred_annotation: false,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue