@@ -29,6 +29,12 @@ declare_clippy_lint! {
29
29
30
30
const BRACKETS : & [ char ] = & [ '<' , '>' ] ;
31
31
32
+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
33
+ struct PathAndSpan {
34
+ path : String ,
35
+ span : Span ,
36
+ }
37
+
32
38
/// `MacroRefData` includes the name of the macro
33
39
/// and the path from `SourceMap::span_to_filename`.
34
40
#[ derive( Debug , Clone ) ]
@@ -110,7 +116,8 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
110
116
for kid in cx. tcx. item_children( id) . iter( ) {
111
117
if let Res :: Def ( DefKind :: Macro ( _mac_type) , mac_id) = kid. res {
112
118
let span = mac_attr. span;
113
- self . imports. push( ( cx. tcx. def_path_str( mac_id) , span) ) ;
119
+ let def_path = cx. tcx. def_path_str( mac_id) ;
120
+ self . imports. push( ( def_path, span) ) ;
114
121
}
115
122
}
116
123
} else {
@@ -147,127 +154,69 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
147
154
}
148
155
#[ allow( clippy:: too_many_lines) ]
149
156
fn check_crate_post ( & mut self , cx : & LateContext < ' _ , ' _ > , _krate : & hir:: Crate < ' _ > ) {
150
- let mut import_map = FxHashMap :: default ( ) ;
157
+ let mut used = FxHashMap :: default ( ) ;
158
+ let mut check_dup = vec ! [ ] ;
151
159
for ( import, span) in & self . imports {
152
160
let found_idx = self . mac_refs . iter ( ) . position ( |mac| import. ends_with ( & mac. name ) ) ;
153
161
154
162
if let Some ( idx) = found_idx {
155
163
let _ = self . mac_refs . remove ( idx) ;
156
- proccess_macro_path ( * span, import, & mut import_map) ;
157
- }
158
- }
159
- // println!("{:#?}", import_map);
160
- let mut imports = vec ! [ ] ;
161
- for ( root, rest) in import_map {
162
- let mut path = format ! ( "use {}::" , root) ;
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 ! [ ] ;
168
- let mut count = 1 ;
169
- let rest_len = rest. len ( ) ;
170
-
171
- if rest_len > 1 {
172
- path. push_str ( "{" ) ;
173
- }
174
-
175
- for m in & rest {
176
- if attr_span. is_none ( ) {
177
- attr_span = Some ( m. span ( ) ) ;
178
- }
179
- if found_nested. contains ( & m) {
180
- continue ;
181
- }
182
- let comma = if rest_len == count { "" } else { ", " } ;
183
- match m {
184
- ModPath :: Item { item, .. } => {
185
- path. push_str ( & format ! ( "{}{}" , item, comma) ) ;
164
+ let seg = import. split ( "::" ) . collect :: < Vec < _ > > ( ) ;
165
+
166
+ match seg. as_slice ( ) {
167
+ [ ] => unreachable ! ( "this should never be empty" ) ,
168
+ [ _] => unreachable ! ( "path must have two segments ?" ) ,
169
+ [ root, item] => {
170
+ if !check_dup. contains ( & item. to_string ( ) ) {
171
+ used. entry ( ( root. to_string ( ) , span) )
172
+ . or_insert ( vec ! [ ] )
173
+ . push ( item. to_string ( ) ) ;
174
+ check_dup. push ( item. to_string ( ) ) ;
175
+ }
186
176
} ,
187
- ModPath :: Nested { segments, item, .. } => {
188
- // do any other Nested paths match the current one
189
- let nested = rest
190
- . iter ( )
191
- // filter "self" out
192
- . filter ( |other_m| other_m != & m)
193
- // filters out Nested we have previously seen
194
- . filter ( |other_m| !found_nested. contains ( other_m) )
195
- // this matches the first path segment and filters non ModPath::Nested items
196
- . filter ( |other_m| other_m. matches ( 0 , m) )
197
- . collect :: < Vec < _ > > ( ) ;
198
-
199
- if nested. is_empty ( ) {
200
- path. push_str ( & format ! ( "{}::{}{}" , segments. join( "::" ) . to_string( ) , item, comma) )
201
- // use mod_a::{mod_b::{one, two}, mod_c::item}
177
+ [ root, rest @ ..] => {
178
+ if !rest. iter ( ) . all ( |item| !check_dup. contains ( & item. to_string ( ) ) ) {
179
+ let mut rest = rest. to_vec ( ) ;
180
+ rest. sort ( ) ;
181
+ used. entry ( ( root. to_string ( ) , span) )
182
+ . or_insert ( vec ! [ ] )
183
+ . push ( rest. join ( "::" ) ) ;
184
+ check_dup. extend ( rest. iter ( ) . map ( ToString :: to_string) ) ;
202
185
} else {
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 ( "}" ) ;
186
+ let mut filtered = rest
187
+ . iter ( )
188
+ . filter ( |item| !check_dup. contains ( & item. to_string ( ) ) )
189
+ . map ( ToString :: to_string)
190
+ . collect :: < Vec < _ > > ( ) ;
191
+ filtered. sort ( ) ;
192
+ used. entry ( ( root. to_string ( ) , span) )
193
+ . or_insert ( vec ! [ ] )
194
+ . push ( filtered. join ( "::" ) ) ;
195
+ check_dup. extend ( filtered) ;
245
196
}
246
197
} ,
247
198
}
248
- count += 1 ;
249
199
}
250
- if rest_len > 1 {
251
- path. push_str ( "};" ) ;
252
- } else {
253
- path. push_str ( ";" ) ;
254
- }
255
- if let Some ( span) = attr_span {
256
- imports. push ( ( span, path) )
200
+ }
201
+
202
+ let mut suggestions = vec ! [ ] ;
203
+ for ( ( root, span) , path) in used {
204
+ if path. len ( ) == 1 {
205
+ suggestions. push ( ( span, format ! ( "{}::{}" , root, path[ 0 ] ) ) )
257
206
} else {
258
- unreachable ! ( "a span must always be attached to a macro_use attribute" )
207
+ suggestions . push ( ( span , format ! ( "{}::{{{}}}" , root , path . join ( ", " ) ) ) )
259
208
}
260
209
}
261
210
262
211
// If mac_refs is not empty we have encountered an import we could not handle
263
212
// such as `std::prelude::v1::foo` or some other macro that expands to an import.
264
213
if self . mac_refs . is_empty ( ) {
265
- for ( span, import) in imports {
214
+ for ( span, import) in suggestions {
266
215
let help = format ! ( "use {}" , import) ;
267
216
span_lint_and_sugg (
268
217
cx,
269
218
MACRO_USE_IMPORTS ,
270
- span,
219
+ * span,
271
220
"`macro_use` attributes are no longer needed in the Rust 2018 edition" ,
272
221
"remove the attribute and import the macro directly, try" ,
273
222
help,
@@ -277,78 +226,3 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
277
226
}
278
227
}
279
228
}
280
-
281
- #[ derive( Debug , PartialEq ) ]
282
- enum ModPath {
283
- Item {
284
- item : String ,
285
- span : Span ,
286
- } ,
287
- Nested {
288
- segments : Vec < String > ,
289
- item : String ,
290
- span : Span ,
291
- } ,
292
- }
293
-
294
- impl ModPath {
295
- fn span ( & self ) -> Span {
296
- match self {
297
- Self :: Item { span, .. } | Self :: Nested { span, .. } => * span,
298
- }
299
- }
300
-
301
- fn item ( & self ) -> & str {
302
- match self {
303
- Self :: Item { item, .. } | Self :: Nested { item, .. } => item,
304
- }
305
- }
306
-
307
- fn matches ( & self , idx : usize , other : & ModPath ) -> bool {
308
- match ( self , other) {
309
- ( Self :: Item { item, .. } , Self :: Item { item : other_item, .. } ) => item == other_item,
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
- } ,
319
- ( _, _) => false ,
320
- }
321
- }
322
- }
323
-
324
- #[ allow( clippy:: comparison_chain) ]
325
- fn proccess_macro_path ( span : Span , import : & str , import_map : & mut FxHashMap < String , Vec < ModPath > > ) {
326
- let mut mod_path = import. split ( "::" ) . collect :: < Vec < _ > > ( ) ;
327
-
328
- if mod_path. len ( ) == 2 {
329
- let item_list = import_map. entry ( mod_path[ 0 ] . to_string ( ) ) . or_insert_with ( Vec :: new) ;
330
-
331
- if !item_list. iter ( ) . any ( |mods| mods. item ( ) == mod_path[ 1 ] ) {
332
- item_list. push ( ModPath :: Item {
333
- item : mod_path[ 1 ] . to_string ( ) ,
334
- span,
335
- } ) ;
336
- }
337
- } else if mod_path. len ( ) > 2 {
338
- let first = mod_path. remove ( 0 ) ;
339
- let name = mod_path. remove ( mod_path. len ( ) - 1 ) ;
340
-
341
- let nested = ModPath :: Nested {
342
- segments : mod_path. into_iter ( ) . map ( ToString :: to_string) . collect ( ) ,
343
- item : name. to_string ( ) ,
344
- span,
345
- } ;
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) ;
351
- } else {
352
- unreachable ! ( "test to see if code path hit TODO REMOVE" )
353
- }
354
- }
0 commit comments