From 16e44fb7d9cddb4f0993ddcf823005c09f74c1cd Mon Sep 17 00:00:00 2001 From: Dhruv Manilawala Date: Sun, 11 Jun 2023 00:27:48 +0530 Subject: [PATCH] Consider submodule imports to detect unused imports --- .../src/rules/pyflakes/rules/unused_import.rs | 67 +++++++++++-------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/crates/ruff/src/rules/pyflakes/rules/unused_import.rs b/crates/ruff/src/rules/pyflakes/rules/unused_import.rs index 36b02cb5c01f77..5d95e6fb570189 100644 --- a/crates/ruff/src/rules/pyflakes/rules/unused_import.rs +++ b/crates/ruff/src/rules/pyflakes/rules/unused_import.rs @@ -104,40 +104,51 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut let mut ignored: FxHashMap<(NodeId, Exceptions), Vec> = FxHashMap::default(); for binding_id in scope.binding_ids() { - let binding = &checker.semantic_model().bindings[binding_id]; + let top_binding = &checker.semantic_model().bindings[binding_id]; - if binding.is_used() || binding.is_explicit_export() { + if top_binding.is_used() || top_binding.is_explicit_export() { continue; } - let Some(qualified_name) = binding.qualified_name() else { - continue; - }; - - let Some(stmt_id) = binding.source else { - continue; - }; - - let import = Import { - qualified_name, - trimmed_range: binding.trimmed_range(checker.semantic_model(), checker.locator), - parent_range: binding.parent_range(checker.semantic_model()), + let bindings = if let Some(module_name) = top_binding.module_name() { + scope + .bindings_for_name(module_name) + .map(|binding_id| &checker.semantic_model().bindings[binding_id]) + .collect::>() + } else { + vec![top_binding] }; - if checker.rule_is_ignored(Rule::UnusedImport, import.trimmed_range.start()) - || import.parent_range.map_or(false, |parent_range| { - checker.rule_is_ignored(Rule::UnusedImport, parent_range.start()) - }) - { - ignored - .entry((stmt_id, binding.exceptions)) - .or_default() - .push(import); - } else { - unused - .entry((stmt_id, binding.exceptions)) - .or_default() - .push(import); + for binding in bindings { + let Some(qualified_name) = binding.qualified_name() else { + break; + }; + + let Some(stmt_id) = binding.source else { + break; + }; + + let import = Import { + qualified_name, + trimmed_range: binding.trimmed_range(checker.semantic_model(), checker.locator), + parent_range: binding.parent_range(checker.semantic_model()), + }; + + if checker.rule_is_ignored(Rule::UnusedImport, import.trimmed_range.start()) + || import.parent_range.map_or(false, |parent_range| { + checker.rule_is_ignored(Rule::UnusedImport, parent_range.start()) + }) + { + ignored + .entry((stmt_id, binding.exceptions)) + .or_default() + .push(import); + } else { + unused + .entry((stmt_id, binding.exceptions)) + .or_default() + .push(import); + } } }