From 13b3c9a648665c9ccb0768485a638b71b4690fbc Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Fri, 17 Mar 2023 11:41:31 +0100 Subject: [PATCH] refactor: Move scope and binding types to `scope.rs` --- crates/ruff/src/checkers/ast/deferred.rs | 2 +- crates/ruff/src/checkers/ast/mod.rs | 11 +- .../rules/cached_instance_method.rs | 3 +- .../rules/private_member_access.rs | 3 +- .../flake8_simplify/rules/ast_unary_op.rs | 3 +- .../src/rules/flake8_type_checking/helpers.rs | 2 +- .../runtime_import_in_type_checking_block.rs | 2 +- .../rules/typing_only_runtime_import.rs | 2 +- .../rules/flake8_unused_arguments/rules.rs | 3 +- .../src/rules/pandas_vet/rules/check_attr.rs | 3 +- .../src/rules/pandas_vet/rules/check_call.rs | 3 +- .../pep8_naming/rules/dunder_function_name.rs | 2 +- ...id_first_argument_name_for_class_method.rs | 3 +- .../invalid_first_argument_name_for_method.rs | 3 +- .../pycodestyle/rules/lambda_assignment.rs | 3 +- .../pyflakes/rules/return_outside_function.rs | 3 +- .../rules/pyflakes/rules/undefined_export.rs | 3 +- .../rules/pyflakes/rules/undefined_local.rs | 3 +- .../rules/pyflakes/rules/unused_annotation.rs | 2 +- .../rules/pyflakes/rules/unused_variable.rs | 3 +- .../pyflakes/rules/yield_outside_function.rs | 3 +- crates/ruff/src/rules/pylint/helpers.rs | 2 +- .../rules/pylint/rules/await_outside_async.rs | 3 +- .../pylint/rules/consider_using_sys_exit.rs | 3 +- .../rules/used_prior_global_declaration.rs | 3 +- .../rules/super_call_with_parameters.rs | 3 +- .../rules/useless_object_inheritance.rs | 4 +- crates/ruff_python_ast/src/context.rs | 138 +------ crates/ruff_python_ast/src/function_type.rs | 2 +- crates/ruff_python_ast/src/helpers.rs | 3 +- crates/ruff_python_ast/src/lib.rs | 1 + crates/ruff_python_ast/src/operations.rs | 2 +- crates/ruff_python_ast/src/scope.rs | 391 ++++++++++++++++++ crates/ruff_python_ast/src/types.rs | 261 +----------- 34 files changed, 451 insertions(+), 430 deletions(-) create mode 100644 crates/ruff_python_ast/src/scope.rs diff --git a/crates/ruff/src/checkers/ast/deferred.rs b/crates/ruff/src/checkers/ast/deferred.rs index ccef124bd32823..169d9b347b3956 100644 --- a/crates/ruff/src/checkers/ast/deferred.rs +++ b/crates/ruff/src/checkers/ast/deferred.rs @@ -1,4 +1,4 @@ -use ruff_python_ast::context::ScopeStack; +use ruff_python_ast::scope::ScopeStack; use rustpython_parser::ast::{Expr, Stmt}; use ruff_python_ast::types::Range; diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 0083fa061dc09e..5fec862c84ee9d 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -14,17 +14,18 @@ use rustpython_parser::ast::{ }; use ruff_diagnostics::Diagnostic; -use ruff_python_ast::context::{Context, ScopeStack}; +use ruff_python_ast::context::Context; use ruff_python_ast::helpers::{ binding_range, extract_handled_exceptions, to_module_path, Exceptions, }; use ruff_python_ast::operations::{extract_all_names, AllNamesFlags}; use ruff_python_ast::relocate::relocate_expr; -use ruff_python_ast::source_code::{Indexer, Locator, Stylist}; -use ruff_python_ast::types::{ - Binding, BindingId, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Node, Range, - RefEquality, Scope, ScopeId, ScopeKind, +use ruff_python_ast::scope::{ + Binding, BindingId, BindingKind, ClassDef, ExecutionContext, FunctionDef, Lambda, Scope, + ScopeId, ScopeKind, ScopeStack, }; +use ruff_python_ast::source_code::{Indexer, Locator, Stylist}; +use ruff_python_ast::types::{Node, Range, RefEquality}; use ruff_python_ast::typing::{match_annotated_subscript, Callable, SubscriptKind}; use ruff_python_ast::visitor::{walk_excepthandler, walk_pattern, Visitor}; use ruff_python_ast::{ diff --git a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs index 6aea88d051ebcf..e849ef94ce8c6e 100644 --- a/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs +++ b/crates/ruff/src/rules/flake8_bugbear/rules/cached_instance_method.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs index 3154fcdefc0fbb..9d1300f75ee246 100644 --- a/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs +++ b/crates/ruff/src/rules/flake8_self/rules/private_member_access.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::collect_call_path; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs b/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs index b6c9989ed932aa..5913d9bf068b85 100644 --- a/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs +++ b/crates/ruff/src/rules/flake8_simplify/rules/ast_unary_op.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Cmpop, Expr, ExprKind, Stmt, StmtKind, Unaryop}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::{create_expr, unparse_expr}; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index 6b520fee7c7690..a03950d3d0b248 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -3,7 +3,7 @@ use rustpython_parser::ast::{Constant, Expr, ExprKind}; use ruff_python_ast::context::Context; use ruff_python_ast::helpers::{map_callable, to_call_path}; -use ruff_python_ast::types::{Binding, BindingKind, ExecutionContext, ScopeKind}; +use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext, ScopeKind}; /// Return `true` if [`Expr`] is a guard for a type-checking block. pub fn is_type_checking_block(context: &Context, test: &Expr) -> bool { diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs b/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs index 12ee0a25601935..27422dc70bcbf3 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/runtime_import_in_type_checking_block.rs @@ -1,6 +1,6 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Binding, BindingKind, ExecutionContext}; +use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext}; #[violation] pub struct RuntimeImportInTypeCheckingBlock { diff --git a/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs b/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs index c376b4c2e3e888..b15642a275a9f5 100644 --- a/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs +++ b/crates/ruff/src/rules/flake8_type_checking/rules/typing_only_runtime_import.rs @@ -2,7 +2,7 @@ use std::path::Path; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Binding, BindingKind, ExecutionContext}; +use ruff_python_ast::scope::{Binding, BindingKind, ExecutionContext}; use crate::rules::isort::{categorize, ImportType}; use crate::settings::Settings; diff --git a/crates/ruff/src/rules/flake8_unused_arguments/rules.rs b/crates/ruff/src/rules/flake8_unused_arguments/rules.rs index c4f94eacfd045b..9285707c924b7b 100644 --- a/crates/ruff/src/rules/flake8_unused_arguments/rules.rs +++ b/crates/ruff/src/rules/flake8_unused_arguments/rules.rs @@ -6,10 +6,9 @@ use rustpython_parser::ast::{Arg, Arguments}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::context::Bindings; use ruff_python_ast::function_type; use ruff_python_ast::function_type::FunctionType; -use ruff_python_ast::types::{BindingId, FunctionDef, Lambda, Scope, ScopeKind}; +use ruff_python_ast::scope::{BindingId, Bindings, FunctionDef, Lambda, Scope, ScopeKind}; use ruff_python_ast::visibility; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs b/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs index edfff77903a80e..fed2f5638da274 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/check_attr.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{BindingKind, Range}; +use ruff_python_ast::scope::BindingKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::Rule; diff --git a/crates/ruff/src/rules/pandas_vet/rules/check_call.rs b/crates/ruff/src/rules/pandas_vet/rules/check_call.rs index d74aaba1574d0a..0e894a4d948d64 100644 --- a/crates/ruff/src/rules/pandas_vet/rules/check_call.rs +++ b/crates/ruff/src/rules/pandas_vet/rules/check_call.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::Violation; use ruff_diagnostics::{Diagnostic, DiagnosticKind}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{BindingKind, Range}; +use ruff_python_ast::scope::BindingKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::Rule; diff --git a/crates/ruff/src/rules/pep8_naming/rules/dunder_function_name.rs b/crates/ruff/src/rules/pep8_naming/rules/dunder_function_name.rs index fa9b9d4319dd8b..8b94be17453bf3 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/dunder_function_name.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/dunder_function_name.rs @@ -3,8 +3,8 @@ use rustpython_parser::ast::Stmt; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::identifier_range; +use ruff_python_ast::scope::{Scope, ScopeKind}; use ruff_python_ast::source_code::Locator; -use ruff_python_ast::types::{Scope, ScopeKind}; /// ## What it does /// Checks for functions with "dunder" names (that is, names with two diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs index 233c22c55dec68..19bce277120c94 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_class_method.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Arguments, Expr}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::function_type; -use ruff_python_ast::types::{Range, Scope}; +use ruff_python_ast::scope::Scope; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs index b7658e419c6af8..f090d8b1cd0ffc 100644 --- a/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs +++ b/crates/ruff/src/rules/pep8_naming/rules/invalid_first_argument_name_for_method.rs @@ -3,7 +3,8 @@ use rustpython_parser::ast::{Arguments, Expr}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::function_type; -use ruff_python_ast::types::{Range, Scope}; +use ruff_python_ast::scope::Scope; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs index 10cda5f65c9e71..3343ac2524eb5d 100644 --- a/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs +++ b/crates/ruff/src/rules/pycodestyle/rules/lambda_assignment.rs @@ -4,8 +4,9 @@ use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::{match_leading_content, match_trailing_content, unparse_stmt}; use ruff_python_ast::newlines::StrExt; +use ruff_python_ast::scope::ScopeKind; use ruff_python_ast::source_code::Stylist; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::types::Range; use ruff_python_ast::whitespace::leading_space; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs b/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs index 2ec768426a9f43..d5688ac43c3bbc 100644 --- a/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs +++ b/crates/ruff/src/rules/pyflakes/rules/return_outside_function.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::Stmt; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyflakes/rules/undefined_export.rs b/crates/ruff/src/rules/pyflakes/rules/undefined_export.rs index 6a22cf24bad75f..29e6a9a12634e9 100644 --- a/crates/ruff/src/rules/pyflakes/rules/undefined_export.rs +++ b/crates/ruff/src/rules/pyflakes/rules/undefined_export.rs @@ -2,7 +2,8 @@ use std::path::Path; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Range, Scope}; +use ruff_python_ast::scope::Scope; +use ruff_python_ast::types::Range; #[violation] pub struct UndefinedExport { diff --git a/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs b/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs index e63af153882282..454c6101f15fbd 100644 --- a/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs +++ b/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs @@ -2,8 +2,7 @@ use std::string::ToString; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::context::Bindings; -use ruff_python_ast::types::{Scope, ScopeKind}; +use ruff_python_ast::scope::{Bindings, Scope, ScopeKind}; #[violation] pub struct UndefinedLocal { diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs b/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs index f7a994b7e76ed8..ab7b83bbf91ce7 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_annotation.rs @@ -1,6 +1,6 @@ use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::ScopeId; +use ruff_python_ast::scope::ScopeId; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs index 109b875ca83f7f..51390903655f7d 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_variable.rs @@ -6,8 +6,9 @@ use rustpython_parser::{lexer, Mode, Tok}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic, Fix}; use ruff_macros::{derive_message_formats, violation}; use ruff_python_ast::helpers::contains_effect; +use ruff_python_ast::scope::{ScopeId, ScopeKind}; use ruff_python_ast::source_code::Locator; -use ruff_python_ast::types::{Range, RefEquality, ScopeId, ScopeKind}; +use ruff_python_ast::types::{Range, RefEquality}; use crate::autofix::helpers::delete_stmt; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs b/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs index 96133bc936f52a..27cc3539eb1f4d 100644 --- a/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs +++ b/crates/ruff/src/rules/pyflakes/rules/yield_outside_function.rs @@ -4,7 +4,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pylint/helpers.rs b/crates/ruff/src/rules/pylint/helpers.rs index e1fb0b413a6bd1..d083a42b04bbef 100644 --- a/crates/ruff/src/rules/pylint/helpers.rs +++ b/crates/ruff/src/rules/pylint/helpers.rs @@ -1,6 +1,6 @@ use ruff_python_ast::function_type; use ruff_python_ast::function_type::FunctionType; -use ruff_python_ast::types::{FunctionDef, ScopeKind}; +use ruff_python_ast::scope::{FunctionDef, ScopeKind}; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pylint/rules/await_outside_async.rs b/crates/ruff/src/rules/pylint/rules/await_outside_async.rs index 4c1728ad43d879..73e72135d48873 100644 --- a/crates/ruff/src/rules/pylint/rules/await_outside_async.rs +++ b/crates/ruff/src/rules/pylint/rules/await_outside_async.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::Expr; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{FunctionDef, Range, ScopeKind}; +use ruff_python_ast::scope::{FunctionDef, ScopeKind}; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs b/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs index 1822a9dc95ae81..76a8d6ee5fe15c 100644 --- a/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs +++ b/crates/ruff/src/rules/pylint/rules/consider_using_sys_exit.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind}; use ruff_diagnostics::{AutofixKind, Availability, Diagnostic, Fix, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{BindingKind, Range}; +use ruff_python_ast::scope::BindingKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs b/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs index 46beebe2d0211e..e3bb759db8af5c 100644 --- a/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs +++ b/crates/ruff/src/rules/pylint/rules/used_prior_global_declaration.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::Expr; use ruff_diagnostics::{Diagnostic, Violation}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; diff --git a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs index 4fdab4b1786763..29c98812c8bb95 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/super_call_with_parameters.rs @@ -2,7 +2,8 @@ use rustpython_parser::ast::{ArgData, Expr, ExprKind, Stmt, StmtKind}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::types::{Range, ScopeKind}; +use ruff_python_ast::scope::ScopeKind; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs index e2bf86f1c45d19..68381a17ae21c9 100644 --- a/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs +++ b/crates/ruff/src/rules/pyupgrade/rules/useless_object_inheritance.rs @@ -2,8 +2,8 @@ use rustpython_parser::ast::{Expr, ExprKind, Keyword, Stmt}; use ruff_diagnostics::{AlwaysAutofixableViolation, Diagnostic}; use ruff_macros::{derive_message_formats, violation}; -use ruff_python_ast::context::Bindings; -use ruff_python_ast::types::{Binding, BindingKind, Range, Scope}; +use ruff_python_ast::scope::{Binding, BindingKind, Bindings, Scope}; +use ruff_python_ast::types::Range; use crate::checkers::ast::Checker; use crate::registry::AsRule; diff --git a/crates/ruff_python_ast/src/context.rs b/crates/ruff_python_ast/src/context.rs index fc2e7327f4e5de..86cee8bf6c68e7 100644 --- a/crates/ruff_python_ast/src/context.rs +++ b/crates/ruff_python_ast/src/context.rs @@ -1,4 +1,3 @@ -use std::ops::{Deref, Index, IndexMut}; use std::path::Path; use nohash_hasher::{BuildNoHashHasher, IntMap}; @@ -10,10 +9,12 @@ use ruff_python_stdlib::path::is_python_stub_file; use ruff_python_stdlib::typing::TYPING_EXTENSIONS; use crate::helpers::{collect_call_path, from_relative_import, Exceptions}; -use crate::types::{ - Binding, BindingId, BindingKind, CallPath, ExecutionContext, RefEquality, Scope, ScopeId, - ScopeKind, +use crate::scope::{ + Binding, BindingId, BindingKind, Bindings, ExecutionContext, Scope, ScopeId, ScopeKind, + ScopeStack, Scopes, }; +use crate::types::{CallPath, RefEquality}; + use crate::visibility::{module_visibility, Modifier, VisibleScope}; #[allow(clippy::struct_excessive_bools)] @@ -304,132 +305,3 @@ impl<'a> Context<'a> { } } } - -/// The scopes of a program indexed by [ScopeId] -#[derive(Debug)] -pub struct Scopes<'a>(Vec>); - -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::new(next_id, kind)); - next_id - } -} - -impl Default for Scopes<'_> { - fn default() -> Self { - Self(vec![Scope::new(ScopeId::global(), ScopeKind::Module)]) - } -} - -impl<'a> Index for Scopes<'a> { - type Output = Scope<'a>; - - fn index(&self, index: ScopeId) -> &Self::Output { - &self.0[usize::from(index)] - } -} - -impl<'a> IndexMut 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); - -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 { - self.0.pop() - } - - /// Returns the id of the top-most - pub fn top(&self) -> Option { - self.0.last().copied() - } - - /// Returns an iterator from the current scope to the top scope (reverse iterator) - pub fn iter(&self) -> std::iter::Rev> { - 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>); - -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 for Bindings<'a> { - type Output = Binding<'a>; - - fn index(&self, index: BindingId) -> &Self::Output { - &self.0[usize::from(index)] - } -} - -impl<'a> IndexMut 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> for Bindings<'a> { - fn from_iter>>(iter: T) -> Self { - Self(Vec::from_iter(iter)) - } -} diff --git a/crates/ruff_python_ast/src/function_type.rs b/crates/ruff_python_ast/src/function_type.rs index d19b0882f3d42d..79c80f4c327f88 100644 --- a/crates/ruff_python_ast/src/function_type.rs +++ b/crates/ruff_python_ast/src/function_type.rs @@ -2,7 +2,7 @@ use rustpython_parser::ast::Expr; use crate::context::Context; use crate::helpers::{map_callable, to_call_path}; -use crate::types::{Scope, ScopeKind}; +use crate::scope::{Scope, ScopeKind}; const CLASS_METHODS: [&str; 3] = ["__new__", "__init_subclass__", "__class_getitem__"]; const METACLASS_BASES: [(&str, &str); 2] = [("", "type"), ("abc", "ABCMeta")]; diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 9070609d821aad..0c399fe6a72f06 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -15,8 +15,9 @@ use smallvec::{smallvec, SmallVec}; use crate::context::Context; use crate::newlines::StrExt; +use crate::scope::{Binding, BindingKind}; use crate::source_code::{Generator, Indexer, Locator, Stylist}; -use crate::types::{Binding, BindingKind, CallPath, Range}; +use crate::types::{CallPath, Range}; use crate::visitor; use crate::visitor::Visitor; diff --git a/crates/ruff_python_ast/src/lib.rs b/crates/ruff_python_ast/src/lib.rs index 2856716fbb3616..afb3a925586f4c 100644 --- a/crates/ruff_python_ast/src/lib.rs +++ b/crates/ruff_python_ast/src/lib.rs @@ -9,6 +9,7 @@ pub mod logging; pub mod newlines; pub mod operations; pub mod relocate; +pub mod scope; pub mod source_code; pub mod str; pub mod types; diff --git a/crates/ruff_python_ast/src/operations.rs b/crates/ruff_python_ast/src/operations.rs index beb8437aaefe69..e0e83fd0c637e2 100644 --- a/crates/ruff_python_ast/src/operations.rs +++ b/crates/ruff_python_ast/src/operations.rs @@ -5,7 +5,7 @@ use rustpython_parser::{lexer, Mode, Tok}; use crate::context::Context; use crate::helpers::any_over_expr; -use crate::types::{BindingKind, Scope}; +use crate::scope::{BindingKind, Scope}; use crate::visitor; use crate::visitor::Visitor; diff --git a/crates/ruff_python_ast/src/scope.rs b/crates/ruff_python_ast/src/scope.rs new file mode 100644 index 00000000000000..21dbd1864519e7 --- /dev/null +++ b/crates/ruff_python_ast/src/scope.rs @@ -0,0 +1,391 @@ +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. + 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>, +} + +impl<'a> Scope<'a> { + pub fn new(id: ScopeId, kind: ScopeKind<'a>) -> Self { + Scope { + id, + kind, + import_starred: false, + uses_locals: false, + bindings: FxHashMap::default(), + rebounds: FxHashMap::default(), + } + } +} + +/// 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`: +/// A new scope requires a statement with a block body (and the right indention). That means, the upper bound of +/// scopes is defined by `u32::max / 8` (`if 1:\n x`) +#[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 for ScopeId { + type Error = TryFromIntError; + + fn try_from(value: usize) -> Result { + Ok(Self(u32::try_from(value)?)) + } +} + +impl From 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>); + +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::new(next_id, kind)); + next_id + } +} + +impl Default for Scopes<'_> { + fn default() -> Self { + Self(vec![Scope::new(ScopeId::global(), ScopeKind::Module)]) + } +} + +impl<'a> Index for Scopes<'a> { + type Output = Scope<'a>; + + fn index(&self, index: ScopeId) -> &Self::Output { + &self.0[usize::from(index)] + } +} + +impl<'a> IndexMut 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); + +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 { + self.0.pop() + } + + /// Returns the id of the top-most + pub fn top(&self) -> Option { + self.0.last().copied() + } + + /// Returns an iterator from the current scope to the top scope (reverse iterator) + pub fn iter(&self) -> std::iter::Rev> { + 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>, + /// 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 for usize { + fn from(value: BindingId) -> Self { + value.0 as usize + } +} + +impl TryFrom for BindingId { + type Error = TryFromIntError; + + fn try_from(value: usize) -> Result { + 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), + FutureImportation, + StarImportation(Option, Option), + 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>); + +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 for Bindings<'a> { + type Output = Binding<'a>; + + fn index(&self, index: BindingId) -> &Self::Output { + &self.0[usize::from(index)] + } +} + +impl<'a> IndexMut 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> for Bindings<'a> { + fn from_iter>>(iter: T) -> Self { + Self(Vec::from_iter(iter)) + } +} diff --git a/crates/ruff_python_ast/src/types.rs b/crates/ruff_python_ast/src/types.rs index 55f86581a07d24..19c020d7d7c7b8 100644 --- a/crates/ruff_python_ast/src/types.rs +++ b/crates/ruff_python_ast/src/types.rs @@ -1,8 +1,6 @@ -use std::num::TryFromIntError; use std::ops::Deref; -use rustc_hash::FxHashMap; -use rustpython_parser::ast::{Arguments, Expr, Keyword, Located, Location, Stmt}; +use rustpython_parser::ast::{Expr, Located, Location, Stmt}; #[derive(Clone)] pub enum Node<'a> { @@ -37,263 +35,6 @@ impl From<&Box>> 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`: -/// A new scope requires a statement with a block body (and the right indention). That means, the upper bound of -/// scopes is defined by `u32::max / 8` (`if 1:\n x`) -#[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 for ScopeId { - type Error = TryFromIntError; - - fn try_from(value: usize) -> Result { - Ok(Self(u32::try_from(value)?)) - } -} - -impl From 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>, -} - -impl<'a> Scope<'a> { - pub fn new(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), - FutureImportation, - StarImportation(Option, Option), - 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 for usize { - fn from(value: BindingId) -> Self { - value.0 as usize - } -} - -impl TryFrom for BindingId { - type Error = TryFromIntError; - - fn try_from(value: usize) -> Result { - 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>, - /// Tuple of (scope index, range) indicating the scope and range at which - /// the binding was last used in a runtime context. - pub runtime_usage: Option<(ScopeId, Range)>, - /// Tuple of (scope index, range) indicating the scope and range at which - /// the binding was last used in a typing-time context. - pub typing_usage: Option<(ScopeId, Range)>, - /// Tuple of (scope index, range) indicating the scope and range at which - /// the binding was last used in a synthetic context. This is used for - /// (e.g.) `__future__` imports, explicit re-exports, and other bindings - /// that should be considered used even if they're never referenced. - pub synthetic_usage: Option<(ScopeId, Range)>, -} - -#[derive(Copy, Debug, Clone)] -pub enum ExecutionContext { - Runtime, - Typing, -} - -impl<'a> Binding<'a> { - pub fn mark_used(&mut self, scope: ScopeId, range: Range, context: ExecutionContext) { - match context { - ExecutionContext::Runtime => self.runtime_usage = Some((scope, range)), - ExecutionContext::Typing => self.typing_usage = Some((scope, range)), - } - } - - pub const fn used(&self) -> bool { - self.runtime_usage.is_some() - || self.synthetic_usage.is_some() - || self.typing_usage.is_some() - } - - pub const fn is_definition(&self) -> bool { - matches!( - self.kind, - BindingKind::ClassDefinition - | BindingKind::FunctionDefinition - | BindingKind::Builtin - | BindingKind::FutureImportation - | BindingKind::StarImportation(..) - | BindingKind::Importation(..) - | BindingKind::FromImportation(..) - | BindingKind::SubmoduleImportation(..) - ) - } - - pub fn redefines(&self, existing: &'a Binding) -> bool { - match &self.kind { - BindingKind::Importation(.., full_name) => { - if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind { - return full_name == existing; - } - } - BindingKind::FromImportation(.., full_name) => { - if let BindingKind::SubmoduleImportation(.., existing) = &existing.kind { - return full_name == existing; - } - } - BindingKind::SubmoduleImportation(.., full_name) => match &existing.kind { - BindingKind::Importation(.., existing) - | BindingKind::SubmoduleImportation(.., existing) => { - return full_name == existing; - } - BindingKind::FromImportation(.., existing) => { - return full_name == existing; - } - _ => {} - }, - BindingKind::Annotation => { - return false; - } - BindingKind::FutureImportation => { - return false; - } - BindingKind::StarImportation(..) => { - return false; - } - _ => {} - } - existing.is_definition() - } -} - #[derive(Debug, Copy, Clone)] pub struct RefEquality<'a, T>(pub &'a T);