]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
Rollup merge of #105123 - BlackHoleFox:fixing-the-macos-deployment, r=oli-obk
[rust.git] / src / tools / clippy / clippy_lints / src / single_component_path_imports.rs
1 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
2 use rustc_ast::node_id::{NodeId, NodeMap};
3 use rustc_ast::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
4 use rustc_errors::Applicability;
5 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
6 use rustc_session::{declare_tool_lint, impl_lint_pass};
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     #[clippy::version = "1.43.0"]
32     pub SINGLE_COMPONENT_PATH_IMPORTS,
33     style,
34     "imports with single component path are redundant"
35 }
36
37 #[derive(Default)]
38 pub struct SingleComponentPathImports {
39     /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected
40     found: NodeMap<Vec<SingleUse>>,
41 }
42
43 struct SingleUse {
44     name: Symbol,
45     span: Span,
46     item_id: NodeId,
47     can_suggest: bool,
48 }
49
50 impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
51
52 impl EarlyLintPass for SingleComponentPathImports {
53     fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
54         if cx.sess().opts.edition < Edition::Edition2018 {
55             return;
56         }
57
58         self.check_mod(cx, &krate.items);
59     }
60
61     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
62         for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() {
63             if can_suggest {
64                 span_lint_and_sugg(
65                     cx,
66                     SINGLE_COMPONENT_PATH_IMPORTS,
67                     span,
68                     "this import is redundant",
69                     "remove it entirely",
70                     String::new(),
71                     Applicability::MachineApplicable,
72                 );
73             } else {
74                 span_lint_and_help(
75                     cx,
76                     SINGLE_COMPONENT_PATH_IMPORTS,
77                     span,
78                     "this import is redundant",
79                     None,
80                     "remove this import",
81                 );
82             }
83         }
84     }
85 }
86
87 impl SingleComponentPathImports {
88     fn check_mod(&mut self, cx: &EarlyContext<'_>, items: &[P<Item>]) {
89         // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
90         // below. Removing the `use crypto_hash;` would make this a compile error
91         // ```
92         // use crypto_hash;
93         //
94         // use self::crypto_hash::{Algorithm, Hasher};
95         // ```
96         let mut imports_reused_with_self = Vec::new();
97
98         // keep track of single use statements such as `crypto_hash` in the example below
99         // ```
100         // use crypto_hash;
101         // ```
102         let mut single_use_usages = Vec::new();
103
104         // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
105         // ```
106         // macro_rules! foo { () => {} };
107         // pub(crate) use foo;
108         // ```
109         let mut macros = Vec::new();
110
111         for item in items {
112             self.track_uses(
113                 cx,
114                 item,
115                 &mut imports_reused_with_self,
116                 &mut single_use_usages,
117                 &mut macros,
118             );
119         }
120
121         for usage in single_use_usages {
122             if !imports_reused_with_self.contains(&usage.name) {
123                 self.found.entry(usage.item_id).or_default().push(usage);
124             }
125         }
126     }
127
128     fn track_uses(
129         &mut self,
130         cx: &EarlyContext<'_>,
131         item: &Item,
132         imports_reused_with_self: &mut Vec<Symbol>,
133         single_use_usages: &mut Vec<SingleUse>,
134         macros: &mut Vec<Symbol>,
135     ) {
136         if item.span.from_expansion() || item.vis.kind.is_pub() {
137             return;
138         }
139
140         match &item.kind {
141             ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
142                 self.check_mod(cx, items);
143             },
144             ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
145                 macros.push(item.ident.name);
146             },
147             ItemKind::Use(use_tree) => {
148                 let segments = &use_tree.prefix.segments;
149
150                 // keep track of `use some_module;` usages
151                 if segments.len() == 1 {
152                     if let UseTreeKind::Simple(None) = use_tree.kind {
153                         let name = segments[0].ident.name;
154                         if !macros.contains(&name) {
155                             single_use_usages.push(SingleUse {
156                                 name,
157                                 span: item.span,
158                                 item_id: item.id,
159                                 can_suggest: true,
160                             });
161                         }
162                     }
163                     return;
164                 }
165
166                 if segments.is_empty() {
167                     // keep track of `use {some_module, some_other_module};` usages
168                     if let UseTreeKind::Nested(trees) = &use_tree.kind {
169                         for tree in trees {
170                             let segments = &tree.0.prefix.segments;
171                             if segments.len() == 1 {
172                                 if let UseTreeKind::Simple(None) = tree.0.kind {
173                                     let name = segments[0].ident.name;
174                                     if !macros.contains(&name) {
175                                         single_use_usages.push(SingleUse {
176                                             name,
177                                             span: tree.0.span,
178                                             item_id: item.id,
179                                             can_suggest: false,
180                                         });
181                                     }
182                                 }
183                             }
184                         }
185                     }
186                 } else {
187                     // keep track of `use self::some_module` usages
188                     if segments[0].ident.name == kw::SelfLower {
189                         // simple case such as `use self::module::SomeStruct`
190                         if segments.len() > 1 {
191                             imports_reused_with_self.push(segments[1].ident.name);
192                             return;
193                         }
194
195                         // nested case such as `use self::{module1::Struct1, module2::Struct2}`
196                         if let UseTreeKind::Nested(trees) = &use_tree.kind {
197                             for tree in trees {
198                                 let segments = &tree.0.prefix.segments;
199                                 if !segments.is_empty() {
200                                     imports_reused_with_self.push(segments[0].ident.name);
201                                 }
202                             }
203                         }
204                     }
205                 }
206             },
207             _ => {},
208         }
209     }
210 }