mirror of
https://github.com/astral-sh/ruff.git
synced 2025-08-04 10:48:32 +00:00
refactor: Move scope and binding types to scope.rs
(#3573)
This commit is contained in:
parent
92179e6369
commit
dedf4cbdeb
36 changed files with 535 additions and 507 deletions
|
@ -1,4 +1,4 @@
|
|||
use ruff_python_ast::context::ScopeStack;
|
||||
use ruff_python_ast::scope::ScopeStack;
|
||||
use rustpython_parser::ast::{Expr, Stmt};
|
||||
|
||||
use ruff_python_ast::types::Range;
|
||||
|
|
|
@ -14,17 +14,18 @@ use rustpython_parser::ast::{
|
|||
};
|
||||
|
||||
use ruff_diagnostics::Diagnostic;
|
||||
use ruff_python_ast::context::{Context, ScopeStack};
|
||||
use ruff_python_ast::context::Context;
|
||||
use ruff_python_ast::helpers::{
|
||||
binding_range, extract_handled_exceptions, to_module_path, Exceptions,
|
||||
};
|
||||
use ruff_python_ast::operations::{extract_all_names, AllNamesFlags};
|
||||
use ruff_python_ast::relocate::relocate_expr;
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
use ruff_python_ast::types::{
|
||||
Binding, BindingId, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range,
|
||||
RefEquality, Scope, ScopeId, ScopeKind,
|
||||
use ruff_python_ast::scope::{
|
||||
Binding, BindingId, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Scope,
|
||||
ScopeId, ScopeKind, ScopeStack,
|
||||
};
|
||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
||||
use ruff_python_ast::types::{Node, Range, RefEquality};
|
||||
use ruff_python_ast::typing::{match_annotated_subscript, Callable, SubscriptKind};
|
||||
use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
|
||||
use ruff_python_ast::{
|
||||
|
@ -208,7 +209,7 @@ where
|
|||
source: Some(RefEquality(stmt)),
|
||||
context,
|
||||
});
|
||||
scope.bindings.insert(name, id);
|
||||
scope.add(name, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -237,7 +238,7 @@ where
|
|||
source: Some(RefEquality(stmt)),
|
||||
context,
|
||||
});
|
||||
scope.bindings.insert(name, id);
|
||||
scope.add(name, id);
|
||||
}
|
||||
|
||||
// Mark the binding in the defining scopes as used too. (Skip the global scope
|
||||
|
@ -249,9 +250,7 @@ where
|
|||
scopes_iter.next_back();
|
||||
|
||||
for index in scopes_iter.skip(1) {
|
||||
if let Some(index) =
|
||||
self.ctx.scopes[*index].bindings.get(&name.as_str())
|
||||
{
|
||||
if let Some(index) = self.ctx.scopes[*index].get(name.as_str()) {
|
||||
exists = true;
|
||||
self.ctx.bindings[*index].runtime_usage = usage;
|
||||
}
|
||||
|
@ -1874,7 +1873,6 @@ where
|
|||
if self
|
||||
.ctx
|
||||
.global_scope()
|
||||
.bindings
|
||||
.get(name)
|
||||
.map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation())
|
||||
{
|
||||
|
@ -1887,7 +1885,7 @@ where
|
|||
source: Some(RefEquality(stmt)),
|
||||
context: self.ctx.execution_context(),
|
||||
});
|
||||
self.ctx.global_scope_mut().bindings.insert(name, id);
|
||||
self.ctx.global_scope_mut().add(name, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1937,7 +1935,6 @@ where
|
|||
if self
|
||||
.ctx
|
||||
.global_scope()
|
||||
.bindings
|
||||
.get(name)
|
||||
.map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation())
|
||||
{
|
||||
|
@ -1950,7 +1947,7 @@ where
|
|||
source: Some(RefEquality(stmt)),
|
||||
context: self.ctx.execution_context(),
|
||||
});
|
||||
self.ctx.global_scope_mut().bindings.insert(name, id);
|
||||
self.ctx.global_scope_mut().add(name, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3713,7 +3710,7 @@ where
|
|||
let name_range =
|
||||
helpers::excepthandler_name_range(excepthandler, self.locator).unwrap();
|
||||
|
||||
if self.ctx.scope().bindings.contains_key(&name.as_str()) {
|
||||
if self.ctx.scope().defines(name.as_str()) {
|
||||
self.handle_node_store(
|
||||
name,
|
||||
&Expr::new(
|
||||
|
@ -3727,7 +3724,7 @@ where
|
|||
);
|
||||
}
|
||||
|
||||
let definition = self.ctx.scope().bindings.get(&name.as_str()).copied();
|
||||
let definition = self.ctx.scope().get(name.as_str()).copied();
|
||||
self.handle_node_store(
|
||||
name,
|
||||
&Expr::new(
|
||||
|
@ -3744,7 +3741,7 @@ where
|
|||
|
||||
if let Some(index) = {
|
||||
let scope = self.ctx.scope_mut();
|
||||
&scope.bindings.remove(&name.as_str())
|
||||
&scope.remove(name.as_str())
|
||||
} {
|
||||
if !self.ctx.bindings[*index].used() {
|
||||
if self.settings.rules.enabled(Rule::UnusedVariable) {
|
||||
|
@ -3778,7 +3775,7 @@ where
|
|||
|
||||
if let Some(index) = definition {
|
||||
let scope = self.ctx.scope_mut();
|
||||
scope.bindings.insert(name, index);
|
||||
scope.add(name, index);
|
||||
}
|
||||
}
|
||||
None => walk_excepthandler(self, excepthandler),
|
||||
|
@ -3933,14 +3930,17 @@ impl<'a> Checker<'a> {
|
|||
'b: 'a,
|
||||
{
|
||||
let binding_id = self.ctx.bindings.next_id();
|
||||
if let Some((stack_index, scope_index)) = self
|
||||
if let Some((stack_index, existing_binding_index)) = self
|
||||
.ctx
|
||||
.scope_stack
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, scope_index)| self.ctx.scopes[**scope_index].bindings.contains_key(&name))
|
||||
.find_map(|(stack_index, scope_index)| {
|
||||
self.ctx.scopes[*scope_index]
|
||||
.get(name)
|
||||
.map(|binding_id| (stack_index, *binding_id))
|
||||
})
|
||||
{
|
||||
let existing_binding_index = self.ctx.scopes[*scope_index].bindings[&name];
|
||||
let existing = &self.ctx.bindings[existing_binding_index];
|
||||
let in_current_scope = stack_index == 0;
|
||||
if !existing.kind.is_builtin()
|
||||
|
@ -4012,7 +4012,7 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
|
||||
let scope = self.ctx.scope();
|
||||
let binding = if let Some(index) = scope.bindings.get(&name) {
|
||||
let binding = if let Some(index) = scope.get(name) {
|
||||
let existing = &self.ctx.bindings[*index];
|
||||
match &existing.kind {
|
||||
BindingKind::Builtin => {
|
||||
|
@ -4044,8 +4044,8 @@ impl<'a> Checker<'a> {
|
|||
// Don't treat annotations as assignments if there is an existing value
|
||||
// in scope.
|
||||
let scope = self.ctx.scope_mut();
|
||||
if !(binding.kind.is_annotation() && scope.bindings.contains_key(name)) {
|
||||
if let Some(rebound_index) = scope.bindings.insert(name, binding_id) {
|
||||
if !(binding.kind.is_annotation() && scope.defines(name)) {
|
||||
if let Some(rebound_index) = scope.add(name, binding_id) {
|
||||
scope
|
||||
.rebounds
|
||||
.entry(name)
|
||||
|
@ -4076,7 +4076,7 @@ impl<'a> Checker<'a> {
|
|||
source: None,
|
||||
context: ExecutionContext::Runtime,
|
||||
});
|
||||
scope.bindings.insert(builtin, id);
|
||||
scope.add(builtin, id);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4101,7 +4101,7 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
if let Some(index) = scope.bindings.get(&id.as_str()) {
|
||||
if let Some(index) = scope.get(id.as_str()) {
|
||||
// Mark the binding as used.
|
||||
let context = self.ctx.execution_context();
|
||||
self.ctx.bindings[*index].mark_used(scope_id, Range::from(expr), context);
|
||||
|
@ -4131,7 +4131,7 @@ impl<'a> Checker<'a> {
|
|||
.unwrap_or_default();
|
||||
if has_alias {
|
||||
// Mark the sub-importation as used.
|
||||
if let Some(index) = scope.bindings.get(full_name) {
|
||||
if let Some(index) = scope.get(full_name) {
|
||||
self.ctx.bindings[*index].mark_used(
|
||||
scope_id,
|
||||
Range::from(expr),
|
||||
|
@ -4148,7 +4148,7 @@ impl<'a> Checker<'a> {
|
|||
.unwrap_or_default();
|
||||
if has_alias {
|
||||
// Mark the sub-importation as used.
|
||||
if let Some(index) = scope.bindings.get(full_name.as_str()) {
|
||||
if let Some(index) = scope.get(full_name.as_str()) {
|
||||
self.ctx.bindings[*index].mark_used(
|
||||
scope_id,
|
||||
Range::from(expr),
|
||||
|
@ -4173,11 +4173,7 @@ impl<'a> Checker<'a> {
|
|||
let mut from_list = vec![];
|
||||
for scope_index in self.ctx.scope_stack.iter() {
|
||||
let scope = &self.ctx.scopes[*scope_index];
|
||||
for binding in scope
|
||||
.bindings
|
||||
.values()
|
||||
.map(|index| &self.ctx.bindings[*index])
|
||||
{
|
||||
for binding in scope.binding_ids().map(|index| &self.ctx.bindings[*index]) {
|
||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||
from_list.push(helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
|
@ -4260,7 +4256,6 @@ impl<'a> Checker<'a> {
|
|||
if !self
|
||||
.ctx
|
||||
.scope()
|
||||
.bindings
|
||||
.get(id)
|
||||
.map_or(false, |index| self.ctx.bindings[*index].kind.is_global())
|
||||
{
|
||||
|
@ -4433,7 +4428,7 @@ impl<'a> Checker<'a> {
|
|||
}
|
||||
|
||||
let scope = self.ctx.scope_mut();
|
||||
if scope.bindings.remove(&id.as_str()).is_some() {
|
||||
if scope.remove(id.as_str()).is_some() {
|
||||
return;
|
||||
}
|
||||
if !self.settings.rules.enabled(Rule::UndefinedName) {
|
||||
|
@ -4663,7 +4658,6 @@ impl<'a> Checker<'a> {
|
|||
let all_bindings: Option<(Vec<BindingId>, Range)> = {
|
||||
let global_scope = self.ctx.global_scope();
|
||||
let all_names: Option<(&Vec<String>, Range)> = global_scope
|
||||
.bindings
|
||||
.get("__all__")
|
||||
.map(|index| &self.ctx.bindings[*index])
|
||||
.and_then(|binding| match &binding.kind {
|
||||
|
@ -4675,7 +4669,7 @@ impl<'a> Checker<'a> {
|
|||
(
|
||||
names
|
||||
.iter()
|
||||
.filter_map(|name| global_scope.bindings.get(name.as_str()).copied())
|
||||
.filter_map(|name| global_scope.get(name.as_str()).copied())
|
||||
.collect(),
|
||||
range,
|
||||
)
|
||||
|
@ -4696,7 +4690,6 @@ impl<'a> Checker<'a> {
|
|||
let all_names: Option<(Vec<&str>, Range)> = self
|
||||
.ctx
|
||||
.global_scope()
|
||||
.bindings
|
||||
.get("__all__")
|
||||
.map(|index| &self.ctx.bindings[*index])
|
||||
.and_then(|binding| match &binding.kind {
|
||||
|
@ -4718,8 +4711,7 @@ impl<'a> Checker<'a> {
|
|||
.iter()
|
||||
.map(|scope| {
|
||||
scope
|
||||
.bindings
|
||||
.values()
|
||||
.binding_ids()
|
||||
.map(|index| &self.ctx.bindings[*index])
|
||||
.filter(|binding| {
|
||||
flake8_type_checking::helpers::is_valid_runtime_import(binding)
|
||||
|
@ -4749,7 +4741,7 @@ impl<'a> Checker<'a> {
|
|||
|
||||
// PLW0602
|
||||
if self.settings.rules.enabled(Rule::GlobalVariableNotAssigned) {
|
||||
for (name, index) in &scope.bindings {
|
||||
for (name, index) in scope.bindings() {
|
||||
let binding = &self.ctx.bindings[*index];
|
||||
if binding.kind.is_global() {
|
||||
if let Some(stmt) = &binding.source {
|
||||
|
@ -4775,7 +4767,7 @@ impl<'a> Checker<'a> {
|
|||
// unused. Note that we only store references in `redefinitions` if
|
||||
// the bindings are in different scopes.
|
||||
if self.settings.rules.enabled(Rule::RedefinedWhileUnused) {
|
||||
for (name, index) in &scope.bindings {
|
||||
for (name, index) in scope.bindings() {
|
||||
let binding = &self.ctx.bindings[*index];
|
||||
|
||||
if matches!(
|
||||
|
@ -4818,11 +4810,7 @@ impl<'a> Checker<'a> {
|
|||
if scope.import_starred {
|
||||
if let Some((names, range)) = &all_names {
|
||||
let mut from_list = vec![];
|
||||
for binding in scope
|
||||
.bindings
|
||||
.values()
|
||||
.map(|index| &self.ctx.bindings[*index])
|
||||
{
|
||||
for binding in scope.binding_ids().map(|index| &self.ctx.bindings[*index]) {
|
||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||
from_list.push(helpers::format_import_from(
|
||||
level.as_ref(),
|
||||
|
@ -4833,7 +4821,7 @@ impl<'a> Checker<'a> {
|
|||
from_list.sort();
|
||||
|
||||
for &name in names {
|
||||
if !scope.bindings.contains_key(name) {
|
||||
if !scope.defines(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
pyflakes::rules::ImportStarUsage {
|
||||
name: name.to_string(),
|
||||
|
@ -4859,7 +4847,7 @@ impl<'a> Checker<'a> {
|
|||
.copied()
|
||||
.collect()
|
||||
};
|
||||
for (.., index) in &scope.bindings {
|
||||
for index in scope.binding_ids() {
|
||||
let binding = &self.ctx.bindings[*index];
|
||||
|
||||
if let Some(diagnostic) =
|
||||
|
@ -4895,7 +4883,7 @@ impl<'a> Checker<'a> {
|
|||
let mut ignored: FxHashMap<BindingContext, Vec<UnusedImport>> =
|
||||
FxHashMap::default();
|
||||
|
||||
for index in scope.bindings.values() {
|
||||
for index in scope.binding_ids() {
|
||||
let binding = &self.ctx.bindings[*index];
|
||||
|
||||
let full_name = match &binding.kind {
|
||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -165,7 +165,6 @@ pub fn unused_loop_control_variable(
|
|||
// Find the `BindingKind::LoopVar` corresponding to the name.
|
||||
let scope = checker.ctx.scope();
|
||||
let binding = scope
|
||||
.bindings
|
||||
.get(name)
|
||||
.into_iter()
|
||||
.chain(scope.rebounds.get(name).into_iter().flatten())
|
||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::collect_call_path;
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt, StmtKind, Unaryop};
|
|||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::{create_expr, unparse_expr};
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind};
|
|||
|
||||
use ruff_python_ast::context::Context;
|
||||
use ruff_python_ast::helpers::{map_callable, to_call_path};
|
||||
use ruff_python_ast::types::{Binding, BindingKind, ExecutionContext, ScopeKind};
|
||||
use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext, ScopeKind};
|
||||
|
||||
/// Return `true` if [`Expr`] is a guard for a type-checking block.
|
||||
pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Binding, BindingKind, ExecutionContext};
|
||||
use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext};
|
||||
|
||||
#[violation]
|
||||
pub struct RuntimeImportInTypeCheckingBlock {
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Binding, BindingKind, ExecutionContext};
|
||||
use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext};
|
||||
|
||||
use crate::rules::isort::{categorize, ImportType};
|
||||
use crate::settings::Settings;
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
use std::iter;
|
||||
|
||||
use regex::Regex;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::{Arg, Arguments};
|
||||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::context::Bindings;
|
||||
use ruff_python_ast::function_type;
|
||||
use ruff_python_ast::function_type::FunctionType;
|
||||
use ruff_python_ast::types::{BindingId, FunctionDef, Lambda, Scope, ScopeKind};
|
||||
use ruff_python_ast::scope::{Bindings, FunctionDef, Lambda, Scope, ScopeKind};
|
||||
use ruff_python_ast::visibility;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -86,7 +84,7 @@ impl Violation for UnusedLambdaArgument {
|
|||
fn function(
|
||||
argumentable: &Argumentable,
|
||||
args: &Arguments,
|
||||
values: &FxHashMap<&str, BindingId>,
|
||||
values: &Scope,
|
||||
bindings: &Bindings,
|
||||
dummy_variable_rgx: &Regex,
|
||||
ignore_variadic_names: bool,
|
||||
|
@ -113,7 +111,7 @@ fn function(
|
|||
fn method(
|
||||
argumentable: &Argumentable,
|
||||
args: &Arguments,
|
||||
values: &FxHashMap<&str, BindingId>,
|
||||
values: &Scope,
|
||||
bindings: &Bindings,
|
||||
dummy_variable_rgx: &Regex,
|
||||
ignore_variadic_names: bool,
|
||||
|
@ -140,14 +138,14 @@ fn method(
|
|||
fn call<'a>(
|
||||
argumentable: &Argumentable,
|
||||
args: impl Iterator<Item = &'a Arg>,
|
||||
values: &FxHashMap<&str, BindingId>,
|
||||
values: &Scope,
|
||||
bindings: &Bindings,
|
||||
dummy_variable_rgx: &Regex,
|
||||
) -> Vec<Diagnostic> {
|
||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||
for arg in args {
|
||||
if let Some(binding) = values
|
||||
.get(&arg.node.arg.as_str())
|
||||
.get(arg.node.arg.as_str())
|
||||
.map(|index| &bindings[*index])
|
||||
{
|
||||
if !binding.used()
|
||||
|
@ -197,7 +195,7 @@ pub fn unused_arguments(
|
|||
function(
|
||||
&Argumentable::Function,
|
||||
args,
|
||||
&scope.bindings,
|
||||
scope,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
checker
|
||||
|
@ -226,7 +224,7 @@ pub fn unused_arguments(
|
|||
method(
|
||||
&Argumentable::Method,
|
||||
args,
|
||||
&scope.bindings,
|
||||
scope,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
checker
|
||||
|
@ -255,7 +253,7 @@ pub fn unused_arguments(
|
|||
method(
|
||||
&Argumentable::ClassMethod,
|
||||
args,
|
||||
&scope.bindings,
|
||||
scope,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
checker
|
||||
|
@ -284,7 +282,7 @@ pub fn unused_arguments(
|
|||
function(
|
||||
&Argumentable::StaticMethod,
|
||||
args,
|
||||
&scope.bindings,
|
||||
scope,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
checker
|
||||
|
@ -307,7 +305,7 @@ pub fn unused_arguments(
|
|||
function(
|
||||
&Argumentable::Lambda,
|
||||
args,
|
||||
&scope.bindings,
|
||||
scope,
|
||||
bindings,
|
||||
&checker.settings.dummy_variable_rgx,
|
||||
checker
|
||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
|||
use ruff_diagnostics::Violation;
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{BindingKind, Range};
|
||||
use ruff_python_ast::scope::BindingKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Rule;
|
||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
|||
use ruff_diagnostics::Violation;
|
||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{BindingKind, Range};
|
||||
use ruff_python_ast::scope::BindingKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::Rule;
|
||||
|
|
|
@ -3,8 +3,8 @@ use rustpython_parser::ast::Stmt;
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::identifier_range;
|
||||
use ruff_python_ast::scope::{Scope, ScopeKind};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::types::{Scope, ScopeKind};
|
||||
|
||||
/// ## What it does
|
||||
/// Checks for functions with "dunder" names (that is, names with two
|
||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Arguments, Expr};
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::function_type;
|
||||
use ruff_python_ast::types::{Range, Scope};
|
||||
use ruff_python_ast::scope::Scope;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Arguments, Expr};
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::function_type;
|
||||
use ruff_python_ast::types::{Range, Scope};
|
||||
use ruff_python_ast::scope::Scope;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -4,8 +4,9 @@ use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
|
|||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt};
|
||||
use ruff_python_ast::newlines::StrExt;
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::source_code::Stylist;
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::types::Range;
|
||||
use ruff_python_ast::whitespace::leading_space;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::Stmt;
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use std::path::Path;
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, Scope};
|
||||
use ruff_python_ast::scope::Scope;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
#[violation]
|
||||
pub struct UndefinedExport {
|
||||
|
@ -27,7 +28,7 @@ pub fn undefined_export(
|
|||
let mut diagnostics = Vec::new();
|
||||
if !scope.import_starred && !path.ends_with("__init__.py") {
|
||||
for name in names {
|
||||
if !scope.bindings.contains_key(name) {
|
||||
if !scope.defines(name) {
|
||||
diagnostics.push(Diagnostic::new(
|
||||
UndefinedExport {
|
||||
name: (*name).to_string(),
|
||||
|
|
|
@ -2,8 +2,7 @@ use std::string::ToString;
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::context::Bindings;
|
||||
use ruff_python_ast::types::{Scope, ScopeKind};
|
||||
use ruff_python_ast::scope::{Bindings, Scope, ScopeKind};
|
||||
|
||||
#[violation]
|
||||
pub struct UndefinedLocal {
|
||||
|
@ -21,10 +20,10 @@ impl Violation for UndefinedLocal {
|
|||
/// F821
|
||||
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &Bindings) -> Option<Diagnostic> {
|
||||
let current = &scopes.last().expect("No current scope found");
|
||||
if matches!(current.kind, ScopeKind::Function(_)) && !current.bindings.contains_key(name) {
|
||||
if matches!(current.kind, ScopeKind::Function(_)) && !current.defines(name) {
|
||||
for scope in scopes.iter().rev().skip(1) {
|
||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
||||
if let Some(binding) = scope.bindings.get(name).map(|index| &bindings[*index]) {
|
||||
if let Some(binding) = scope.get(name).map(|index| &bindings[*index]) {
|
||||
if let Some((scope_id, location)) = binding.runtime_usage {
|
||||
if scope_id == current.id {
|
||||
return Some(Diagnostic::new(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::ScopeId;
|
||||
use ruff_python_ast::scope::ScopeId;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
@ -21,8 +21,7 @@ impl Violation for UnusedAnnotation {
|
|||
pub fn unused_annotation(checker: &mut Checker, scope: ScopeId) {
|
||||
let scope = &checker.ctx.scopes[scope];
|
||||
for (name, binding) in scope
|
||||
.bindings
|
||||
.iter()
|
||||
.bindings()
|
||||
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
|
||||
{
|
||||
if !binding.used()
|
||||
|
|
|
@ -6,8 +6,9 @@ use rustpython_parser::{lexer, Mode, Tok};
|
|||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::helpers::contains_effect;
|
||||
use ruff_python_ast::scope::{ScopeId, ScopeKind};
|
||||
use ruff_python_ast::source_code::Locator;
|
||||
use ruff_python_ast::types::{Range, RefEquality, ScopeId, ScopeKind};
|
||||
use ruff_python_ast::types::{Range, RefEquality};
|
||||
|
||||
use crate::autofix::helpers::delete_stmt;
|
||||
use crate::checkers::ast::Checker;
|
||||
|
@ -319,8 +320,7 @@ pub fn unused_variable(checker: &mut Checker, scope: ScopeId) {
|
|||
}
|
||||
|
||||
for (name, binding) in scope
|
||||
.bindings
|
||||
.iter()
|
||||
.bindings()
|
||||
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
|
||||
{
|
||||
if !binding.used()
|
||||
|
|
|
@ -4,7 +4,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ruff_python_ast::function_type;
|
||||
use ruff_python_ast::function_type::FunctionType;
|
||||
use ruff_python_ast::types::{FunctionDef, ScopeKind};
|
||||
use ruff_python_ast::scope::{FunctionDef, ScopeKind};
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::Expr;
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{FunctionDef, Range, ScopeKind};
|
||||
use ruff_python_ast::scope::{FunctionDef, ScopeKind};
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
|||
|
||||
use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{BindingKind, Range};
|
||||
use ruff_python_ast::scope::BindingKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
@ -29,7 +30,7 @@ impl Violation for ConsiderUsingSysExit {
|
|||
/// sys import *`).
|
||||
fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
|
||||
checker.ctx.scopes().any(|scope| {
|
||||
scope.bindings.values().any(|index| {
|
||||
scope.binding_ids().any(|index| {
|
||||
if let BindingKind::StarImportation(_, name) = &checker.ctx.bindings[*index].kind {
|
||||
name.as_ref().map(|name| name == module).unwrap_or_default()
|
||||
} else {
|
||||
|
@ -44,8 +45,7 @@ fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
|
|||
fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -> Option<String> {
|
||||
checker.ctx.scopes().find_map(|scope| {
|
||||
scope
|
||||
.bindings
|
||||
.values()
|
||||
.binding_ids()
|
||||
.find_map(|index| match &checker.ctx.bindings[*index].kind {
|
||||
// e.g. module=sys object=exit
|
||||
// `import sys` -> `sys.exit`
|
||||
|
|
|
@ -53,7 +53,7 @@ impl Violation for GlobalStatement {
|
|||
/// PLW0603
|
||||
pub fn global_statement(checker: &mut Checker, name: &str) {
|
||||
let scope = checker.ctx.scope();
|
||||
if let Some(index) = scope.bindings.get(name) {
|
||||
if let Some(index) = scope.get(name) {
|
||||
let binding = &checker.ctx.bindings[*index];
|
||||
if binding.kind.is_global() {
|
||||
let source: &Stmt = binding
|
||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::Expr;
|
|||
|
||||
use ruff_diagnostics::{Diagnostic, Violation};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
|
||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::{ArgData, Expr, ExprKind, Stmt, StmtKind};
|
|||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::types::{Range, ScopeKind};
|
||||
use ruff_python_ast::scope::ScopeKind;
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
|
|
@ -2,8 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt};
|
|||
|
||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||
use ruff_macros::{derive_message_formats, violation};
|
||||
use ruff_python_ast::context::Bindings;
|
||||
use ruff_python_ast::types::{Binding, BindingKind, Range, Scope};
|
||||
use ruff_python_ast::scope::{Binding, BindingKind, Bindings, Scope};
|
||||
use ruff_python_ast::types::Range;
|
||||
|
||||
use crate::checkers::ast::Checker;
|
||||
use crate::registry::AsRule;
|
||||
|
@ -36,10 +36,7 @@ fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &Bindings) -> Optio
|
|||
continue;
|
||||
}
|
||||
if !matches!(
|
||||
scope
|
||||
.bindings
|
||||
.get(&id.as_str())
|
||||
.map(|index| &bindings[*index]),
|
||||
scope.get(id.as_str()).map(|index| &bindings[*index]),
|
||||
None | Some(Binding {
|
||||
kind: BindingKind::Builtin,
|
||||
..
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use std::ops::{Deref, Index, IndexMut};
|
||||
use std::path::Path;
|
||||
|
||||
use nohash_hasher::{BuildNoHashHasher, IntMap};
|
||||
|
@ -10,10 +9,11 @@ use ruff_python_stdlib::path::is_python_stub_file;
|
|||
use ruff_python_stdlib::typing::TYPING_EXTENSIONS;
|
||||
|
||||
use crate::helpers::{collect_call_path, from_relative_import, Exceptions};
|
||||
use crate::types::{
|
||||
Binding, BindingId, BindingKind, CallPath, ExecutionContext, RefEquality, Scope, ScopeId,
|
||||
ScopeKind,
|
||||
use crate::scope::{
|
||||
Binding, BindingId, BindingKind, Bindings, ExecutionContext, Scope, ScopeId, ScopeKind,
|
||||
ScopeStack, Scopes,
|
||||
};
|
||||
use crate::types::{CallPath, RefEquality};
|
||||
use crate::visibility::{module_visibility, Modifier, VisibleScope};
|
||||
|
||||
#[allow(clippy::struct_excessive_bools)]
|
||||
|
@ -125,7 +125,7 @@ impl<'a> Context<'a> {
|
|||
/// Return the current `Binding` for a given `name`.
|
||||
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
|
||||
self.scopes()
|
||||
.find_map(|scope| scope.bindings.get(member))
|
||||
.find_map(|scope| scope.get(member))
|
||||
.map(|index| &self.bindings[*index])
|
||||
}
|
||||
|
||||
|
@ -319,132 +319,3 @@ impl<'a> Context<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The scopes of a program indexed by [`ScopeId`]
|
||||
#[derive(Debug)]
|
||||
pub struct Scopes<'a>(Vec<Scope<'a>>);
|
||||
|
||||
impl<'a> Scopes<'a> {
|
||||
/// Returns a reference to the global scope
|
||||
pub fn global(&self) -> &Scope<'a> {
|
||||
&self[ScopeId::global()]
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the global scope
|
||||
pub fn global_mut(&mut self) -> &mut Scope<'a> {
|
||||
&mut self[ScopeId::global()]
|
||||
}
|
||||
|
||||
/// Pushes a new scope and returns its unique id
|
||||
fn push_scope(&mut self, kind: ScopeKind<'a>) -> ScopeId {
|
||||
let next_id = ScopeId::try_from(self.0.len()).unwrap();
|
||||
self.0.push(Scope::local(next_id, kind));
|
||||
next_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Scopes<'_> {
|
||||
fn default() -> Self {
|
||||
Self(vec![Scope::global(ScopeKind::Module)])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<ScopeId> for Scopes<'a> {
|
||||
type Output = Scope<'a>;
|
||||
|
||||
fn index(&self, index: ScopeId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<ScopeId> for Scopes<'a> {
|
||||
fn index_mut(&mut self, index: ScopeId) -> &mut Self::Output {
|
||||
&mut self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Scopes<'a> {
|
||||
type Target = [Scope<'a>];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeStack(Vec<ScopeId>);
|
||||
|
||||
impl ScopeStack {
|
||||
/// Pushes a new scope on the stack
|
||||
pub fn push(&mut self, id: ScopeId) {
|
||||
self.0.push(id);
|
||||
}
|
||||
|
||||
/// Pops the top most scope
|
||||
pub fn pop(&mut self) -> Option<ScopeId> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
/// Returns the id of the top-most
|
||||
pub fn top(&self) -> Option<ScopeId> {
|
||||
self.0.last().copied()
|
||||
}
|
||||
|
||||
/// Returns an iterator from the current scope to the top scope (reverse iterator)
|
||||
pub fn iter(&self) -> std::iter::Rev<std::slice::Iter<ScopeId>> {
|
||||
self.0.iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScopeStack {
|
||||
fn default() -> Self {
|
||||
Self(vec![ScopeId::global()])
|
||||
}
|
||||
}
|
||||
|
||||
/// The bindings in a program.
|
||||
///
|
||||
/// Bindings are indexed by [`BindingId`]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Bindings<'a>(Vec<Binding<'a>>);
|
||||
|
||||
impl<'a> Bindings<'a> {
|
||||
/// Pushes a new binding and returns its id
|
||||
pub fn push(&mut self, binding: Binding<'a>) -> BindingId {
|
||||
let id = self.next_id();
|
||||
self.0.push(binding);
|
||||
id
|
||||
}
|
||||
|
||||
/// Returns the id that will be assigned when pushing the next binding
|
||||
pub fn next_id(&self) -> BindingId {
|
||||
BindingId::try_from(self.0.len()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<BindingId> for Bindings<'a> {
|
||||
type Output = Binding<'a>;
|
||||
|
||||
fn index(&self, index: BindingId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<BindingId> for Bindings<'a> {
|
||||
fn index_mut(&mut self, index: BindingId) -> &mut Self::Output {
|
||||
&mut self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Bindings<'a> {
|
||||
type Target = [Binding<'a>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Binding<'a>> for Bindings<'a> {
|
||||
fn from_iter<T: IntoIterator<Item = Binding<'a>>>(iter: T) -> Self {
|
||||
Self(Vec::from_iter(iter))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use rustpython_parser::ast::Expr;
|
|||
|
||||
use crate::context::Context;
|
||||
use crate::helpers::{map_callable, to_call_path};
|
||||
use crate::types::{Scope, ScopeKind};
|
||||
use crate::scope::{Scope, ScopeKind};
|
||||
|
||||
const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
||||
|
|
|
@ -15,8 +15,9 @@ use smallvec::{smallvec, SmallVec};
|
|||
|
||||
use crate::context::Context;
|
||||
use crate::newlines::StrExt;
|
||||
use crate::scope::{Binding, BindingKind};
|
||||
use crate::source_code::{Generator, Indexer, Locator, Stylist};
|
||||
use crate::types::{Binding, BindingKind, CallPath, Range};
|
||||
use crate::types::{CallPath, Range};
|
||||
use crate::visitor;
|
||||
use crate::visitor::Visitor;
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ pub mod logging;
|
|||
pub mod newlines;
|
||||
pub mod operations;
|
||||
pub mod relocate;
|
||||
pub mod scope;
|
||||
pub mod source_code;
|
||||
pub mod str;
|
||||
pub mod types;
|
||||
|
|
|
@ -5,7 +5,7 @@ use rustpython_parser::{lexer, Mode, Tok};
|
|||
|
||||
use crate::context::Context;
|
||||
use crate::helpers::any_over_expr;
|
||||
use crate::types::{BindingKind, Scope};
|
||||
use crate::scope::{BindingKind, Scope};
|
||||
use crate::visitor;
|
||||
use crate::visitor::Visitor;
|
||||
|
||||
|
@ -95,7 +95,7 @@ pub fn extract_all_names(
|
|||
|
||||
// Grab the existing bound __all__ values.
|
||||
if let StmtKind::AugAssign { .. } = &stmt.node {
|
||||
if let Some(index) = scope.bindings.get("__all__") {
|
||||
if let Some(index) = scope.get("__all__") {
|
||||
if let BindingKind::Export(existing) = &ctx.bindings[*index].kind {
|
||||
names.extend_from_slice(existing);
|
||||
}
|
||||
|
|
424
crates/ruff_python_ast/src/scope.rs
Normal file
424
crates/ruff_python_ast/src/scope.rs
Normal file
|
@ -0,0 +1,424 @@
|
|||
use crate::types::{Range, RefEquality};
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::{Arguments, Expr, Keyword, Stmt};
|
||||
use std::num::TryFromIntError;
|
||||
use std::ops::{Deref, Index, IndexMut};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scope<'a> {
|
||||
pub id: ScopeId,
|
||||
pub kind: ScopeKind<'a>,
|
||||
pub import_starred: bool,
|
||||
pub uses_locals: bool,
|
||||
/// A map from bound name to binding index, for live bindings.
|
||||
bindings: FxHashMap<&'a str, BindingId>,
|
||||
/// A map from bound name to binding index, for bindings that were created
|
||||
/// in the scope but rebound (and thus overridden) later on in the same
|
||||
/// scope.
|
||||
pub rebounds: FxHashMap<&'a str, Vec<BindingId>>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
pub fn global() -> Self {
|
||||
Scope::local(ScopeId::global(), ScopeKind::Module)
|
||||
}
|
||||
|
||||
pub fn local(id: ScopeId, kind: ScopeKind<'a>) -> Self {
|
||||
Scope {
|
||||
id,
|
||||
kind,
|
||||
import_starred: false,
|
||||
uses_locals: false,
|
||||
bindings: FxHashMap::default(),
|
||||
rebounds: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the [id](BindingId) of the binding with the given name.
|
||||
pub fn get(&self, name: &str) -> Option<&BindingId> {
|
||||
self.bindings.get(name)
|
||||
}
|
||||
|
||||
/// Adds a new binding with the given name to this scope.
|
||||
pub fn add(&mut self, name: &'a str, id: BindingId) -> Option<BindingId> {
|
||||
self.bindings.insert(name, id)
|
||||
}
|
||||
|
||||
/// Returns `true` if this scope defines a binding with the given name.
|
||||
pub fn defines(&self, name: &str) -> bool {
|
||||
self.bindings.contains_key(name)
|
||||
}
|
||||
|
||||
/// Removes the binding with the given name
|
||||
pub fn remove(&mut self, name: &str) -> Option<BindingId> {
|
||||
self.bindings.remove(name)
|
||||
}
|
||||
|
||||
/// Returns the ids of all bindings defined in this scope.
|
||||
pub fn binding_ids(&self) -> std::collections::hash_map::Values<&str, BindingId> {
|
||||
self.bindings.values()
|
||||
}
|
||||
|
||||
pub fn bindings(&self) -> std::collections::hash_map::Iter<&'a str, BindingId> {
|
||||
self.bindings.iter()
|
||||
}
|
||||
}
|
||||
|
||||
/// Id uniquely identifying a scope in a program.
|
||||
///
|
||||
/// Using a `u32` is sufficient because Ruff only supports parsing documents with a size of max `u32::max`
|
||||
/// and it is impossible to have more scopes than characters in the file (because defining a function or class
|
||||
/// requires more than one character).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct ScopeId(u32);
|
||||
|
||||
impl ScopeId {
|
||||
/// Returns the ID for the global scope
|
||||
#[inline]
|
||||
pub const fn global() -> Self {
|
||||
ScopeId(0)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is the id of the global scope
|
||||
pub const fn is_global(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for ScopeId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ScopeId> for usize {
|
||||
fn from(value: ScopeId) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScopeKind<'a> {
|
||||
Class(ClassDef<'a>),
|
||||
Function(FunctionDef<'a>),
|
||||
Generator,
|
||||
Module,
|
||||
Lambda(Lambda<'a>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionDef<'a> {
|
||||
// Properties derived from StmtKind::FunctionDef.
|
||||
pub name: &'a str,
|
||||
pub args: &'a Arguments,
|
||||
pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
// pub returns: Option<&'a Expr>,
|
||||
// pub type_comment: Option<&'a str>,
|
||||
// Scope-specific properties.
|
||||
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
|
||||
pub async_: bool,
|
||||
pub globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClassDef<'a> {
|
||||
// Properties derived from StmtKind::ClassDef.
|
||||
pub name: &'a str,
|
||||
pub bases: &'a [Expr],
|
||||
pub keywords: &'a [Keyword],
|
||||
// pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
// Scope-specific properties.
|
||||
pub globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Lambda<'a> {
|
||||
pub args: &'a Arguments,
|
||||
pub body: &'a Expr,
|
||||
}
|
||||
|
||||
/// The scopes of a program indexed by [`ScopeId`]
|
||||
#[derive(Debug)]
|
||||
pub struct Scopes<'a>(Vec<Scope<'a>>);
|
||||
|
||||
impl<'a> Scopes<'a> {
|
||||
/// Returns a reference to the global scope
|
||||
pub fn global(&self) -> &Scope<'a> {
|
||||
&self[ScopeId::global()]
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the global scope
|
||||
pub fn global_mut(&mut self) -> &mut Scope<'a> {
|
||||
&mut self[ScopeId::global()]
|
||||
}
|
||||
|
||||
/// Pushes a new scope and returns its unique id
|
||||
pub(crate) fn push_scope(&mut self, kind: ScopeKind<'a>) -> ScopeId {
|
||||
let next_id = ScopeId::try_from(self.0.len()).unwrap();
|
||||
self.0.push(Scope::local(next_id, kind));
|
||||
next_id
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Scopes<'_> {
|
||||
fn default() -> Self {
|
||||
Self(vec![Scope::global()])
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<ScopeId> for Scopes<'a> {
|
||||
type Output = Scope<'a>;
|
||||
|
||||
fn index(&self, index: ScopeId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<ScopeId> for Scopes<'a> {
|
||||
fn index_mut(&mut self, index: ScopeId) -> &mut Self::Output {
|
||||
&mut self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Scopes<'a> {
|
||||
type Target = [Scope<'a>];
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ScopeStack(Vec<ScopeId>);
|
||||
|
||||
impl ScopeStack {
|
||||
/// Pushes a new scope on the stack
|
||||
pub fn push(&mut self, id: ScopeId) {
|
||||
self.0.push(id);
|
||||
}
|
||||
|
||||
/// Pops the top most scope
|
||||
pub fn pop(&mut self) -> Option<ScopeId> {
|
||||
self.0.pop()
|
||||
}
|
||||
|
||||
/// Returns the id of the top-most
|
||||
pub fn top(&self) -> Option<ScopeId> {
|
||||
self.0.last().copied()
|
||||
}
|
||||
|
||||
/// Returns an iterator from the current scope to the top scope (reverse iterator)
|
||||
pub fn iter(&self) -> std::iter::Rev<std::slice::Iter<ScopeId>> {
|
||||
self.0.iter().rev()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ScopeStack {
|
||||
fn default() -> Self {
|
||||
Self(vec![ScopeId::global()])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Binding<'a> {
|
||||
pub kind: BindingKind<'a>,
|
||||
pub range: Range,
|
||||
/// The context in which the binding was created.
|
||||
pub context: ExecutionContext,
|
||||
/// The statement in which the [`Binding`] was defined.
|
||||
pub source: Option<RefEquality<'a, Stmt>>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used in a runtime context.
|
||||
pub runtime_usage: Option<(ScopeId, Range)>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used in a typing-time context.
|
||||
pub typing_usage: Option<(ScopeId, Range)>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used in a synthetic context. This is used for
|
||||
/// (e.g.) `__future__` imports, explicit re-exports, and other bindings
|
||||
/// that should be considered used even if they're never referenced.
|
||||
pub synthetic_usage: Option<(ScopeId, Range)>,
|
||||
}
|
||||
|
||||
impl<'a> Binding<'a> {
|
||||
pub fn mark_used(&mut self, scope: ScopeId, range: Range, context: ExecutionContext) {
|
||||
match context {
|
||||
ExecutionContext::Runtime => self.runtime_usage = Some((scope, range)),
|
||||
ExecutionContext::Typing => self.typing_usage = Some((scope, range)),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn used(&self) -> bool {
|
||||
self.runtime_usage.is_some()
|
||||
|| self.synthetic_usage.is_some()
|
||||
|| self.typing_usage.is_some()
|
||||
}
|
||||
|
||||
pub const fn is_definition(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
BindingKind::ClassDefinition
|
||||
| BindingKind::FunctionDefinition
|
||||
| BindingKind::Builtin
|
||||
| BindingKind::FutureImportation
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::Importation(..)
|
||||
| BindingKind::FromImportation(..)
|
||||
| BindingKind::SubmoduleImportation(..)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn redefines(&self, existing: &'a Binding) -> bool {
|
||||
match &self.kind {
|
||||
BindingKind::Importation(.., full_name) => {
|
||||
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
|
||||
return full_name == existing;
|
||||
}
|
||||
}
|
||||
BindingKind::FromImportation(.., full_name) => {
|
||||
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
|
||||
return full_name == existing;
|
||||
}
|
||||
}
|
||||
BindingKind::SubmoduleImportation(.., full_name) => match &existing.kind {
|
||||
BindingKind::Importation(.., existing)
|
||||
| BindingKind::SubmoduleImportation(.., existing) => {
|
||||
return full_name == existing;
|
||||
}
|
||||
BindingKind::FromImportation(.., existing) => {
|
||||
return full_name == existing;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
BindingKind::Annotation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::FutureImportation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::StarImportation(..) => {
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
existing.is_definition()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub enum ExecutionContext {
|
||||
Runtime,
|
||||
Typing,
|
||||
}
|
||||
|
||||
/// ID uniquely identifying a [Binding] in a program.
|
||||
///
|
||||
/// Using a `u32` to identify [Binding]s should is sufficient because Ruff only supports documents with a
|
||||
/// size smaller than or equal to `u32::max`. A document with the size of `u32::max` must have fewer than `u32::max`
|
||||
/// bindings because bindings must be separated by whitespace (and have an assignment).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct BindingId(u32);
|
||||
|
||||
impl From<BindingId> for usize {
|
||||
fn from(value: BindingId) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for BindingId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl nohash_hasher::IsEnabled for BindingId {}
|
||||
|
||||
// Pyflakes defines the following binding hierarchy (via inheritance):
|
||||
// Binding
|
||||
// ExportBinding
|
||||
// Annotation
|
||||
// Argument
|
||||
// Assignment
|
||||
// NamedExprAssignment
|
||||
// Definition
|
||||
// FunctionDefinition
|
||||
// ClassDefinition
|
||||
// Builtin
|
||||
// Importation
|
||||
// SubmoduleImportation
|
||||
// ImportationFrom
|
||||
// StarImportation
|
||||
// FutureImportation
|
||||
|
||||
#[derive(Clone, Debug, is_macro::Is)]
|
||||
pub enum BindingKind<'a> {
|
||||
Annotation,
|
||||
Argument,
|
||||
Assignment,
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
FunctionDefinition,
|
||||
Export(Vec<String>),
|
||||
FutureImportation,
|
||||
StarImportation(Option<usize>, Option<String>),
|
||||
Importation(&'a str, &'a str),
|
||||
FromImportation(&'a str, String),
|
||||
SubmoduleImportation(&'a str, &'a str),
|
||||
}
|
||||
|
||||
/// The bindings in a program.
|
||||
///
|
||||
/// Bindings are indexed by [`BindingId`]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Bindings<'a>(Vec<Binding<'a>>);
|
||||
|
||||
impl<'a> Bindings<'a> {
|
||||
/// Pushes a new binding and returns its id
|
||||
pub fn push(&mut self, binding: Binding<'a>) -> BindingId {
|
||||
let id = self.next_id();
|
||||
self.0.push(binding);
|
||||
id
|
||||
}
|
||||
|
||||
/// Returns the id that will be assigned when pushing the next binding
|
||||
pub fn next_id(&self) -> BindingId {
|
||||
BindingId::try_from(self.0.len()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Index<BindingId> for Bindings<'a> {
|
||||
type Output = Binding<'a>;
|
||||
|
||||
fn index(&self, index: BindingId) -> &Self::Output {
|
||||
&self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IndexMut<BindingId> for Bindings<'a> {
|
||||
fn index_mut(&mut self, index: BindingId) -> &mut Self::Output {
|
||||
&mut self.0[usize::from(index)]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for Bindings<'a> {
|
||||
type Target = [Binding<'a>];
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> FromIterator<Binding<'a>> for Bindings<'a> {
|
||||
fn from_iter<T: IntoIterator<Item = Binding<'a>>>(iter: T) -> Self {
|
||||
Self(Vec::from_iter(iter))
|
||||
}
|
||||
}
|
|
@ -1,8 +1,6 @@
|
|||
use std::num::TryFromIntError;
|
||||
use std::ops::Deref;
|
||||
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustpython_parser::ast::{Arguments, Expr, Keyword, Located, Location, Stmt};
|
||||
use rustpython_parser::ast::{Expr, Located, Location, Stmt};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum Node<'a> {
|
||||
|
@ -37,267 +35,6 @@ impl<T> From<&Box<Located<T>>> for Range {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionDef<'a> {
|
||||
// Properties derived from StmtKind::FunctionDef.
|
||||
pub name: &'a str,
|
||||
pub args: &'a Arguments,
|
||||
pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
// pub returns: Option<&'a Expr>,
|
||||
// pub type_comment: Option<&'a str>,
|
||||
// Scope-specific properties.
|
||||
// TODO(charlie): Create AsyncFunctionDef to mirror the AST.
|
||||
pub async_: bool,
|
||||
pub globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ClassDef<'a> {
|
||||
// Properties derived from StmtKind::ClassDef.
|
||||
pub name: &'a str,
|
||||
pub bases: &'a [Expr],
|
||||
pub keywords: &'a [Keyword],
|
||||
// pub body: &'a [Stmt],
|
||||
pub decorator_list: &'a [Expr],
|
||||
// Scope-specific properties.
|
||||
pub globals: FxHashMap<&'a str, &'a Stmt>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Lambda<'a> {
|
||||
pub args: &'a Arguments,
|
||||
pub body: &'a Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ScopeKind<'a> {
|
||||
Class(ClassDef<'a>),
|
||||
Function(FunctionDef<'a>),
|
||||
Generator,
|
||||
Module,
|
||||
Lambda(Lambda<'a>),
|
||||
}
|
||||
|
||||
/// Id uniquely identifying a scope in a program.
|
||||
///
|
||||
/// Using a `u32` is sufficient because Ruff only supports parsing documents with a size of max `u32::max`
|
||||
/// and it is impossible to have more scopes than characters in the file (because defining a function or class
|
||||
/// requires more than one character).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
|
||||
pub struct ScopeId(u32);
|
||||
|
||||
impl ScopeId {
|
||||
/// Returns the ID for the global scope
|
||||
#[inline]
|
||||
pub const fn global() -> Self {
|
||||
ScopeId(0)
|
||||
}
|
||||
|
||||
/// Returns `true` if this is the id of the global scope
|
||||
pub const fn is_global(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for ScopeId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ScopeId> for usize {
|
||||
fn from(value: ScopeId) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scope<'a> {
|
||||
pub id: ScopeId,
|
||||
pub kind: ScopeKind<'a>,
|
||||
pub import_starred: bool,
|
||||
pub uses_locals: bool,
|
||||
/// A map from bound name to binding index, for live bindings.
|
||||
pub bindings: FxHashMap<&'a str, BindingId>,
|
||||
/// A map from bound name to binding index, for bindings that were created
|
||||
/// in the scope but rebound (and thus overridden) later on in the same
|
||||
/// scope.
|
||||
pub rebounds: FxHashMap<&'a str, Vec<BindingId>>,
|
||||
}
|
||||
|
||||
impl<'a> Scope<'a> {
|
||||
pub fn global(kind: ScopeKind<'a>) -> Self {
|
||||
Self::local(ScopeId::global(), kind)
|
||||
}
|
||||
|
||||
pub fn local(id: ScopeId, kind: ScopeKind<'a>) -> Self {
|
||||
Scope {
|
||||
id,
|
||||
kind,
|
||||
import_starred: false,
|
||||
uses_locals: false,
|
||||
bindings: FxHashMap::default(),
|
||||
rebounds: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pyflakes defines the following binding hierarchy (via inheritance):
|
||||
// Binding
|
||||
// ExportBinding
|
||||
// Annotation
|
||||
// Argument
|
||||
// Assignment
|
||||
// NamedExprAssignment
|
||||
// Definition
|
||||
// FunctionDefinition
|
||||
// ClassDefinition
|
||||
// Builtin
|
||||
// Importation
|
||||
// SubmoduleImportation
|
||||
// ImportationFrom
|
||||
// StarImportation
|
||||
// FutureImportation
|
||||
|
||||
#[derive(Clone, Debug, is_macro::Is)]
|
||||
pub enum BindingKind<'a> {
|
||||
Annotation,
|
||||
Argument,
|
||||
Assignment,
|
||||
Binding,
|
||||
LoopVar,
|
||||
Global,
|
||||
Nonlocal,
|
||||
Builtin,
|
||||
ClassDefinition,
|
||||
FunctionDefinition,
|
||||
Export(Vec<String>),
|
||||
FutureImportation,
|
||||
StarImportation(Option<usize>, Option<String>),
|
||||
Importation(&'a str, &'a str),
|
||||
FromImportation(&'a str, String),
|
||||
SubmoduleImportation(&'a str, &'a str),
|
||||
}
|
||||
|
||||
/// ID uniquely identifying a [Binding] in a program.
|
||||
///
|
||||
/// Using a `u32` to identify [Binding]s should is sufficient because Ruff only supports documents with a
|
||||
/// size smaller than or equal to `u32::max`. A document with the size of `u32::max` must have fewer than `u32::max`
|
||||
/// bindings because bindings must be separated by whitespace (and have an assignment).
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct BindingId(u32);
|
||||
|
||||
impl From<BindingId> for usize {
|
||||
fn from(value: BindingId) -> Self {
|
||||
value.0 as usize
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for BindingId {
|
||||
type Error = TryFromIntError;
|
||||
|
||||
fn try_from(value: usize) -> Result<Self, Self::Error> {
|
||||
Ok(Self(u32::try_from(value)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl nohash_hasher::IsEnabled for BindingId {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Binding<'a> {
|
||||
pub kind: BindingKind<'a>,
|
||||
pub range: Range,
|
||||
/// The context in which the binding was created.
|
||||
pub context: ExecutionContext,
|
||||
/// The statement in which the [`Binding`] was defined.
|
||||
pub source: Option<RefEquality<'a, Stmt>>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used in a runtime context.
|
||||
pub runtime_usage: Option<(ScopeId, Range)>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used in a typing-time context.
|
||||
pub typing_usage: Option<(ScopeId, Range)>,
|
||||
/// Tuple of (scope index, range) indicating the scope and range at which
|
||||
/// the binding was last used in a synthetic context. This is used for
|
||||
/// (e.g.) `__future__` imports, explicit re-exports, and other bindings
|
||||
/// that should be considered used even if they're never referenced.
|
||||
pub synthetic_usage: Option<(ScopeId, Range)>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Debug, Clone)]
|
||||
pub enum ExecutionContext {
|
||||
Runtime,
|
||||
Typing,
|
||||
}
|
||||
|
||||
impl<'a> Binding<'a> {
|
||||
pub fn mark_used(&mut self, scope: ScopeId, range: Range, context: ExecutionContext) {
|
||||
match context {
|
||||
ExecutionContext::Runtime => self.runtime_usage = Some((scope, range)),
|
||||
ExecutionContext::Typing => self.typing_usage = Some((scope, range)),
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn used(&self) -> bool {
|
||||
self.runtime_usage.is_some()
|
||||
|| self.synthetic_usage.is_some()
|
||||
|| self.typing_usage.is_some()
|
||||
}
|
||||
|
||||
pub const fn is_definition(&self) -> bool {
|
||||
matches!(
|
||||
self.kind,
|
||||
BindingKind::ClassDefinition
|
||||
| BindingKind::FunctionDefinition
|
||||
| BindingKind::Builtin
|
||||
| BindingKind::FutureImportation
|
||||
| BindingKind::StarImportation(..)
|
||||
| BindingKind::Importation(..)
|
||||
| BindingKind::FromImportation(..)
|
||||
| BindingKind::SubmoduleImportation(..)
|
||||
)
|
||||
}
|
||||
|
||||
pub fn redefines(&self, existing: &'a Binding) -> bool {
|
||||
match &self.kind {
|
||||
BindingKind::Importation(.., full_name) => {
|
||||
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
|
||||
return full_name == existing;
|
||||
}
|
||||
}
|
||||
BindingKind::FromImportation(.., full_name) => {
|
||||
if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind {
|
||||
return full_name == existing;
|
||||
}
|
||||
}
|
||||
BindingKind::SubmoduleImportation(.., full_name) => match &existing.kind {
|
||||
BindingKind::Importation(.., existing)
|
||||
| BindingKind::SubmoduleImportation(.., existing) => {
|
||||
return full_name == existing;
|
||||
}
|
||||
BindingKind::FromImportation(.., existing) => {
|
||||
return full_name == existing;
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
BindingKind::Annotation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::FutureImportation => {
|
||||
return false;
|
||||
}
|
||||
BindingKind::StarImportation(..) => {
|
||||
return false;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
existing.is_definition()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct RefEquality<'a, T>(pub &'a T);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue