[red-knot] Resolve references in eager nested scopes eagerly (#16079)

We now resolve references in "eager" scopes correctly — using the
bindings and declarations that are visible at the point where the eager
scope is created, not the "public" type of the symbol (typically the
bindings visible at the end of the scope).

---------

Co-authored-by: Alex Waygood <alex.waygood@gmail.com>
This commit is contained in:
Douglas Creager 2025-02-19 10:22:30 -05:00 committed by GitHub
parent f50849aeef
commit cfc6941d5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 645 additions and 47 deletions

View file

@ -19,7 +19,7 @@ use crate::semantic_index::expression::Expression;
use crate::semantic_index::symbol::{
FileScopeId, NodeWithScopeKey, NodeWithScopeRef, Scope, ScopeId, ScopedSymbolId, SymbolTable,
};
use crate::semantic_index::use_def::UseDefMap;
use crate::semantic_index::use_def::{EagerBindingsKey, ScopedEagerBindingsId, UseDefMap};
use crate::Db;
pub mod ast_ids;
@ -165,6 +165,9 @@ pub(crate) struct SemanticIndex<'db> {
/// Maps from class body scopes to attribute assignments that were found
/// in methods of that class.
attribute_assignments: FxHashMap<FileScopeId, Arc<AttributeAssignments<'db>>>,
/// Map of all of the eager bindings that appear in this file.
eager_bindings: FxHashMap<EagerBindingsKey, ScopedEagerBindingsId>,
}
impl<'db> SemanticIndex<'db> {
@ -220,7 +223,7 @@ impl<'db> SemanticIndex<'db> {
/// Returns the id of the parent scope.
pub(crate) fn parent_scope_id(&self, scope_id: FileScopeId) -> Option<FileScopeId> {
let scope = self.scope(scope_id);
scope.parent
scope.parent()
}
/// Returns the parent scope of `scope_id`.
@ -290,6 +293,23 @@ impl<'db> SemanticIndex<'db> {
pub(super) fn has_future_annotations(&self) -> bool {
self.has_future_annotations
}
/// Returns an iterator of bindings for a particular nested eager scope reference.
pub(crate) fn eager_bindings(
&self,
enclosing_scope: FileScopeId,
symbol: &str,
nested_scope: FileScopeId,
) -> Option<BindingWithConstraintsIterator<'_, 'db>> {
let symbol_id = self.symbol_tables[enclosing_scope].symbol_id_by_name(symbol)?;
let key = EagerBindingsKey {
enclosing_scope,
enclosing_symbol: symbol_id,
nested_scope,
};
let id = self.eager_bindings.get(&key)?;
self.use_def_maps[enclosing_scope].eager_bindings(*id)
}
}
pub struct AncestorsIter<'a> {
@ -312,7 +332,7 @@ impl<'a> Iterator for AncestorsIter<'a> {
fn next(&mut self) -> Option<Self::Item> {
let current_id = self.next_id?;
let current = &self.scopes[current_id];
self.next_id = current.parent;
self.next_id = current.parent();
Some((current_id, current))
}
@ -328,7 +348,7 @@ pub struct DescendentsIter<'a> {
impl<'a> DescendentsIter<'a> {
fn new(symbol_table: &'a SemanticIndex, scope_id: FileScopeId) -> Self {
let scope = &symbol_table.scopes[scope_id];
let scopes = &symbol_table.scopes[scope.descendents.clone()];
let scopes = &symbol_table.scopes[scope.descendents()];
Self {
next_id: scope_id + 1,
@ -378,7 +398,7 @@ impl<'a> Iterator for ChildrenIter<'a> {
fn next(&mut self) -> Option<Self::Item> {
self.descendents
.find(|(_, scope)| scope.parent == Some(self.parent))
.find(|(_, scope)| scope.parent() == Some(self.parent))
}
}