respect annotation-only declarations in infer_place_load

This commit is contained in:
Jack O'Connor 2025-07-11 14:18:11 -07:00
parent 893f5727e5
commit 3b4667ec32
2 changed files with 10 additions and 10 deletions

View file

@ -31,17 +31,17 @@ def f():
reveal_type(x) # revealed: Unknown | Literal[1] reveal_type(x) # revealed: Unknown | Literal[1]
``` ```
## Skips annotation-only assignment ## Reads respect annotation-only declarations
```py ```py
def f(): def f():
x = 1 x: int = 1
def g(): def g():
# it's pretty weird to have an annotated assignment in a function where the # TODO: This example should actually be an unbound variable error. However to avoid false
# name is otherwise not defined; maybe should be an error? # positives, we'd need to analyze `nonlocal x` statements in other inner functions.
x: int x: str
def h(): def h():
reveal_type(x) # revealed: Unknown | Literal[1] reveal_type(x) # revealed: str
``` ```
## The `nonlocal` keyword ## The `nonlocal` keyword
@ -229,7 +229,7 @@ def f():
nonlocal x # error: [invalid-syntax] "no binding for nonlocal `x` found" nonlocal x # error: [invalid-syntax] "no binding for nonlocal `x` found"
``` ```
## `nonlocal` bindings respect declared types from the defining scope, even without a binding ## Assigning to a `nonlocal` respects the declared type from its defining scope, even without a binding in that scope
```py ```py
def f(): def f():

View file

@ -1618,8 +1618,8 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
// here and just bail out of this loop. // here and just bail out of this loop.
break; break;
} }
// We found the closest definition. Note that (unlike in `infer_place_load`) this // We found the closest definition. Note that (as in `infer_place_load`) this does
// does *not* need to be a binding. It could be just `x: int`. // *not* need to be a binding. It could be just a declaration, e.g. `x: int`.
nonlocal_use_def_map = self.index.use_def_map(enclosing_scope_file_id); nonlocal_use_def_map = self.index.use_def_map(enclosing_scope_file_id);
declarations = nonlocal_use_def_map.end_of_scope_declarations(enclosing_place_id); declarations = nonlocal_use_def_map.end_of_scope_declarations(enclosing_place_id);
is_local = false; is_local = false;
@ -6079,7 +6079,7 @@ impl<'db, 'ast> TypeInferenceBuilder<'db, 'ast> {
let Some(enclosing_place) = enclosing_place_table.place_by_expr(expr) else { let Some(enclosing_place) = enclosing_place_table.place_by_expr(expr) else {
continue; continue;
}; };
if enclosing_place.is_bound() { if enclosing_place.is_bound() || enclosing_place.is_declared() {
// We can return early here, because the nearest function-like scope that // We can return early here, because the nearest function-like scope that
// defines a name must be the only source for the nonlocal reference (at // defines a name must be the only source for the nonlocal reference (at
// runtime, it is the scope that creates the cell for our closure.) If the name // runtime, it is the scope that creates the cell for our closure.) If the name