Always defer
This commit is contained in:
@@ -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]
|
||||
```
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user