|
1 | 1 | use crate::utils::{in_macro, span_lint_and_sugg};
|
2 | 2 | use if_chain::if_chain;
|
3 |
| -use rustc_ast::{Item, ItemKind, UseTreeKind}; |
| 3 | +use rustc_ast::{Crate, Item, ItemKind, UseTreeKind}; |
4 | 4 | use rustc_errors::Applicability;
|
5 | 5 | 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}; |
7 | 7 | use rustc_span::edition::Edition;
|
| 8 | +use rustc_span::symbol::kw; |
| 9 | +use rustc_span::{Span, Symbol}; |
8 | 10 |
|
9 | 11 | declare_clippy_lint! {
|
10 | 12 | /// **What it does:** Checking for imports with single component use path.
|
@@ -34,29 +36,87 @@ declare_clippy_lint! {
|
34 | 36 | "imports with single component path are redundant"
|
35 | 37 | }
|
36 | 38 |
|
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]); |
38 | 58 |
|
39 | 59 | 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) { |
50 | 69 | span_lint_and_sugg(
|
51 | 70 | cx,
|
52 | 71 | SINGLE_COMPONENT_PATH_IMPORTS,
|
53 |
| - item.span, |
| 72 | + single_use.1, |
54 | 73 | "this import is redundant",
|
55 | 74 | "remove it entirely",
|
56 | 75 | String::new(),
|
57 |
| - Applicability::MachineApplicable |
| 76 | + Applicability::MachineApplicable, |
58 | 77 | );
|
59 | 78 | }
|
60 | 79 | }
|
61 | 80 | }
|
62 | 81 | }
|
| 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 | +} |
0 commit comments