Skip to content

Commit 321d64a

Browse files
committed
F#*$! Let's do it the easy way
Queries are cool, but too hard to find.
1 parent 7566a5c commit 321d64a

File tree

1 file changed

+40
-203
lines changed

1 file changed

+40
-203
lines changed

clippy_lints/src/wildcard_imports.rs

Lines changed: 40 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
use crate::utils::{in_macro, snippet_with_applicability, span_lint_and_sugg};
22
use if_chain::if_chain;
33
use rustc::declare_lint_pass;
4-
use rustc::hir::map::{definitions::DefPathData, Map};
54
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
6-
use rustc::ty::DefIdTree;
7-
use rustc_data_structures::fx::FxHashSet;
85
use rustc_errors::Applicability;
9-
use rustc_hir::def_id::DefId;
10-
use rustc_hir::intravisit::{walk_item, NestedVisitorMap, Visitor};
116
use rustc_hir::*;
127
use rustc_session::declare_tool_lint;
13-
use rustc_span::{symbol::Symbol, BytePos};
8+
use rustc_span::BytePos;
149

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

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

240-
impl ImportsUsedVisitor<'_, '_> {
241-
fn skip_def_id(&self, def_id: DefId) -> DefId {
242-
let def_key = self.cx.tcx.def_key(def_id);
243-
match def_key.disambiguated_data.data {
244-
DefPathData::Ctor => {
245-
if let Some(def_id) = self.cx.tcx.parent(def_id) {
246-
self.skip_def_id(def_id)
89+
let sugg = if import_source.is_empty() {
90+
imports_string
24791
} else {
248-
def_id
249-
}
250-
},
251-
_ => def_id,
252-
}
253-
}
92+
format!("{}::{}", import_source, imports_string)
93+
};
25494

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

0 commit comments

Comments
 (0)