mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 22:01:47 +00:00
[red-knot] Track reachability of scopes (#17332)
## Summary Track the reachability of nested scopes within their parent scopes. We use this as an additional requirement for emitting `unresolved-reference` diagnostics (and in the future, `unresolved-attribute` and `unresolved-import`). This means that we only emit `unresolved-reference` for a given use of a symbol if the use itself is reachable (within its own scope), *and if the scope itself is reachable*. For example, no diagnostic should be emitted for the use of `x` here: ```py if False: x = 1 def f(): print(x) # this use of `x` is reachable inside the `f` scope, # but the whole `f` scope is not reachable. ``` There are probably more fine-grained ways of solving this problem, but they require a more sophisticated understanding of nested scopes (see #15777, in particular https://github.com/astral-sh/ruff/issues/15777#issuecomment-2788950267). But it doesn't seem completely unreasonable to silence *this specific kind of error* in unreachable scopes. ## Test Plan Observed changes in reachability tests and ecosystem.
This commit is contained in:
parent
06ffeb2e09
commit
4d50ee6f52
6 changed files with 79 additions and 24 deletions
|
@ -129,7 +129,11 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
eager_bindings: FxHashMap::default(),
|
||||
};
|
||||
|
||||
builder.push_scope_with_parent(NodeWithScopeRef::Module, None);
|
||||
builder.push_scope_with_parent(
|
||||
NodeWithScopeRef::Module,
|
||||
None,
|
||||
ScopedVisibilityConstraintId::ALWAYS_TRUE,
|
||||
);
|
||||
|
||||
builder
|
||||
}
|
||||
|
@ -191,17 +195,28 @@ impl<'db> SemanticIndexBuilder<'db> {
|
|||
|
||||
fn push_scope(&mut self, node: NodeWithScopeRef) {
|
||||
let parent = self.current_scope();
|
||||
self.push_scope_with_parent(node, Some(parent));
|
||||
let reachabililty = self.current_use_def_map().reachability;
|
||||
self.push_scope_with_parent(node, Some(parent), reachabililty);
|
||||
}
|
||||
|
||||
fn push_scope_with_parent(&mut self, node: NodeWithScopeRef, parent: Option<FileScopeId>) {
|
||||
fn push_scope_with_parent(
|
||||
&mut self,
|
||||
node: NodeWithScopeRef,
|
||||
parent: Option<FileScopeId>,
|
||||
reachability: ScopedVisibilityConstraintId,
|
||||
) {
|
||||
let children_start = self.scopes.next_index() + 1;
|
||||
|
||||
// SAFETY: `node` is guaranteed to be a child of `self.module`
|
||||
#[allow(unsafe_code)]
|
||||
let node_with_kind = unsafe { node.to_kind(self.module.clone()) };
|
||||
|
||||
let scope = Scope::new(parent, node_with_kind, children_start..children_start);
|
||||
let scope = Scope::new(
|
||||
parent,
|
||||
node_with_kind,
|
||||
children_start..children_start,
|
||||
reachability,
|
||||
);
|
||||
self.try_node_context_stack_manager.enter_nested_scope();
|
||||
|
||||
let file_scope_id = self.scopes.push(scope);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue