1 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
2 use clippy_utils::in_macro;
3 use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind, VisibilityKind};
4 use rustc_errors::Applicability;
5 use rustc_lint::{EarlyContext, EarlyLintPass};
6 use rustc_session::{declare_lint_pass, declare_tool_lint};
7 use rustc_span::{edition::Edition, symbol::kw, Span, Symbol};
8 
9 declare_clippy_lint! {
10     /// ### What it does
11     /// Checking for imports with single component use path.
12     ///
13     /// ### Why is this bad?
14     /// Import with single component use path such as `use cratename;`
15     /// is not necessary, and thus should be removed.
16     ///
17     /// ### Example
18     /// ```rust,ignore
19     /// use regex;
20     ///
21     /// fn main() {
22     ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
23     /// }
24     /// ```
25     /// Better as
26     /// ```rust,ignore
27     /// fn main() {
28     ///     regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
29     /// }
30     /// ```
31     pub SINGLE_COMPONENT_PATH_IMPORTS,
32     style,
33     "imports with single component path are redundant"
34 }
35 
36 declare_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
37 
38 impl EarlyLintPass for SingleComponentPathImports {
check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate)39     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
40         if cx.sess.opts.edition < Edition::Edition2018 {
41             return;
42         }
43         check_mod(cx, &krate.items);
44     }
45 }
46 
check_mod(cx: &EarlyContext<'_>, items: &[P<Item>])47 fn check_mod(cx: &EarlyContext<'_>, items: &[P<Item>]) {
48     // keep track of imports reused with `self` keyword,
49     // such as `self::crypto_hash` in the example below
50     // ```rust,ignore
51     // use self::crypto_hash::{Algorithm, Hasher};
52     // ```
53     let mut imports_reused_with_self = Vec::new();
54 
55     // keep track of single use statements
56     // such as `crypto_hash` in the example below
57     // ```rust,ignore
58     // use crypto_hash;
59     // ```
60     let mut single_use_usages = Vec::new();
61 
62     // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
63     // ```rust,ignore
64     // macro_rules! foo { () => {} };
65     // pub(crate) use foo;
66     // ```
67     let mut macros = Vec::new();
68 
69     for item in items {
70         track_uses(
71             cx,
72             item,
73             &mut imports_reused_with_self,
74             &mut single_use_usages,
75             &mut macros,
76         );
77     }
78 
79     for single_use in &single_use_usages {
80         if !imports_reused_with_self.contains(&single_use.0) {
81             let can_suggest = single_use.2;
82             if can_suggest {
83                 span_lint_and_sugg(
84                     cx,
85                     SINGLE_COMPONENT_PATH_IMPORTS,
86                     single_use.1,
87                     "this import is redundant",
88                     "remove it entirely",
89                     String::new(),
90                     Applicability::MachineApplicable,
91                 );
92             } else {
93                 span_lint_and_help(
94                     cx,
95                     SINGLE_COMPONENT_PATH_IMPORTS,
96                     single_use.1,
97                     "this import is redundant",
98                     None,
99                     "remove this import",
100                 );
101             }
102         }
103     }
104 }
105 
track_uses( cx: &EarlyContext<'_>, item: &Item, imports_reused_with_self: &mut Vec<Symbol>, single_use_usages: &mut Vec<(Symbol, Span, bool)>, macros: &mut Vec<Symbol>, )106 fn track_uses(
107     cx: &EarlyContext<'_>,
108     item: &Item,
109     imports_reused_with_self: &mut Vec<Symbol>,
110     single_use_usages: &mut Vec<(Symbol, Span, bool)>,
111     macros: &mut Vec<Symbol>,
112 ) {
113     if in_macro(item.span) || item.vis.kind.is_pub() {
114         return;
115     }
116 
117     match &item.kind {
118         ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
119             check_mod(cx, items);
120         },
121         ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
122             macros.push(item.ident.name);
123         },
124         ItemKind::Use(use_tree) => {
125             let segments = &use_tree.prefix.segments;
126 
127             let should_report =
128                 |name: &Symbol| !macros.contains(name) || matches!(item.vis.kind, VisibilityKind::Inherited);
129 
130             // keep track of `use some_module;` usages
131             if segments.len() == 1 {
132                 if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
133                     let name = segments[0].ident.name;
134                     if should_report(&name) {
135                         single_use_usages.push((name, item.span, true));
136                     }
137                 }
138                 return;
139             }
140 
141             if segments.is_empty() {
142                 // keep track of `use {some_module, some_other_module};` usages
143                 if let UseTreeKind::Nested(trees) = &use_tree.kind {
144                     for tree in trees {
145                         let segments = &tree.0.prefix.segments;
146                         if segments.len() == 1 {
147                             if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
148                                 let name = segments[0].ident.name;
149                                 if should_report(&name) {
150                                     single_use_usages.push((name, tree.0.span, false));
151                                 }
152                             }
153                         }
154                     }
155                 }
156             } else {
157                 // keep track of `use self::some_module` usages
158                 if segments[0].ident.name == kw::SelfLower {
159                     // simple case such as `use self::module::SomeStruct`
160                     if segments.len() > 1 {
161                         imports_reused_with_self.push(segments[1].ident.name);
162                         return;
163                     }
164 
165                     // nested case such as `use self::{module1::Struct1, module2::Struct2}`
166                     if let UseTreeKind::Nested(trees) = &use_tree.kind {
167                         for tree in trees {
168                             let segments = &tree.0.prefix.segments;
169                             if !segments.is_empty() {
170                                 imports_reused_with_self.push(segments[0].ident.name);
171                             }
172                         }
173                     }
174                 }
175             }
176         },
177         _ => {},
178     }
179 }
180