]> git.proxmox.com Git - rustc.git/blob - src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
New upstream version 1.67.1+dfsg1
[rustc.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 }