mirror of
https://github.com/astral-sh/ruff.git
synced 2025-10-11 10:52:04 +00:00
[red-knot] fix narrowing in nested scopes (#17630)
## Summary This PR fixes #17595. ## Test Plan New test cases are added to `mdtest/narrow/conditionals/nested.md`. --------- Co-authored-by: Carl Meyer <carl@astral.sh>
This commit is contained in:
parent
a4c8e43c5f
commit
fd76d70a31
9 changed files with 414 additions and 124 deletions
|
@ -42,7 +42,7 @@ use crate::semantic_index::symbol::{
|
|||
ScopedSymbolId, SymbolTableBuilder,
|
||||
};
|
||||
use crate::semantic_index::use_def::{
|
||||
EagerBindingsKey, FlowSnapshot, ScopedEagerBindingsId, UseDefMapBuilder,
|
||||
EagerSnapshotKey, FlowSnapshot, ScopedEagerSnapshotId, UseDefMapBuilder,
|
||||
};
|
||||
use crate::semantic_index::visibility_constraints::{
|
||||
ScopedVisibilityConstraintId, VisibilityConstraintsBuilder,
|
||||
|
@ -113,7 +113,7 @@ pub(super) struct SemanticIndexBuilder<'db> {
|
|||
///
|
||||
/// [generator functions]: https://docs.python.org/3/glossary.html#term-generator
|
||||
generator_functions: FxHashSet<FileScopeId>,
|
||||
eager_bindings: FxHashMap<EagerBindingsKey, ScopedEagerBindingsId>,
|
||||
eager_snapshots: FxHashMap<EagerSnapshotKey, ScopedEagerSnapshotId>,
|
||||
/// Errors collected by the `semantic_checker`.
|
||||
semantic_syntax_errors: RefCell<Vec<SemanticSyntaxError>>,
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
imported_modules: FxHashSet::default(),
|
||||
generator_functions: FxHashSet::default(),
|
||||
|
||||
eager_bindings: FxHashMap::default(),
|
||||
eager_snapshots: FxHashMap::default(),
|
||||
|
||||
python_version: Program::get(db).python_version(db),
|
||||
source_text: OnceCell::new(),
|
||||
|
@ -253,13 +253,15 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
children_start..children_start,
|
||||
reachability,
|
||||
);
|
||||
let is_class_scope = scope.kind().is_class();
|
||||
self.try_node_context_stack_manager.enter_nested_scope();
|
||||
|
||||
let file_scope_id = self.scopes.push(scope);
|
||||
self.symbol_tables.push(SymbolTableBuilder::default());
|
||||
self.instance_attribute_tables
|
||||
.push(SymbolTableBuilder::default());
|
||||
self.use_def_maps.push(UseDefMapBuilder::default());
|
||||
self.use_def_maps
|
||||
.push(UseDefMapBuilder::new(is_class_scope));
|
||||
let ast_id_scope = self.ast_ids.push(AstIdsBuilder::default());
|
||||
|
||||
let scope_id = ScopeId::new(self.db, self.file, file_scope_id, countme::Count::default());
|
||||
|
@ -303,12 +305,6 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
let enclosing_scope_kind = self.scopes[enclosing_scope_id].kind();
|
||||
let enclosing_symbol_table = &self.symbol_tables[enclosing_scope_id];
|
||||
|
||||
// Names bound in class scopes are never visible to nested scopes, so we never need to
|
||||
// save eager scope bindings in a class scope.
|
||||
if enclosing_scope_kind.is_class() {
|
||||
continue;
|
||||
}
|
||||
|
||||
for nested_symbol in self.symbol_tables[popped_scope_id].symbols() {
|
||||
// Skip this symbol if this enclosing scope doesn't contain any bindings for it.
|
||||
// Note that even if this symbol is bound in the popped scope,
|
||||
|
@ -321,24 +317,26 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
continue;
|
||||
};
|
||||
let enclosing_symbol = enclosing_symbol_table.symbol(enclosing_symbol_id);
|
||||
if !enclosing_symbol.is_bound() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Snapshot the bindings of this symbol that are visible at this point in this
|
||||
// Snapshot the state of this symbol that are visible at this point in this
|
||||
// enclosing scope.
|
||||
let key = EagerBindingsKey {
|
||||
let key = EagerSnapshotKey {
|
||||
enclosing_scope: enclosing_scope_id,
|
||||
enclosing_symbol: enclosing_symbol_id,
|
||||
nested_scope: popped_scope_id,
|
||||
};
|
||||
let eager_bindings = self.use_def_maps[enclosing_scope_id]
|
||||
.snapshot_eager_bindings(enclosing_symbol_id);
|
||||
self.eager_bindings.insert(key, eager_bindings);
|
||||
let eager_snapshot = self.use_def_maps[enclosing_scope_id].snapshot_eager_state(
|
||||
enclosing_symbol_id,
|
||||
enclosing_scope_kind,
|
||||
enclosing_symbol.is_bound(),
|
||||
);
|
||||
self.eager_snapshots.insert(key, eager_snapshot);
|
||||
}
|
||||
|
||||
// Lazy scopes are "sticky": once we see a lazy scope we stop doing lookups
|
||||
// eagerly, even if we would encounter another eager enclosing scope later on.
|
||||
// Also, narrowing constraints outside a lazy scope are not applicable.
|
||||
// TODO: If the symbol has never been rewritten, they are applicable.
|
||||
if !enclosing_scope_kind.is_eager() {
|
||||
break;
|
||||
}
|
||||
|
@ -1085,8 +1083,8 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
|
||||
self.scope_ids_by_scope.shrink_to_fit();
|
||||
self.scopes_by_node.shrink_to_fit();
|
||||
self.eager_bindings.shrink_to_fit();
|
||||
self.generator_functions.shrink_to_fit();
|
||||
self.eager_snapshots.shrink_to_fit();
|
||||
|
||||
SemanticIndex {
|
||||
symbol_tables,
|
||||
|
@ -1101,7 +1099,7 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
use_def_maps,
|
||||
imported_modules: Arc::new(self.imported_modules),
|
||||
has_future_annotations: self.has_future_annotations,
|
||||
eager_bindings: self.eager_bindings,
|
||||
eager_snapshots: self.eager_snapshots,
|
||||
semantic_syntax_errors: self.semantic_syntax_errors.into_inner(),
|
||||
generator_functions: self.generator_functions,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue