mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-09 21:28:21 +00:00
Run shadowed-variable analyses in deferred handlers (#5181)
## Summary This PR extracts a bunch of complex logic from `add_binding`, instead running the the shadowing rules in the deferred handler, thereby decoupling the binding phase (during which we build up the semantic model) from the analysis phase, and generally making `add_binding` much more focused. This was made possible by improving the semantic model to better handle deletions -- previously, we'd "lose track" of bindings if they were deleted, which made this kind of refactor impossible. ## Test Plan We have good automated coverage for this, but I want to benchmark it separately.
This commit is contained in:
parent
139a9f757b
commit
0e89c94947
6 changed files with 222 additions and 109 deletions
|
@ -1052,6 +1052,69 @@ impl<'a> SemanticModel<'a> {
|
|||
pub const fn future_annotations(&self) -> bool {
|
||||
self.flags.contains(SemanticModelFlags::FUTURE_ANNOTATIONS)
|
||||
}
|
||||
|
||||
/// Return an iterator over all bindings shadowed by the given [`BindingId`], within the
|
||||
/// containing scope, and across scopes.
|
||||
pub fn shadowed_bindings(
|
||||
&self,
|
||||
scope_id: ScopeId,
|
||||
binding_id: BindingId,
|
||||
) -> impl Iterator<Item = ShadowedBinding> + '_ {
|
||||
let mut first = true;
|
||||
let mut binding_id = binding_id;
|
||||
std::iter::from_fn(move || {
|
||||
// First, check whether this binding is shadowing another binding in a different scope.
|
||||
if std::mem::take(&mut first) {
|
||||
if let Some(shadowed_id) = self.shadowed_bindings.get(&binding_id).copied() {
|
||||
return Some(ShadowedBinding {
|
||||
binding_id,
|
||||
shadowed_id,
|
||||
same_scope: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, check whether this binding is shadowing another binding in the same scope.
|
||||
if let Some(shadowed_id) = self.scopes[scope_id].shadowed_binding(binding_id) {
|
||||
let next = ShadowedBinding {
|
||||
binding_id,
|
||||
shadowed_id,
|
||||
same_scope: true,
|
||||
};
|
||||
|
||||
// Advance to the next binding in the scope.
|
||||
first = true;
|
||||
binding_id = shadowed_id;
|
||||
|
||||
return Some(next);
|
||||
}
|
||||
|
||||
None
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShadowedBinding {
|
||||
/// The binding that is shadowing another binding.
|
||||
binding_id: BindingId,
|
||||
/// The binding that is being shadowed.
|
||||
shadowed_id: BindingId,
|
||||
/// Whether the shadowing and shadowed bindings are in the same scope.
|
||||
same_scope: bool,
|
||||
}
|
||||
|
||||
impl ShadowedBinding {
|
||||
pub const fn binding_id(&self) -> BindingId {
|
||||
self.binding_id
|
||||
}
|
||||
|
||||
pub const fn shadowed_id(&self) -> BindingId {
|
||||
self.shadowed_id
|
||||
}
|
||||
|
||||
pub const fn same_scope(&self) -> bool {
|
||||
self.same_scope
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue