1
- use crate :: utils:: { snippet , span_lint_and_sugg , in_macro } ;
1
+ use crate :: utils:: { in_macro , snippet , span_lint_and_sugg } ;
2
2
use if_chain:: if_chain;
3
- use rustc_ast:: ast;
4
3
use rustc_data_structures:: fx:: FxHashMap ;
5
4
use rustc_errors:: Applicability ;
6
- use rustc_lint:: { EarlyContext , EarlyLintPass , LintContext , Lint } ;
7
- use rustc_session:: { impl_lint_pass, declare_tool_lint} ;
8
- use rustc_span:: { edition:: Edition , Span } ;
9
5
use rustc_hir as hir;
6
+ use hir:: def:: { Res , DefKind } ;
7
+ use rustc_lint:: { LintContext , LateLintPass , LateContext } ;
8
+ use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
9
+ use rustc_span:: { edition:: Edition , Span } ;
10
10
11
11
const PRELUDE : & [ & str ] = & [
12
- "marker" , "ops" , "convert" , "iter" , "option" , "result" , "borrow" , "boxed" , "string" , "vec" ,
13
- "macros"
12
+ "marker" , "ops" , "convert" , "iter" , "option" , "result" , "borrow" , "boxed" , "string" , "vec" , "macros" ,
14
13
] ;
15
14
const BRACKETS : & [ char ] = & [ '<' , '>' ] ;
16
15
@@ -34,14 +33,15 @@ declare_clippy_lint! {
34
33
35
34
/// MacroRefData includes the name of the macro
36
35
/// and the path from `SourceMap::span_to_filename`.
36
+ #[ derive( Debug , Clone ) ]
37
37
pub struct MacroRefData {
38
38
name : String ,
39
39
path : String ,
40
40
}
41
41
42
42
impl MacroRefData {
43
- pub fn new ( name : String , span : Span , ecx : & EarlyContext < ' _ > ) -> Self {
44
- let mut path = ecx. sess . source_map ( ) . span_to_filename ( span) . to_string ( ) ;
43
+ pub fn new ( name : String , span : Span , ecx : & LateContext < ' _ , ' _ > ) -> Self {
44
+ let mut path = ecx. sess ( ) . source_map ( ) . span_to_filename ( span) . to_string ( ) ;
45
45
46
46
// std lib paths are <::std::module::file type>
47
47
// so remove brackets and space
@@ -51,7 +51,10 @@ impl MacroRefData {
51
51
if path. contains ( ' ' ) {
52
52
path = path. split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ;
53
53
}
54
- Self { name : name. to_string ( ) , path, }
54
+ Self {
55
+ name : name. to_string ( ) ,
56
+ path,
57
+ }
55
58
}
56
59
}
57
60
@@ -62,122 +65,144 @@ pub struct MacroUseImports {
62
65
/// the span of the macro reference and the `MacroRefData`
63
66
/// for the use of the macro.
64
67
collected : FxHashMap < Span , MacroRefData > ,
68
+ mac_refs : Vec < ( Span , MacroRefData ) > ,
65
69
}
66
70
67
- impl MacroUseImports {
68
- fn import_path_mac ( & self , use_path : & str ) -> String {
69
- for mac in self . collected . values ( ) {
70
- if paths_match ( mac, use_path) {
71
- return make_path ( mac, use_path)
72
- }
73
- }
74
- format ! ( "{}::<macro name>" , use_path)
75
- }
76
- }
77
-
78
- fn paths_match ( mac : & MacroRefData , use_path : & str ) -> bool {
79
- let segs = mac. path . split ( "::" )
80
- . filter ( |s| * s != "" )
81
- . collect :: < Vec < _ > > ( ) ;
71
+ /// This is somewhat of a fallback for imports from `std::prelude` because they
72
+ /// are not recognized by `LateLintPass::check_item` `lcx.tcx.item_children(id)`
73
+ fn make_path ( mac : & MacroRefData , use_path : & str ) -> String {
74
+ let segs = mac. path . split ( "::" ) . filter ( |s| * s != "" ) . collect :: < Vec < _ > > ( ) ;
82
75
83
- if segs. starts_with ( & [ "std" ] ) {
84
- return PRELUDE . iter ( ) . any ( |m| segs . contains ( m ) )
76
+ if segs. starts_with ( & [ "std" ] ) && PRELUDE . iter ( ) . any ( |m| segs . contains ( m ) ) {
77
+ return format ! ( "std::prelude::{} is imported by default, remove `use` statement" , mac . name ) ;
85
78
}
86
-
87
- segs. starts_with ( & use_path. split ( "::" ) . collect :: < Vec < _ > > ( ) )
88
- }
89
79
90
- fn make_path ( mac : & MacroRefData , use_path : & str ) -> String {
91
80
if use_path. split ( "::" ) . count ( ) == 1 {
92
81
return format ! ( "{}::{}" , use_path, mac. name) ;
93
82
}
94
83
95
- let segs = mac. path . split ( "::" )
96
- . filter ( |s| * s != "" )
97
- . collect :: < Vec < _ > > ( ) ;
98
-
99
- if segs. starts_with ( & [ "std" ] ) && PRELUDE . iter ( ) . any ( |m| segs. contains ( m) ) {
100
- return format ! ( "std::prelude::{}" , mac. name) ;
101
- }
102
-
103
84
mac. path . clone ( )
104
85
}
105
86
106
87
impl_lint_pass ! ( MacroUseImports => [ MACRO_USE_IMPORTS ] ) ;
107
88
108
- impl EarlyLintPass for MacroUseImports {
109
- fn check_item ( & mut self , ecx : & EarlyContext < ' _ > , item : & ast :: Item ) {
89
+ impl < ' l , ' txc > LateLintPass < ' l , ' txc > for MacroUseImports {
90
+ fn check_item ( & mut self , lcx : & LateContext < ' _ , ' _ > , item : & hir :: Item < ' _ > ) {
110
91
if_chain ! {
111
- if ecx . sess. opts. edition == Edition :: Edition2018 ;
112
- if let ast :: ItemKind :: Use ( use_tree ) = & item. kind;
92
+ if lcx . sess( ) . opts. edition == Edition :: Edition2018 ;
93
+ if let hir :: ItemKind :: Use ( path , _kind ) = & item. kind;
113
94
if let Some ( mac_attr) = item
114
95
. attrs
115
96
. iter( )
116
97
. find( |attr| attr. ident( ) . map( |s| s. to_string( ) ) == Some ( "macro_use" . to_string( ) ) ) ;
98
+ if let Res :: Def ( DefKind :: Mod , id) = path. res;
117
99
then {
118
- let import_path = snippet( ecx, use_tree. span, "_" ) ;
119
- let span = mac_attr. span. clone( ) ;
120
- self . imports. push( ( import_path. to_string( ) , span) ) ;
100
+ for kid in lcx. tcx. item_children( id) . iter( ) {
101
+ if let Res :: Def ( DefKind :: Macro ( _mac_type) , mac_id) = kid. res {
102
+ let span = mac_attr. span. clone( ) ;
103
+ println!( "{:#?}" , lcx. tcx. def_path_str( mac_id) ) ;
104
+ self . imports. push( ( lcx. tcx. def_path_str( mac_id) , span) ) ;
105
+ }
106
+ }
107
+ } else {
108
+ if in_macro( item. span) {
109
+ let call_site = item. span. source_callsite( ) ;
110
+ let name = snippet( lcx, lcx. sess( ) . source_map( ) . span_until_char( call_site, '!' ) , "_" ) ;
111
+ if let Some ( callee) = item. span. source_callee( ) {
112
+ if !self . collected. contains_key( & call_site) {
113
+ let mac = MacroRefData :: new( name. to_string( ) , callee. def_site, lcx) ;
114
+ self . mac_refs. push( ( call_site, mac. clone( ) ) ) ;
115
+ self . collected. insert( call_site, mac) ;
116
+ // println!("EXPR {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site));
117
+ }
118
+ }
119
+ }
121
120
}
122
121
}
123
122
}
124
123
125
- fn check_expr ( & mut self , ecx : & EarlyContext < ' _ > , expr : & ast :: Expr ) {
124
+ fn check_expr ( & mut self , lcx : & LateContext < ' _ , ' _ > , expr : & hir :: Expr < ' _ > ) {
126
125
if in_macro ( expr. span ) {
127
126
let call_site = expr. span . source_callsite ( ) ;
128
- let name = snippet ( ecx , ecx . sess . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
127
+ let name = snippet ( lcx , lcx . sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
129
128
if let Some ( callee) = expr. span . source_callee ( ) {
130
- self . collected . entry ( call_site)
131
- . or_insert_with ( || {
132
- MacroRefData :: new ( name. to_string ( ) , callee. def_site , ecx)
133
- } ) ;
129
+ if !self . collected . contains_key ( & call_site) {
130
+ let mac = MacroRefData :: new ( name. to_string ( ) , callee. def_site , lcx) ;
131
+ self . mac_refs . push ( ( call_site, mac. clone ( ) ) ) ;
132
+ self . collected . insert ( call_site, mac) ;
133
+ // println!("EXPR {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site));
134
+ }
134
135
}
135
136
}
136
137
}
137
- fn check_stmt ( & mut self , ecx : & EarlyContext < ' _ > , stmt : & ast :: Stmt ) {
138
+ fn check_stmt ( & mut self , lcx : & LateContext < ' _ , ' _ > , stmt : & hir :: Stmt < ' _ > ) {
138
139
if in_macro ( stmt. span ) {
139
140
let call_site = stmt. span . source_callsite ( ) ;
140
- let name = snippet ( ecx , ecx . sess . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
141
+ let name = snippet ( lcx , lcx . sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
141
142
if let Some ( callee) = stmt. span . source_callee ( ) {
142
- self . collected . entry ( call_site)
143
- . or_insert_with ( || {
144
- MacroRefData :: new ( name. to_string ( ) , callee. def_site , ecx)
145
- } ) ;
143
+ if !self . collected . contains_key ( & call_site) {
144
+ let mac = MacroRefData :: new ( name. to_string ( ) , callee. def_site , lcx) ;
145
+ self . mac_refs . push ( ( call_site, mac. clone ( ) ) ) ;
146
+ self . collected . insert ( call_site, mac) ;
147
+ // println!("STMT {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site));
148
+ }
146
149
}
147
150
}
148
151
}
149
- fn check_pat ( & mut self , ecx : & EarlyContext < ' _ > , pat : & ast :: Pat ) {
152
+ fn check_pat ( & mut self , lcx : & LateContext < ' _ , ' _ > , pat : & hir :: Pat < ' _ > ) {
150
153
if in_macro ( pat. span ) {
151
154
let call_site = pat. span . source_callsite ( ) ;
152
- let name = snippet ( ecx , ecx . sess . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
155
+ let name = snippet ( lcx , lcx . sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
153
156
if let Some ( callee) = pat. span . source_callee ( ) {
154
- self . collected . entry ( call_site)
155
- . or_insert_with ( || {
156
- MacroRefData :: new ( name. to_string ( ) , callee. def_site , ecx)
157
- } ) ;
157
+ if !self . collected . contains_key ( & call_site) {
158
+ let mac = MacroRefData :: new ( name. to_string ( ) , callee. def_site , lcx) ;
159
+ self . mac_refs . push ( ( call_site, mac. clone ( ) ) ) ;
160
+ self . collected . insert ( call_site, mac) ;
161
+ // println!("PAT {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site));
162
+ }
158
163
}
159
164
}
160
165
}
161
- fn check_ty ( & mut self , ecx : & EarlyContext < ' _ > , ty : & ast :: Ty ) {
166
+ fn check_ty ( & mut self , lcx : & LateContext < ' _ , ' _ > , ty : & hir :: Ty < ' _ > ) {
162
167
if in_macro ( ty. span ) {
163
168
let call_site = ty. span . source_callsite ( ) ;
164
- let name = snippet ( ecx , ecx . sess . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
169
+ let name = snippet ( lcx , lcx . sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
165
170
if let Some ( callee) = ty. span . source_callee ( ) {
166
- self . collected . entry ( call_site)
167
- . or_insert_with ( || {
168
- MacroRefData :: new ( name. to_string ( ) , callee. def_site , ecx)
169
- } ) ;
171
+ if !self . collected . contains_key ( & call_site) {
172
+ let mac = MacroRefData :: new ( name. to_string ( ) , callee. def_site , lcx) ;
173
+ self . mac_refs . push ( ( call_site, mac. clone ( ) ) ) ;
174
+ self . collected . insert ( call_site, mac) ;
175
+ // println!("TYPE {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site));
176
+ }
170
177
}
171
178
}
172
179
}
173
180
174
- fn check_crate_post ( & mut self , ecx : & EarlyContext < ' _ > , _krate : & ast:: Crate ) {
175
- for ( name, span) in self . imports . iter ( ) {
176
- let import_path = self . import_path_mac ( & name) ;
181
+ fn check_crate_post ( & mut self , lcx : & LateContext < ' _ , ' _ > , _krate : & hir:: Crate < ' _ > ) {
182
+ for ( import, span) in self . imports . iter ( ) {
183
+
184
+ let matched = self . mac_refs . iter ( ) . find ( |( _span, mac) | !import. ends_with ( & mac. name ) ) . is_some ( ) ;
185
+ if matched {
186
+ self . mac_refs . retain ( |( _span, mac) | !import. ends_with ( & mac. name ) ) ;
187
+ let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition" ;
188
+ let help = format ! ( "use {}" , import) ;
189
+ span_lint_and_sugg (
190
+ lcx,
191
+ MACRO_USE_IMPORTS ,
192
+ * span,
193
+ msg,
194
+ "remove the attribute and import the macro directly, try" ,
195
+ help,
196
+ Applicability :: HasPlaceholders ,
197
+ )
198
+ }
199
+ }
200
+
201
+ for ( span, mac) in self . mac_refs . iter ( ) {
177
202
let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition" ;
178
- let help = format ! ( "use {}" , import_path ) ;
203
+ let help = make_path ( mac , "hello" ) ;
179
204
span_lint_and_sugg (
180
- ecx ,
205
+ lcx ,
181
206
MACRO_USE_IMPORTS ,
182
207
* span,
183
208
msg,
0 commit comments