Skip to content

Commit ede366b

Browse files
committed
collected all the imports and names
how to compare macro to import path add more imports to test
1 parent a4b8bb8 commit ede366b

File tree

7 files changed

+281
-73
lines changed

7 files changed

+281
-73
lines changed

clippy_lints/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ extern crate rustc_trait_selection;
6060
#[allow(unused_extern_crates)]
6161
extern crate rustc_typeck;
6262

63+
use rustc::session::Session;
6364
use rustc_data_structures::fx::FxHashSet;
6465
use rustc_lint::LintId;
6566
use rustc_session::Session;
@@ -1060,9 +1061,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10601061
let max_struct_bools = conf.max_struct_bools;
10611062
store.register_early_pass(move || box excessive_bools::ExcessiveBools::new(max_struct_bools, max_fn_params_bools));
10621063
store.register_early_pass(|| box option_env_unwrap::OptionEnvUnwrap);
1063-
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
1064-
store.register_late_pass(move || box wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports));
1065-
store.register_early_pass(|| box macro_use::MacroUseImports);
1064+
store.register_late_pass(|| box wildcard_imports::WildcardImports);
10661065
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
10671066
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
10681067
store.register_late_pass(|| box unnamed_address::UnnamedAddress);
@@ -1080,6 +1079,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10801079
single_char_binding_names_threshold,
10811080
});
10821081
store.register_early_pass(|| box unnested_or_patterns::UnnestedOrPatterns);
1082+
store.register_late_pass(|| box macro_use::MacroUseImports::default());
10831083

10841084
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10851085
LintId::of(&arithmetic::FLOAT_ARITHMETIC),

clippy_lints/src/macro_use.rs

Lines changed: 190 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
use crate::utils::{snippet, span_lint_and_sugg, in_macro};
1+
use crate::utils::{in_macro, snippet, span_lint_and_sugg};
2+
use hir::def::{DefKind, Res};
23
use if_chain::if_chain;
34
use rustc_ast::ast;
4-
use rustc_data_structures::fx::FxHashSet;
5+
use rustc_data_structures::fx::FxHashMap;
56
use rustc_errors::Applicability;
6-
use rustc_lint::{EarlyContext, EarlyLintPass};
7-
use rustc_session::{impl_lint_pass, declare_tool_lint};
7+
use rustc_hir as hir;
8+
use rustc_lint::{LateContext, LateLintPass, LintContext};
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
810
use rustc_span::{edition::Edition, Span};
911

1012
declare_clippy_lint! {
@@ -20,82 +22,226 @@ declare_clippy_lint! {
2022
/// #[macro_use]
2123
/// use lazy_static;
2224
/// ```
23-
pub MACRO_USE_IMPORT,
25+
pub MACRO_USE_IMPORTS,
2426
pedantic,
2527
"#[macro_use] is no longer needed"
2628
}
2729

28-
#[derive(Default)]
29-
pub struct MacroUseImport {
30-
collected: FxHashSet<Span>,
30+
const BRACKETS: &[char] = &['<', '>'];
31+
32+
/// MacroRefData includes the name of the macro
33+
/// and the path from `SourceMap::span_to_filename`.
34+
#[derive(Debug, Clone)]
35+
pub struct MacroRefData {
36+
name: String,
37+
path: String,
3138
}
3239

33-
impl_lint_pass!(MacroUseImport => [MACRO_USE_IMPORT]);
40+
impl MacroRefData {
41+
pub fn new(name: String, span: Span, ecx: &LateContext<'_, '_>) -> Self {
42+
let mut path = ecx.sess().source_map().span_to_filename(span).to_string();
3443

35-
impl EarlyLintPass for MacroUseImport {
44+
// std lib paths are <::std::module::file type>
45+
// so remove brackets and space
46+
if path.contains('<') {
47+
path = path.replace(BRACKETS, "");
48+
}
49+
if path.contains(' ') {
50+
path = path.split(' ').next().unwrap().to_string();
51+
}
52+
Self {
53+
name: name.to_string(),
54+
path,
55+
}
56+
}
57+
}
3658

37-
fn check_item(&mut self, ecx: &EarlyContext<'_>, item: &ast::Item) {
59+
#[derive(Default)]
60+
pub struct MacroUseImports {
61+
/// the actual import path used and the span of the attribute above it.
62+
imports: Vec<(String, Span)>,
63+
/// the span of the macro reference and the `MacroRefData`
64+
/// for the use of the macro.
65+
/// TODO make this FxHashSet<Span> to guard against inserting already found macros
66+
collected: FxHashMap<Span, MacroRefData>,
67+
mac_refs: Vec<(Span, MacroRefData)>,
68+
}
69+
70+
impl_lint_pass!(MacroUseImports => [MACRO_USE_IMPORTS]);
71+
72+
impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
73+
fn check_item(&mut self, lcx: &LateContext<'_, '_>, item: &hir::Item<'_>) {
3874
if_chain! {
39-
if ecx.sess.opts.edition == Edition::Edition2018;
40-
if let ast::ItemKind::Use(use_tree) = &item.kind;
75+
if lcx.sess().opts.edition == Edition::Edition2018;
76+
if let hir::ItemKind::Use(path, _kind) = &item.kind;
4177
if let Some(mac_attr) = item
4278
.attrs
4379
.iter()
4480
.find(|attr| attr.ident().map(|s| s.to_string()) == Some("macro_use".to_string()));
81+
if let Res::Def(DefKind::Mod, id) = path.res;
4582
then {
46-
let import_path = snippet(ecx, use_tree.span, "_");
47-
let mac_names = find_used_macros(ecx, &import_path);
48-
let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
49-
let help = format!("use {}::<macro name>", import_path);
50-
span_lint_and_sugg(
51-
ecx,
52-
MACRO_USE_IMPORT,
53-
mac_attr.span,
54-
msg,
55-
// "remove the attribute and import the macro directly, try",
56-
"",
57-
help,
58-
Applicability::HasPlaceholders,
59-
);
83+
// println!("{:#?}", lcx.tcx.def_path_str(id));
84+
for kid in lcx.tcx.item_children(id).iter() {
85+
// println!("{:#?}", kid);
86+
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
87+
let span = mac_attr.span.clone();
88+
89+
// println!("{:#?}", lcx.tcx.def_path_str(mac_id));
90+
91+
self.imports.push((lcx.tcx.def_path_str(mac_id), span));
92+
}
93+
}
94+
} else {
95+
if in_macro(item.span) {
96+
let call_site = item.span.source_callsite();
97+
let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_");
98+
if let Some(callee) = item.span.source_callee() {
99+
if !self.collected.contains_key(&call_site) {
100+
let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx);
101+
self.mac_refs.push((call_site, mac.clone()));
102+
self.collected.insert(call_site, mac);
103+
}
104+
}
105+
}
60106
}
61107
}
62108
}
109+
fn check_attribute(&mut self, lcx: &LateContext<'_, '_>, attr: &ast::Attribute) {
110+
if in_macro(attr.span) {
111+
let call_site = attr.span.source_callsite();
112+
let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_");
113+
if let Some(callee) = attr.span.source_callee() {
114+
if !self.collected.contains_key(&call_site) {
115+
println!("{:?}\n{:#?}", call_site, attr);
116+
117+
let name = if name.contains("::") {
118+
name.split("::").last().unwrap().to_string()
119+
} else {
120+
name.to_string()
121+
};
63122

64-
fn check_expr(&mut self, ecx: &EarlyContext<'_>, expr: &ast::Expr) {
123+
let mac = MacroRefData::new(name, callee.def_site, lcx);
124+
self.mac_refs.push((call_site, mac.clone()));
125+
self.collected.insert(call_site, mac);
126+
}
127+
}
128+
}
129+
}
130+
fn check_expr(&mut self, lcx: &LateContext<'_, '_>, expr: &hir::Expr<'_>) {
65131
if in_macro(expr.span) {
66-
let name = snippet(ecx, ecx.sess.source_map().span_until_char(expr.span.source_callsite(), '!'), "_");
132+
let call_site = expr.span.source_callsite();
133+
let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_");
67134
if let Some(callee) = expr.span.source_callee() {
68-
if self.collected.insert(callee.def_site) {
69-
println!("EXPR {:#?}", name);
135+
if !self.collected.contains_key(&call_site) {
136+
let name = if name.contains("::") {
137+
name.split("::").last().unwrap().to_string()
138+
} else {
139+
name.to_string()
140+
};
141+
142+
let mac = MacroRefData::new(name, callee.def_site, lcx);
143+
self.mac_refs.push((call_site, mac.clone()));
144+
self.collected.insert(call_site, mac);
70145
}
71146
}
72147
}
73148
}
74-
fn check_stmt(&mut self, ecx: &EarlyContext<'_>, stmt: &ast::Stmt) {
149+
fn check_stmt(&mut self, lcx: &LateContext<'_, '_>, stmt: &hir::Stmt<'_>) {
75150
if in_macro(stmt.span) {
76-
let name = snippet(ecx, ecx.sess.source_map().span_until_char(stmt.span.source_callsite(), '!'), "_");
151+
let call_site = stmt.span.source_callsite();
152+
let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_");
77153
if let Some(callee) = stmt.span.source_callee() {
78-
println!("EXPR {:#?}", name);
154+
if !self.collected.contains_key(&call_site) {
155+
let name = if name.contains("::") {
156+
name.split("::").last().unwrap().to_string()
157+
} else {
158+
name.to_string()
159+
};
160+
161+
let mac = MacroRefData::new(name, callee.def_site, lcx);
162+
self.mac_refs.push((call_site, mac.clone()));
163+
self.collected.insert(call_site, mac);
164+
}
79165
}
80166
}
81167
}
82-
fn check_pat(&mut self, ecx: &EarlyContext<'_>, pat: &ast::Pat) {
168+
fn check_pat(&mut self, lcx: &LateContext<'_, '_>, pat: &hir::Pat<'_>) {
83169
if in_macro(pat.span) {
84-
let name = snippet(ecx, ecx.sess.source_map().span_until_char(pat.span.source_callsite(), '!'), "_");
170+
let call_site = pat.span.source_callsite();
171+
let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_");
85172
if let Some(callee) = pat.span.source_callee() {
86-
println!("EXPR {:#?}", name);
173+
if !self.collected.contains_key(&call_site) {
174+
let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx);
175+
self.mac_refs.push((call_site, mac.clone()));
176+
self.collected.insert(call_site, mac);
177+
}
87178
}
88179
}
89180
}
90-
}
181+
fn check_ty(&mut self, lcx: &LateContext<'_, '_>, ty: &hir::Ty<'_>) {
182+
if in_macro(ty.span) {
183+
let call_site = ty.span.source_callsite();
184+
let name = snippet(lcx, lcx.sess().source_map().span_until_char(call_site, '!'), "_");
185+
if let Some(callee) = ty.span.source_callee() {
186+
if !self.collected.contains_key(&call_site) {
187+
let mac = MacroRefData::new(name.to_string(), callee.def_site, lcx);
188+
self.mac_refs.push((call_site, mac.clone()));
189+
self.collected.insert(call_site, mac);
190+
}
191+
}
192+
}
193+
}
194+
195+
fn check_crate_post(&mut self, lcx: &LateContext<'_, '_>, _krate: &hir::Crate<'_>) {
196+
for (import, span) in self.imports.iter() {
197+
let matched = self
198+
.mac_refs
199+
.iter()
200+
.find(|(_span, mac)| import.ends_with(&mac.name))
201+
.is_some();
91202

92-
fn find_used_macros(ecx: &EarlyContext<'_>, path: &str) {
93-
for it in ecx.krate.module.items.iter() {
94-
if in_macro(it.span) {
95-
// println!("{:#?}", it)
203+
if matched {
204+
self.mac_refs.retain(|(_span, mac)| !import.ends_with(&mac.name));
205+
let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition";
206+
let help = format!("use {}", import);
207+
span_lint_and_sugg(
208+
lcx,
209+
MACRO_USE_IMPORTS,
210+
*span,
211+
msg,
212+
"remove the attribute and import the macro directly, try",
213+
help,
214+
Applicability::HasPlaceholders,
215+
)
216+
}
217+
}
218+
if !self.mac_refs.is_empty() {
219+
// TODO if not empty we found one we could not make a suggestion for
220+
// such as std::prelude::v1 or something else I haven't thought of.
221+
// println!("{:#?}", self.mac_refs);
96222
}
97223
}
98-
for x in ecx.sess.imported_macro_spans.borrow().iter() {
99-
// println!("{:?}", x);
224+
}
225+
226+
const PRELUDE: &[&str] = &[
227+
"marker", "ops", "convert", "iter", "option", "result", "borrow", "boxed", "string", "vec", "macros",
228+
];
229+
230+
/// This is somewhat of a fallback for imports from `std::prelude` because they
231+
/// are not recognized by `LateLintPass::check_item` `lcx.tcx.item_children(id)`
232+
fn make_path(mac: &MacroRefData, use_path: &str) -> String {
233+
let segs = mac.path.split("::").filter(|s| *s != "").collect::<Vec<_>>();
234+
235+
if segs.starts_with(&["std"]) && PRELUDE.iter().any(|m| segs.contains(m)) {
236+
return format!(
237+
"std::prelude::{} is imported by default, remove `use` statement",
238+
mac.name
239+
);
240+
}
241+
242+
if use_path.split("::").count() == 1 {
243+
return format!("{}::{}", use_path, mac.name);
100244
}
245+
246+
mac.path.clone()
101247
}

macro_use_import

-2.69 MB
Binary file not shown.
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
extern crate macro_rules;
2+
3+
// STMT
4+
#[macro_export]
5+
macro_rules! pub_macro {
6+
() => {
7+
let _ = "hello Mr. Vonnegut";
8+
};
9+
}
10+
11+
pub mod inner {
12+
pub use super::*;
13+
14+
// RE-EXPORT
15+
// this will stick in `inner` module
16+
pub use macro_rules::try_err;
17+
18+
// ITEM
19+
#[macro_export]
20+
macro_rules! inner_mod {
21+
() => {
22+
#[allow(dead_code)]
23+
pub struct Tardis;
24+
};
25+
}
26+
}
27+
28+
// EXPR
29+
#[macro_export]
30+
macro_rules! function {
31+
() => {
32+
if true {
33+
} else {
34+
}
35+
};
36+
}
37+
38+
// TYPE
39+
#[macro_export]
40+
macro_rules! ty_mac {
41+
() => {
42+
Vec<u8>
43+
};
44+
}
45+
46+
mod extern_exports {
47+
pub(super) mod private_inner {
48+
#[macro_export]
49+
macro_rules! pub_in_private {
50+
($name:ident) => {
51+
let $name = String::from("secrets and lies");
52+
};
53+
}
54+
}
55+
}

tests/ui/macro_use_import.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/ui/macro_use_import.stderr

Lines changed: 0 additions & 10 deletions
This file was deleted.

0 commit comments

Comments
 (0)