@@ -49,10 +49,7 @@ impl MacroRefData {
49
49
if path. contains ( ' ' ) {
50
50
path = path. split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ;
51
51
}
52
- Self {
53
- name,
54
- path,
55
- }
52
+ Self { name, path }
56
53
}
57
54
}
58
55
@@ -79,7 +76,7 @@ impl MacroUseImports {
79
76
} else {
80
77
name. to_string ( )
81
78
} ;
82
-
79
+
83
80
self . mac_refs . push ( MacroRefData :: new ( name, callee. def_site , cx) ) ;
84
81
self . collected . insert ( call_site) ;
85
82
}
@@ -91,7 +88,8 @@ impl MacroUseImports {
91
88
let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
92
89
if let Some ( callee) = span. source_callee ( ) {
93
90
if !self . collected . contains ( & call_site) {
94
- self . mac_refs . push ( MacroRefData :: new ( name. to_string ( ) , callee. def_site , cx) ) ;
91
+ self . mac_refs
92
+ . push ( MacroRefData :: new ( name. to_string ( ) , callee. def_site , cx) ) ;
95
93
self . collected . insert ( call_site) ;
96
94
}
97
95
}
@@ -147,78 +145,123 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
147
145
self . push_unique_macro_pat_ty ( cx, ty. span ) ;
148
146
}
149
147
}
150
-
148
+ # [ allow ( clippy :: too_many_lines ) ]
151
149
fn check_crate_post ( & mut self , cx : & LateContext < ' _ , ' _ > , _krate : & hir:: Crate < ' _ > ) {
152
150
let mut import_map = FxHashMap :: default ( ) ;
153
151
for ( import, span) in & self . imports {
154
152
let found_idx = self . mac_refs . iter ( ) . position ( |mac| import. ends_with ( & mac. name ) ) ;
155
-
153
+
156
154
if let Some ( idx) = found_idx {
157
155
let _ = self . mac_refs . remove ( idx) ;
158
156
proccess_macro_path ( * span, import, & mut import_map) ;
159
157
}
160
158
}
161
- println ! ( "{:#?}" , import_map) ;
159
+ // println!("{:#?}", import_map);
162
160
let mut imports = vec ! [ ] ;
163
161
for ( root, rest) in import_map {
164
162
let mut path = format ! ( "use {}::" , root) ;
165
- let mut s = None ;
163
+ let mut attr_span = None ;
164
+ // when a multiple nested paths are found one may be written to the string
165
+ // before it is found in this loop so we make note and skip it when this
166
+ // loop finds it
167
+ let mut found_nested = vec ! [ ] ;
166
168
let mut count = 1 ;
167
169
let rest_len = rest. len ( ) ;
170
+
168
171
if rest_len > 1 {
169
172
path. push_str ( "{" ) ;
170
173
}
174
+
171
175
for m in & rest {
172
- println ! ( "{} => {:?}" , root, m) ;
173
- if count == 1 {
174
- s = Some ( m. span ( ) ) ;
176
+ if attr_span. is_none ( ) {
177
+ attr_span = Some ( m. span ( ) ) ;
178
+ }
179
+ if found_nested. contains ( & m) {
180
+ continue ;
175
181
}
176
-
177
182
let comma = if rest_len == count { "" } else { ", " } ;
178
183
match m {
179
184
ModPath :: Item { item, .. } => {
180
185
path. push_str ( & format ! ( "{}{}" , item, comma) ) ;
181
- }
182
- ModPath :: Nested { names, item, span } => {
183
- let nested = rest. iter ( )
186
+ } ,
187
+ ModPath :: Nested { segments, item, .. } => {
188
+ // do any other Nested paths match the current one
189
+ let nested = rest
190
+ . iter ( )
184
191
// filter "self" out
185
192
. filter ( |other_m| other_m != & m)
193
+ // filters out Nested we have previously seen
194
+ . filter ( |other_m| !found_nested. contains ( other_m) )
186
195
// this matches the first path segment and filters non ModPath::Nested items
187
196
. filter ( |other_m| other_m. matches ( 0 , m) )
188
197
. collect :: < Vec < _ > > ( ) ;
189
198
190
- println ! ( "{:#?}" , nested) ;
191
-
192
199
if nested. is_empty ( ) {
193
- path. push_str ( & format ! ( "{}::{}{}" , names. join( "::" ) . to_string( ) , item, comma) )
200
+ path. push_str ( & format ! ( "{}::{}{}" , segments. join( "::" ) . to_string( ) , item, comma) )
201
+ // use mod_a::{mod_b::{one, two}, mod_c::item}
194
202
} 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
+ found_nested. extend ( nested. iter ( ) ) ;
204
+ found_nested. push ( & m) ;
205
+ // we check each segment for matches with other import paths if
206
+ // one differs we have to open a new `{}`
207
+ for ( idx, seg) in segments. iter ( ) . enumerate ( ) {
208
+ path. push_str ( & format ! ( "{}::" , seg) ) ;
209
+ if nested. iter ( ) . all ( |other_m| other_m. matches ( idx, & m) ) {
210
+ continue ;
211
+ }
212
+
213
+ path. push_str ( "{" ) ;
214
+ let matched_seg_items = nested
215
+ . iter ( )
216
+ . filter ( |other_m| !other_m. matches ( idx, & m) )
217
+ . collect :: < Vec < _ > > ( ) ;
218
+ for item in matched_seg_items {
219
+ if let ModPath :: Nested { item, .. } = item {
220
+ path. push_str ( & format ! (
221
+ "{}{}" ,
222
+ item,
223
+ if nested. len( ) == idx + 1 { "" } else { ", " }
224
+ ) ) ;
225
+ }
226
+ }
227
+ path. push_str ( "}" ) ;
228
+ }
229
+ path. push_str ( & format ! ( "{{{}{}" , item, comma) ) ;
230
+ for ( i, item) in nested. iter ( ) . enumerate ( ) {
231
+ if let ModPath :: Nested { item, segments : matched_seg, .. } = item {
232
+ path. push_str ( & format ! (
233
+ "{}{}{}" ,
234
+ if matched_seg > segments {
235
+ format!( "{}::" , matched_seg[ segments. len( ) ..] . join( "::" ) )
236
+ } else {
237
+ String :: new( )
238
+ } ,
239
+ item,
240
+ if nested. len( ) == i + 1 { "" } else { ", " }
241
+ ) ) ;
242
+ }
243
+ }
244
+ path. push_str ( "}" ) ;
203
245
}
204
- }
246
+ } ,
205
247
}
206
- count += 1 ;
248
+ count += 1 ;
207
249
}
208
250
if rest_len > 1 {
209
251
path. push_str ( "};" ) ;
252
+ } else {
253
+ path. push_str ( ";" ) ;
210
254
}
211
- if let Some ( span) = s {
255
+ if let Some ( span) = attr_span {
212
256
imports. push ( ( span, path) )
257
+ } else {
258
+ unreachable ! ( "a span must always be attached to a macro_use attribute" )
213
259
}
214
260
}
215
261
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 {
262
+ // If mac_refs is not empty we have encountered an import we could not handle
263
+ // such as `std::prelude::v1::foo` or some other macro that expands to an import.
264
+ if self . mac_refs . is_empty ( ) {
222
265
for ( span, import) in imports {
223
266
let help = format ! ( "use {}" , import) ;
224
267
span_lint_and_sugg (
@@ -237,48 +280,56 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
237
280
238
281
#[ derive( Debug , PartialEq ) ]
239
282
enum ModPath {
240
- Item { item : String , span : Span , } ,
241
- Nested { names : Vec < String > , item : String , span : Span , } ,
283
+ Item {
284
+ item : String ,
285
+ span : Span ,
286
+ } ,
287
+ Nested {
288
+ segments : Vec < String > ,
289
+ item : String ,
290
+ span : Span ,
291
+ } ,
242
292
}
243
293
244
294
impl ModPath {
245
295
fn span ( & self ) -> Span {
246
296
match self {
247
- Self :: Item { span, .. } => * span,
248
- Self :: Nested { span, .. } => * span,
297
+ Self :: Item { span, .. } | Self :: Nested { span, .. } => * span,
249
298
}
250
299
}
251
300
252
301
fn item ( & self ) -> & str {
253
302
match self {
254
- Self :: Item { item, .. } => item,
255
- Self :: Nested { item, .. } => item,
303
+ Self :: Item { item, .. } | Self :: Nested { item, .. } => item,
256
304
}
257
305
}
258
306
259
307
fn matches ( & self , idx : usize , other : & ModPath ) -> bool {
260
308
match ( self , other) {
261
309
( 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
- }
310
+ (
311
+ Self :: Nested { segments, .. } ,
312
+ Self :: Nested {
313
+ segments : other_names, ..
314
+ } ,
315
+ ) => match ( segments. get ( idx) , other_names. get ( idx) ) {
316
+ ( Some ( seg) , Some ( other_seg) ) => seg == other_seg,
317
+ ( _, _) => false ,
318
+ } ,
268
319
( _, _) => false ,
269
320
}
270
321
}
271
322
}
272
323
324
+ #[ allow( clippy:: comparison_chain) ]
273
325
fn proccess_macro_path ( span : Span , import : & str , import_map : & mut FxHashMap < String , Vec < ModPath > > ) {
274
326
let mut mod_path = import. split ( "::" ) . collect :: < Vec < _ > > ( ) ;
275
327
276
328
if mod_path. len ( ) == 2 {
277
- let item_list = import_map. entry ( mod_path[ 0 ] . to_string ( ) )
278
- . or_insert ( vec ! [ ] ) ;
329
+ let item_list = import_map. entry ( mod_path[ 0 ] . to_string ( ) ) . or_insert_with ( Vec :: new) ;
279
330
280
331
if !item_list. iter ( ) . any ( |mods| mods. item ( ) == mod_path[ 1 ] ) {
281
- item_list. push ( ModPath :: Item {
332
+ item_list. push ( ModPath :: Item {
282
333
item : mod_path[ 1 ] . to_string ( ) ,
283
334
span,
284
335
} ) ;
@@ -288,46 +339,16 @@ fn proccess_macro_path(span: Span, import: &str, import_map: &mut FxHashMap<Stri
288
339
let name = mod_path. remove ( mod_path. len ( ) - 1 ) ;
289
340
290
341
let nested = ModPath :: Nested {
291
- names : mod_path. into_iter ( ) . map ( ToString :: to_string) . collect ( ) ,
342
+ segments : mod_path. into_iter ( ) . map ( ToString :: to_string) . collect ( ) ,
292
343
item : name. to_string ( ) ,
293
344
span,
294
345
} ;
295
- import_map. entry ( first. to_string ( ) )
296
- . or_insert ( vec ! [ ] )
297
- . push ( nested) ;
346
+ // CLIPPY NOTE: this told me to use `or_insert_with(vec![])`
347
+ // import_map.entry(first.to_string()).or_insert(vec![]).push(nested);
348
+ // which failed as `vec!` is not a closure then told me to add `||` which failed
349
+ // with the redundant_closure lint so I finally gave up and used this.
350
+ import_map. entry ( first. to_string ( ) ) . or_insert_with ( Vec :: new) . push ( nested) ;
298
351
} else {
299
352
unreachable ! ( "test to see if code path hit TODO REMOVE" )
300
353
}
301
354
}
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
- }
330
- }
331
- }
332
- path
333
- }
0 commit comments