From 43eb51b15af364e789c758574bb0aa8618a229e6 Mon Sep 17 00:00:00 2001 From: Charlie Marsh Date: Wed, 26 Jul 2023 14:17:50 -0400 Subject: [PATCH] Handle relative --- crates/ruff/src/checkers/ast/mod.rs | 15 ++++++++++---- crates/ruff_python_ast/src/helpers.rs | 30 +++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/crates/ruff/src/checkers/ast/mod.rs b/crates/ruff/src/checkers/ast/mod.rs index 9946f26aa8..3382bc5a4f 100644 --- a/crates/ruff/src/checkers/ast/mod.rs +++ b/crates/ruff/src/checkers/ast/mod.rs @@ -39,7 +39,9 @@ use ruff_text_size::{TextRange, TextSize}; use ruff_diagnostics::{Diagnostic, IsolationLevel}; use ruff_python_ast::all::{extract_all_names, DunderAllFlags}; -use ruff_python_ast::helpers::{extract_handled_exceptions, to_module_path}; +use ruff_python_ast::helpers::{ + extract_handled_exceptions, from_relative_import, from_relative_import_parts, to_module_path, +}; use ruff_python_ast::identifier::Identifier; use ruff_python_ast::str::trailing_quote; use ruff_python_ast::visitor::{walk_except_handler, walk_pattern, Visitor}; @@ -397,11 +399,16 @@ where // be "foo.bar". Given `from foo import bar as baz`, `name` would be "baz" // and `qualified_name` would be "foo.bar". let name = alias.asname.as_ref().unwrap_or(&alias.name); + let qualified_name = helpers::format_import_from_member(level, module, &alias.name); - let module = module.unwrap(); - let call_path: Box<[&str]> = - module.split('.').chain(once(alias.name.as_str())).collect(); + let call_path = from_relative_import_parts( + self.module_path.unwrap_or_default(), + level, + module, + &alias.name, + ); + let call_path: Box<[&str]> = call_path.into_boxed_slice(); self.add_binding( name, alias.identifier(), diff --git a/crates/ruff_python_ast/src/helpers.rs b/crates/ruff_python_ast/src/helpers.rs index 923c732699..6d048d82cf 100644 --- a/crates/ruff_python_ast/src/helpers.rs +++ b/crates/ruff_python_ast/src/helpers.rs @@ -894,6 +894,36 @@ pub fn from_relative_import<'a>(module: &'a [String], name: &'a str) -> CallPath call_path } +pub fn from_relative_import_parts<'a>( + module_path: &'a [String], + level: Option, + module: Option<&'a str>, + member: &'a str, +) -> CallPath<'a> { + let mut call_path: CallPath = SmallVec::with_capacity(module_path.len() + 1); + + // Start with the module path. + call_path.extend(module_path.iter().map(String::as_str)); + + // Remove segments based on the number of dots. + for _ in 0..level.unwrap_or(0) { + if call_path.is_empty() { + return SmallVec::new(); + } + call_path.pop(); + } + + // Add the remaining segments. + if let Some(module) = module { + call_path.extend(module.split('.')); + } + + // Add the member. + call_path.push(member); + + call_path +} + /// Given an imported module (based on its relative import level and module name), return the /// fully-qualified module path. pub fn resolve_imported_module_path<'a>(