]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
Rollup merge of #96336 - Nilstrieb:link-to-correct-as_mut-in-ptr-as_ref, r=JohnTitor
[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::{ptr::P, Crate, Item, ItemKind, MacroDef, ModKind, UseTreeKind};
3 use rustc_errors::Applicability;
4 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
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     #[clippy::version = "1.43.0"]
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 {
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
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 (name, span, can_suggest) in single_use_usages {
80         if !imports_reused_with_self.contains(&name) {
81             if can_suggest {
82                 span_lint_and_sugg(
83                     cx,
84                     SINGLE_COMPONENT_PATH_IMPORTS,
85                     span,
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                     span,
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             // keep track of `use some_module;` usages
127             if segments.len() == 1 {
128                 if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
129                     let name = segments[0].ident.name;
130                     if !macros.contains(&name) {
131                         single_use_usages.push((name, item.span, true));
132                     }
133                 }
134                 return;
135             }
136
137             if segments.is_empty() {
138                 // keep track of `use {some_module, some_other_module};` usages
139                 if let UseTreeKind::Nested(trees) = &use_tree.kind {
140                     for tree in trees {
141                         let segments = &tree.0.prefix.segments;
142                         if segments.len() == 1 {
143                             if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
144                                 let name = segments[0].ident.name;
145                                 if !macros.contains(&name) {
146                                     single_use_usages.push((name, tree.0.span, false));
147                                 }
148                             }
149                         }
150                     }
151                 }
152             } else {
153                 // keep track of `use self::some_module` usages
154                 if segments[0].ident.name == kw::SelfLower {
155                     // simple case such as `use self::module::SomeStruct`
156                     if segments.len() > 1 {
157                         imports_reused_with_self.push(segments[1].ident.name);
158                         return;
159                     }
160
161                     // nested case such as `use self::{module1::Struct1, module2::Struct2}`
162                     if let UseTreeKind::Nested(trees) = &use_tree.kind {
163                         for tree in trees {
164                             let segments = &tree.0.prefix.segments;
165                             if !segments.is_empty() {
166                                 imports_reused_with_self.push(segments[0].ident.name);
167                             }
168                         }
169                     }
170                 }
171             }
172         },
173         _ => {},
174     }
175 }