]> git.proxmox.com Git - rustc.git/blame - src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
New upstream version 1.73.0+dfsg1
[rustc.git] / src / tools / clippy / clippy_lints / src / single_component_path_imports.rs
CommitLineData
cdc7bbd5 1use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
487cf647 2use rustc_ast::node_id::{NodeId, NodeMap};
add651ee 3use rustc_ast::ptr::P;
353b0b11 4use rustc_ast::visit::{walk_expr, Visitor};
add651ee 5use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind};
f20569fa 6use rustc_errors::Applicability;
5099ac24 7use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
487cf647 8use rustc_session::{declare_tool_lint, impl_lint_pass};
add651ee
FG
9use rustc_span::edition::Edition;
10use rustc_span::symbol::kw;
11use rustc_span::{Span, Symbol};
f20569fa
XL
12
13declare_clippy_lint! {
94222f64
XL
14 /// ### What it does
15 /// Checking for imports with single component use path.
f20569fa 16 ///
94222f64
XL
17 /// ### Why is this bad?
18 /// Import with single component use path such as `use cratename;`
f20569fa
XL
19 /// is not necessary, and thus should be removed.
20 ///
94222f64 21 /// ### Example
f20569fa
XL
22 /// ```rust,ignore
23 /// use regex;
24 ///
25 /// fn main() {
26 /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
27 /// }
28 /// ```
29 /// Better as
30 /// ```rust,ignore
31 /// fn main() {
32 /// regex::Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
33 /// }
34 /// ```
a2a8927a 35 #[clippy::version = "1.43.0"]
f20569fa
XL
36 pub SINGLE_COMPONENT_PATH_IMPORTS,
37 style,
38 "imports with single component path are redundant"
39}
40
487cf647
FG
41#[derive(Default)]
42pub struct SingleComponentPathImports {
43 /// Buffer found usages to emit when visiting that item so that `#[allow]` works as expected
44 found: NodeMap<Vec<SingleUse>>,
45}
46
47struct SingleUse {
48 name: Symbol,
49 span: Span,
50 item_id: NodeId,
51 can_suggest: bool,
52}
53
54impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]);
f20569fa
XL
55
56impl EarlyLintPass for SingleComponentPathImports {
cdc7bbd5 57 fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) {
5099ac24 58 if cx.sess().opts.edition < Edition::Edition2018 {
cdc7bbd5
XL
59 return;
60 }
cdc7bbd5 61
353b0b11 62 self.check_mod(&krate.items);
cdc7bbd5
XL
63 }
64
487cf647
FG
65 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
66 for SingleUse { span, can_suggest, .. } in self.found.remove(&item.id).into_iter().flatten() {
cdc7bbd5 67 if can_suggest {
f20569fa
XL
68 span_lint_and_sugg(
69 cx,
70 SINGLE_COMPONENT_PATH_IMPORTS,
5e7ed085 71 span,
f20569fa
XL
72 "this import is redundant",
73 "remove it entirely",
74 String::new(),
cdc7bbd5
XL
75 Applicability::MachineApplicable,
76 );
77 } else {
78 span_lint_and_help(
79 cx,
80 SINGLE_COMPONENT_PATH_IMPORTS,
5e7ed085 81 span,
cdc7bbd5
XL
82 "this import is redundant",
83 None,
84 "remove this import",
f20569fa
XL
85 );
86 }
87 }
88 }
89}
cdc7bbd5 90
353b0b11
FG
91#[derive(Default)]
92struct ImportUsageVisitor {
93 // keep track of imports reused with `self` keyword, such as `self::std` in the example below.
94 // Removing the `use std;` would make this a compile error (#10549)
95 // ```
96 // use std;
97 //
98 // fn main() {
99 // let _ = self::std::io::stdout();
100 // }
101 // ```
102 imports_referenced_with_self: Vec<Symbol>,
103}
104
105impl<'tcx> Visitor<'tcx> for ImportUsageVisitor {
106 fn visit_expr(&mut self, expr: &Expr) {
107 if let ExprKind::Path(_, path) = &expr.kind
108 && path.segments.len() > 1
109 && path.segments[0].ident.name == kw::SelfLower
110 {
111 self.imports_referenced_with_self.push(path.segments[1].ident.name);
112 }
113 walk_expr(self, expr);
114 }
115
116 fn visit_ty(&mut self, ty: &Ty) {
117 if let TyKind::Path(_, path) = &ty.kind
118 && path.segments.len() > 1
119 && path.segments[0].ident.name == kw::SelfLower
120 {
121 self.imports_referenced_with_self.push(path.segments[1].ident.name);
122 }
123 }
124}
125
487cf647 126impl SingleComponentPathImports {
353b0b11 127 fn check_mod(&mut self, items: &[P<Item>]) {
487cf647
FG
128 // keep track of imports reused with `self` keyword, such as `self::crypto_hash` in the example
129 // below. Removing the `use crypto_hash;` would make this a compile error
130 // ```
131 // use crypto_hash;
132 //
133 // use self::crypto_hash::{Algorithm, Hasher};
134 // ```
135 let mut imports_reused_with_self = Vec::new();
cdc7bbd5 136
487cf647
FG
137 // keep track of single use statements such as `crypto_hash` in the example below
138 // ```
139 // use crypto_hash;
140 // ```
141 let mut single_use_usages = Vec::new();
142
143 // keep track of macros defined in the module as we don't want it to trigger on this (#7106)
144 // ```
145 // macro_rules! foo { () => {} };
146 // pub(crate) use foo;
147 // ```
148 let mut macros = Vec::new();
149
353b0b11 150 let mut import_usage_visitor = ImportUsageVisitor::default();
487cf647 151 for item in items {
353b0b11
FG
152 self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros);
153 import_usage_visitor.visit_item(item);
487cf647
FG
154 }
155
156 for usage in single_use_usages {
353b0b11
FG
157 if !imports_reused_with_self.contains(&usage.name)
158 && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name)
159 {
487cf647 160 self.found.entry(usage.item_id).or_default().push(usage);
cdc7bbd5 161 }
487cf647
FG
162 }
163 }
cdc7bbd5 164
487cf647
FG
165 fn track_uses(
166 &mut self,
487cf647
FG
167 item: &Item,
168 imports_reused_with_self: &mut Vec<Symbol>,
169 single_use_usages: &mut Vec<SingleUse>,
170 macros: &mut Vec<Symbol>,
171 ) {
172 if item.span.from_expansion() || item.vis.kind.is_pub() {
173 return;
174 }
175
176 match &item.kind {
177 ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
353b0b11 178 self.check_mod(items);
487cf647
FG
179 },
180 ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
181 macros.push(item.ident.name);
182 },
183 ItemKind::Use(use_tree) => {
184 let segments = &use_tree.prefix.segments;
185
186 // keep track of `use some_module;` usages
187 if segments.len() == 1 {
188 if let UseTreeKind::Simple(None) = use_tree.kind {
189 let name = segments[0].ident.name;
190 if !macros.contains(&name) {
191 single_use_usages.push(SingleUse {
192 name,
193 span: item.span,
194 item_id: item.id,
195 can_suggest: true,
196 });
cdc7bbd5
XL
197 }
198 }
487cf647 199 return;
cdc7bbd5 200 }
cdc7bbd5 201
487cf647
FG
202 if segments.is_empty() {
203 // keep track of `use {some_module, some_other_module};` usages
cdc7bbd5
XL
204 if let UseTreeKind::Nested(trees) = &use_tree.kind {
205 for tree in trees {
206 let segments = &tree.0.prefix.segments;
487cf647
FG
207 if segments.len() == 1 {
208 if let UseTreeKind::Simple(None) = tree.0.kind {
209 let name = segments[0].ident.name;
210 if !macros.contains(&name) {
211 single_use_usages.push(SingleUse {
212 name,
213 span: tree.0.span,
214 item_id: item.id,
215 can_suggest: false,
216 });
217 }
218 }
219 }
220 }
221 }
222 } else {
223 // keep track of `use self::some_module` usages
224 if segments[0].ident.name == kw::SelfLower {
225 // simple case such as `use self::module::SomeStruct`
226 if segments.len() > 1 {
227 imports_reused_with_self.push(segments[1].ident.name);
228 return;
229 }
230
231 // nested case such as `use self::{module1::Struct1, module2::Struct2}`
232 if let UseTreeKind::Nested(trees) = &use_tree.kind {
233 for tree in trees {
234 let segments = &tree.0.prefix.segments;
235 if !segments.is_empty() {
236 imports_reused_with_self.push(segments[0].ident.name);
237 }
cdc7bbd5
XL
238 }
239 }
240 }
241 }
487cf647
FG
242 },
243 _ => {},
244 }
cdc7bbd5
XL
245 }
246}