mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-30 05:45:24 +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 rustpython_parser::ast::{Expr, Stmt};
|
||||||
|
|
||||||
use ruff_python_ast::types::Range;
|
use ruff_python_ast::types::Range;
|
||||||
|
|
|
@ -14,17 +14,18 @@ use rustpython_parser::ast::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ruff_diagnostics::Diagnostic;
|
use ruff_diagnostics::Diagnostic;
|
||||||
use ruff_python_ast::context::{Context, ScopeStack};
|
use ruff_python_ast::context::Context;
|
||||||
use ruff_python_ast::helpers::{
|
use ruff_python_ast::helpers::{
|
||||||
binding_range, extract_handled_exceptions, to_module_path, Exceptions,
|
binding_range, extract_handled_exceptions, to_module_path, Exceptions,
|
||||||
};
|
};
|
||||||
use ruff_python_ast::operations::{extract_all_names, AllNamesFlags};
|
use ruff_python_ast::operations::{extract_all_names, AllNamesFlags};
|
||||||
use ruff_python_ast::relocate::relocate_expr;
|
use ruff_python_ast::relocate::relocate_expr;
|
||||||
use ruff_python_ast::source_code::{Indexer, Locator, Stylist};
|
use ruff_python_ast::scope::{
|
||||||
use ruff_python_ast::types::{
|
Binding, BindingId, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Scope,
|
||||||
Binding, BindingId, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range,
|
ScopeId, ScopeKind, ScopeStack,
|
||||||
RefEquality, Scope, ScopeId, ScopeKind,
|
|
||||||
};
|
};
|
||||||
|
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::typing::{match_annotated_subscript, Callable, SubscriptKind};
|
||||||
use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
|
use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor};
|
||||||
use ruff_python_ast::{
|
use ruff_python_ast::{
|
||||||
|
@ -208,7 +209,7 @@ where
|
||||||
source: Some(RefEquality(stmt)),
|
source: Some(RefEquality(stmt)),
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
scope.bindings.insert(name, id);
|
scope.add(name, id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +238,7 @@ where
|
||||||
source: Some(RefEquality(stmt)),
|
source: Some(RefEquality(stmt)),
|
||||||
context,
|
context,
|
||||||
});
|
});
|
||||||
scope.bindings.insert(name, id);
|
scope.add(name, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark the binding in the defining scopes as used too. (Skip the global scope
|
// Mark the binding in the defining scopes as used too. (Skip the global scope
|
||||||
|
@ -249,9 +250,7 @@ where
|
||||||
scopes_iter.next_back();
|
scopes_iter.next_back();
|
||||||
|
|
||||||
for index in scopes_iter.skip(1) {
|
for index in scopes_iter.skip(1) {
|
||||||
if let Some(index) =
|
if let Some(index) = self.ctx.scopes[*index].get(name.as_str()) {
|
||||||
self.ctx.scopes[*index].bindings.get(&name.as_str())
|
|
||||||
{
|
|
||||||
exists = true;
|
exists = true;
|
||||||
self.ctx.bindings[*index].runtime_usage = usage;
|
self.ctx.bindings[*index].runtime_usage = usage;
|
||||||
}
|
}
|
||||||
|
@ -1874,7 +1873,6 @@ where
|
||||||
if self
|
if self
|
||||||
.ctx
|
.ctx
|
||||||
.global_scope()
|
.global_scope()
|
||||||
.bindings
|
|
||||||
.get(name)
|
.get(name)
|
||||||
.map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation())
|
.map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation())
|
||||||
{
|
{
|
||||||
|
@ -1887,7 +1885,7 @@ where
|
||||||
source: Some(RefEquality(stmt)),
|
source: Some(RefEquality(stmt)),
|
||||||
context: self.ctx.execution_context(),
|
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
|
if self
|
||||||
.ctx
|
.ctx
|
||||||
.global_scope()
|
.global_scope()
|
||||||
.bindings
|
|
||||||
.get(name)
|
.get(name)
|
||||||
.map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation())
|
.map_or(true, |index| self.ctx.bindings[*index].kind.is_annotation())
|
||||||
{
|
{
|
||||||
|
@ -1950,7 +1947,7 @@ where
|
||||||
source: Some(RefEquality(stmt)),
|
source: Some(RefEquality(stmt)),
|
||||||
context: self.ctx.execution_context(),
|
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 =
|
let name_range =
|
||||||
helpers::excepthandler_name_range(excepthandler, self.locator).unwrap();
|
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(
|
self.handle_node_store(
|
||||||
name,
|
name,
|
||||||
&Expr::new(
|
&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(
|
self.handle_node_store(
|
||||||
name,
|
name,
|
||||||
&Expr::new(
|
&Expr::new(
|
||||||
|
@ -3744,7 +3741,7 @@ where
|
||||||
|
|
||||||
if let Some(index) = {
|
if let Some(index) = {
|
||||||
let scope = self.ctx.scope_mut();
|
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.ctx.bindings[*index].used() {
|
||||||
if self.settings.rules.enabled(Rule::UnusedVariable) {
|
if self.settings.rules.enabled(Rule::UnusedVariable) {
|
||||||
|
@ -3778,7 +3775,7 @@ where
|
||||||
|
|
||||||
if let Some(index) = definition {
|
if let Some(index) = definition {
|
||||||
let scope = self.ctx.scope_mut();
|
let scope = self.ctx.scope_mut();
|
||||||
scope.bindings.insert(name, index);
|
scope.add(name, index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => walk_excepthandler(self, excepthandler),
|
None => walk_excepthandler(self, excepthandler),
|
||||||
|
@ -3933,14 +3930,17 @@ impl<'a> Checker<'a> {
|
||||||
'b: 'a,
|
'b: 'a,
|
||||||
{
|
{
|
||||||
let binding_id = self.ctx.bindings.next_id();
|
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
|
.ctx
|
||||||
.scope_stack
|
.scope_stack
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.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 existing = &self.ctx.bindings[existing_binding_index];
|
||||||
let in_current_scope = stack_index == 0;
|
let in_current_scope = stack_index == 0;
|
||||||
if !existing.kind.is_builtin()
|
if !existing.kind.is_builtin()
|
||||||
|
@ -4012,7 +4012,7 @@ impl<'a> Checker<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
let scope = self.ctx.scope();
|
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];
|
let existing = &self.ctx.bindings[*index];
|
||||||
match &existing.kind {
|
match &existing.kind {
|
||||||
BindingKind::Builtin => {
|
BindingKind::Builtin => {
|
||||||
|
@ -4044,8 +4044,8 @@ impl<'a> Checker<'a> {
|
||||||
// Don't treat annotations as assignments if there is an existing value
|
// Don't treat annotations as assignments if there is an existing value
|
||||||
// in scope.
|
// in scope.
|
||||||
let scope = self.ctx.scope_mut();
|
let scope = self.ctx.scope_mut();
|
||||||
if !(binding.kind.is_annotation() && scope.bindings.contains_key(name)) {
|
if !(binding.kind.is_annotation() && scope.defines(name)) {
|
||||||
if let Some(rebound_index) = scope.bindings.insert(name, binding_id) {
|
if let Some(rebound_index) = scope.add(name, binding_id) {
|
||||||
scope
|
scope
|
||||||
.rebounds
|
.rebounds
|
||||||
.entry(name)
|
.entry(name)
|
||||||
|
@ -4076,7 +4076,7 @@ impl<'a> Checker<'a> {
|
||||||
source: None,
|
source: None,
|
||||||
context: ExecutionContext::Runtime,
|
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.
|
// Mark the binding as used.
|
||||||
let context = self.ctx.execution_context();
|
let context = self.ctx.execution_context();
|
||||||
self.ctx.bindings[*index].mark_used(scope_id, Range::from(expr), context);
|
self.ctx.bindings[*index].mark_used(scope_id, Range::from(expr), context);
|
||||||
|
@ -4131,7 +4131,7 @@ impl<'a> Checker<'a> {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if has_alias {
|
if has_alias {
|
||||||
// Mark the sub-importation as used.
|
// 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(
|
self.ctx.bindings[*index].mark_used(
|
||||||
scope_id,
|
scope_id,
|
||||||
Range::from(expr),
|
Range::from(expr),
|
||||||
|
@ -4148,7 +4148,7 @@ impl<'a> Checker<'a> {
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if has_alias {
|
if has_alias {
|
||||||
// Mark the sub-importation as used.
|
// 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(
|
self.ctx.bindings[*index].mark_used(
|
||||||
scope_id,
|
scope_id,
|
||||||
Range::from(expr),
|
Range::from(expr),
|
||||||
|
@ -4173,11 +4173,7 @@ impl<'a> Checker<'a> {
|
||||||
let mut from_list = vec![];
|
let mut from_list = vec![];
|
||||||
for scope_index in self.ctx.scope_stack.iter() {
|
for scope_index in self.ctx.scope_stack.iter() {
|
||||||
let scope = &self.ctx.scopes[*scope_index];
|
let scope = &self.ctx.scopes[*scope_index];
|
||||||
for binding in scope
|
for binding in scope.binding_ids().map(|index| &self.ctx.bindings[*index]) {
|
||||||
.bindings
|
|
||||||
.values()
|
|
||||||
.map(|index| &self.ctx.bindings[*index])
|
|
||||||
{
|
|
||||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||||
from_list.push(helpers::format_import_from(
|
from_list.push(helpers::format_import_from(
|
||||||
level.as_ref(),
|
level.as_ref(),
|
||||||
|
@ -4260,7 +4256,6 @@ impl<'a> Checker<'a> {
|
||||||
if !self
|
if !self
|
||||||
.ctx
|
.ctx
|
||||||
.scope()
|
.scope()
|
||||||
.bindings
|
|
||||||
.get(id)
|
.get(id)
|
||||||
.map_or(false, |index| self.ctx.bindings[*index].kind.is_global())
|
.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();
|
let scope = self.ctx.scope_mut();
|
||||||
if scope.bindings.remove(&id.as_str()).is_some() {
|
if scope.remove(id.as_str()).is_some() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if !self.settings.rules.enabled(Rule::UndefinedName) {
|
if !self.settings.rules.enabled(Rule::UndefinedName) {
|
||||||
|
@ -4663,7 +4658,6 @@ impl<'a> Checker<'a> {
|
||||||
let all_bindings: Option<(Vec<BindingId>, Range)> = {
|
let all_bindings: Option<(Vec<BindingId>, Range)> = {
|
||||||
let global_scope = self.ctx.global_scope();
|
let global_scope = self.ctx.global_scope();
|
||||||
let all_names: Option<(&Vec<String>, Range)> = global_scope
|
let all_names: Option<(&Vec<String>, Range)> = global_scope
|
||||||
.bindings
|
|
||||||
.get("__all__")
|
.get("__all__")
|
||||||
.map(|index| &self.ctx.bindings[*index])
|
.map(|index| &self.ctx.bindings[*index])
|
||||||
.and_then(|binding| match &binding.kind {
|
.and_then(|binding| match &binding.kind {
|
||||||
|
@ -4675,7 +4669,7 @@ impl<'a> Checker<'a> {
|
||||||
(
|
(
|
||||||
names
|
names
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|name| global_scope.bindings.get(name.as_str()).copied())
|
.filter_map(|name| global_scope.get(name.as_str()).copied())
|
||||||
.collect(),
|
.collect(),
|
||||||
range,
|
range,
|
||||||
)
|
)
|
||||||
|
@ -4696,7 +4690,6 @@ impl<'a> Checker<'a> {
|
||||||
let all_names: Option<(Vec<&str>, Range)> = self
|
let all_names: Option<(Vec<&str>, Range)> = self
|
||||||
.ctx
|
.ctx
|
||||||
.global_scope()
|
.global_scope()
|
||||||
.bindings
|
|
||||||
.get("__all__")
|
.get("__all__")
|
||||||
.map(|index| &self.ctx.bindings[*index])
|
.map(|index| &self.ctx.bindings[*index])
|
||||||
.and_then(|binding| match &binding.kind {
|
.and_then(|binding| match &binding.kind {
|
||||||
|
@ -4718,8 +4711,7 @@ impl<'a> Checker<'a> {
|
||||||
.iter()
|
.iter()
|
||||||
.map(|scope| {
|
.map(|scope| {
|
||||||
scope
|
scope
|
||||||
.bindings
|
.binding_ids()
|
||||||
.values()
|
|
||||||
.map(|index| &self.ctx.bindings[*index])
|
.map(|index| &self.ctx.bindings[*index])
|
||||||
.filter(|binding| {
|
.filter(|binding| {
|
||||||
flake8_type_checking::helpers::is_valid_runtime_import(binding)
|
flake8_type_checking::helpers::is_valid_runtime_import(binding)
|
||||||
|
@ -4749,7 +4741,7 @@ impl<'a> Checker<'a> {
|
||||||
|
|
||||||
// PLW0602
|
// PLW0602
|
||||||
if self.settings.rules.enabled(Rule::GlobalVariableNotAssigned) {
|
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];
|
let binding = &self.ctx.bindings[*index];
|
||||||
if binding.kind.is_global() {
|
if binding.kind.is_global() {
|
||||||
if let Some(stmt) = &binding.source {
|
if let Some(stmt) = &binding.source {
|
||||||
|
@ -4775,7 +4767,7 @@ impl<'a> Checker<'a> {
|
||||||
// unused. Note that we only store references in `redefinitions` if
|
// unused. Note that we only store references in `redefinitions` if
|
||||||
// the bindings are in different scopes.
|
// the bindings are in different scopes.
|
||||||
if self.settings.rules.enabled(Rule::RedefinedWhileUnused) {
|
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];
|
let binding = &self.ctx.bindings[*index];
|
||||||
|
|
||||||
if matches!(
|
if matches!(
|
||||||
|
@ -4818,11 +4810,7 @@ impl<'a> Checker<'a> {
|
||||||
if scope.import_starred {
|
if scope.import_starred {
|
||||||
if let Some((names, range)) = &all_names {
|
if let Some((names, range)) = &all_names {
|
||||||
let mut from_list = vec![];
|
let mut from_list = vec![];
|
||||||
for binding in scope
|
for binding in scope.binding_ids().map(|index| &self.ctx.bindings[*index]) {
|
||||||
.bindings
|
|
||||||
.values()
|
|
||||||
.map(|index| &self.ctx.bindings[*index])
|
|
||||||
{
|
|
||||||
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
if let BindingKind::StarImportation(level, module) = &binding.kind {
|
||||||
from_list.push(helpers::format_import_from(
|
from_list.push(helpers::format_import_from(
|
||||||
level.as_ref(),
|
level.as_ref(),
|
||||||
|
@ -4833,7 +4821,7 @@ impl<'a> Checker<'a> {
|
||||||
from_list.sort();
|
from_list.sort();
|
||||||
|
|
||||||
for &name in names {
|
for &name in names {
|
||||||
if !scope.bindings.contains_key(name) {
|
if !scope.defines(name) {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
pyflakes::rules::ImportStarUsage {
|
pyflakes::rules::ImportStarUsage {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
@ -4859,7 +4847,7 @@ impl<'a> Checker<'a> {
|
||||||
.copied()
|
.copied()
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
for (.., index) in &scope.bindings {
|
for index in scope.binding_ids() {
|
||||||
let binding = &self.ctx.bindings[*index];
|
let binding = &self.ctx.bindings[*index];
|
||||||
|
|
||||||
if let Some(diagnostic) =
|
if let Some(diagnostic) =
|
||||||
|
@ -4895,7 +4883,7 @@ impl<'a> Checker<'a> {
|
||||||
let mut ignored: FxHashMap<BindingContext, Vec<UnusedImport>> =
|
let mut ignored: FxHashMap<BindingContext, Vec<UnusedImport>> =
|
||||||
FxHashMap::default();
|
FxHashMap::default();
|
||||||
|
|
||||||
for index in scope.bindings.values() {
|
for index in scope.binding_ids() {
|
||||||
let binding = &self.ctx.bindings[*index];
|
let binding = &self.ctx.bindings[*index];
|
||||||
|
|
||||||
let full_name = match &binding.kind {
|
let full_name = match &binding.kind {
|
||||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
|
|
@ -165,7 +165,6 @@ pub fn unused_loop_control_variable(
|
||||||
// Find the `BindingKind::LoopVar` corresponding to the name.
|
// Find the `BindingKind::LoopVar` corresponding to the name.
|
||||||
let scope = checker.ctx.scope();
|
let scope = checker.ctx.scope();
|
||||||
let binding = scope
|
let binding = scope
|
||||||
.bindings
|
|
||||||
.get(name)
|
.get(name)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(scope.rebounds.get(name).into_iter().flatten())
|
.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_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::collect_call_path;
|
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;
|
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_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::{create_expr, unparse_expr};
|
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::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
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::context::Context;
|
||||||
use ruff_python_ast::helpers::{map_callable, to_call_path};
|
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.
|
/// Return `true` if [`Expr`] is a guard for a type-checking block.
|
||||||
pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool {
|
pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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]
|
#[violation]
|
||||||
pub struct RuntimeImportInTypeCheckingBlock {
|
pub struct RuntimeImportInTypeCheckingBlock {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::path::Path;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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::rules::isort::{categorize, ImportType};
|
||||||
use crate::settings::Settings;
|
use crate::settings::Settings;
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustc_hash::FxHashMap;
|
|
||||||
use rustpython_parser::ast::{Arg, Arguments};
|
use rustpython_parser::ast::{Arg, Arguments};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
||||||
use ruff_python_ast::function_type::FunctionType;
|
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 ruff_python_ast::visibility;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -86,7 +84,7 @@ impl Violation for UnusedLambdaArgument {
|
||||||
fn function(
|
fn function(
|
||||||
argumentable: &Argumentable,
|
argumentable: &Argumentable,
|
||||||
args: &Arguments,
|
args: &Arguments,
|
||||||
values: &FxHashMap<&str, BindingId>,
|
values: &Scope,
|
||||||
bindings: &Bindings,
|
bindings: &Bindings,
|
||||||
dummy_variable_rgx: &Regex,
|
dummy_variable_rgx: &Regex,
|
||||||
ignore_variadic_names: bool,
|
ignore_variadic_names: bool,
|
||||||
|
@ -113,7 +111,7 @@ fn function(
|
||||||
fn method(
|
fn method(
|
||||||
argumentable: &Argumentable,
|
argumentable: &Argumentable,
|
||||||
args: &Arguments,
|
args: &Arguments,
|
||||||
values: &FxHashMap<&str, BindingId>,
|
values: &Scope,
|
||||||
bindings: &Bindings,
|
bindings: &Bindings,
|
||||||
dummy_variable_rgx: &Regex,
|
dummy_variable_rgx: &Regex,
|
||||||
ignore_variadic_names: bool,
|
ignore_variadic_names: bool,
|
||||||
|
@ -140,14 +138,14 @@ fn method(
|
||||||
fn call<'a>(
|
fn call<'a>(
|
||||||
argumentable: &Argumentable,
|
argumentable: &Argumentable,
|
||||||
args: impl Iterator<Item = &'a Arg>,
|
args: impl Iterator<Item = &'a Arg>,
|
||||||
values: &FxHashMap<&str, BindingId>,
|
values: &Scope,
|
||||||
bindings: &Bindings,
|
bindings: &Bindings,
|
||||||
dummy_variable_rgx: &Regex,
|
dummy_variable_rgx: &Regex,
|
||||||
) -> Vec<Diagnostic> {
|
) -> Vec<Diagnostic> {
|
||||||
let mut diagnostics: Vec<Diagnostic> = vec![];
|
let mut diagnostics: Vec<Diagnostic> = vec![];
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if let Some(binding) = values
|
if let Some(binding) = values
|
||||||
.get(&arg.node.arg.as_str())
|
.get(arg.node.arg.as_str())
|
||||||
.map(|index| &bindings[*index])
|
.map(|index| &bindings[*index])
|
||||||
{
|
{
|
||||||
if !binding.used()
|
if !binding.used()
|
||||||
|
@ -197,7 +195,7 @@ pub fn unused_arguments(
|
||||||
function(
|
function(
|
||||||
&Argumentable::Function,
|
&Argumentable::Function,
|
||||||
args,
|
args,
|
||||||
&scope.bindings,
|
scope,
|
||||||
bindings,
|
bindings,
|
||||||
&checker.settings.dummy_variable_rgx,
|
&checker.settings.dummy_variable_rgx,
|
||||||
checker
|
checker
|
||||||
|
@ -226,7 +224,7 @@ pub fn unused_arguments(
|
||||||
method(
|
method(
|
||||||
&Argumentable::Method,
|
&Argumentable::Method,
|
||||||
args,
|
args,
|
||||||
&scope.bindings,
|
scope,
|
||||||
bindings,
|
bindings,
|
||||||
&checker.settings.dummy_variable_rgx,
|
&checker.settings.dummy_variable_rgx,
|
||||||
checker
|
checker
|
||||||
|
@ -255,7 +253,7 @@ pub fn unused_arguments(
|
||||||
method(
|
method(
|
||||||
&Argumentable::ClassMethod,
|
&Argumentable::ClassMethod,
|
||||||
args,
|
args,
|
||||||
&scope.bindings,
|
scope,
|
||||||
bindings,
|
bindings,
|
||||||
&checker.settings.dummy_variable_rgx,
|
&checker.settings.dummy_variable_rgx,
|
||||||
checker
|
checker
|
||||||
|
@ -284,7 +282,7 @@ pub fn unused_arguments(
|
||||||
function(
|
function(
|
||||||
&Argumentable::StaticMethod,
|
&Argumentable::StaticMethod,
|
||||||
args,
|
args,
|
||||||
&scope.bindings,
|
scope,
|
||||||
bindings,
|
bindings,
|
||||||
&checker.settings.dummy_variable_rgx,
|
&checker.settings.dummy_variable_rgx,
|
||||||
checker
|
checker
|
||||||
|
@ -307,7 +305,7 @@ pub fn unused_arguments(
|
||||||
function(
|
function(
|
||||||
&Argumentable::Lambda,
|
&Argumentable::Lambda,
|
||||||
args,
|
args,
|
||||||
&scope.bindings,
|
scope,
|
||||||
bindings,
|
bindings,
|
||||||
&checker.settings.dummy_variable_rgx,
|
&checker.settings.dummy_variable_rgx,
|
||||||
checker
|
checker
|
||||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||||
use ruff_macros::{derive_message_formats, 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::checkers::ast::Checker;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
||||||
use ruff_diagnostics::Violation;
|
use ruff_diagnostics::Violation;
|
||||||
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
use ruff_diagnostics::{Diagnostic, DiagnosticKind};
|
||||||
use ruff_macros::{derive_message_formats, 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::checkers::ast::Checker;
|
||||||
use crate::registry::Rule;
|
use crate::registry::Rule;
|
||||||
|
|
|
@ -3,8 +3,8 @@ use rustpython_parser::ast::Stmt;
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::identifier_range;
|
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::source_code::Locator;
|
||||||
use ruff_python_ast::types::{Scope, ScopeKind};
|
|
||||||
|
|
||||||
/// ## What it does
|
/// ## What it does
|
||||||
/// Checks for functions with "dunder" names (that is, names with two
|
/// 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_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::function_type;
|
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;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ use rustpython_parser::ast::{Arguments, Expr};
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::function_type;
|
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;
|
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_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt};
|
use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt};
|
||||||
use ruff_python_ast::newlines::StrExt;
|
use ruff_python_ast::newlines::StrExt;
|
||||||
|
use ruff_python_ast::scope::ScopeKind;
|
||||||
use ruff_python_ast::source_code::Stylist;
|
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 ruff_python_ast::whitespace::leading_space;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::Stmt;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ use std::path::Path;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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]
|
#[violation]
|
||||||
pub struct UndefinedExport {
|
pub struct UndefinedExport {
|
||||||
|
@ -27,7 +28,7 @@ pub fn undefined_export(
|
||||||
let mut diagnostics = Vec::new();
|
let mut diagnostics = Vec::new();
|
||||||
if !scope.import_starred && !path.ends_with("__init__.py") {
|
if !scope.import_starred && !path.ends_with("__init__.py") {
|
||||||
for name in names {
|
for name in names {
|
||||||
if !scope.bindings.contains_key(name) {
|
if !scope.defines(name) {
|
||||||
diagnostics.push(Diagnostic::new(
|
diagnostics.push(Diagnostic::new(
|
||||||
UndefinedExport {
|
UndefinedExport {
|
||||||
name: (*name).to_string(),
|
name: (*name).to_string(),
|
||||||
|
|
|
@ -2,8 +2,7 @@ use std::string::ToString;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::context::Bindings;
|
use ruff_python_ast::scope::{Bindings, Scope, ScopeKind};
|
||||||
use ruff_python_ast::types::{Scope, ScopeKind};
|
|
||||||
|
|
||||||
#[violation]
|
#[violation]
|
||||||
pub struct UndefinedLocal {
|
pub struct UndefinedLocal {
|
||||||
|
@ -21,10 +20,10 @@ impl Violation for UndefinedLocal {
|
||||||
/// F821
|
/// F821
|
||||||
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &Bindings) -> Option<Diagnostic> {
|
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &Bindings) -> Option<Diagnostic> {
|
||||||
let current = &scopes.last().expect("No current scope found");
|
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) {
|
for scope in scopes.iter().rev().skip(1) {
|
||||||
if matches!(scope.kind, ScopeKind::Function(_) | ScopeKind::Module) {
|
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 let Some((scope_id, location)) = binding.runtime_usage {
|
||||||
if scope_id == current.id {
|
if scope_id == current.id {
|
||||||
return Some(Diagnostic::new(
|
return Some(Diagnostic::new(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
@ -21,8 +21,7 @@ impl Violation for UnusedAnnotation {
|
||||||
pub fn unused_annotation(checker: &mut Checker, scope: ScopeId) {
|
pub fn unused_annotation(checker: &mut Checker, scope: ScopeId) {
|
||||||
let scope = &checker.ctx.scopes[scope];
|
let scope = &checker.ctx.scopes[scope];
|
||||||
for (name, binding) in scope
|
for (name, binding) in scope
|
||||||
.bindings
|
.bindings()
|
||||||
.iter()
|
|
||||||
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
|
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
|
||||||
{
|
{
|
||||||
if !binding.used()
|
if !binding.used()
|
||||||
|
|
|
@ -6,8 +6,9 @@ use rustpython_parser::{lexer, Mode, Tok};
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::helpers::contains_effect;
|
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::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::autofix::helpers::delete_stmt;
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
|
@ -319,8 +320,7 @@ pub fn unused_variable(checker: &mut Checker, scope: ScopeId) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (name, binding) in scope
|
for (name, binding) in scope
|
||||||
.bindings
|
.bindings()
|
||||||
.iter()
|
|
||||||
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
|
.map(|(name, index)| (name, &checker.ctx.bindings[*index]))
|
||||||
{
|
{
|
||||||
if !binding.used()
|
if !binding.used()
|
||||||
|
|
|
@ -4,7 +4,8 @@ use rustpython_parser::ast::{Expr, ExprKind};
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use ruff_python_ast::function_type;
|
use ruff_python_ast::function_type;
|
||||||
use ruff_python_ast::function_type::FunctionType;
|
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;
|
use crate::checkers::ast::Checker;
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::Expr;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
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_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
@ -29,7 +30,7 @@ impl Violation for ConsiderUsingSysExit {
|
||||||
/// sys import *`).
|
/// sys import *`).
|
||||||
fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
|
fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
|
||||||
checker.ctx.scopes().any(|scope| {
|
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 {
|
if let BindingKind::StarImportation(_, name) = &checker.ctx.bindings[*index].kind {
|
||||||
name.as_ref().map(|name| name == module).unwrap_or_default()
|
name.as_ref().map(|name| name == module).unwrap_or_default()
|
||||||
} else {
|
} 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> {
|
fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -> Option<String> {
|
||||||
checker.ctx.scopes().find_map(|scope| {
|
checker.ctx.scopes().find_map(|scope| {
|
||||||
scope
|
scope
|
||||||
.bindings
|
.binding_ids()
|
||||||
.values()
|
|
||||||
.find_map(|index| match &checker.ctx.bindings[*index].kind {
|
.find_map(|index| match &checker.ctx.bindings[*index].kind {
|
||||||
// e.g. module=sys object=exit
|
// e.g. module=sys object=exit
|
||||||
// `import sys` -> `sys.exit`
|
// `import sys` -> `sys.exit`
|
||||||
|
|
|
@ -53,7 +53,7 @@ impl Violation for GlobalStatement {
|
||||||
/// PLW0603
|
/// PLW0603
|
||||||
pub fn global_statement(checker: &mut Checker, name: &str) {
|
pub fn global_statement(checker: &mut Checker, name: &str) {
|
||||||
let scope = checker.ctx.scope();
|
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];
|
let binding = &checker.ctx.bindings[*index];
|
||||||
if binding.kind.is_global() {
|
if binding.kind.is_global() {
|
||||||
let source: &Stmt = binding
|
let source: &Stmt = binding
|
||||||
|
|
|
@ -2,7 +2,8 @@ use rustpython_parser::ast::Expr;
|
||||||
|
|
||||||
use ruff_diagnostics::{Diagnostic, Violation};
|
use ruff_diagnostics::{Diagnostic, Violation};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
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_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||||
use ruff_macros::{derive_message_formats, 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;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
|
|
@ -2,8 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt};
|
||||||
|
|
||||||
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic};
|
||||||
use ruff_macros::{derive_message_formats, violation};
|
use ruff_macros::{derive_message_formats, violation};
|
||||||
use ruff_python_ast::context::Bindings;
|
use ruff_python_ast::scope::{Binding, BindingKind, Bindings, Scope};
|
||||||
use ruff_python_ast::types::{Binding, BindingKind, Range, Scope};
|
use ruff_python_ast::types::Range;
|
||||||
|
|
||||||
use crate::checkers::ast::Checker;
|
use crate::checkers::ast::Checker;
|
||||||
use crate::registry::AsRule;
|
use crate::registry::AsRule;
|
||||||
|
@ -36,10 +36,7 @@ fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &Bindings) -> Optio
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if !matches!(
|
if !matches!(
|
||||||
scope
|
scope.get(id.as_str()).map(|index| &bindings[*index]),
|
||||||
.bindings
|
|
||||||
.get(&id.as_str())
|
|
||||||
.map(|index| &bindings[*index]),
|
|
||||||
None | Some(Binding {
|
None | Some(Binding {
|
||||||
kind: BindingKind::Builtin,
|
kind: BindingKind::Builtin,
|
||||||
..
|
..
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use std::ops::{Deref, Index, IndexMut};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use nohash_hasher::{BuildNoHashHasher, IntMap};
|
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 ruff_python_stdlib::typing::TYPING_EXTENSIONS;
|
||||||
|
|
||||||
use crate::helpers::{collect_call_path, from_relative_import, Exceptions};
|
use crate::helpers::{collect_call_path, from_relative_import, Exceptions};
|
||||||
use crate::types::{
|
use crate::scope::{
|
||||||
Binding, BindingId, BindingKind, CallPath, ExecutionContext, RefEquality, Scope, ScopeId,
|
Binding, BindingId, BindingKind, Bindings, ExecutionContext, Scope, ScopeId, ScopeKind,
|
||||||
ScopeKind,
|
ScopeStack, Scopes,
|
||||||
};
|
};
|
||||||
|
use crate::types::{CallPath, RefEquality};
|
||||||
use crate::visibility::{module_visibility, Modifier, VisibleScope};
|
use crate::visibility::{module_visibility, Modifier, VisibleScope};
|
||||||
|
|
||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
|
@ -125,7 +125,7 @@ impl<'a> Context<'a> {
|
||||||
/// Return the current `Binding` for a given `name`.
|
/// Return the current `Binding` for a given `name`.
|
||||||
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
|
pub fn find_binding(&self, member: &str) -> Option<&Binding> {
|
||||||
self.scopes()
|
self.scopes()
|
||||||
.find_map(|scope| scope.bindings.get(member))
|
.find_map(|scope| scope.get(member))
|
||||||
.map(|index| &self.bindings[*index])
|
.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::context::Context;
|
||||||
use crate::helpers::{map_callable, to_call_path};
|
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 CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"];
|
||||||
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")];
|
||||||
|
|
|
@ -15,8 +15,9 @@ use smallvec::{smallvec, SmallVec};
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::newlines::StrExt;
|
use crate::newlines::StrExt;
|
||||||
|
use crate::scope::{Binding, BindingKind};
|
||||||
use crate::source_code::{Generator, Indexer, Locator, Stylist};
|
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;
|
||||||
use crate::visitor::Visitor;
|
use crate::visitor::Visitor;
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ pub mod logging;
|
||||||
pub mod newlines;
|
pub mod newlines;
|
||||||
pub mod operations;
|
pub mod operations;
|
||||||
pub mod relocate;
|
pub mod relocate;
|
||||||
|
pub mod scope;
|
||||||
pub mod source_code;
|
pub mod source_code;
|
||||||
pub mod str;
|
pub mod str;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use rustpython_parser::{lexer, Mode, Tok};
|
||||||
|
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::helpers::any_over_expr;
|
use crate::helpers::any_over_expr;
|
||||||
use crate::types::{BindingKind, Scope};
|
use crate::scope::{BindingKind, Scope};
|
||||||
use crate::visitor;
|
use crate::visitor;
|
||||||
use crate::visitor::Visitor;
|
use crate::visitor::Visitor;
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ pub fn extract_all_names(
|
||||||
|
|
||||||
// Grab the existing bound __all__ values.
|
// Grab the existing bound __all__ values.
|
||||||
if let StmtKind::AugAssign { .. } = &stmt.node {
|
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 {
|
if let BindingKind::Export(existing) = &ctx.bindings[*index].kind {
|
||||||
names.extend_from_slice(existing);
|
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 std::ops::Deref;
|
||||||
|
|
||||||
use rustc_hash::FxHashMap;
|
use rustpython_parser::ast::{Expr, Located, Location, Stmt};
|
||||||
use rustpython_parser::ast::{Arguments, Expr, Keyword, Located, Location, Stmt};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum Node<'a> {
|
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)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct RefEquality<'a, T>(pub &'a T);
|
pub struct RefEquality<'a, T>(pub &'a T);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue