mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-03 18:28:24 +00:00
Rename semantic model methods to use current_*
prefix (#6347)
## Summary This PR attempts to draw a clearer divide between "methods that take (e.g.) an expression or statement as input" and "methods that rely on the _current_ expression or statement" in the semantic model, by renaming methods like `stmt()` to `current_statement()`. This had led to confusion in the past. For example, prior to this PR, we had `scope()` (which returns the current scope), and `parent_scope`, which returns the parent _of a scope that's passed in_. Now, the API is clearer: `current_scope` returns the current scope, and `parent_scope` takes a scope as argument and returns its parent. Per above, I also changed `stmt` to `statement` and `expr` to `expression`.
This commit is contained in:
parent
b763973357
commit
bae87fa016
54 changed files with 331 additions and 290 deletions
|
@ -185,7 +185,7 @@ impl<'a> Binding<'a> {
|
|||
/// Returns the range of the binding's parent.
|
||||
pub fn parent_range(&self, semantic: &SemanticModel) -> Option<TextRange> {
|
||||
self.source
|
||||
.map(|node_id| semantic.stmts[node_id])
|
||||
.map(|node_id| semantic.statements[node_id])
|
||||
.and_then(|parent| {
|
||||
if parent.is_import_from_stmt() {
|
||||
Some(parent.range())
|
||||
|
|
|
@ -31,16 +31,16 @@ pub struct SemanticModel<'a> {
|
|||
module_path: Option<&'a [String]>,
|
||||
|
||||
/// Stack of all visited statements.
|
||||
pub stmts: Nodes<'a, Stmt>,
|
||||
pub statements: Nodes<'a, Stmt>,
|
||||
|
||||
/// The identifier of the current statement.
|
||||
stmt_id: Option<NodeId>,
|
||||
statement_id: Option<NodeId>,
|
||||
|
||||
/// Stack of all visited expressions.
|
||||
exprs: Nodes<'a, Expr>,
|
||||
expressions: Nodes<'a, Expr>,
|
||||
|
||||
/// The identifier of the current expression.
|
||||
expr_id: Option<NodeId>,
|
||||
expression_id: Option<NodeId>,
|
||||
|
||||
/// Stack of all scopes, along with the identifier of the current scope.
|
||||
pub scopes: Scopes<'a>,
|
||||
|
@ -132,10 +132,10 @@ impl<'a> SemanticModel<'a> {
|
|||
Self {
|
||||
typing_modules,
|
||||
module_path: module.path(),
|
||||
stmts: Nodes::<Stmt>::default(),
|
||||
stmt_id: None,
|
||||
exprs: Nodes::<Expr>::default(),
|
||||
expr_id: None,
|
||||
statements: Nodes::<Stmt>::default(),
|
||||
statement_id: None,
|
||||
expressions: Nodes::<Expr>::default(),
|
||||
expression_id: None,
|
||||
scopes: Scopes::default(),
|
||||
scope_id: ScopeId::global(),
|
||||
definitions: Definitions::for_module(module),
|
||||
|
@ -225,7 +225,7 @@ impl<'a> SemanticModel<'a> {
|
|||
flags,
|
||||
references: Vec::new(),
|
||||
scope: self.scope_id,
|
||||
source: self.stmt_id,
|
||||
source: self.statement_id,
|
||||
context: self.execution_context(),
|
||||
exceptions: self.exceptions(),
|
||||
})
|
||||
|
@ -233,7 +233,7 @@ impl<'a> SemanticModel<'a> {
|
|||
|
||||
/// Return the current [`Binding`] for a given `name`.
|
||||
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
|
||||
self.scopes()
|
||||
self.current_scopes()
|
||||
.find_map(|scope| scope.get(member))
|
||||
.map(|binding_id| &self.bindings[binding_id])
|
||||
}
|
||||
|
@ -686,102 +686,110 @@ impl<'a> SemanticModel<'a> {
|
|||
) -> Option<ImportedName> {
|
||||
// TODO(charlie): Pass in a slice.
|
||||
let module_path: Vec<&str> = module.split('.').collect();
|
||||
self.scopes().enumerate().find_map(|(scope_index, scope)| {
|
||||
scope.bindings().find_map(|(name, binding_id)| {
|
||||
let binding = &self.bindings[binding_id];
|
||||
match &binding.kind {
|
||||
// Ex) Given `module="sys"` and `object="exit"`:
|
||||
// `import sys` -> `sys.exit`
|
||||
// `import sys as sys2` -> `sys2.exit`
|
||||
BindingKind::Import(Import { call_path }) => {
|
||||
if call_path.as_ref() == module_path.as_slice() {
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `sys` isn't bound in an inner scope.
|
||||
if self
|
||||
.scopes()
|
||||
.take(scope_index)
|
||||
.all(|scope| !scope.has(name))
|
||||
{
|
||||
return Some(ImportedName {
|
||||
name: format!("{name}.{member}"),
|
||||
range: self.stmts[source].range(),
|
||||
context: binding.context,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ex) Given `module="os.path"` and `object="join"`:
|
||||
// `from os.path import join` -> `join`
|
||||
// `from os.path import join as join2` -> `join2`
|
||||
BindingKind::FromImport(FromImport { call_path }) => {
|
||||
if let Some((target_member, target_module)) = call_path.split_last() {
|
||||
if target_module == module_path.as_slice() && target_member == &member {
|
||||
self.current_scopes()
|
||||
.enumerate()
|
||||
.find_map(|(scope_index, scope)| {
|
||||
scope.bindings().find_map(|(name, binding_id)| {
|
||||
let binding = &self.bindings[binding_id];
|
||||
match &binding.kind {
|
||||
// Ex) Given `module="sys"` and `object="exit"`:
|
||||
// `import sys` -> `sys.exit`
|
||||
// `import sys as sys2` -> `sys2.exit`
|
||||
BindingKind::Import(Import { call_path }) => {
|
||||
if call_path.as_ref() == module_path.as_slice() {
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `join` isn't bound in an inner scope.
|
||||
// Verify that `sys` isn't bound in an inner scope.
|
||||
if self
|
||||
.scopes()
|
||||
.current_scopes()
|
||||
.take(scope_index)
|
||||
.all(|scope| !scope.has(name))
|
||||
{
|
||||
return Some(ImportedName {
|
||||
name: (*name).to_string(),
|
||||
range: self.stmts[source].range(),
|
||||
name: format!("{name}.{member}"),
|
||||
range: self.statements[source].range(),
|
||||
context: binding.context,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ex) Given `module="os"` and `object="name"`:
|
||||
// `import os.path ` -> `os.name`
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { .. }) => {
|
||||
if name == module {
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `os` isn't bound in an inner scope.
|
||||
if self
|
||||
.scopes()
|
||||
.take(scope_index)
|
||||
.all(|scope| !scope.has(name))
|
||||
// Ex) Given `module="os.path"` and `object="join"`:
|
||||
// `from os.path import join` -> `join`
|
||||
// `from os.path import join as join2` -> `join2`
|
||||
BindingKind::FromImport(FromImport { call_path }) => {
|
||||
if let Some((target_member, target_module)) = call_path.split_last() {
|
||||
if target_module == module_path.as_slice()
|
||||
&& target_member == &member
|
||||
{
|
||||
return Some(ImportedName {
|
||||
name: format!("{name}.{member}"),
|
||||
range: self.stmts[source].range(),
|
||||
context: binding.context,
|
||||
});
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `join` isn't bound in an inner scope.
|
||||
if self
|
||||
.current_scopes()
|
||||
.take(scope_index)
|
||||
.all(|scope| !scope.has(name))
|
||||
{
|
||||
return Some(ImportedName {
|
||||
name: (*name).to_string(),
|
||||
range: self.statements[source].range(),
|
||||
context: binding.context,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Ex) Given `module="os"` and `object="name"`:
|
||||
// `import os.path ` -> `os.name`
|
||||
BindingKind::SubmoduleImport(SubmoduleImport { .. }) => {
|
||||
if name == module {
|
||||
if let Some(source) = binding.source {
|
||||
// Verify that `os` isn't bound in an inner scope.
|
||||
if self
|
||||
.current_scopes()
|
||||
.take(scope_index)
|
||||
.all(|scope| !scope.has(name))
|
||||
{
|
||||
return Some(ImportedName {
|
||||
name: format!("{name}.{member}"),
|
||||
range: self.statements[source].range(),
|
||||
context: binding.context,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Non-imports.
|
||||
_ => {}
|
||||
}
|
||||
// Non-imports.
|
||||
_ => {}
|
||||
}
|
||||
None
|
||||
None
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Push a [`Stmt`] onto the stack.
|
||||
pub fn push_stmt(&mut self, stmt: &'a Stmt) {
|
||||
self.stmt_id = Some(self.stmts.insert(stmt, self.stmt_id));
|
||||
pub fn push_statement(&mut self, stmt: &'a Stmt) {
|
||||
self.statement_id = Some(self.statements.insert(stmt, self.statement_id));
|
||||
}
|
||||
|
||||
/// Pop the current [`Stmt`] off the stack.
|
||||
pub fn pop_stmt(&mut self) {
|
||||
let node_id = self.stmt_id.expect("Attempted to pop without statement");
|
||||
self.stmt_id = self.stmts.parent_id(node_id);
|
||||
pub fn pop_statement(&mut self) {
|
||||
let node_id = self
|
||||
.statement_id
|
||||
.expect("Attempted to pop without statement");
|
||||
self.statement_id = self.statements.parent_id(node_id);
|
||||
}
|
||||
|
||||
/// Push a [`Expr`] onto the stack.
|
||||
pub fn push_expr(&mut self, expr: &'a Expr) {
|
||||
self.expr_id = Some(self.exprs.insert(expr, self.expr_id));
|
||||
pub fn push_expression(&mut self, expr: &'a Expr) {
|
||||
self.expression_id = Some(self.expressions.insert(expr, self.expression_id));
|
||||
}
|
||||
|
||||
/// Pop the current [`Expr`] off the stack.
|
||||
pub fn pop_expr(&mut self) {
|
||||
let node_id = self.expr_id.expect("Attempted to pop without expression");
|
||||
self.expr_id = self.exprs.parent_id(node_id);
|
||||
pub fn pop_expression(&mut self) {
|
||||
let node_id = self
|
||||
.expression_id
|
||||
.expect("Attempted to pop without expression");
|
||||
self.expression_id = self.expressions.parent_id(node_id);
|
||||
}
|
||||
|
||||
/// Push a [`Scope`] with the given [`ScopeKind`] onto the stack.
|
||||
|
@ -811,69 +819,94 @@ impl<'a> SemanticModel<'a> {
|
|||
}
|
||||
|
||||
/// Return the current `Stmt`.
|
||||
pub fn stmt(&self) -> &'a Stmt {
|
||||
let node_id = self.stmt_id.expect("No current statement");
|
||||
self.stmts[node_id]
|
||||
pub fn current_statement(&self) -> &'a Stmt {
|
||||
let node_id = self.statement_id.expect("No current statement");
|
||||
self.statements[node_id]
|
||||
}
|
||||
|
||||
/// Return the parent `Stmt` of the current `Stmt`, if any.
|
||||
pub fn stmt_parent(&self) -> Option<&'a Stmt> {
|
||||
let node_id = self.stmt_id.expect("No current statement");
|
||||
let parent_id = self.stmts.parent_id(node_id)?;
|
||||
Some(self.stmts[parent_id])
|
||||
}
|
||||
|
||||
/// Return the current `Expr`.
|
||||
pub fn expr(&self) -> Option<&'a Expr> {
|
||||
let node_id = self.expr_id?;
|
||||
Some(self.exprs[node_id])
|
||||
}
|
||||
|
||||
/// Return the parent `Expr` of the current `Expr`, if any.
|
||||
pub fn expr_parent(&self) -> Option<&'a Expr> {
|
||||
self.expr_ancestors().next()
|
||||
}
|
||||
|
||||
/// Return the grandparent `Expr` of the current `Expr`, if any.
|
||||
pub fn expr_grandparent(&self) -> Option<&'a Expr> {
|
||||
self.expr_ancestors().nth(1)
|
||||
}
|
||||
|
||||
/// Return an [`Iterator`] over the current `Expr` parents.
|
||||
pub fn expr_ancestors(&self) -> impl Iterator<Item = &'a Expr> + '_ {
|
||||
self.expr_id
|
||||
/// Returns an [`Iterator`] over the current statement hierarchy, from the current [`Stmt`]
|
||||
/// through to any parents.
|
||||
pub fn current_statements(&self) -> impl Iterator<Item = &'a Stmt> + '_ {
|
||||
self.statement_id
|
||||
.iter()
|
||||
.copied()
|
||||
.flat_map(|id| {
|
||||
self.exprs
|
||||
.ancestor_ids(id)
|
||||
.skip(1)
|
||||
.map(|id| &self.exprs[id])
|
||||
self.statements
|
||||
.ancestor_ids(*id)
|
||||
.map(|id| &self.statements[id])
|
||||
})
|
||||
.copied()
|
||||
}
|
||||
|
||||
/// Returns a reference to the global scope
|
||||
/// Return the parent `Stmt` of the current `Stmt`, if any.
|
||||
pub fn current_statement_parent(&self) -> Option<&'a Stmt> {
|
||||
self.current_statements().nth(1)
|
||||
}
|
||||
|
||||
/// Return the grandparent `Stmt` of the current `Stmt`, if any.
|
||||
pub fn current_statement_grandparent(&self) -> Option<&'a Stmt> {
|
||||
self.current_statements().nth(2)
|
||||
}
|
||||
|
||||
/// Return the current `Expr`.
|
||||
pub fn current_expression(&self) -> Option<&'a Expr> {
|
||||
let node_id = self.expression_id?;
|
||||
Some(self.expressions[node_id])
|
||||
}
|
||||
|
||||
/// Returns an [`Iterator`] over the current statement hierarchy, from the current [`Expr`]
|
||||
/// through to any parents.
|
||||
pub fn current_expressions(&self) -> impl Iterator<Item = &'a Expr> + '_ {
|
||||
self.expression_id
|
||||
.iter()
|
||||
.flat_map(|id| {
|
||||
self.expressions
|
||||
.ancestor_ids(*id)
|
||||
.map(|id| &self.expressions[id])
|
||||
})
|
||||
.copied()
|
||||
}
|
||||
|
||||
/// Return the parent [`Expr`] of the current [`Expr`], if any.
|
||||
pub fn current_expression_parent(&self) -> Option<&'a Expr> {
|
||||
self.current_expressions().nth(1)
|
||||
}
|
||||
|
||||
/// Return the grandparent [`Expr`] of the current [`Expr`], if any.
|
||||
pub fn current_expression_grandparent(&self) -> Option<&'a Expr> {
|
||||
self.current_expressions().nth(2)
|
||||
}
|
||||
|
||||
/// Returns a reference to the global [`Scope`].
|
||||
pub fn global_scope(&self) -> &Scope<'a> {
|
||||
self.scopes.global()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the global scope
|
||||
/// Returns a mutable reference to the global [`Scope`].
|
||||
pub fn global_scope_mut(&mut self) -> &mut Scope<'a> {
|
||||
self.scopes.global_mut()
|
||||
}
|
||||
|
||||
/// Returns the current top most scope.
|
||||
pub fn scope(&self) -> &Scope<'a> {
|
||||
/// Returns the current top-most [`Scope`].
|
||||
pub fn current_scope(&self) -> &Scope<'a> {
|
||||
&self.scopes[self.scope_id]
|
||||
}
|
||||
|
||||
/// Returns the parent of the given scope, if any.
|
||||
/// Returns a mutable reference to the current top-most [`Scope`].
|
||||
pub fn current_scope_mut(&mut self) -> &mut Scope<'a> {
|
||||
&mut self.scopes[self.scope_id]
|
||||
}
|
||||
|
||||
/// Returns an iterator over all scopes, starting from the current [`Scope`].
|
||||
pub fn current_scopes(&self) -> impl Iterator<Item = &Scope> {
|
||||
self.scopes.ancestors(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])
|
||||
}
|
||||
|
||||
/// Returns the first parent of the given scope that is not a [`ScopeKind::Type`] scope, if any.
|
||||
/// Returns the first parent of the given [`Scope`] that is not of [`ScopeKind::Type`], if any.
|
||||
pub fn first_non_type_parent_scope(&self, scope: &Scope) -> Option<&Scope<'a>> {
|
||||
let mut current_scope = scope;
|
||||
while let Some(parent) = self.parent_scope(current_scope) {
|
||||
|
@ -886,22 +919,6 @@ impl<'a> SemanticModel<'a> {
|
|||
None
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the current top most scope.
|
||||
pub fn scope_mut(&mut self) -> &mut Scope<'a> {
|
||||
&mut self.scopes[self.scope_id]
|
||||
}
|
||||
|
||||
/// Returns an iterator over all scopes, starting from the current scope.
|
||||
pub fn scopes(&self) -> impl Iterator<Item = &Scope> {
|
||||
self.scopes.ancestors(self.scope_id)
|
||||
}
|
||||
|
||||
/// Returns an iterator over all parent statements.
|
||||
pub fn parents(&self) -> impl Iterator<Item = &Stmt> + '_ {
|
||||
let node_id = self.stmt_id.expect("No current statement");
|
||||
self.stmts.ancestor_ids(node_id).map(|id| self.stmts[id])
|
||||
}
|
||||
|
||||
/// Set the [`Globals`] for the current [`Scope`].
|
||||
pub fn set_globals(&mut self, globals: Globals<'a>) {
|
||||
// If any global bindings don't already exist in the global scope, add them.
|
||||
|
@ -916,7 +933,7 @@ impl<'a> SemanticModel<'a> {
|
|||
range: *range,
|
||||
references: Vec::new(),
|
||||
scope: self.scope_id,
|
||||
source: self.stmt_id,
|
||||
source: self.statement_id,
|
||||
context: self.execution_context(),
|
||||
exceptions: self.exceptions(),
|
||||
flags: BindingFlags::empty(),
|
||||
|
@ -964,13 +981,13 @@ impl<'a> SemanticModel<'a> {
|
|||
pub fn at_top_level(&self) -> bool {
|
||||
self.scope_id.is_global()
|
||||
&& self
|
||||
.stmt_id
|
||||
.map_or(true, |stmt_id| self.stmts.parent_id(stmt_id).is_none())
|
||||
.statement_id
|
||||
.map_or(true, |stmt_id| self.statements.parent_id(stmt_id).is_none())
|
||||
}
|
||||
|
||||
/// Return `true` if the model is in an async context.
|
||||
pub fn in_async_context(&self) -> bool {
|
||||
for scope in self.scopes() {
|
||||
for scope in self.current_scopes() {
|
||||
if scope.kind.is_async_function() {
|
||||
return true;
|
||||
}
|
||||
|
@ -1048,8 +1065,8 @@ impl<'a> SemanticModel<'a> {
|
|||
pub fn snapshot(&self) -> Snapshot {
|
||||
Snapshot {
|
||||
scope_id: self.scope_id,
|
||||
stmt_id: self.stmt_id,
|
||||
expr_id: self.expr_id,
|
||||
stmt_id: self.statement_id,
|
||||
expr_id: self.expression_id,
|
||||
definition_id: self.definition_id,
|
||||
flags: self.flags,
|
||||
}
|
||||
|
@ -1065,8 +1082,8 @@ impl<'a> SemanticModel<'a> {
|
|||
flags,
|
||||
} = snapshot;
|
||||
self.scope_id = scope_id;
|
||||
self.stmt_id = stmt_id;
|
||||
self.expr_id = expr_id;
|
||||
self.statement_id = stmt_id;
|
||||
self.expression_id = expr_id;
|
||||
self.definition_id = definition_id;
|
||||
self.flags = flags;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue