]>
Commit | Line | Data |
---|---|---|
cdc7bbd5 | 1 | use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; |
487cf647 | 2 | use rustc_ast::node_id::{NodeId, NodeMap}; |
add651ee | 3 | use rustc_ast::ptr::P; |
353b0b11 | 4 | use rustc_ast::visit::{walk_expr, Visitor}; |
add651ee | 5 | use rustc_ast::{Crate, Expr, ExprKind, Item, ItemKind, MacroDef, ModKind, Ty, TyKind, UseTreeKind}; |
f20569fa | 6 | use rustc_errors::Applicability; |
5099ac24 | 7 | use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; |
487cf647 | 8 | use rustc_session::{declare_tool_lint, impl_lint_pass}; |
add651ee FG |
9 | use rustc_span::edition::Edition; |
10 | use rustc_span::symbol::kw; | |
11 | use rustc_span::{Span, Symbol}; | |
f20569fa XL |
12 | |
13 | declare_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)] |
42 | pub 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 | ||
47 | struct SingleUse { | |
48 | name: Symbol, | |
49 | span: Span, | |
50 | item_id: NodeId, | |
51 | can_suggest: bool, | |
52 | } | |
53 | ||
54 | impl_lint_pass!(SingleComponentPathImports => [SINGLE_COMPONENT_PATH_IMPORTS]); | |
f20569fa XL |
55 | |
56 | impl 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)] |
92 | struct 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 | ||
105 | impl<'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 | 126 | impl 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 | } |