From 451363dc59b3030fee82e4faf04684c068f619cc Mon Sep 17 00:00:00 2001 From: Devin R <devin.ragotzy@gmail.com> Date: Thu, 14 May 2020 18:20:07 -0400 Subject: [PATCH] still working on displaying nested imports --- clippy_lints/src/macro_use.rs | 291 +++++++++++++++++++++++++--------- 1 file changed, 214 insertions(+), 77 deletions(-) diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 9c8035f54a9..8dddd6d716d 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -2,7 +2,7 @@ use crate::utils::{in_macro, snippet, span_lint_and_sugg}; use hir::def::{DefKind, Res}; use if_chain::if_chain; use rustc_ast::ast; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -38,7 +38,7 @@ pub struct MacroRefData { } impl MacroRefData { - pub fn new(name: &str, callee: Span, cx: &LateContext<'_, '_>) -> Self { + pub fn new(name: String, callee: Span, cx: &LateContext<'_, '_>) -> Self { let mut path = cx.sess().source_map().span_to_filename(callee).to_string(); // std lib paths are <::std::module::file type> @@ -50,7 +50,7 @@ impl MacroRefData { path = path.split(' ').next().unwrap().to_string(); } Self { - name: name.to_string(), + name, path, } } @@ -69,23 +69,31 @@ pub struct MacroUseImports { impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]); impl MacroUseImports { - fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) { - if !self.collected.contains(&call_site) { - let name = if name.contains("::") { - name.split("::").last().unwrap().to_string() - } else { - name.to_string() - }; - - self.mac_refs.push(MacroRefData::new(&name, callee, cx)); - self.collected.insert(call_site); + fn push_unique_macro(&mut self, cx: &LateContext<'_, '_>, span: Span) { + let call_site = span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = span.source_callee() { + if !self.collected.contains(&call_site) { + let name = if name.contains("::") { + name.split("::").last().unwrap().to_string() + } else { + name.to_string() + }; + + self.mac_refs.push(MacroRefData::new(name, callee.def_site, cx)); + self.collected.insert(call_site); + } } } - fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, name: &str, call_site: Span, callee: Span) { - if !self.collected.contains(&call_site) { - self.mac_refs.push(MacroRefData::new(&name, callee, cx)); - self.collected.insert(call_site); + fn push_unique_macro_pat_ty(&mut self, cx: &LateContext<'_, '_>, span: Span) { + let call_site = span.source_callsite(); + let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); + if let Some(callee) = span.source_callee() { + if !self.collected.contains(&call_site) { + self.mac_refs.push(MacroRefData::new(name.to_string(), callee.def_site, cx)); + self.collected.insert(call_site); + } } } } @@ -93,104 +101,233 @@ impl MacroUseImports { impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports { fn check_item(&mut self, cx: &LateContext<'_, '_>, item: &hir::Item<'_>) { if_chain! { - if cx.sess().opts.edition == Edition::Edition2018; - if let hir::ItemKind::Use(path, _kind) = &item.kind; - if let Some(mac_attr) = item - .attrs - .iter() - .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); - if let Res::Def(DefKind::Mod, id) = path.res; - then { - for kid in cx.tcx.item_children(id).iter() { - if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { - let span = mac_attr.span; - self.imports.push((cx.tcx.def_path_str(mac_id), span)); - } - } - } else { - if in_macro(item.span) { - let call_site = item.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = item.span.source_callee() { - if !self.collected.contains(&call_site) { - self.mac_refs.push(MacroRefData::new(&name, callee.def_site, cx)); - self.collected.insert(call_site); - } - } + if cx.sess().opts.edition == Edition::Edition2018; + if let hir::ItemKind::Use(path, _kind) = &item.kind; + if let Some(mac_attr) = item + .attrs + .iter() + .find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string())); + if let Res::Def(DefKind::Mod, id) = path.res; + then { + for kid in cx.tcx.item_children(id).iter() { + if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { + let span = mac_attr.span; + self.imports.push((cx.tcx.def_path_str(mac_id), span)); } + } + } else { + if in_macro(item.span) { + self.push_unique_macro_pat_ty(cx, item.span); + } } } } fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) { if in_macro(attr.span) { - let call_site = attr.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = attr.span.source_callee() { - self.push_unique_macro(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro(cx, attr.span); } } fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) { if in_macro(expr.span) { - let call_site = expr.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = expr.span.source_callee() { - self.push_unique_macro(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro(cx, expr.span); } } fn check_stmt(&mut self, cx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) { if in_macro(stmt.span) { - let call_site = stmt.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = stmt.span.source_callee() { - self.push_unique_macro(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro(cx, stmt.span); } } fn check_pat(&mut self, cx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) { if in_macro(pat.span) { - let call_site = pat.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = pat.span.source_callee() { - self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro_pat_ty(cx, pat.span); } } fn check_ty(&mut self, cx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) { if in_macro(ty.span) { - let call_site = ty.span.source_callsite(); - let name = snippet(cx, cx.sess().source_map().span_until_char(call_site, '!'), "_"); - if let Some(callee) = ty.span.source_callee() { - self.push_unique_macro_pat_ty(cx, &name, call_site, callee.def_site); - } + self.push_unique_macro_pat_ty(cx, ty.span); } } fn check_crate_post(&mut self, cx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) { + let mut import_map = FxHashMap::default(); for (import, span) in &self.imports { - let matched = self.mac_refs.iter().any(|mac| import.ends_with(&mac.name)); + let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name)); + + if let Some(idx) = found_idx { + let _ = self.mac_refs.remove(idx); + proccess_macro_path(*span, import, &mut import_map); + } + } + println!("{:#?}", import_map); + let mut imports = vec![]; + for (root, rest) in import_map { + let mut path = format!("use {}::", root); + let mut s = None; + let mut count = 1; + let rest_len = rest.len(); + if rest_len > 1 { + path.push_str("{"); + } + for m in &rest { + println!("{} => {:?}", root, m); + if count == 1 { + s = Some(m.span()); + } + + let comma = if rest_len == count { "" } else { ", " }; + match m { + ModPath::Item { item, .. } => { + path.push_str(&format!("{}{}", item, comma)); + } + ModPath::Nested { names, item, span } => { + let nested = rest.iter() + // filter "self" out + .filter(|other_m| other_m != &m) + // this matches the first path segment and filters non ModPath::Nested items + .filter(|other_m| other_m.matches(0, m)) + .collect::<Vec<_>>(); - if matched { - self.mac_refs.retain(|mac| !import.ends_with(&mac.name)); - let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition"; + println!("{:#?}", nested); + + if nested.is_empty() { + path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma)) + } else { + // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2} + let mod_path = if names.len() - 1 > 0 { + ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, } + } else { + ModPath::Item { item: names[0].to_string(), span: *span, } + }; + let names = recursive_path_push(mod_path, comma, &rest, String::new()); + path.push_str(&format!("{}::{{{}}}{}", names, item, comma)) + } + } + } + count += 1; + } + if rest_len > 1 { + path.push_str("};"); + } + if let Some(span) = s { + imports.push((span, path)) + } + } + + if !self.mac_refs.is_empty() { + // TODO if not empty we found one we could not make a suggestion for + // such as std::prelude::v1 or something else I haven't thought of. + // If we defer the calling of span_lint_and_sugg we can make a decision about its + // applicability? + } else { + for (span, import) in imports { let help = format!("use {}", import); span_lint_and_sugg( cx, MACRO_USE_IMPORTS, - *span, - msg, + span, + "`macro_use` attributes are no longer needed in the Rust 2018 edition", "remove the attribute and import the macro directly, try", help, Applicability::MaybeIncorrect, ) } } - if !self.mac_refs.is_empty() { - // TODO if not empty we found one we could not make a suggestion for - // such as std::prelude::v1 or something else I haven't thought of. - // If we defer the calling of span_lint_and_sugg we can make a decision about its - // applicability? + } +} + +#[derive(Debug, PartialEq)] +enum ModPath { + Item { item: String, span: Span, }, + Nested { names: Vec<String>, item: String, span: Span, }, +} + +impl ModPath { + fn span(&self) -> Span { + match self { + Self::Item { span, .. } => *span, + Self::Nested { span, .. } => *span, + } + } + + fn item(&self) -> &str { + match self { + Self::Item { item, .. } => item, + Self::Nested { item, .. } => item, + } + } + + fn matches(&self, idx: usize, other: &ModPath) -> bool { + match (self, other) { + (Self::Item { item, .. }, Self::Item { item: other_item, .. }) => item == other_item, + (Self::Nested { names, .. }, Self::Nested { names: other_names, .. }) => { + match (names.get(idx), other_names.get(idx)) { + (Some(seg), Some(other_seg)) => seg == other_seg, + (_, _) => false, + } + } + (_, _) => false, } } } + +fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap<String, Vec<ModPath>>) { + let mut mod_path = import.split("::").collect::<Vec<_>>(); + + if mod_path.len() == 2 { + let item_list = import_map.entry(mod_path[0].to_string()) + .or_insert(vec![]); + + if !item_list.iter().any(|mods| mods.item() == mod_path[1]) { + item_list.push(ModPath::Item{ + item: mod_path[1].to_string(), + span, + }); + } + } else if mod_path.len() > 2 { + let first = mod_path.remove(0); + let name = mod_path.remove(mod_path.len() - 1); + + let nested = ModPath::Nested { + names: mod_path.into_iter().map(ToString::to_string).collect(), + item: name.to_string(), + span, + }; + import_map.entry(first.to_string()) + .or_insert(vec![]) + .push(nested); + } else { + unreachable!("test to see if code path hit TODO REMOVE") + } +} + +fn recursive_path_push(module: ModPath, comma: &str, rest: &[ModPath], mut path: String) -> String { + match &module { + ModPath::Item { item, .. } => { + path.push_str(&format!("{}{}", item, comma)); + } + ModPath::Nested { names, item, span } => { + let nested = rest.iter() + // filter "self" out + .filter(|other_m| other_m != &&module) + // this matches the first path segment and filters non ModPath::Nested items + .filter(|other_m| other_m.matches(0, &module)) + .collect::<Vec<_>>(); + + println!("{:#?}", nested); + + if nested.is_empty() { + path.push_str(&format!("{}::{}{}", names.join("::").to_string(), item, comma)) + } else { + // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2} + let mod_path = if names.len() - 1 > 0 { + ModPath::Nested { names: names.clone(), item: item.to_string(), span: *span, } + } else { + ModPath::Item { item: names[0].to_string(), span: *span, } + }; + let names = recursive_path_push(mod_path, comma, rest, path.to_string()); + // path.push_str(&format!("{}{}", item, comma)); + } + } + } + path +}