diff --git a/crates/ruff_linter/src/checkers/ast/mod.rs b/crates/ruff_linter/src/checkers/ast/mod.rs index feabe4aded..b95af879f7 100644 --- a/crates/ruff_linter/src/checkers/ast/mod.rs +++ b/crates/ruff_linter/src/checkers/ast/mod.rs @@ -43,6 +43,7 @@ use ruff_python_ast::helpers::{ collect_import_from_member, extract_handled_exceptions, is_docstring_stmt, to_module_path, }; use ruff_python_ast::identifier::Identifier; +use ruff_python_ast::name::QualifiedName; use ruff_python_ast::str::trailing_quote; use ruff_python_ast::visitor::{walk_except_handler, walk_f_string_element, walk_pattern, Visitor}; use ruff_python_ast::{helpers, str, visitor, PySourceType}; @@ -418,7 +419,7 @@ impl<'a> Visitor<'a> for Checker<'a> { self.semantic.add_module(module); if alias.asname.is_none() && alias.name.contains('.') { - let qualified_name: Box<[&str]> = alias.name.split('.').collect(); + let qualified_name = QualifiedName::imported(&alias.name); self.add_binding( module, alias.identifier(), @@ -439,7 +440,7 @@ impl<'a> Visitor<'a> for Checker<'a> { } let name = alias.asname.as_ref().unwrap_or(&alias.name); - let qualified_name: Box<[&str]> = alias.name.split('.').collect(); + let qualified_name = QualifiedName::imported(&alias.name); self.add_binding( name, alias.identifier(), @@ -503,8 +504,7 @@ impl<'a> Visitor<'a> for Checker<'a> { // Attempt to resolve any relative imports; but if we don't know the current // module path, or the relative import extends beyond the package root, // fallback to a literal representation (e.g., `[".", "foo"]`). - let qualified_name = collect_import_from_member(level, module, &alias.name) - .into_boxed_slice(); + let qualified_name = collect_import_from_member(level, module, &alias.name); self.add_binding( name, alias.identifier(), diff --git a/crates/ruff_linter/src/renamer.rs b/crates/ruff_linter/src/renamer.rs index 4fa303d462..8f4d560afe 100644 --- a/crates/ruff_linter/src/renamer.rs +++ b/crates/ruff_linter/src/renamer.rs @@ -232,7 +232,7 @@ impl Renamer { } BindingKind::SubmoduleImport(import) => { // Ex) Rename `import pandas.core` to `import pandas as pd`. - let module_name = import.qualified_name.first().unwrap(); + let module_name = import.qualified_name.segments().first().unwrap(); Some(Edit::range_replacement( format!("{module_name} as {target}"), binding.range(), diff --git a/crates/ruff_python_semantic/src/binding.rs b/crates/ruff_python_semantic/src/binding.rs index 10b4d13439..7c4d79b74e 100644 --- a/crates/ruff_python_semantic/src/binding.rs +++ b/crates/ruff_python_semantic/src/binding.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut}; use bitflags::bitflags; use ruff_index::{newtype_index, IndexSlice, IndexVec}; -use ruff_python_ast::name::format_qualified_name_segments; +use ruff_python_ast::name::{format_qualified_name_segments, QualifiedName}; use ruff_python_ast::Stmt; use ruff_source_file::Locator; use ruff_text_size::{Ranged, TextRange}; @@ -362,7 +362,7 @@ pub struct Import<'a> { /// The full name of the module being imported. /// Ex) Given `import foo`, `qualified_name` would be "foo". /// Ex) Given `import foo as bar`, `qualified_name` would be "foo". - pub qualified_name: Box<[&'a str]>, + pub qualified_name: QualifiedName<'a>, } /// A binding for a member imported from a module, keyed on the name to which the member is bound. @@ -373,7 +373,7 @@ pub struct FromImport<'a> { /// The full name of the member being imported. /// Ex) Given `from foo import bar`, `qualified_name` would be "foo.bar". /// Ex) Given `from foo import bar as baz`, `qualified_name` would be "foo.bar". - pub qualified_name: Box<[&'a str]>, + pub qualified_name: QualifiedName<'a>, } /// A binding for a submodule imported from a module, keyed on the name of the parent module. @@ -382,7 +382,7 @@ pub struct FromImport<'a> { pub struct SubmoduleImport<'a> { /// The full name of the submodule being imported. /// Ex) Given `import foo.bar`, `qualified_name` would be "foo.bar". - pub qualified_name: Box<[&'a str]>, + pub qualified_name: QualifiedName<'a>, } #[derive(Debug, Clone, is_macro::Is)] @@ -569,12 +569,12 @@ pub trait Imported<'a> { impl<'a> Imported<'a> for Import<'a> { /// For example, given `import foo`, returns `["foo"]`. fn call_path(&self) -> &[&'a str] { - self.qualified_name.as_ref() + self.qualified_name.segments() } /// For example, given `import foo`, returns `["foo"]`. fn module_name(&self) -> &[&'a str] { - &self.qualified_name[..1] + &self.qualified_name.segments()[..1] } /// For example, given `import foo`, returns `"foo"`. @@ -586,12 +586,12 @@ impl<'a> Imported<'a> for Import<'a> { impl<'a> Imported<'a> for SubmoduleImport<'a> { /// For example, given `import foo.bar`, returns `["foo", "bar"]`. fn call_path(&self) -> &[&'a str] { - self.qualified_name.as_ref() + self.qualified_name.segments() } /// For example, given `import foo.bar`, returns `["foo"]`. fn module_name(&self) -> &[&'a str] { - &self.qualified_name[..1] + &self.qualified_name.segments()[..1] } /// For example, given `import foo.bar`, returns `"foo.bar"`. @@ -603,17 +603,17 @@ impl<'a> Imported<'a> for SubmoduleImport<'a> { impl<'a> Imported<'a> for FromImport<'a> { /// For example, given `from foo import bar`, returns `["foo", "bar"]`. fn call_path(&self) -> &[&'a str] { - &self.qualified_name + self.qualified_name.segments() } /// For example, given `from foo import bar`, returns `["foo"]`. fn module_name(&self) -> &[&'a str] { - &self.qualified_name[..self.qualified_name.len() - 1] + &self.qualified_name.segments()[..self.qualified_name.segments().len() - 1] } /// For example, given `from foo import bar`, returns `"bar"`. fn member_name(&self) -> Cow<'a, str> { - Cow::Borrowed(self.qualified_name[self.qualified_name.len() - 1]) + Cow::Borrowed(self.qualified_name.segments()[self.qualified_name.segments().len() - 1]) } } diff --git a/crates/ruff_python_semantic/src/model.rs b/crates/ruff_python_semantic/src/model.rs index 3556fb28a9..6396f14927 100644 --- a/crates/ruff_python_semantic/src/model.rs +++ b/crates/ruff_python_semantic/src/model.rs @@ -25,6 +25,9 @@ use crate::reference::{ use crate::scope::{Scope, ScopeId, ScopeKind, Scopes}; use crate::Imported; +// pub trait Captures {} +// impl Captures for T {} + /// A semantic model for a Python module, to enable querying the module's semantic information. pub struct SemanticModel<'a> { typing_modules: &'a [String], @@ -692,8 +695,12 @@ impl<'a> SemanticModel<'a> { BindingKind::Import(Import { qualified_name }) => { let unqualified_name = UnqualifiedName::from_expr(value)?; let (_, tail) = unqualified_name.segments().split_first()?; - let resolved: QualifiedName = - qualified_name.iter().chain(tail.iter()).copied().collect(); + let resolved: QualifiedName = qualified_name + .segments() + .iter() + .chain(tail.iter()) + .copied() + .collect(); Some(resolved) } BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }) => { @@ -702,6 +709,7 @@ impl<'a> SemanticModel<'a> { Some( qualified_name + .segments() .iter() .take(1) .chain(tail.iter()) @@ -714,12 +722,18 @@ impl<'a> SemanticModel<'a> { let (_, tail) = value_name.segments().split_first()?; let resolved: QualifiedName = if qualified_name + .segments() .first() .map_or(false, |segment| *segment == ".") { - from_relative_import(self.module_path?, qualified_name, tail)? + from_relative_import(self.module_path?, qualified_name.segments(), tail)? } else { - qualified_name.iter().chain(tail.iter()).copied().collect() + qualified_name + .segments() + .iter() + .chain(tail.iter()) + .copied() + .collect() }; Some(resolved) } @@ -780,7 +794,7 @@ impl<'a> SemanticModel<'a> { // `import sys` -> `sys.exit` // `import sys as sys2` -> `sys2.exit` BindingKind::Import(Import { qualified_name }) => { - if qualified_name.as_ref() == module_path.as_slice() { + if qualified_name.segments() == module_path.as_slice() { if let Some(source) = binding.source { // Verify that `sys` isn't bound in an inner scope. if self @@ -803,7 +817,7 @@ impl<'a> SemanticModel<'a> { // `from os.path import join as join2` -> `join2` BindingKind::FromImport(FromImport { qualified_name }) => { if let Some((target_member, target_module)) = - qualified_name.split_last() + qualified_name.segments().split_last() { if target_module == module_path.as_slice() && target_member == &member @@ -831,7 +845,7 @@ impl<'a> SemanticModel<'a> { // Ex) Given `module="os.path"` and `object="join"`: // `import os.path ` -> `os.path.join` BindingKind::SubmoduleImport(SubmoduleImport { qualified_name }) => { - if qualified_name.starts_with(&module_path) { + if qualified_name.segments().starts_with(&module_path) { if let Some(source) = binding.source { // Verify that `os` isn't bound in an inner scope. if self @@ -959,6 +973,7 @@ impl<'a> SemanticModel<'a> { /// Returns an [`Iterator`] over the current statement hierarchy represented as [`NodeId`], /// from the current [`NodeId`] through to any parents. pub fn current_statement_ids(&self) -> impl Iterator + '_ { + // ) -> impl Iterator + Captures<(&'a (), &'_ ())> { self.node_id .iter() .flat_map(|id| self.nodes.ancestor_ids(*id)) @@ -1555,6 +1570,7 @@ impl<'a> SemanticModel<'a> { scope_id: ScopeId, binding_id: BindingId, ) -> impl Iterator + '_ { + // ) -> impl Iterator + Captures<(&'a (), &'_ ())> { let mut first = true; let mut binding_id = binding_id; std::iter::from_fn(move || {