mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-16 22:16:53 +00:00
still working on displaying nested imports
This commit is contained in:
parent
8bc106b29d
commit
451363dc59
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user