@@ -29,6 +29,12 @@ declare_clippy_lint! {
2929
3030const BRACKETS : & [ char ] = & [ '<' , '>' ] ;
3131
32+ #[ derive( Clone , Debug , PartialEq , Eq ) ]
33+ struct PathAndSpan {
34+ path : String ,
35+ span : Span ,
36+ }
37+
3238/// `MacroRefData` includes the name of the macro
3339/// and the path from `SourceMap::span_to_filename`.
3440#[ derive( Debug , Clone ) ]
@@ -110,7 +116,8 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
110116 for kid in cx. tcx. item_children( id) . iter( ) {
111117 if let Res :: Def ( DefKind :: Macro ( _mac_type) , mac_id) = kid. res {
112118 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) ) ;
114121 }
115122 }
116123 } else {
@@ -147,127 +154,69 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
147154 }
148155 #[ allow( clippy:: too_many_lines) ]
149156 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 ! [ ] ;
151159 for ( import, span) in & self . imports {
152160 let found_idx = self . mac_refs . iter ( ) . position ( |mac| import. ends_with ( & mac. name ) ) ;
153161
154162 if let Some ( idx) = found_idx {
155163 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+ }
186176 } ,
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) ) ;
202185 } 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) ;
245196 }
246197 } ,
247198 }
248- count += 1 ;
249199 }
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 ] ) ) )
257206 } else {
258- unreachable ! ( "a span must always be attached to a macro_use attribute" )
207+ suggestions . push ( ( span , format ! ( "{}::{{{}}}" , root , path . join ( ", " ) ) ) )
259208 }
260209 }
261210
262211 // If mac_refs is not empty we have encountered an import we could not handle
263212 // such as `std::prelude::v1::foo` or some other macro that expands to an import.
264213 if self . mac_refs . is_empty ( ) {
265- for ( span, import) in imports {
214+ for ( span, import) in suggestions {
266215 let help = format ! ( "use {}" , import) ;
267216 span_lint_and_sugg (
268217 cx,
269218 MACRO_USE_IMPORTS ,
270- span,
219+ * span,
271220 "`macro_use` attributes are no longer needed in the Rust 2018 edition" ,
272221 "remove the attribute and import the macro directly, try" ,
273222 help,
@@ -277,78 +226,3 @@ impl<'l, 'txc> LateLintPass<'l, 'txc> for MacroUseImports {
277226 }
278227 }
279228}
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