Skip to content

Commit 0500d47

Browse files
committed
Let's do it the easy way
Queries are cool, but too hard to find.
1 parent cf048b8 commit 0500d47

File tree

1 file changed

+40
-202
lines changed

1 file changed

+40
-202
lines changed

clippy_lints/src/wildcard_imports.rs

Lines changed: 40 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg};
22
use if_chain::if_chain;
3-
use rustc::ty::DefIdTree;
4-
use rustc_data_structures::fx::FxHashSet;
53
use rustc_errors::Applicability;
6-
use rustc_hir::def_id::DefId;
7-
use rustc_hir::intravisit::{walk_item, NestedVisitorMap, Visitor};
84
use rustc_hir::*;
95
use rustc_lint::{LateContext, LateLintPass};
106
use rustc_session::{declare_lint_pass, declare_tool_lint};
11-
use rustc_span::{symbol::Symbol, BytePos};
7+
use rustc_span::BytePos;
128

139
declare_clippy_lint! {
1410
/// **What it does:** Checks for wildcard imports `use _::*`.
@@ -56,213 +52,55 @@ impl LateLintPass<'_, '_> for WildcardImports {
5652
if_chain! {
5753
if !in_macro(item.span);
5854
if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind;
59-
if let Some(def_id) = use_path.res.opt_def_id();
55+
let used_imports = cx.tcx.names_imported_by_glob_use(item.hir_id.owner_def_id());
56+
if !used_imports.is_empty();
6057
then {
61-
let hir = cx.tcx.hir();
62-
let parent_id = hir.get_parent_item(item.hir_id);
63-
let (items, in_module) = if parent_id == CRATE_HIR_ID {
64-
let items = hir
65-
.krate()
66-
.module
67-
.item_ids
68-
.iter()
69-
.map(|item_id| hir.get(item_id.id))
70-
.filter_map(|node| {
71-
if let Node::Item(item) = node {
72-
Some(item)
73-
} else {
74-
None
75-
}
76-
})
77-
.collect();
78-
(items, true)
79-
} else if let Node::Item(item) = hir.get(parent_id) {
80-
(vec![item], false)
58+
let mut applicability = Applicability::MachineApplicable;
59+
let import_source = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
60+
let (span, braced_glob) = if import_source.is_empty() {
61+
// This is a `_::{_, *}` import
62+
(
63+
use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
64+
true,
65+
)
8166
} else {
82-
(vec![], false)
67+
(
68+
use_path.span.with_hi(use_path.span.hi() + BytePos(3)),
69+
false,
70+
)
8371
};
8472

85-
let mut import_used_visitor = ImportsUsedVisitor {
86-
cx,
87-
wildcard_def_id: def_id,
88-
in_module,
89-
used_imports: FxHashSet::default(),
90-
};
91-
for item in items {
92-
import_used_visitor.visit_item(item);
93-
}
94-
95-
if !import_used_visitor.used_imports.is_empty() {
96-
let module_name = use_path
97-
.segments
73+
let imports_string = if used_imports.len() == 1 {
74+
used_imports.iter().next().unwrap().to_string()
75+
} else {
76+
let mut imports = used_imports
9877
.iter()
99-
.last()
100-
.expect("path has at least one segment")
101-
.ident
102-
.name;
103-
104-
let mut applicability = Applicability::MachineApplicable;
105-
let import_source = snippet_with_applicability(cx, use_path.span, "..", &mut applicability);
106-
let (span, braced_glob) = if import_source.is_empty() {
107-
// This is a `_::{_, *}` import
108-
// Probably it's `_::{self, *}`, in that case we don't want to suggest to
109-
// import `self`.
110-
// If it is something else, we also don't want to include `self` in the
111-
// suggestion, since either it was imported through another use statement:
112-
// ```
113-
// use foo::bar;
114-
// use foo::bar::{baz, *};
115-
// ```
116-
// or it couldn't be used anywhere.
117-
(
118-
use_path.span.with_hi(use_path.span.hi() + BytePos(1)),
119-
true,
120-
)
121-
} else {
122-
(
123-
use_path.span.with_hi(use_path.span.hi() + BytePos(3)),
124-
false,
125-
)
126-
};
127-
128-
let imports_string = if import_used_visitor.used_imports.len() == 1 {
129-
// We don't need to check for accidental suggesting the module name instead
130-
// of `self` here, since if `used_imports.len() == 1`, and the only usage
131-
// is `self`, then it's not through a `*` and if there is a `*`, it gets
132-
// already linted by `unused_imports` of rustc.
133-
import_used_visitor.used_imports.iter().next().unwrap().to_string()
134-
} else {
135-
let mut imports = import_used_visitor
136-
.used_imports
137-
.iter()
138-
.filter_map(|import_name| {
139-
if braced_glob && *import_name == module_name {
140-
None
141-
} else if *import_name == module_name {
142-
Some("self".to_string())
143-
} else {
144-
Some(import_name.to_string())
145-
}
146-
})
147-
.collect::<Vec<_>>();
148-
imports.sort();
149-
if braced_glob {
150-
imports.join(", ")
151-
} else {
152-
format!("{{{}}}", imports.join(", "))
153-
}
154-
};
155-
156-
let sugg = if import_source.is_empty() {
157-
imports_string
78+
.map(ToString::to_string)
79+
.collect::<Vec<_>>();
80+
imports.sort();
81+
if braced_glob {
82+
imports.join(", ")
15883
} else {
159-
format!("{}::{}", import_source, imports_string)
160-
};
161-
162-
span_lint_and_sugg(
163-
cx,
164-
WILDCARD_IMPORTS,
165-
span,
166-
"usage of wildcard import",
167-
"try",
168-
sugg,
169-
applicability,
170-
);
171-
}
172-
}
173-
}
174-
}
175-
}
176-
177-
struct ImportsUsedVisitor<'a, 'tcx> {
178-
cx: &'a LateContext<'a, 'tcx>,
179-
wildcard_def_id: def_id::DefId,
180-
in_module: bool,
181-
used_imports: FxHashSet<Symbol>,
182-
}
183-
184-
impl<'a, 'tcx> Visitor<'tcx> for ImportsUsedVisitor<'a, 'tcx> {
185-
type Map = Map<'tcx>;
186-
187-
fn visit_item(&mut self, item: &'tcx Item<'_>) {
188-
match item.kind {
189-
ItemKind::Use(..) => {},
190-
ItemKind::Mod(..) if self.in_module => {},
191-
ItemKind::Mod(..) => self.in_module = true,
192-
_ => walk_item(self, item),
193-
}
194-
}
195-
196-
fn visit_path(&mut self, path: &Path<'_>, _: HirId) {
197-
if let Some(def_id) = self.first_path_segment_def_id(path) {
198-
// Check if the function/enum/... was exported
199-
if let Some(exports) = self.cx.tcx.module_exports(self.wildcard_def_id) {
200-
for export in exports {
201-
if let Some(export_def_id) = export.res.opt_def_id() {
202-
if export_def_id == def_id {
203-
self.used_imports.insert(
204-
path.segments
205-
.iter()
206-
.next()
207-
.expect("path has at least one segment")
208-
.ident
209-
.name,
210-
);
211-
return;
212-
}
84+
format!("{{{}}}", imports.join(", "))
21385
}
214-
}
215-
}
216-
217-
// Check if it is directly in the module
218-
if let Some(parent_def_id) = self.cx.tcx.parent(def_id) {
219-
if self.wildcard_def_id == parent_def_id {
220-
self.used_imports.insert(
221-
path.segments
222-
.iter()
223-
.next()
224-
.expect("path has at least one segment")
225-
.ident
226-
.name,
227-
);
228-
}
229-
}
230-
}
231-
}
232-
233-
fn nested_visit_map(&mut self) -> NestedVisitorMap<'_, Self::Map> {
234-
NestedVisitorMap::All(&self.cx.tcx.hir())
235-
}
236-
}
86+
};
23787

238-
impl ImportsUsedVisitor<'_, '_> {
239-
fn skip_def_id(&self, def_id: DefId) -> DefId {
240-
let def_key = self.cx.tcx.def_key(def_id);
241-
match def_key.disambiguated_data.data {
242-
DefPathData::Ctor => {
243-
if let Some(def_id) = self.cx.tcx.parent(def_id) {
244-
self.skip_def_id(def_id)
88+
let sugg = if import_source.is_empty() {
89+
imports_string
24590
} else {
246-
def_id
247-
}
248-
},
249-
_ => def_id,
250-
}
251-
}
91+
format!("{}::{}", import_source, imports_string)
92+
};
25293

253-
fn first_path_segment_def_id(&self, path: &Path<'_>) -> Option<DefId> {
254-
path.res.opt_def_id().and_then(|mut def_id| {
255-
def_id = self.skip_def_id(def_id);
256-
for _ in path.segments.iter().skip(1) {
257-
def_id = self.skip_def_id(def_id);
258-
if let Some(parent_def_id) = self.cx.tcx.parent(def_id) {
259-
def_id = parent_def_id;
260-
} else {
261-
return None;
262-
}
94+
span_lint_and_sugg(
95+
cx,
96+
WILDCARD_IMPORTS,
97+
span,
98+
"usage of wildcard import",
99+
"try",
100+
sugg,
101+
applicability,
102+
);
263103
}
264-
265-
Some(def_id)
266-
})
104+
}
267105
}
268106
}

0 commit comments

Comments
 (0)