Skip to content

Commit 47f2c84

Browse files
committed
Fix FP in single_component_path_imports lint
1 parent 65d046c commit 47f2c84

4 files changed

+109
-16
lines changed

clippy_lints/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
12311231
store.register_early_pass(|| box as_conversions::AsConversions);
12321232
store.register_late_pass(|| box let_underscore::LetUnderscore);
12331233
store.register_late_pass(|| box atomic_ordering::AtomicOrdering);
1234-
store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports);
1234+
store.register_early_pass(|| box single_component_path_imports::SingleComponentPathImports::default());
12351235
let max_fn_params_bools = conf.max_fn_params_bools;
12361236
let max_struct_bools = conf.max_struct_bools;
12371237
store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));

clippy_lints/src/single_component_path_imports.rs

Lines changed: 75 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
use crate::utils::{in_macro, span_lint_and_sugg};
22
use if_chain::if_chain;
3-
use rustc_ast::{Item, ItemKind, UseTreeKind};
3+
use rustc_ast::{Crate, Item, ItemKind, UseTreeKind};
44
use rustc_errors::Applicability;
55
use rustc_lint::{EarlyContext, EarlyLintPass};
6-
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
use rustc_session::{declare_tool_lint, impl_lint_pass};
77
use rustc_span::edition::Edition;
8+
use rustc_span::symbol::kw;
9+
use rustc_span::{Span, Symbol};
810

911
declare_clippy_lint! {
1012
/// **What it does:** Checking for imports with single component use path.
@@ -34,29 +36,87 @@ declare_clippy_lint! {
3436
"imports with single component path are redundant"
3537
}
3638

37-
declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
39+
#[derive(Default)]
40+
pub struct SingleComponentPathImports {
41+
/// keep track of imports reused with `self` keyword,
42+
/// such as `self::crypto_hash` in the example below
43+
///
44+
/// ```rust,ignore
45+
/// use self::crypto_hash::{Algorithm, Hasher};
46+
/// ```
47+
imports_reused_with_self: Vec<Symbol>,
48+
/// keep track of single use statements
49+
/// such as `crypto_hash` in the example below
50+
///
51+
/// ```rust,ignore
52+
/// use crypto_hash;
53+
/// ```
54+
single_use_usages: Vec<(Symbol, Span)>,
55+
}
56+
57+
impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
3858

3959
impl EarlyLintPass for SingleComponentPathImports {
40-
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
41-
if_chain! {
42-
if !in_macro(item.span);
43-
if cx.sess.opts.edition >= Edition::Edition2018;
44-
if !item.vis.kind.is_pub();
45-
if let ItemKind::Use(use_tree) = &item.kind;
46-
if let segments = &use_tree.prefix.segments;
47-
if segments.len() == 1;
48-
if let UseTreeKind::Simple(None, _, _) = use_tree.kind;
49-
then {
60+
fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
61+
if cx.sess.opts.edition < Edition::Edition2018 {
62+
return;
63+
}
64+
for item in &krate.items {
65+
self.track_uses(&item);
66+
}
67+
for single_use in &self.single_use_usages {
68+
if !self.imports_reused_with_self.contains(&single_use.0) {
5069
span_lint_and_sugg(
5170
cx,
5271
SINGLE_COMPONENT_PATH_IMPORTS,
53-
item.span,
72+
single_use.1,
5473
"this import is redundant",
5574
"remove it entirely",
5675
String::new(),
57-
Applicability::MachineApplicable
76+
Applicability::MachineApplicable,
5877
);
5978
}
6079
}
6180
}
6281
}
82+
83+
impl SingleComponentPathImports {
84+
fn track_uses(&mut self, item: &Item) {
85+
if_chain! {
86+
if !in_macro(item.span);
87+
if !item.vis.kind.is_pub();
88+
if let ItemKind::Use(use_tree) = &item.kind;
89+
if let segments = &use_tree.prefix.segments;
90+
91+
then {
92+
// keep track of `use some_module;` usages
93+
if segments.len() == 1 {
94+
if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
95+
let ident = &segments[0].ident;
96+
self.single_use_usages.push((ident.name, item.span));
97+
}
98+
return;
99+
}
100+
101+
// keep track of `use self::some_module` usages
102+
if segments[0].ident.name == kw::SelfLower {
103+
// simple case such as `use self::module::SomeStruct`
104+
if segments.len() > 1 {
105+
self.imports_reused_with_self.push(segments[1].ident.name);
106+
return;
107+
}
108+
109+
// nested case such as `use self::{module1::Struct1, module2::Struct2}`
110+
if let UseTreeKind::Nested(trees) = &use_tree.kind {
111+
for tree in trees {
112+
let segments = &tree.0.prefix.segments;
113+
if !segments.is_empty() {
114+
self.imports_reused_with_self.push(segments[0].ident.name);
115+
}
116+
}
117+
}
118+
}
119+
}
120+
}
121+
}
122+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// edition:2018
2+
#![warn(clippy::single_component_path_imports)]
3+
#![allow(unused_imports)]
4+
5+
use self::regex::{Regex as xeger, RegexSet as tesxeger};
6+
pub use self::{
7+
regex::{Regex, RegexSet},
8+
some_mod::SomeType,
9+
};
10+
use regex;
11+
12+
mod some_mod {
13+
pub struct SomeType;
14+
}
15+
16+
fn main() {}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// edition:2018
2+
#![warn(clippy::single_component_path_imports)]
3+
#![allow(unused_imports)]
4+
5+
use regex;
6+
7+
use self::regex::{Regex as xeger, RegexSet as tesxeger};
8+
pub use self::{
9+
regex::{Regex, RegexSet},
10+
some_mod::SomeType,
11+
};
12+
13+
mod some_mod {
14+
pub struct SomeType;
15+
}
16+
17+
fn main() {}

0 commit comments

Comments
 (0)