Always defer

This commit is contained in:
Charlie Marsh
2025-12-17 13:35:53 -05:00
parent 96dbe609eb
commit 85f5039b11
2 changed files with 14 additions and 33 deletions

View File

@@ -50,10 +50,10 @@ reveal_type(custom_builtin) # revealed: Custom
reveal_type(str) # revealed: Unknown
```
## Unknown builtin (later defined)
## Forward reference in builtins stub
`foo` has a type of `Unknown` in this example, as it relies on `bar` which has not been defined at
that point:
In stub files, forward references are allowed in all expressions, so `foo` correctly resolves to the
type of `bar` even though `bar` is defined later:
```toml
[environment]
@@ -74,5 +74,5 @@ def reveal_type(obj, /): ...
```
```py
reveal_type(foo) # revealed: Unknown
reveal_type(foo) # revealed: Literal[1]
```

View File

@@ -327,6 +327,15 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
module: &'ast ParsedModuleRef,
) -> Self {
let scope = region.scope(db);
let file = scope.file(db);
// In stub files, all expressions are evaluated in deferred mode to allow
// forward references. This matches Pyright's behavior.
let deferred_state = if file.is_stub(db) {
DeferredExpressionState::Deferred
} else {
DeferredExpressionState::None
};
Self {
context: InferContext::new(db, scope, module),
@@ -335,7 +344,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
scope,
return_types_and_ranges: vec![],
called_functions: FxIndexSet::default(),
deferred_state: DeferredExpressionState::None,
deferred_state,
multi_inference_state: MultiInferenceState::Panic,
inner_expression_inference_state: InnerExpressionInferenceState::Infer,
expressions: FxHashMap::default(),
@@ -1955,9 +1964,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.infer_type_parameters(type_params);
if let Some(arguments) = class.arguments.as_deref() {
let in_stub = self.in_stub();
let previous_deferred_state =
std::mem::replace(&mut self.deferred_state, in_stub.into());
let mut call_arguments =
CallArguments::from_arguments(arguments, |argument, splatted_value| {
let ty = self.infer_expression(splatted_value, TypeContext::default());
@@ -1968,7 +1974,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
});
let argument_forms = vec![Some(ParameterForm::Value); call_arguments.len()];
self.infer_argument_types(arguments, &mut call_arguments, &argument_forms);
self.deferred_state = previous_deferred_state;
}
self.typevar_binding_context = previous_typevar_binding_context;
@@ -2358,16 +2363,12 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
decorator_types_and_nodes.push((decorator_type, decorator));
}
// In stub files, default values may reference names that are defined later in the file.
let in_stub = self.in_stub();
let previous_deferred_state = std::mem::replace(&mut self.deferred_state, in_stub.into());
for default in parameters
.iter_non_variadic_params()
.filter_map(|param| param.default.as_deref())
{
self.infer_expression(default, TypeContext::default());
}
self.deferred_state = previous_deferred_state;
// 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
@@ -3002,14 +3003,9 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// if there are type parameters, then the keywords and bases are within that scope
// and we don't need to run inference here
if type_params.is_none() {
// In stub files, keyword values may reference names that are defined later in the file.
let in_stub = self.in_stub();
let previous_deferred_state =
std::mem::replace(&mut self.deferred_state, in_stub.into());
for keyword in class_node.keywords() {
self.infer_expression(&keyword.value, TypeContext::default());
}
self.deferred_state = previous_deferred_state;
// Inference of bases deferred in stubs, or if any are string literals.
if self.in_stub() || class_node.bases().iter().any(contains_string_literal) {
@@ -5922,13 +5918,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.dataclass_field_specifiers = specifiers;
}
// We defer the r.h.s. of PEP-613 `TypeAlias` assignments in stub files.
let previous_deferred_state = self.deferred_state;
if is_pep_613_type_alias && self.in_stub() {
self.deferred_state = DeferredExpressionState::Deferred;
}
// This might be a PEP-613 type alias (`OptionalList: TypeAlias = list[T] | None`). Use
// the definition of `OptionalList` as the binding context while inferring the
// RHS (`list[T] | None`), in order to bind `T` to `OptionalList`.
@@ -5941,8 +5930,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
self.typevar_binding_context = previous_typevar_binding_context;
self.deferred_state = previous_deferred_state;
self.dataclass_field_specifiers.clear();
let inferred_ty = if target
@@ -8331,10 +8318,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
body: _,
} = lambda_expression;
// In stub files, default values may reference names that are defined later in the file.
let in_stub = self.in_stub();
let previous_deferred_state = std::mem::replace(&mut self.deferred_state, in_stub.into());
let parameters = if let Some(parameters) = parameters {
let positional_only = parameters
.posonlyargs
@@ -8400,8 +8383,6 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
Parameters::empty()
};
self.deferred_state = previous_deferred_state;
// TODO: Useful inference of a lambda's return type will require a different approach,
// which does the inference of the body expression based on arguments at each call site,
// rather than eagerly computing a return type without knowing the argument types.