Compare commits
3 Commits
pythonplus
...
dhruv/unus
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a3b4adab3f | ||
|
|
4110065a18 | ||
|
|
059ba0c59c |
6
crates/ruff/resources/test/fixtures/pyflakes/F401_19.py
vendored
Normal file
6
crates/ruff/resources/test/fixtures/pyflakes/F401_19.py
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"""Test: submodule import access."""
|
||||
|
||||
import logging.config
|
||||
import logging.handlers
|
||||
|
||||
logging.config.fileConfig("logging.conf")
|
||||
9
crates/ruff/resources/test/fixtures/pyflakes/F401_20.py
vendored
Normal file
9
crates/ruff/resources/test/fixtures/pyflakes/F401_20.py
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
"""Testing that multiple submodule imports are handled correctly."""
|
||||
|
||||
# The logic goes through each of the shadowed bindings upwards to get all the unused
|
||||
# imports. It should only detect imports, not any other kind of binding.
|
||||
multiprocessing = None
|
||||
|
||||
import multiprocessing.connection
|
||||
import multiprocessing.pool
|
||||
import multiprocessing.queues
|
||||
@@ -44,6 +44,8 @@ mod tests {
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_16.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_17.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_18.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_19.py"))]
|
||||
#[test_case(Rule::UnusedImport, Path::new("F401_20.py"))]
|
||||
#[test_case(Rule::ImportShadowedByLoopVar, Path::new("F402.py"))]
|
||||
#[test_case(Rule::UndefinedLocalWithImportStar, Path::new("F403.py"))]
|
||||
#[test_case(Rule::LateFutureImport, Path::new("F404.py"))]
|
||||
|
||||
@@ -101,44 +101,53 @@ pub(crate) fn unused_import(checker: &Checker, scope: &Scope, diagnostics: &mut
|
||||
let mut ignored: FxHashMap<(NodeId, Exceptions), Vec<Import>> = FxHashMap::default();
|
||||
|
||||
for binding_id in scope.binding_ids() {
|
||||
let binding = checker.semantic().binding(binding_id);
|
||||
let top_binding = checker.semantic().binding(binding_id);
|
||||
|
||||
if binding.is_used()
|
||||
|| binding.is_explicit_export()
|
||||
|| binding.is_nonlocal()
|
||||
|| binding.is_global()
|
||||
if top_binding.is_used()
|
||||
|| top_binding.is_explicit_export()
|
||||
|| top_binding.is_nonlocal()
|
||||
|| top_binding.is_global()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(qualified_name) = binding.qualified_name() else {
|
||||
let Some(binding_name) = scope.get_name(binding_id) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(stmt_id) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let import = Import {
|
||||
qualified_name,
|
||||
range: binding.range,
|
||||
parent_range: binding.parent_range(checker.semantic()),
|
||||
};
|
||||
|
||||
if checker.rule_is_ignored(Rule::UnusedImport, import.range.start())
|
||||
|| import.parent_range.map_or(false, |parent_range| {
|
||||
checker.rule_is_ignored(Rule::UnusedImport, parent_range.start())
|
||||
})
|
||||
for binding in scope
|
||||
.get_all(binding_name)
|
||||
.map(|binding_id| checker.semantic().binding(binding_id))
|
||||
{
|
||||
ignored
|
||||
.entry((stmt_id, binding.exceptions))
|
||||
.or_default()
|
||||
.push(import);
|
||||
} else {
|
||||
unused
|
||||
.entry((stmt_id, binding.exceptions))
|
||||
.or_default()
|
||||
.push(import);
|
||||
let Some(qualified_name) = binding.qualified_name() else {
|
||||
break;
|
||||
};
|
||||
|
||||
let Some(stmt_id) = binding.source else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let import = Import {
|
||||
qualified_name,
|
||||
range: binding.range,
|
||||
parent_range: binding.parent_range(checker.semantic()),
|
||||
};
|
||||
|
||||
if checker.rule_is_ignored(Rule::UnusedImport, import.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,26 @@ F401_0.py:6:5: F401 [*] `collections.OrderedDict` imported but unused
|
||||
8 7 | )
|
||||
9 8 | import multiprocessing.pool
|
||||
|
||||
F401_0.py:11:8: F401 [*] `logging.config` imported but unused
|
||||
|
|
||||
9 | import multiprocessing.pool
|
||||
10 | import multiprocessing.process
|
||||
11 | import logging.config
|
||||
| ^^^^^^^^^^^^^^ F401
|
||||
12 | import logging.handlers
|
||||
13 | from typing import (
|
||||
|
|
||||
= help: Remove unused import: `logging.config`
|
||||
|
||||
ℹ Fix
|
||||
8 8 | )
|
||||
9 9 | import multiprocessing.pool
|
||||
10 10 | import multiprocessing.process
|
||||
11 |-import logging.config
|
||||
12 11 | import logging.handlers
|
||||
13 12 | from typing import (
|
||||
14 13 | TYPE_CHECKING,
|
||||
|
||||
F401_0.py:12:8: F401 [*] `logging.handlers` imported but unused
|
||||
|
|
||||
10 | import multiprocessing.process
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
source: crates/ruff/src/rules/pyflakes/mod.rs
|
||||
---
|
||||
F401_20.py:7:8: F401 [*] `multiprocessing.connection` imported but unused
|
||||
|
|
||||
5 | multiprocessing = None
|
||||
6 |
|
||||
7 | import multiprocessing.connection
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ F401
|
||||
8 | import multiprocessing.pool
|
||||
9 | import multiprocessing.queues
|
||||
|
|
||||
= help: Remove unused import: `multiprocessing.connection`
|
||||
|
||||
ℹ Fix
|
||||
4 4 | # imports. It should only detect imports, not any other kind of binding.
|
||||
5 5 | multiprocessing = None
|
||||
6 6 |
|
||||
7 |-import multiprocessing.connection
|
||||
8 7 | import multiprocessing.pool
|
||||
9 8 | import multiprocessing.queues
|
||||
|
||||
F401_20.py:8:8: F401 [*] `multiprocessing.pool` imported but unused
|
||||
|
|
||||
7 | import multiprocessing.connection
|
||||
8 | import multiprocessing.pool
|
||||
| ^^^^^^^^^^^^^^^^^^^^ F401
|
||||
9 | import multiprocessing.queues
|
||||
|
|
||||
= help: Remove unused import: `multiprocessing.pool`
|
||||
|
||||
ℹ Fix
|
||||
5 5 | multiprocessing = None
|
||||
6 6 |
|
||||
7 7 | import multiprocessing.connection
|
||||
8 |-import multiprocessing.pool
|
||||
9 8 | import multiprocessing.queues
|
||||
|
||||
F401_20.py:9:8: F401 [*] `multiprocessing.queues` imported but unused
|
||||
|
|
||||
7 | import multiprocessing.connection
|
||||
8 | import multiprocessing.pool
|
||||
9 | import multiprocessing.queues
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ F401
|
||||
|
|
||||
= help: Remove unused import: `multiprocessing.queues`
|
||||
|
||||
ℹ Fix
|
||||
6 6 |
|
||||
7 7 | import multiprocessing.connection
|
||||
8 8 | import multiprocessing.pool
|
||||
9 |-import multiprocessing.queues
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ pub struct Scope<'a> {
|
||||
/// A map from bound name to binding ID.
|
||||
bindings: FxHashMap<&'a str, BindingId>,
|
||||
|
||||
/// A map from binding ID to bound name.
|
||||
binding_name: FxHashMap<BindingId, &'a str>,
|
||||
|
||||
/// A map from binding ID to binding ID that it shadows.
|
||||
///
|
||||
/// For example:
|
||||
@@ -53,6 +56,7 @@ impl<'a> Scope<'a> {
|
||||
parent: None,
|
||||
star_imports: Vec::default(),
|
||||
bindings: FxHashMap::default(),
|
||||
binding_name: FxHashMap::default(),
|
||||
shadowed_bindings: IntMap::default(),
|
||||
globals_id: None,
|
||||
flags: ScopeFlags::empty(),
|
||||
@@ -65,6 +69,7 @@ impl<'a> Scope<'a> {
|
||||
parent: Some(parent),
|
||||
star_imports: Vec::default(),
|
||||
bindings: FxHashMap::default(),
|
||||
binding_name: FxHashMap::default(),
|
||||
shadowed_bindings: IntMap::default(),
|
||||
globals_id: None,
|
||||
flags: ScopeFlags::empty(),
|
||||
@@ -76,8 +81,14 @@ impl<'a> Scope<'a> {
|
||||
self.bindings.get(name).copied()
|
||||
}
|
||||
|
||||
/// Returns the name bound to the given [id](BindingId).
|
||||
pub fn get_name(&self, id: BindingId) -> Option<&'a str> {
|
||||
self.binding_name.get(&id).copied()
|
||||
}
|
||||
|
||||
/// Adds a new binding with the given name to this scope.
|
||||
pub fn add(&mut self, name: &'a str, id: BindingId) -> Option<BindingId> {
|
||||
self.binding_name.insert(id, name);
|
||||
if let Some(shadowed) = self.bindings.insert(name, id) {
|
||||
self.shadowed_bindings.insert(id, shadowed);
|
||||
Some(shadowed)
|
||||
|
||||
Reference in New Issue
Block a user