|
1 | 1 | use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg}; |
2 | 2 | use if_chain::if_chain; |
3 | | -use rustc::ty::DefIdTree; |
4 | | -use rustc_data_structures::fx::FxHashSet; |
5 | 3 | use rustc_errors::Applicability; |
6 | | -use rustc_hir::def_id::DefId; |
7 | | -use rustc_hir::intravisit::{walk_item, NestedVisitorMap, Visitor}; |
8 | 4 | use rustc_hir::*; |
9 | 5 | use rustc_lint::{LateContext, LateLintPass}; |
10 | 6 | use rustc_session::{declare_lint_pass, declare_tool_lint}; |
11 | | -use rustc_span::{symbol::Symbol, BytePos}; |
| 7 | +use rustc_span::BytePos; |
12 | 8 |
|
13 | 9 | declare_clippy_lint! { |
14 | 10 | /// **What it does:** Checks for wildcard imports `use _::*`. |
@@ -59,213 +55,55 @@ impl LateLintPass<'_, '_> for WildcardImports { |
59 | 55 | if_chain! { |
60 | 56 | if !in_macro(item.span); |
61 | 57 | if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind; |
62 | | - if let Some(def_id) = use_path.res.opt_def_id(); |
| 58 | + let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner_def_id()); |
| 59 | + if !used_imports.is_empty(); // Already handled by `unused_imports` |
63 | 60 | then { |
64 | | - let hir = cx.tcx.hir(); |
65 | | - let parent_id = hir.get_parent_item(item.hir_id); |
66 | | - let (items, in_module) = if parent_id == CRATE_HIR_ID { |
67 | | - let items = hir |
68 | | - .krate() |
69 | | - .module |
70 | | - .item_ids |
71 | | - .iter() |
72 | | - .map(|item_id| hir.get(item_id.id)) |
73 | | - .filter_map(|node| { |
74 | | - if let Node::Item(item) = node { |
75 | | - Some(item) |
76 | | - } else { |
77 | | - None |
78 | | - } |
79 | | - }) |
80 | | - .collect(); |
81 | | - (items, true) |
82 | | - } else if let Node::Item(item) = hir.get(parent_id) { |
83 | | - (vec![item], false) |
| 61 | + let mut applicability = Applicability::MachineApplicable; |
| 62 | + let import_source = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); |
| 63 | + let (span, braced_glob) = if import_source.is_empty() { |
| 64 | + // This is a `_::{_, *}` import |
| 65 | + ( |
| 66 | + use_path.span.with_hi(use_path.span.hi() + BytePos(1)), |
| 67 | + true, |
| 68 | + ) |
84 | 69 | } else { |
85 | | - (vec![], false) |
| 70 | + ( |
| 71 | + use_path.span.with_hi(use_path.span.hi() + BytePos(3)), |
| 72 | + false, |
| 73 | + ) |
86 | 74 | }; |
87 | 75 |
|
88 | | - let mut import_used_visitor = ImportsUsedVisitor { |
89 | | - cx, |
90 | | - wildcard_def_id: def_id, |
91 | | - in_module, |
92 | | - used_imports: FxHashSet::default(), |
93 | | - }; |
94 | | - for item in items { |
95 | | - import_used_visitor.visit_item(item); |
96 | | - } |
97 | | - |
98 | | - if !import_used_visitor.used_imports.is_empty() { |
99 | | - let module_name = use_path |
100 | | - .segments |
| 76 | + let imports_string = if used_imports.len() == 1 { |
| 77 | + used_imports.iter().next().unwrap().to_string() |
| 78 | + } else { |
| 79 | + let mut imports = used_imports |
101 | 80 | .iter() |
102 | | - .last() |
103 | | - .expect("path has at least one segment") |
104 | | - .ident |
105 | | - .name; |
106 | | - |
107 | | - let mut applicability = Applicability::MachineApplicable; |
108 | | - let import_source = snippet_with_applicability(cx, use_path.span, "..", &mut applicability); |
109 | | - let (span, braced_glob) = if import_source.is_empty() { |
110 | | - // This is a `_::{_, *}` import |
111 | | - // Probably it's `_::{self, *}`, in that case we don't want to suggest to |
112 | | - // import `self`. |
113 | | - // If it is something else, we also don't want to include `self` in the |
114 | | - // suggestion, since either it was imported through another use statement: |
115 | | - // ``` |
116 | | - // use foo::bar; |
117 | | - // use foo::bar::{baz, *}; |
118 | | - // ``` |
119 | | - // or it couldn't be used anywhere. |
120 | | - ( |
121 | | - use_path.span.with_hi(use_path.span.hi() + BytePos(1)), |
122 | | - true, |
123 | | - ) |
124 | | - } else { |
125 | | - ( |
126 | | - use_path.span.with_hi(use_path.span.hi() + BytePos(3)), |
127 | | - false, |
128 | | - ) |
129 | | - }; |
130 | | - |
131 | | - let imports_string = if import_used_visitor.used_imports.len() == 1 { |
132 | | - // We don't need to check for accidental suggesting the module name instead |
133 | | - // of `self` here, since if `used_imports.len() == 1`, and the only usage |
134 | | - // is `self`, then it's not through a `*` and if there is a `*`, it gets |
135 | | - // already linted by `unused_imports` of rustc. |
136 | | - import_used_visitor.used_imports.iter().next().unwrap().to_string() |
137 | | - } else { |
138 | | - let mut imports = import_used_visitor |
139 | | - .used_imports |
140 | | - .iter() |
141 | | - .filter_map(|import_name| { |
142 | | - if braced_glob && *import_name == module_name { |
143 | | - None |
144 | | - } else if *import_name == module_name { |
145 | | - Some("self".to_string()) |
146 | | - } else { |
147 | | - Some(import_name.to_string()) |
148 | | - } |
149 | | - }) |
150 | | - .collect::<Vec<_>>(); |
151 | | - imports.sort(); |
152 | | - if braced_glob { |
153 | | - imports.join(", ") |
154 | | - } else { |
155 | | - format!("{{{}}}", imports.join(", ")) |
156 | | - } |
157 | | - }; |
158 | | - |
159 | | - let sugg = if import_source.is_empty() { |
160 | | - imports_string |
| 81 | + .map(ToString::to_string) |
| 82 | + .collect::<Vec<_>>(); |
| 83 | + imports.sort(); |
| 84 | + if braced_glob { |
| 85 | + imports.join(", ") |
161 | 86 | } else { |
162 | | - format!("{}::{}", import_source, imports_string) |
163 | | - }; |
164 | | - |
165 | | - span_lint_and_sugg( |
166 | | - cx, |
167 | | - WILDCARD_IMPORTS, |
168 | | - span, |
169 | | - "usage of wildcard import", |
170 | | - "try", |
171 | | - sugg, |
172 | | - applicability, |
173 | | - ); |
174 | | - } |
175 | | - } |
176 | | - } |
177 | | - } |
178 | | -} |
179 | | - |
180 | | -struct ImportsUsedVisitor<'a, 'tcx> { |
181 | | - cx: &'a LateContext<'a, 'tcx>, |
182 | | - wildcard_def_id: def_id::DefId, |
183 | | - in_module: bool, |
184 | | - used_imports: FxHashSet<Symbol>, |
185 | | -} |
186 | | - |
187 | | -impl<'a, 'tcx> Visitor<'tcx> for ImportsUsedVisitor<'a, 'tcx> { |
188 | | - type Map = Map<'tcx>; |
189 | | - |
190 | | - fn visit_item(&mut self, item: &'tcx Item<'_>) { |
191 | | - match item.kind { |
192 | | - ItemKind::Use(..) => {}, |
193 | | - ItemKind::Mod(..) if self.in_module => {}, |
194 | | - ItemKind::Mod(..) => self.in_module = true, |
195 | | - _ => walk_item(self, item), |
196 | | - } |
197 | | - } |
198 | | - |
199 | | - fn visit_path(&mut self, path: &Path<'_>, _: HirId) { |
200 | | - if let Some(def_id) = self.first_path_segment_def_id(path) { |
201 | | - // Check if the function/enum/... was exported |
202 | | - if let Some(exports) = self.cx.tcx.module_exports(self.wildcard_def_id) { |
203 | | - for export in exports { |
204 | | - if let Some(export_def_id) = export.res.opt_def_id() { |
205 | | - if export_def_id == def_id { |
206 | | - self.used_imports.insert( |
207 | | - path.segments |
208 | | - .iter() |
209 | | - .next() |
210 | | - .expect("path has at least one segment") |
211 | | - .ident |
212 | | - .name, |
213 | | - ); |
214 | | - return; |
215 | | - } |
| 87 | + format!("{{{}}}", imports.join(", ")) |
216 | 88 | } |
217 | | - } |
218 | | - } |
219 | | - |
220 | | - // Check if it is directly in the module |
221 | | - if let Some(parent_def_id) = self.cx.tcx.parent(def_id) { |
222 | | - if self.wildcard_def_id == parent_def_id { |
223 | | - self.used_imports.insert( |
224 | | - path.segments |
225 | | - .iter() |
226 | | - .next() |
227 | | - .expect("path has at least one segment") |
228 | | - .ident |
229 | | - .name, |
230 | | - ); |
231 | | - } |
232 | | - } |
233 | | - } |
234 | | - } |
235 | | - |
236 | | - fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> { |
237 | | - NestedVisitorMap::All(&self.cx.tcx.hir()) |
238 | | - } |
239 | | -} |
| 89 | + }; |
240 | 90 |
|
241 | | -impl ImportsUsedVisitor<'_, '_> { |
242 | | - fn skip_def_id(&self, def_id: DefId) -> DefId { |
243 | | - let def_key = self.cx.tcx.def_key(def_id); |
244 | | - match def_key.disambiguated_data.data { |
245 | | - DefPathData::Ctor => { |
246 | | - if let Some(def_id) = self.cx.tcx.parent(def_id) { |
247 | | - self.skip_def_id(def_id) |
| 91 | + let sugg = if import_source.is_empty() { |
| 92 | + imports_string |
248 | 93 | } else { |
249 | | - def_id |
250 | | - } |
251 | | - }, |
252 | | - _ => def_id, |
253 | | - } |
254 | | - } |
| 94 | + format!("{}::{}", import_source, imports_string) |
| 95 | + }; |
255 | 96 |
|
256 | | - fn first_path_segment_def_id(&self, path: &Path<'_>) -> Option<DefId> { |
257 | | - path.res.opt_def_id().and_then(|mut def_id| { |
258 | | - def_id = self.skip_def_id(def_id); |
259 | | - for _ in path.segments.iter().skip(1) { |
260 | | - def_id = self.skip_def_id(def_id); |
261 | | - if let Some(parent_def_id) = self.cx.tcx.parent(def_id) { |
262 | | - def_id = parent_def_id; |
263 | | - } else { |
264 | | - return None; |
265 | | - } |
| 97 | + span_lint_and_sugg( |
| 98 | + cx, |
| 99 | + WILDCARD_IMPORTS, |
| 100 | + span, |
| 101 | + "usage of wildcard import", |
| 102 | + "try", |
| 103 | + sugg, |
| 104 | + applicability, |
| 105 | + ); |
266 | 106 | } |
267 | | - |
268 | | - Some(def_id) |
269 | | - }) |
| 107 | + } |
270 | 108 | } |
271 | 109 | } |
0 commit comments