diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index e0e912d776..43b788d0b4 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -29,7 +29,7 @@ use ruff_python_semantic::binding::{ use ruff_python_semantic::context::ExecutionContext; use ruff_python_semantic::definition::{ContextualizedDefinition, Module, ModuleKind}; use ruff_python_semantic::globals::Globals; -use ruff_python_semantic::model::{ResolvedReference, SemanticModel, SemanticModelFlags}; +use ruff_python_semantic::model::{ResolvedRead, SemanticModel, SemanticModelFlags}; use ruff_python_semantic::scope::{Scope, ScopeId, ScopeKind}; use ruff_python_stdlib::builtins::{BUILTINS, MAGIC_GLOBALS}; use ruff_python_stdlib::path::is_python_stub_file; @@ -4506,11 +4506,11 @@ impl<'a> Checker<'a> { let Expr::Name(ast::ExprName { id, .. } )= expr else { return; }; - match self.semantic_model.resolve_reference(id, expr.range()) { - ResolvedReference::Resolved(..) | ResolvedReference::ImplicitGlobal => { + match self.semantic_model.resolve_read(id, expr.range()) { + ResolvedRead::Resolved(..) | ResolvedRead::ImplicitGlobal => { // Nothing to do. } - ResolvedReference::StarImport => { + ResolvedRead::StarImport => { // F405 if self.enabled(Rule::UndefinedLocalWithImportStarUsage) { let sources: Vec = self @@ -4533,7 +4533,7 @@ impl<'a> Checker<'a> { )); } } - ResolvedReference::NotFound => { + ResolvedRead::NotFound => { // F821 if self.enabled(Rule::UndefinedName) { // Allow __path__. @@ -5040,13 +5040,12 @@ impl<'a> Checker<'a> { // the bindings are in different scopes. if self.enabled(Rule::RedefinedWhileUnused) { for (name, binding_id) in scope.bindings() { - if let Some(shadowed) = self.semantic_model.shadowed_binding(binding_id) { + if let Some(shadowed_id) = self.semantic_model.shadowed_binding(binding_id) { + let shadowed = &self.semantic_model.bindings[shadowed_id]; if shadowed.is_used() { continue; } - let binding = &self.semantic_model.bindings[binding_id]; - #[allow(deprecated)] let line = self.locator.compute_line_index( shadowed @@ -5054,6 +5053,7 @@ impl<'a> Checker<'a> { .start(), ); + let binding = &self.semantic_model.bindings[binding_id]; let mut diagnostic = Diagnostic::new( pyflakes::rules::RedefinedWhileUnused { name: (*name).to_string(), diff --git a/crates/ruff/src/rules/flake8_type_checking/helpers.rs b/crates/ruff/src/rules/flake8_type_checking/helpers.rs index 53f8f1fa93..b8d399aa01 100644 --- a/crates/ruff/src/rules/flake8_type_checking/helpers.rs +++ b/crates/ruff/src/rules/flake8_type_checking/helpers.rs @@ -1,9 +1,10 @@ +use rustpython_parser::ast; + use ruff_python_ast::call_path::from_qualified_name; use ruff_python_ast::helpers::map_callable; use ruff_python_semantic::binding::{Binding, BindingKind}; use ruff_python_semantic::model::SemanticModel; use ruff_python_semantic::scope::ScopeKind; -use rustpython_parser::ast; pub(crate) fn is_valid_runtime_import(semantic_model: &SemanticModel, binding: &Binding) -> bool { if matches!( @@ -15,8 +16,7 @@ pub(crate) fn is_valid_runtime_import(semantic_model: &SemanticModel, binding: & binding.context.is_runtime() && binding.references().any(|reference_id| { semantic_model - .references - .resolve(reference_id) + .reference(reference_id) .context() .is_runtime() }) 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 a9e0e322ef..c4edc8b8d5 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 @@ -89,8 +89,7 @@ pub(crate) fn runtime_import_in_type_checking_block( && binding.references().any(|reference_id| { checker .semantic_model() - .references - .resolve(reference_id) + .reference(reference_id) .context() .is_runtime() }) @@ -204,8 +203,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result .map(|Import { reference_id, .. }| { checker .semantic_model() - .references - .resolve(*reference_id) + .reference(*reference_id) .range() .start() }) 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 c5c1d14269..c509e252db 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 @@ -234,8 +234,7 @@ pub(crate) fn typing_only_runtime_import( && binding.references().all(|reference_id| { checker .semantic_model() - .references - .resolve(reference_id) + .reference(reference_id) .context() .is_typing() }) @@ -430,8 +429,7 @@ fn fix_imports(checker: &Checker, stmt_id: NodeId, imports: &[Import]) -> Result .map(|Import { reference_id, .. }| { checker .semantic_model() - .references - .resolve(*reference_id) + .reference(*reference_id) .range() .start() }) diff --git a/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs b/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs index 20c8fdef62..312e5a1d77 100644 --- a/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs +++ b/crates/ruff/src/rules/pyflakes/rules/undefined_local.rs @@ -72,7 +72,7 @@ pub(crate) fn undefined_local(checker: &mut Checker, name: &str) { { // And has already been accessed in the current scope... if let Some(range) = binding.references().find_map(|reference_id| { - let reference = checker.semantic_model().references.resolve(reference_id); + let reference = checker.semantic_model().reference(reference_id); if checker .semantic_model() .is_current_scope(reference.scope_id()) diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index 96d0628c9a..0edd9451ca 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -20,7 +20,7 @@ use crate::context::ExecutionContext; use crate::definition::{Definition, DefinitionId, Definitions, Member, Module}; use crate::globals::{Globals, GlobalsArena}; use crate::node::{NodeId, Nodes}; -use crate::reference::References; +use crate::reference::{Reference, ReferenceId, References}; use crate::scope::{Scope, ScopeId, ScopeKind, Scopes}; /// A semantic model for a Python module, to enable querying the module's semantic information. @@ -43,7 +43,7 @@ pub struct SemanticModel<'a> { // A stack of all bindings created in any scope, at any point in execution. pub bindings: Bindings<'a>, // Stack of all references created in any scope, at any point in execution. - pub references: References, + references: References, // Arena of global bindings. globals: GlobalsArena<'a>, // Map from binding index to indexes of bindings that shadow it in other scopes. @@ -152,14 +152,11 @@ impl<'a> SemanticModel<'a> { .map(|binding_id| &self.bindings[binding_id]) } - /// Return the [`Binding`] that the given [`BindingId`] shadows, if any. + /// Return the [`BindingId`] that the given [`BindingId`] shadows, if any. /// /// Note that this will only return bindings that are shadowed by a binding in a parent scope. - pub fn shadowed_binding(&self, binding_id: BindingId) -> Option<&Binding> { - self.shadowed_bindings - .get(&binding_id) - .copied() - .map(|id| &self.bindings[id]) + pub fn shadowed_binding(&self, binding_id: BindingId) -> Option { + self.shadowed_bindings.get(&binding_id).copied() } /// Return `true` if `member` is bound as a builtin. @@ -174,8 +171,8 @@ impl<'a> SemanticModel<'a> { .map_or(true, |binding| binding.kind.is_builtin()) } - /// Resolve a reference to the given symbol. - pub fn resolve_reference(&mut self, symbol: &str, range: TextRange) -> ResolvedReference { + /// Resolve a read reference to `symbol` at `range`. + pub fn resolve_read(&mut self, symbol: &str, range: TextRange) -> ResolvedRead { // PEP 563 indicates that if a forward reference can be resolved in the module scope, we // should prefer it over local resolutions. if self.in_forward_reference() { @@ -193,7 +190,7 @@ impl<'a> SemanticModel<'a> { self.bindings[binding_id].references.push(reference_id); } - return ResolvedReference::Resolved(binding_id); + return ResolvedRead::Resolved(binding_id); } } @@ -210,7 +207,7 @@ impl<'a> SemanticModel<'a> { // print(__class__) // ``` if seen_function && matches!(symbol, "__class__") { - return ResolvedReference::ImplicitGlobal; + return ResolvedRead::ImplicitGlobal; } if index > 0 { continue; @@ -243,7 +240,7 @@ impl<'a> SemanticModel<'a> { continue; } - return ResolvedReference::Resolved(binding_id); + return ResolvedRead::Resolved(binding_id); } // Allow usages of `__module__` and `__qualname__` within class scopes, e.g.: @@ -263,7 +260,7 @@ impl<'a> SemanticModel<'a> { // ``` if index == 0 && scope.kind.is_class() { if matches!(symbol, "__module__" | "__qualname__") { - return ResolvedReference::ImplicitGlobal; + return ResolvedRead::ImplicitGlobal; } } @@ -272,9 +269,9 @@ impl<'a> SemanticModel<'a> { } if import_starred { - ResolvedReference::StarImport + ResolvedRead::StarImport } else { - ResolvedReference::NotFound + ResolvedRead::NotFound } } @@ -673,6 +670,11 @@ impl<'a> SemanticModel<'a> { self.bindings[binding_id].references.push(reference_id); } + /// Resolve a [`ReferenceId`]. + pub fn reference(&self, reference_id: ReferenceId) -> &Reference { + self.references.resolve(reference_id) + } + /// Return the [`ExecutionContext`] of the current scope. pub const fn execution_context(&self) -> ExecutionContext { if self.in_type_checking_block() @@ -1019,16 +1021,16 @@ pub struct Snapshot { } #[derive(Debug)] -pub enum ResolvedReference { - /// The reference is resolved to a specific binding. +pub enum ResolvedRead { + /// The read reference is resolved to a specific binding. Resolved(BindingId), - /// The reference is resolved to a context-specific, implicit global (e.g., `__class__` within - /// a class scope). + /// The read reference is resolved to a context-specific, implicit global (e.g., `__class__` + /// within a class scope). ImplicitGlobal, - /// The reference is unresolved, but at least one of the containing scopes contains a star + /// The read reference is unresolved, but at least one of the containing scopes contains a star /// import. StarImport, - /// The reference is definitively unresolved. + /// The read reference is definitively unresolved. NotFound, }