1- use  crate :: utils:: { snippet,  span_lint_and_sugg} ; 
1+ use  crate :: utils:: { in_macro,  snippet,  span_lint_and_sugg} ; 
2+ use  hir:: def:: { DefKind ,  Res } ; 
23use  if_chain:: if_chain; 
34use  rustc_ast:: ast; 
5+ use  rustc_data_structures:: fx:: { FxHashMap ,  FxHashSet } ; 
46use  rustc_errors:: Applicability ; 
5- use  rustc_lint:: { EarlyContext ,  EarlyLintPass } ; 
6- use  rustc_session:: { declare_lint_pass,  declare_tool_lint} ; 
7- use  rustc_span:: edition:: Edition ; 
7+ use  rustc_hir as  hir; 
8+ use  rustc_lint:: { LateContext ,  LateLintPass ,  LintContext } ; 
9+ use  rustc_session:: { declare_tool_lint,  impl_lint_pass} ; 
10+ use  rustc_span:: { edition:: Edition ,  Span } ; 
811
912declare_clippy_lint !  { 
1013    /// **What it does:** Checks for `#[macro_use] use...`. 
1114/// 
1215/// **Why is this bad?** Since the Rust 2018 edition you can import 
1316/// macro's directly, this is considered idiomatic. 
1417/// 
15- /// **Known problems:** This lint does not generate an auto-applicable suggestion . 
18+ /// **Known problems:** None . 
1619/// 
1720/// **Example:** 
1821/// ```rust 
@@ -24,29 +27,205 @@ declare_clippy_lint! {
2427    "#[macro_use] is no longer needed" 
2528} 
2629
27- declare_lint_pass ! ( MacroUseImports  =>  [ MACRO_USE_IMPORTS ] ) ; 
30+ const   BRACKETS :   & [ char ]  =  & [ '<' ,   '>' ] ; 
2831
29- impl  EarlyLintPass  for  MacroUseImports  { 
30-     fn  check_item ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  item :  & ast:: Item )  { 
32+ #[ derive( Clone ,  Debug ,  PartialEq ,  Eq ) ]  
33+ struct  PathAndSpan  { 
34+     path :  String , 
35+     span :  Span , 
36+ } 
37+ 
38+ /// `MacroRefData` includes the name of the macro 
39+ /// and the path from `SourceMap::span_to_filename`. 
40+ #[ derive( Debug ,  Clone ) ]  
41+ pub  struct  MacroRefData  { 
42+     name :  String , 
43+     path :  String , 
44+ } 
45+ 
46+ impl  MacroRefData  { 
47+     pub  fn  new ( name :  String ,  callee :  Span ,  cx :  & LateContext < ' _ ,  ' _ > )  -> Self  { 
48+         let  mut  path = cx. sess ( ) . source_map ( ) . span_to_filename ( callee) . to_string ( ) ; 
49+ 
50+         // std lib paths are <::std::module::file type> 
51+         // so remove brackets, space and type. 
52+         if  path. contains ( '<' )  { 
53+             path = path. replace ( BRACKETS ,  "" ) ; 
54+         } 
55+         if  path. contains ( ' ' )  { 
56+             path = path. split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ; 
57+         } 
58+         Self  {  name,  path } 
59+     } 
60+ } 
61+ 
62+ #[ derive( Default ) ]  
63+ #[ allow( clippy:: module_name_repetitions) ]  
64+ pub  struct  MacroUseImports  { 
65+     /// the actual import path used and the span of the attribute above it. 
66+ imports :  Vec < ( String ,  Span ) > , 
67+     /// the span of the macro reference, kept to ensure only one reference is used per macro call. 
68+ collected :  FxHashSet < Span > , 
69+     mac_refs :  Vec < MacroRefData > , 
70+ } 
71+ 
72+ impl_lint_pass ! ( MacroUseImports  => [ MACRO_USE_IMPORTS ] ) ; 
73+ 
74+ impl  MacroUseImports  { 
75+     fn  push_unique_macro ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  span :  Span )  { 
76+         let  call_site = span. source_callsite ( ) ; 
77+         let  name = snippet ( cx,  cx. sess ( ) . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
78+         if  let  Some ( callee)  = span. source_callee ( )  { 
79+             if  !self . collected . contains ( & call_site)  { 
80+                 let  name = if  name. contains ( "::" )  { 
81+                     name. split ( "::" ) . last ( ) . unwrap ( ) . to_string ( ) 
82+                 }  else  { 
83+                     name. to_string ( ) 
84+                 } ; 
85+ 
86+                 self . mac_refs . push ( MacroRefData :: new ( name,  callee. def_site ,  cx) ) ; 
87+                 self . collected . insert ( call_site) ; 
88+             } 
89+         } 
90+     } 
91+ 
92+     fn  push_unique_macro_pat_ty ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  span :  Span )  { 
93+         let  call_site = span. source_callsite ( ) ; 
94+         let  name = snippet ( cx,  cx. sess ( ) . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
95+         if  let  Some ( callee)  = span. source_callee ( )  { 
96+             if  !self . collected . contains ( & call_site)  { 
97+                 self . mac_refs 
98+                     . push ( MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  cx) ) ; 
99+                 self . collected . insert ( call_site) ; 
100+             } 
101+         } 
102+     } 
103+ } 
104+ 
105+ impl < ' l ,  ' txc >  LateLintPass < ' l ,  ' txc >  for  MacroUseImports  { 
106+     fn  check_item ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  item :  & hir:: Item < ' _ > )  { 
31107        if_chain !  { 
32-             if  ecx . sess. opts. edition == Edition :: Edition2018 ; 
33-             if  let  ast :: ItemKind :: Use ( use_tree )  = & item. kind; 
108+             if  cx . sess( ) . opts. edition == Edition :: Edition2018 ; 
109+             if  let  hir :: ItemKind :: Use ( path ,  _kind )  = & item. kind; 
34110            if  let  Some ( mac_attr)  = item
35111                . attrs
36112                . iter( ) 
37113                . find( |attr| attr. ident( ) . map( |s| s. to_string( ) )  == Some ( "macro_use" . to_string( ) ) ) ; 
114+             if  let  Res :: Def ( DefKind :: Mod ,  id)  = path. res; 
38115            then { 
39-                 let  msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition" ; 
40-                 let  help = format!( "use {}::<macro name>" ,  snippet( ecx,  use_tree. span,  "_" ) ) ; 
116+                 for  kid in cx. tcx. item_children( id) . iter( )  { 
117+                     if  let  Res :: Def ( DefKind :: Macro ( _mac_type) ,  mac_id)  = kid. res { 
118+                         let  span = mac_attr. span; 
119+                         let  def_path = cx. tcx. def_path_str( mac_id) ; 
120+                         self . imports. push( ( def_path,  span) ) ; 
121+                     } 
122+                 } 
123+             }  else { 
124+                 if  in_macro( item. span)  { 
125+                     self . push_unique_macro_pat_ty( cx,  item. span) ; 
126+                 } 
127+             } 
128+         } 
129+     } 
130+     fn  check_attribute ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  attr :  & ast:: Attribute )  { 
131+         if  in_macro ( attr. span )  { 
132+             self . push_unique_macro ( cx,  attr. span ) ; 
133+         } 
134+     } 
135+     fn  check_expr ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  expr :  & hir:: Expr < ' _ > )  { 
136+         if  in_macro ( expr. span )  { 
137+             self . push_unique_macro ( cx,  expr. span ) ; 
138+         } 
139+     } 
140+     fn  check_stmt ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  stmt :  & hir:: Stmt < ' _ > )  { 
141+         if  in_macro ( stmt. span )  { 
142+             self . push_unique_macro ( cx,  stmt. span ) ; 
143+         } 
144+     } 
145+     fn  check_pat ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  pat :  & hir:: Pat < ' _ > )  { 
146+         if  in_macro ( pat. span )  { 
147+             self . push_unique_macro_pat_ty ( cx,  pat. span ) ; 
148+         } 
149+     } 
150+     fn  check_ty ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  ty :  & hir:: Ty < ' _ > )  { 
151+         if  in_macro ( ty. span )  { 
152+             self . push_unique_macro_pat_ty ( cx,  ty. span ) ; 
153+         } 
154+     } 
155+     #[ allow( clippy:: too_many_lines) ]  
156+     fn  check_crate_post ( & mut  self ,  cx :  & LateContext < ' _ ,  ' _ > ,  _krate :  & hir:: Crate < ' _ > )  { 
157+         let  mut  used = FxHashMap :: default ( ) ; 
158+         let  mut  check_dup = vec ! [ ] ; 
159+         for  ( import,  span)  in  & self . imports  { 
160+             let  found_idx = self . mac_refs . iter ( ) . position ( |mac| import. ends_with ( & mac. name ) ) ; 
161+ 
162+             if  let  Some ( idx)  = found_idx { 
163+                 let  _ = self . mac_refs . remove ( idx) ; 
164+                 let  seg = import. split ( "::" ) . collect :: < Vec < _ > > ( ) ; 
165+ 
166+                 match  seg. as_slice ( )  { 
167+                     // an empty path is impossible 
168+                     // a path should always consist of 2 or more segments 
169+                     [ ]  | [ _]  => return , 
170+                     [ root,  item]  => { 
171+                         if  !check_dup. contains ( & ( * item) . to_string ( ) )  { 
172+                             used. entry ( ( ( * root) . to_string ( ) ,  span) ) 
173+                                 . or_insert_with ( Vec :: new) 
174+                                 . push ( ( * item) . to_string ( ) ) ; 
175+                             check_dup. push ( ( * item) . to_string ( ) ) ; 
176+                         } 
177+                     } , 
178+                     [ root,  rest @ ..]  => { 
179+                         if  rest. iter ( ) . all ( |item| !check_dup. contains ( & ( * item) . to_string ( ) ) )  { 
180+                             let  filtered = rest
181+                                 . iter ( ) 
182+                                 . filter_map ( |item| { 
183+                                     if  check_dup. contains ( & ( * item) . to_string ( ) )  { 
184+                                         None 
185+                                     }  else  { 
186+                                         Some ( ( * item) . to_string ( ) ) 
187+                                     } 
188+                                 } ) 
189+                                 . collect :: < Vec < _ > > ( ) ; 
190+                             used. entry ( ( ( * root) . to_string ( ) ,  span) ) 
191+                                 . or_insert_with ( Vec :: new) 
192+                                 . push ( filtered. join ( "::" ) ) ; 
193+                             check_dup. extend ( filtered) ; 
194+                         }  else  { 
195+                             let  rest = rest. to_vec ( ) ; 
196+                             used. entry ( ( ( * root) . to_string ( ) ,  span) ) 
197+                                 . or_insert_with ( Vec :: new) 
198+                                 . push ( rest. join ( "::" ) ) ; 
199+                             check_dup. extend ( rest. iter ( ) . map ( ToString :: to_string) ) ; 
200+                         } 
201+                     } , 
202+                 } 
203+             } 
204+         } 
205+ 
206+         let  mut  suggestions = vec ! [ ] ; 
207+         for  ( ( root,  span) ,  path)  in  used { 
208+             if  path. len ( )  == 1  { 
209+                 suggestions. push ( ( span,  format ! ( "{}::{}" ,  root,  path[ 0 ] ) ) ) 
210+             }  else  { 
211+                 suggestions. push ( ( span,  format ! ( "{}::{{{}}}" ,  root,  path. join( ", " ) ) ) ) 
212+             } 
213+         } 
214+ 
215+         // If mac_refs is not empty we have encountered an import we could not handle 
216+         // such as `std::prelude::v1::foo` or some other macro that expands to an import. 
217+         if  self . mac_refs . is_empty ( )  { 
218+             for  ( span,  import)  in  suggestions { 
219+                 let  help = format ! ( "use {};" ,  import) ; 
41220                span_lint_and_sugg ( 
42-                     ecx , 
221+                     cx , 
43222                    MACRO_USE_IMPORTS , 
44-                     mac_attr . span, 
45-                     msg , 
223+                     * span, 
224+                     "`macro_use` attributes are no longer needed in the Rust 2018 edition" , 
46225                    "remove the attribute and import the macro directly, try" , 
47226                    help, 
48-                     Applicability :: HasPlaceholders , 
49-                 ) ; 
227+                     Applicability :: MaybeIncorrect , 
228+                 ) 
50229            } 
51230        } 
52231    } 
0 commit comments