]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/single_component_path_imports.rs
Merge commit '98e2b9f25b6db4b2680a3d388456d9f95cb28344' into clippyup
[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, ModKind, UseTreeKind};
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     for item in items {
64         track_uses(cx, &item, &mut imports_reused_with_self, &mut single_use_usages);
65     }
66
67     for single_use in &single_use_usages {
68         if !imports_reused_with_self.contains(&single_use.0) {
69             let can_suggest = single_use.2;
70             if can_suggest {
71                 span_lint_and_sugg(
72                     cx,
73                     SINGLE_COMPONENT_PATH_IMPORTS,
74                     single_use.1,
75                     "this import is redundant",
76                     "remove it entirely",
77                     String::new(),
78                     Applicability::MachineApplicable,
79                 );
80             } else {
81                 span_lint_and_help(
82                     cx,
83                     SINGLE_COMPONENT_PATH_IMPORTS,
84                     single_use.1,
85                     "this import is redundant",
86                     None,
87                     "remove this import",
88                 );
89             }
90         }
91     }
92 }
93
94 fn track_uses(
95     cx: &EarlyContext<'_>,
96     item: &Item,
97     imports_reused_with_self: &mut Vec<Symbol>,
98     single_use_usages: &mut Vec<(Symbol, Span, bool)>,
99 ) {
100     if in_macro(item.span) || item.vis.kind.is_pub() {
101         return;
102     }
103
104     match &item.kind {
105         ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
106             check_mod(cx, &items);
107         },
108         ItemKind::Use(use_tree) => {
109             let segments = &use_tree.prefix.segments;
110
111             // keep track of `use some_module;` usages
112             if segments.len() == 1 {
113                 if let UseTreeKind::Simple(None, _, _) = use_tree.kind {
114                     let ident = &segments[0].ident;
115                     single_use_usages.push((ident.name, item.span, true));
116                 }
117                 return;
118             }
119
120             if segments.is_empty() {
121                 // keep track of `use {some_module, some_other_module};` usages
122                 if let UseTreeKind::Nested(trees) = &use_tree.kind {
123                     for tree in trees {
124                         let segments = &tree.0.prefix.segments;
125                         if segments.len() == 1 {
126                             if let UseTreeKind::Simple(None, _, _) = tree.0.kind {
127                                 let ident = &segments[0].ident;
128                                 single_use_usages.push((ident.name, tree.0.span, false));
129                             }
130                         }
131                     }
132                 }
133             } else {
134                 // keep track of `use self::some_module` usages
135                 if segments[0].ident.name == kw::SelfLower {
136                     // simple case such as `use self::module::SomeStruct`
137                     if segments.len() > 1 {
138                         imports_reused_with_self.push(segments[1].ident.name);
139                         return;
140                     }
141
142                     // nested case such as `use self::{module1::Struct1, module2::Struct2}`
143                     if let UseTreeKind::Nested(trees) = &use_tree.kind {
144                         for tree in trees {
145                             let segments = &tree.0.prefix.segments;
146                             if !segments.is_empty() {
147                                 imports_reused_with_self.push(segments[0].ident.name);
148                             }
149                         }
150                     }
151                 }
152             }
153         },
154         _ => {},
155     }
156 }