@@ -2,7 +2,7 @@ use crate::utils::{in_macro, snippet, span_lint_and_sugg};
2
2
use hir:: def:: { DefKind , Res } ;
3
3
use if_chain:: if_chain;
4
4
use rustc_ast:: ast;
5
- use rustc_data_structures:: fx:: FxHashSet ;
5
+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
6
6
use rustc_errors:: Applicability ;
7
7
use rustc_hir as hir;
8
8
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -38,7 +38,7 @@ pub struct MacroRefData {
38
38
}
39
39
40
40
impl MacroRefData {
41
- pub fn new ( name : & str , callee : Span , cx : & LateContext < ' _ , ' _ > ) -> Self {
41
+ pub fn new ( name : String , callee : Span , cx : & LateContext < ' _ , ' _ > ) -> Self {
42
42
let mut path = cx. sess ( ) . source_map ( ) . span_to_filename ( callee) . to_string ( ) ;
43
43
44
44
// std lib paths are <::std::module::file type>
@@ -50,7 +50,7 @@ impl MacroRefData {
50
50
path = path. split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ;
51
51
}
52
52
Self {
53
- name : name . to_string ( ) ,
53
+ name,
54
54
path,
55
55
}
56
56
}
@@ -69,128 +69,265 @@ pub struct MacroUseImports {
69
69
impl_lint_pass ! ( MacroUseImports => [ MACRO_USE_IMPORTS ] ) ;
70
70
71
71
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
+ }
82
86
}
83
87
}
84
88
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
+ }
89
97
}
90
98
}
91
99
}
92
100
93
101
impl < ' l , ' txc > LateLintPass < ' l , ' txc > for MacroUseImports {
94
102
fn check_item ( & mut self , cx : & LateContext < ' _ , ' _ > , item : & hir:: Item < ' _ > ) {
95
103
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) ) ;
120
116
}
117
+ }
118
+ } else {
119
+ if in_macro( item. span) {
120
+ self . push_unique_macro_pat_ty( cx, item. span) ;
121
+ }
121
122
}
122
123
}
123
124
}
124
125
fn check_attribute ( & mut self , cx : & LateContext < ' _ , ' _ > , attr : & ast:: Attribute ) {
125
126
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 ) ;
131
128
}
132
129
}
133
130
fn check_expr ( & mut self , cx : & LateContext < ' _ , ' _ > , expr : & hir:: Expr < ' _ > ) {
134
131
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 ) ;
140
133
}
141
134
}
142
135
fn check_stmt ( & mut self , cx : & LateContext < ' _ , ' _ > , stmt : & hir:: Stmt < ' _ > ) {
143
136
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 ) ;
149
138
}
150
139
}
151
140
fn check_pat ( & mut self , cx : & LateContext < ' _ , ' _ > , pat : & hir:: Pat < ' _ > ) {
152
141
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 ) ;
158
143
}
159
144
}
160
145
fn check_ty ( & mut self , cx : & LateContext < ' _ , ' _ > , ty : & hir:: Ty < ' _ > ) {
161
146
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 ) ;
167
148
}
168
149
}
169
150
170
151
fn check_crate_post ( & mut self , cx : & LateContext < ' _ , ' _ > , _krate : & hir:: Crate < ' _ > ) {
152
+ let mut import_map = FxHashMap :: default ( ) ;
171
153
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
+ }
173
215
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 {
177
223
let help = format ! ( "use {}" , import) ;
178
224
span_lint_and_sugg (
179
225
cx,
180
226
MACRO_USE_IMPORTS ,
181
- * span,
182
- msg ,
227
+ span,
228
+ "`macro_use` attributes are no longer needed in the Rust 2018 edition" ,
183
229
"remove the attribute and import the macro directly, try" ,
184
230
help,
185
231
Applicability :: MaybeIncorrect ,
186
232
)
187
233
}
188
234
}
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
+ }
194
330
}
195
331
}
332
+ path
196
333
}
0 commit comments