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