Skip to content

Commit 451363d

Browse files
committed
still working on displaying nested imports
1 parent 8bc106b commit 451363d

File tree

1 file changed

+214
-77
lines changed

1 file changed

+214
-77
lines changed

clippy_lints/src/macro_use.rs

Lines changed: 214 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use crate::utils::{in_macro, snippet, span_lint_and_sugg};
22
use hir::def::{DefKind, Res};
33
use if_chain::if_chain;
44
use rustc_ast::ast;
5-
use rustc_data_structures::fx::FxHashSet;
5+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
66
use rustc_errors::Applicability;
77
use rustc_hir as hir;
88
use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -38,7 +38,7 @@ pub struct MacroRefData {
3838
}
3939

4040
impl MacroRefData {
41-
pub fn new(name: &str, callee: Span, cx: &LateContext<'_, '_>) -> Self {
41+
pub fn new(name: String, callee: Span, cx: &LateContext<'_, '_>) -> Self {
4242
let mut path = cx.sess().source_map().span_to_filename(callee).to_string();
4343

4444
// std lib paths are <::std::module::file type>
@@ -50,7 +50,7 @@ impl MacroRefData {
5050
path = path.split(' ').next().unwrap().to_string();
5151
}
5252
Self {
53-
name: name.to_string(),
53+
name,
5454
path,
5555
}
5656
}
@@ -69,128 +69,265 @@ pub struct MacroUseImports {
6969
impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
7070

7171
impl MacroUseImports {
72-
fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) {
73-
if !self.collected.contains(&call_site) {
74-
let name = if name.contains("::") {
75-
name.split("::").last().unwrap().to_string()
76-
} else {
77-
name.to_string()
78-
};
79-
80-
self.mac_refs.push(MacroRefData::new(&name, callee, cx));
81-
self.collected.insert(call_site);
72+
fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, span: Span) {
73+
let call_site = span.source_callsite();
74+
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
75+
if let Some(callee) = span.source_callee() {
76+
if !self.collected.contains(&call_site) {
77+
let name = if name.contains("::") {
78+
name.split("::").last().unwrap().to_string()
79+
} else {
80+
name.to_string()
81+
};
82+
83+
self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx));
84+
self.collected.insert(call_site);
85+
}
8286
}
8387
}
8488

85-
fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) {
86-
if !self.collected.contains(&call_site) {
87-
self.mac_refs.push(MacroRefData::new(&name, callee, cx));
88-
self.collected.insert(call_site);
89+
fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, span: Span) {
90+
let call_site = span.source_callsite();
91+
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
92+
if let Some(callee) = span.source_callee() {
93+
if !self.collected.contains(&call_site) {
94+
self.mac_refs.push(MacroRefData::new(name.to_string(), callee.def_site, cx));
95+
self.collected.insert(call_site);
96+
}
8997
}
9098
}
9199
}
92100

93101
impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
94102
fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) {
95103
if_chain! {
96-
if cx.sess().opts.edition == Edition::Edition2018;
97-
if let hir::ItemKind::Use(path, _kind) = &item.kind;
98-
if let Some(mac_attr) = item
99-
.attrs
100-
.iter()
101-
.find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
102-
if let Res::Def(DefKind::Mod, id) = path.res;
103-
then {
104-
for kid in cx.tcx.item_children(id).iter() {
105-
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
106-
let span = mac_attr.span;
107-
self.imports.push((cx.tcx.def_path_str(mac_id), span));
108-
}
109-
}
110-
} else {
111-
if in_macro(item.span) {
112-
let call_site = item.span.source_callsite();
113-
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
114-
if let Some(callee) = item.span.source_callee() {
115-
if !self.collected.contains(&call_site) {
116-
self.mac_refs.push(MacroRefData::new(&name, callee.def_site, cx));
117-
self.collected.insert(call_site);
118-
}
119-
}
104+
if cx.sess().opts.edition == Edition::Edition2018;
105+
if let hir::ItemKind::Use(path, _kind) = &item.kind;
106+
if let Some(mac_attr) = item
107+
.attrs
108+
.iter()
109+
.find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
110+
if let Res::Def(DefKind::Mod, id) = path.res;
111+
then {
112+
for kid in cx.tcx.item_children(id).iter() {
113+
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
114+
let span = mac_attr.span;
115+
self.imports.push((cx.tcx.def_path_str(mac_id), span));
120116
}
117+
}
118+
} else {
119+
if in_macro(item.span) {
120+
self.push_unique_macro_pat_ty(cx, item.span);
121+
}
121122
}
122123
}
123124
}
124125
fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) {
125126
if in_macro(attr.span) {
126-
let call_site = attr.span.source_callsite();
127-
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
128-
if let Some(callee) = attr.span.source_callee() {
129-
self.push_unique_macro(cx, &name, call_site, callee.def_site);
130-
}
127+
self.push_unique_macro(cx, attr.span);
131128
}
132129
}
133130
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
134131
if in_macro(expr.span) {
135-
let call_site = expr.span.source_callsite();
136-
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
137-
if let Some(callee) = expr.span.source_callee() {
138-
self.push_unique_macro(cx, &name, call_site, callee.def_site);
139-
}
132+
self.push_unique_macro(cx, expr.span);
140133
}
141134
}
142135
fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) {
143136
if in_macro(stmt.span) {
144-
let call_site = stmt.span.source_callsite();
145-
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
146-
if let Some(callee) = stmt.span.source_callee() {
147-
self.push_unique_macro(cx, &name, call_site, callee.def_site);
148-
}
137+
self.push_unique_macro(cx, stmt.span);
149138
}
150139
}
151140
fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) {
152141
if in_macro(pat.span) {
153-
let call_site = pat.span.source_callsite();
154-
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
155-
if let Some(callee) = pat.span.source_callee() {
156-
self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site);
157-
}
142+
self.push_unique_macro_pat_ty(cx, pat.span);
158143
}
159144
}
160145
fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) {
161146
if in_macro(ty.span) {
162-
let call_site = ty.span.source_callsite();
163-
let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_");
164-
if let Some(callee) = ty.span.source_callee() {
165-
self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site);
166-
}
147+
self.push_unique_macro_pat_ty(cx, ty.span);
167148
}
168149
}
169150

170151
fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) {
152+
let mut import_map = FxHashMap::default();
171153
for (import, span) in &self.imports {
172-
let matched = self.mac_refs.iter().any(|mac| import.ends_with(&mac.name));
154+
let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
155+
156+
if let Some(idx) = found_idx {
157+
let _ = self.mac_refs.remove(idx);
158+
proccess_macro_path(*span, import, &mut import_map);
159+
}
160+
}
161+
println!("{:#?}", import_map);
162+
let mut imports = vec![];
163+
for (root, rest) in import_map {
164+
let mut path = format!("use {}::", root);
165+
let mut s = None;
166+
let mut count = 1;
167+
let rest_len = rest.len();
168+
if rest_len > 1 {
169+
path.push_str("{");
170+
}
171+
for m in &rest {
172+
println!("{} => {:?}", root, m);
173+
if count == 1 {
174+
s = Some(m.span());
175+
}
176+
177+
let comma = if rest_len == count { "" } else { ", " };
178+
match m {
179+
ModPath::Item { item, .. } => {
180+
path.push_str(&format!("{}{}", item, comma));
181+
}
182+
ModPath::Nested { names, item, span } => {
183+
let nested = rest.iter()
184+
// filter "self" out
185+
.filter(|other_m| other_m != &m)
186+
// this matches the first path segment and filters non ModPath::Nested items
187+
.filter(|other_m| other_m.matches(0, m))
188+
.collect::<Vec<_>>();
189+
190+
println!("{:#?}", nested);
191+
192+
if nested.is_empty() {
193+
path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma))
194+
} else {
195+
// use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2}
196+
let mod_path = if names.len() - 1 > 0 {
197+
ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, }
198+
} else {
199+
ModPath::Item { item: names[0].to_string(), span: *span, }
200+
};
201+
let names = recursive_path_push(mod_path, comma, &rest, String::new());
202+
path.push_str(&format!("{}::{{{}}}{}", names, item, comma))
203+
}
204+
}
205+
}
206+
count += 1;
207+
}
208+
if rest_len > 1 {
209+
path.push_str("};");
210+
}
211+
if let Some(span) = s {
212+
imports.push((span, path))
213+
}
214+
}
173215

174-
if matched {
175-
self.mac_refs.retain(|mac| !import.ends_with(&mac.name));
176-
let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
216+
if !self.mac_refs.is_empty() {
217+
// TODO if not empty we found one we could not make a suggestion for
218+
// such as std::prelude::v1 or something else I haven't thought of.
219+
// If we defer the calling of span_lint_and_sugg we can make a decision about its
220+
// applicability?
221+
} else {
222+
for (span, import) in imports {
177223
let help = format!("use {}", import);
178224
span_lint_and_sugg(
179225
cx,
180226
MACRO_USE_IMPORTS,
181-
*span,
182-
msg,
227+
span,
228+
"`macro_use` attributes are no longer needed in the Rust 2018 edition",
183229
"remove the attribute and import the macro directly, try",
184230
help,
185231
Applicability::MaybeIncorrect,
186232
)
187233
}
188234
}
189-
if !self.mac_refs.is_empty() {
190-
// TODO if not empty we found one we could not make a suggestion for
191-
// such as std::prelude::v1 or something else I haven't thought of.
192-
// If we defer the calling of span_lint_and_sugg we can make a decision about its
193-
// applicability?
235+
}
236+
}
237+
238+
#[derive(Debug, PartialEq)]
239+
enum ModPath {
240+
Item { item: String, span: Span, },
241+
Nested { names: Vec<String>, item: String, span: Span, },
242+
}
243+
244+
impl ModPath {
245+
fn span(&self) -> Span {
246+
match self {
247+
Self::Item { span, .. } => *span,
248+
Self::Nested { span, .. } => *span,
249+
}
250+
}
251+
252+
fn item(&self) -> &str {
253+
match self {
254+
Self::Item { item, .. } => item,
255+
Self::Nested { item, .. } => item,
256+
}
257+
}
258+
259+
fn matches(&self, idx: usize, other: &ModPath) -> bool {
260+
match (self, other) {
261+
(Self::Item { item, .. }, Self::Item { item: other_item, .. }) => item == other_item,
262+
(Self::Nested { names, .. }, Self::Nested { names: other_names, .. }) => {
263+
match (names.get(idx), other_names.get(idx)) {
264+
(Some(seg), Some(other_seg)) => seg == other_seg,
265+
(_, _) => false,
266+
}
267+
}
268+
(_, _) => false,
269+
}
270+
}
271+
}
272+
273+
fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap<String, Vec<ModPath>>) {
274+
let mut mod_path = import.split("::").collect::<Vec<_>>();
275+
276+
if mod_path.len() == 2 {
277+
let item_list = import_map.entry(mod_path[0].to_string())
278+
.or_insert(vec![]);
279+
280+
if !item_list.iter().any(|mods| mods.item() == mod_path[1]) {
281+
item_list.push(ModPath::Item{
282+
item: mod_path[1].to_string(),
283+
span,
284+
});
285+
}
286+
} else if mod_path.len() > 2 {
287+
let first = mod_path.remove(0);
288+
let name = mod_path.remove(mod_path.len() - 1);
289+
290+
let nested = ModPath::Nested {
291+
names: mod_path.into_iter().map(ToString::to_string).collect(),
292+
item: name.to_string(),
293+
span,
294+
};
295+
import_map.entry(first.to_string())
296+
.or_insert(vec![])
297+
.push(nested);
298+
} else {
299+
unreachable!("test to see if code path hit TODO REMOVE")
300+
}
301+
}
302+
303+
fn recursive_path_push(module: ModPath, comma: &str, rest: &[ModPath], mut path: String) -> String {
304+
match &module {
305+
ModPath::Item { item, .. } => {
306+
path.push_str(&format!("{}{}", item, comma));
307+
}
308+
ModPath::Nested { names, item, span } => {
309+
let nested = rest.iter()
310+
// filter "self" out
311+
.filter(|other_m| other_m != &&module)
312+
// this matches the first path segment and filters non ModPath::Nested items
313+
.filter(|other_m| other_m.matches(0, &module))
314+
.collect::<Vec<_>>();
315+
316+
println!("{:#?}", nested);
317+
318+
if nested.is_empty() {
319+
path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma))
320+
} else {
321+
// use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2}
322+
let mod_path = if names.len() - 1 > 0 {
323+
ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, }
324+
} else {
325+
ModPath::Item { item: names[0].to_string(), span: *span, }
326+
};
327+
let names = recursive_path_push(mod_path, comma, rest, path.to_string());
328+
// path.push_str(&format!("{}{}", item, comma));
329+
}
194330
}
195331
}
332+
path
196333
}

0 commit comments

Comments
 (0)