Remove SemanticModel#find_binding (#6546)

## Summary

This method is almost never what you actually want, because it doesn't
respect Python's scoping semantics. For example, if you call this within
a class method, it will return class attributes, whereas Python actually
_skips_ symbols in classes unless the load occurs within the class
itself. I also want to move away from these kinds of dynamic lookups and
more towards `resolve_name`, which performs a lookup based on the stored
`BindingId` at the time of symbol resolution, and will make it much
easier for us to separate model building from linting in the near
future.

## Test Plan

`cargo test`
This commit is contained in:
Charlie Marsh 2023-08-14 00:09:05 -04:00 committed by GitHub
parent bf4c6473c8
commit 1a9536c4e2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 55 additions and 65 deletions

View file

@ -233,13 +233,6 @@ impl<'a> SemanticModel<'a> {
})
}
/// Return the current [`Binding`] for a given `name`.
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
self.current_scopes()
.find_map(|scope| scope.get(member))
.map(|binding_id| &self.bindings[binding_id])
}
/// Return the [`BindingId`] that the given [`BindingId`] shadows, if any.
///
/// Note that this will only return bindings that are shadowed by a binding in a parent scope.
@ -618,6 +611,11 @@ impl<'a> SemanticModel<'a> {
Some(binding_id)
}
/// Resolves the [`ast::ExprName`] to the [`BindingId`] of the symbol it refers to, if any.
pub fn resolve_name(&self, name: &ast::ExprName) -> Option<BindingId> {
self.resolved_names.get(&name.into()).copied()
}
/// Resolves the [`Expr`] to a fully-qualified symbol-name, if `value` resolves to an imported
/// or builtin symbol.
///
@ -642,11 +640,10 @@ impl<'a> SemanticModel<'a> {
// If the name was already resolved, look it up; otherwise, search for the symbol.
let head = match_head(value)?;
let binding = if let Some(id) = self.resolved_names.get(&head.into()) {
self.binding(*id)
} else {
self.find_binding(&head.id)?
};
let binding = self
.resolve_name(head)
.or_else(|| self.lookup_symbol(&head.id))
.map(|id| self.binding(id))?;
match &binding.kind {
BindingKind::Import(Import { call_path }) => {
@ -917,6 +914,11 @@ impl<'a> SemanticModel<'a> {
self.scopes.ancestors(self.scope_id)
}
/// Returns an iterator over all scopes IDs, starting from the current [`Scope`].
pub fn current_scope_ids(&self) -> impl Iterator<Item = ScopeId> + '_ {
self.scopes.ancestor_ids(self.scope_id)
}
/// Returns the parent of the given [`Scope`], if any.
pub fn parent_scope(&self, scope: &Scope) -> Option<&Scope<'a>> {
scope.parent.map(|scope_id| &self.scopes[scope_id])