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