Skip to content

Commit

Permalink
Scope and Binding IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser committed Mar 17, 2023
1 parent 33d2457 commit f6283e6
Show file tree
Hide file tree
Showing 22 changed files with 400 additions and 250 deletions.
3 changes: 2 additions & 1 deletion crates/ruff/src/checkers/ast/deferred.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use ruff_python_ast::context::ScopeStack;
use rustpython_parser::ast::{Expr, Stmt};

use ruff_python_ast::types::Range;
Expand All @@ -7,7 +8,7 @@ use ruff_python_ast::visibility::{Visibility, VisibleScope};
use crate::checkers::ast::AnnotationContext;
use crate::docstrings::definition::Definition;

type Context<'a> = (Vec<usize>, Vec<RefEquality<'a, Stmt>>);
type Context<'a> = (ScopeStack, Vec<RefEquality<'a, Stmt>>);

/// A collection of AST nodes that are deferred for later analysis.
/// Used to, e.g., store functions, whose bodies shouldn't be analyzed until all
Expand Down
296 changes: 117 additions & 179 deletions crates/ruff/src/checkers/ast/mod.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ fn is_cache_func(checker: &Checker, expr: &Expr) -> bool {

/// B019
pub fn cached_instance_method(checker: &mut Checker, decorator_list: &[Expr]) {
if !matches!(checker.ctx.current_scope().kind, ScopeKind::Class(_)) {
if !matches!(checker.ctx.scope().kind, ScopeKind::Class(_)) {
return;
}
for decorator in decorator_list {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ pub fn unused_loop_control_variable(
if let Some(rename) = rename {
if certainty.into() && checker.patch(diagnostic.kind.rule()) {
// Find the `BindingKind::LoopVar` corresponding to the name.
let scope = checker.ctx.current_scope();
let scope = checker.ctx.scope();
let binding = scope
.bindings
.get(name)
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ pub fn negation_with_equal_op(checker: &mut Checker, expr: &Expr, op: &Unaryop,
}

// Avoid flagging issues in dunder implementations.
if let ScopeKind::Function(def) = &checker.ctx.current_scope().kind {
if let ScopeKind::Function(def) = &checker.ctx.scope().kind {
if DUNDER_METHODS.contains(&def.name) {
return;
}
Expand Down Expand Up @@ -144,7 +144,7 @@ pub fn negation_with_not_equal_op(
}

// Avoid flagging issues in dunder implementations.
if let ScopeKind::Function(def) = &checker.ctx.current_scope().kind {
if let ScopeKind::Function(def) = &checker.ctx.scope().kind {
if DUNDER_METHODS.contains(&def.name) {
return;
}
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff/src/rules/flake8_type_checking/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub fn runtime_evaluated(
}

fn runtime_evaluated_base_class(context: &Context, base_classes: &[String]) -> bool {
if let ScopeKind::Class(class_def) = &context.current_scope().kind {
if let ScopeKind::Class(class_def) = &context.scope().kind {
for base in class_def.bases.iter() {
if let Some(call_path) = context.resolve_call_path(base) {
if base_classes
Expand All @@ -87,7 +87,7 @@ fn runtime_evaluated_base_class(context: &Context, base_classes: &[String]) -> b
}

fn runtime_evaluated_decorators(context: &Context, decorators: &[String]) -> bool {
if let ScopeKind::Class(class_def) = &context.current_scope().kind {
if let ScopeKind::Class(class_def) = &context.scope().kind {
for decorator in class_def.decorator_list.iter() {
if let Some(call_path) = context.resolve_call_path(map_callable(decorator)) {
if decorators
Expand Down
17 changes: 9 additions & 8 deletions crates/ruff/src/rules/flake8_unused_arguments/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ 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::{Binding, FunctionDef, Lambda, Scope, ScopeKind};
use ruff_python_ast::types::{BindingId, FunctionDef, Lambda, Scope, ScopeKind};
use ruff_python_ast::visibility;

use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -85,8 +86,8 @@ impl Violation for UnusedLambdaArgument {
fn function(
argumentable: &Argumentable,
args: &Arguments,
values: &FxHashMap<&str, usize>,
bindings: &[Binding],
values: &FxHashMap<&str, BindingId>,
bindings: &Bindings,
dummy_variable_rgx: &Regex,
ignore_variadic_names: bool,
) -> Vec<Diagnostic> {
Expand All @@ -112,8 +113,8 @@ fn function(
fn method(
argumentable: &Argumentable,
args: &Arguments,
values: &FxHashMap<&str, usize>,
bindings: &[Binding],
values: &FxHashMap<&str, BindingId>,
bindings: &Bindings,
dummy_variable_rgx: &Regex,
ignore_variadic_names: bool,
) -> Vec<Diagnostic> {
Expand All @@ -139,8 +140,8 @@ fn method(
fn call<'a>(
argumentable: &Argumentable,
args: impl Iterator<Item = &'a Arg>,
values: &FxHashMap<&str, usize>,
bindings: &[Binding],
values: &FxHashMap<&str, BindingId>,
bindings: &Bindings,
dummy_variable_rgx: &Regex,
) -> Vec<Diagnostic> {
let mut diagnostics: Vec<Diagnostic> = vec![];
Expand Down Expand Up @@ -168,7 +169,7 @@ pub fn unused_arguments(
checker: &Checker,
parent: &Scope,
scope: &Scope,
bindings: &[Binding],
bindings: &Bindings,
) -> Vec<Diagnostic> {
match &scope.kind {
ScopeKind::Function(FunctionDef {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub fn lambda_assignment(checker: &mut Checker, target: &Expr, value: &Expr, stm
// package like dataclasses, which wouldn't consider the
// rewritten function definition to be equivalent.
// See https://github.com/charliermarsh/ruff/issues/3046
let fixable = !matches!(checker.ctx.current_scope().kind, ScopeKind::Class(_));
let fixable = !matches!(checker.ctx.scope().kind, ScopeKind::Class(_));

let mut diagnostic = Diagnostic::new(
LambdaAssignment {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl Violation for ReturnOutsideFunction {
}

pub fn return_outside_function(checker: &mut Checker, stmt: &Stmt) {
if let Some(&index) = checker.ctx.scope_stack.last() {
if let Some(index) = checker.ctx.scope_stack.top() {
if matches!(
checker.ctx.scopes[index].kind,
ScopeKind::Class(_) | ScopeKind::Module
Expand Down
5 changes: 3 additions & 2 deletions crates/ruff/src/rules/pyflakes/rules/undefined_local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ use std::string::ToString;

use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::{Binding, Scope, ScopeKind};
use ruff_python_ast::context::Bindings;
use ruff_python_ast::types::{Scope, ScopeKind};

#[violation]
pub struct UndefinedLocal {
Expand All @@ -18,7 +19,7 @@ impl Violation for UndefinedLocal {
}

/// F821
pub fn undefined_local(name: &str, scopes: &[&Scope], bindings: &[Binding]) -> Option<Diagnostic> {
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) {
for scope in scopes.iter().rev().skip(1) {
Expand Down
3 changes: 2 additions & 1 deletion crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use ruff_diagnostics::{Diagnostic, Violation};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::types::ScopeId;

use crate::checkers::ast::Checker;

Expand All @@ -17,7 +18,7 @@ impl Violation for UnusedAnnotation {
}

/// F842
pub fn unused_annotation(checker: &mut Checker, scope: usize) {
pub fn unused_annotation(checker: &mut Checker, scope: ScopeId) {
let scope = &checker.ctx.scopes[scope];
for (name, binding) in scope
.bindings
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff/src/rules/pyflakes/rules/unused_variable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix};
use ruff_macros::{derive_message_formats, violation};
use ruff_python_ast::helpers::contains_effect;
use ruff_python_ast::source_code::Locator;
use ruff_python_ast::types::{Range, RefEquality, ScopeKind};
use ruff_python_ast::types::{Range, RefEquality, ScopeId, ScopeKind};

use crate::autofix::helpers::delete_stmt;
use crate::checkers::ast::Checker;
Expand Down Expand Up @@ -312,7 +312,7 @@ fn remove_unused_variable(
}

/// F841
pub fn unused_variable(checker: &mut Checker, scope: usize) {
pub fn unused_variable(checker: &mut Checker, scope: ScopeId) {
let scope = &checker.ctx.scopes[scope];
if scope.uses_locals && matches!(scope.kind, ScopeKind::Function(..)) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ impl Violation for YieldOutsideFunction {

pub fn yield_outside_function(checker: &mut Checker, expr: &Expr) {
if matches!(
checker.ctx.current_scope().kind,
checker.ctx.scope().kind,
ScopeKind::Class(_) | ScopeKind::Module
) {
let keyword = match expr.node {
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff/src/rules/pylint/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ruff_python_ast::types::{FunctionDef, ScopeKind};
use crate::checkers::ast::Checker;

pub fn in_dunder_init(checker: &Checker) -> bool {
let scope = checker.ctx.current_scope();
let scope = checker.ctx.scope();
let ScopeKind::Function(FunctionDef {
name,
decorator_list,
Expand All @@ -16,7 +16,7 @@ pub fn in_dunder_init(checker: &Checker) -> bool {
if name != "__init__" {
return false;
}
let Some(parent) = checker.ctx.current_scope_parent() else {
let Some(parent) = checker.ctx.parent_scope() else {
return false;
};

Expand Down
2 changes: 1 addition & 1 deletion crates/ruff/src/rules/pylint/rules/await_outside_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ impl Violation for AwaitOutsideAsync {
pub fn await_outside_async(checker: &mut Checker, expr: &Expr) {
if !checker
.ctx
.current_scopes()
.scopes()
.find_map(|scope| {
if let ScopeKind::Function(FunctionDef { async_, .. }) = &scope.kind {
Some(*async_)
Expand Down
4 changes: 2 additions & 2 deletions crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ impl Violation for ConsiderUsingSysExit {
/// Return `true` if the `module` was imported using a star import (e.g., `from
/// sys import *`).
fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
checker.ctx.current_scopes().any(|scope| {
checker.ctx.scopes().any(|scope| {
scope.bindings.values().any(|index| {
if let BindingKind::StarImportation(_, name) = &checker.ctx.bindings[*index].kind {
name.as_ref().map(|name| name == module).unwrap_or_default()
Expand All @@ -42,7 +42,7 @@ fn is_module_star_imported(checker: &Checker, module: &str) -> bool {
/// Return the appropriate `sys.exit` reference based on the current set of
/// imports, or `None` is `sys.exit` hasn't been imported.
fn get_member_import_name_alias(checker: &Checker, module: &str, member: &str) -> Option<String> {
checker.ctx.current_scopes().find_map(|scope| {
checker.ctx.scopes().find_map(|scope| {
scope
.bindings
.values()
Expand Down
2 changes: 1 addition & 1 deletion crates/ruff/src/rules/pylint/rules/global_statement.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ impl Violation for GlobalStatement {

/// PLW0603
pub fn global_statement(checker: &mut Checker, name: &str) {
let scope = checker.ctx.current_scope();
let scope = checker.ctx.scope();
if let Some(index) = scope.bindings.get(name) {
let binding = &checker.ctx.bindings[*index];
if binding.kind.is_global() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl Violation for UsedPriorGlobalDeclaration {
}
/// PLE0118
pub fn used_prior_global_declaration(checker: &mut Checker, name: &str, expr: &Expr) {
let globals = match &checker.ctx.current_scope().kind {
let globals = match &checker.ctx.scope().kind {
ScopeKind::Class(class_def) => &class_def.globals,
ScopeKind::Function(function_def) => &function_def.globals,
_ => return,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub fn super_call_with_parameters(checker: &mut Checker, expr: &Expr, func: &Exp
if !is_super_call_with_arguments(func, args) {
return;
}
let scope = checker.ctx.current_scope();
let scope = checker.ctx.scope();
let parents: Vec<&Stmt> = checker.ctx.parents.iter().map(Into::into).collect();

// Check: are we in a Function scope?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ 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 crate::checkers::ast::Checker;
Expand All @@ -26,7 +27,7 @@ impl AlwaysAutofixableViolation for UselessObjectInheritance {
}
}

fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &[Binding]) -> Option<Diagnostic> {
fn rule(name: &str, bases: &[Expr], scope: &Scope, bindings: &Bindings) -> Option<Diagnostic> {
for expr in bases {
let ExprKind::Name { id, .. } = &expr.node else {
continue;
Expand Down Expand Up @@ -65,7 +66,7 @@ pub fn useless_object_inheritance(
bases: &[Expr],
keywords: &[Keyword],
) {
let Some(mut diagnostic) = rule(name, bases, checker.ctx.current_scope(), &checker.ctx.bindings) else {
let Some(mut diagnostic) = rule(name, bases, checker.ctx.scope(), &checker.ctx.bindings) else {
return;
};
if checker.patch(diagnostic.kind.rule()) {
Expand Down

0 comments on commit f6283e6

Please sign in to comment.