1- use  crate :: utils:: { snippet ,  span_lint_and_sugg ,  in_macro } ; 
1+ use  crate :: utils:: { in_macro ,  snippet ,  span_lint_and_sugg } ; 
22use  if_chain:: if_chain; 
3- use  rustc_ast:: ast; 
43use  rustc_data_structures:: fx:: FxHashMap ; 
54use  rustc_errors:: Applicability ; 
6- use  rustc_lint:: { EarlyContext ,  EarlyLintPass ,  LintContext ,  Lint } ; 
7- use  rustc_session:: { impl_lint_pass,  declare_tool_lint} ; 
8- use  rustc_span:: { edition:: Edition ,  Span } ; 
95use  rustc_hir as  hir; 
6+ use  hir:: def:: { Res ,  DefKind } ; 
7+ use  rustc_lint:: { LintContext ,  LateLintPass ,  LateContext } ; 
8+ use  rustc_session:: { declare_tool_lint,  impl_lint_pass} ; 
9+ use  rustc_span:: { edition:: Edition ,  Span } ; 
1010
1111const  PRELUDE :  & [ & str ]  = & [ 
12-     "marker" ,  "ops" ,  "convert" ,  "iter" ,  "option" ,  "result" ,  "borrow" ,  "boxed" ,  "string" ,  "vec" , 
13-     "macros" 
12+     "marker" ,  "ops" ,  "convert" ,  "iter" ,  "option" ,  "result" ,  "borrow" ,  "boxed" ,  "string" ,  "vec" ,  "macros" , 
1413] ; 
1514const  BRACKETS :  & [ char ]  = & [ '<' ,  '>' ] ; 
1615
@@ -34,14 +33,15 @@ declare_clippy_lint! {
3433
3534/// MacroRefData includes the name of the macro 
3635/// and the path from `SourceMap::span_to_filename`. 
36+ #[ derive( Debug ,  Clone ) ]  
3737pub  struct  MacroRefData  { 
3838    name :  String , 
3939    path :  String , 
4040} 
4141
4242impl  MacroRefData  { 
43-     pub  fn  new ( name :  String ,  span :  Span ,  ecx :  & EarlyContext < ' _ > )  -> Self  { 
44-         let  mut  path = ecx. sess . source_map ( ) . span_to_filename ( span) . to_string ( ) ; 
43+     pub  fn  new ( name :  String ,  span :  Span ,  ecx :  & LateContext < ' _ ,   ' _ > )  -> Self  { 
44+         let  mut  path = ecx. sess ( ) . source_map ( ) . span_to_filename ( span) . to_string ( ) ; 
4545
4646        // std lib paths are <::std::module::file type> 
4747        // so remove brackets and space 
@@ -51,7 +51,10 @@ impl MacroRefData {
5151        if  path. contains ( ' ' )  { 
5252            path = path. split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ; 
5353        } 
54-         Self  {  name :  name. to_string ( ) ,  path,  } 
54+         Self  { 
55+             name :  name. to_string ( ) , 
56+             path, 
57+         } 
5558    } 
5659} 
5760
@@ -62,122 +65,144 @@ pub struct MacroUseImports {
6265    /// the span of the macro reference and the `MacroRefData` 
6366/// for the use of the macro. 
6467collected :  FxHashMap < Span ,  MacroRefData > , 
68+     mac_refs :  Vec < ( Span ,  MacroRefData ) > , 
6569} 
6670
67- impl  MacroUseImports  { 
68-     fn  import_path_mac ( & self ,  use_path :  & str )  -> String  { 
69-         for  mac in  self . collected . values ( )  { 
70-             if  paths_match ( mac,  use_path)  { 
71-                 return  make_path ( mac,  use_path) 
72-             } 
73-         } 
74-         format ! ( "{}::<macro name>" ,  use_path) 
75-     } 
76- } 
77- 
78- fn  paths_match ( mac :  & MacroRefData ,  use_path :  & str )  -> bool  { 
79-     let  segs = mac. path . split ( "::" ) 
80-         . filter ( |s| * s != "" ) 
81-         . collect :: < Vec < _ > > ( ) ; 
71+ /// This is somewhat of a fallback for imports from `std::prelude` because they 
72+ /// are not recognized by `LateLintPass::check_item` `lcx.tcx.item_children(id)` 
73+ fn  make_path ( mac :  & MacroRefData ,  use_path :  & str )  -> String  { 
74+     let  segs = mac. path . split ( "::" ) . filter ( |s| * s != "" ) . collect :: < Vec < _ > > ( ) ; 
8275
83-     if  segs. starts_with ( & [ "std" ] )  { 
84-         return  PRELUDE . iter ( ) . any ( |m| segs . contains ( m ) ) 
76+     if  segs. starts_with ( & [ "std" ] )  &&  PRELUDE . iter ( ) . any ( |m| segs . contains ( m ) )   { 
77+         return  format ! ( "std::prelude::{} is imported by default, remove `use` statement" ,  mac . name ) ; 
8578    } 
86-     
87-     segs. starts_with ( & use_path. split ( "::" ) . collect :: < Vec < _ > > ( ) ) 
88- } 
8979
90- fn  make_path ( mac :  & MacroRefData ,  use_path :  & str )  -> String  { 
9180    if  use_path. split ( "::" ) . count ( )  == 1  { 
9281        return  format ! ( "{}::{}" ,  use_path,  mac. name) ; 
9382    } 
9483
95-     let  segs = mac. path . split ( "::" ) 
96-         . filter ( |s| * s != "" ) 
97-         . collect :: < Vec < _ > > ( ) ; 
98-     
99-     if  segs. starts_with ( & [ "std" ] )  && PRELUDE . iter ( ) . any ( |m| segs. contains ( m) )  { 
100-         return  format ! ( "std::prelude::{}" ,  mac. name) ; 
101-     } 
102-     
10384    mac. path . clone ( ) 
10485} 
10586
10687impl_lint_pass ! ( MacroUseImports  => [ MACRO_USE_IMPORTS ] ) ; 
10788
108- impl   EarlyLintPass  for  MacroUseImports  { 
109-     fn  check_item ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  item :  & ast :: Item )  { 
89+ impl < ' l ,   ' txc >   LateLintPass < ' l ,   ' txc >  for  MacroUseImports  { 
90+     fn  check_item ( & mut  self ,  lcx :  & LateContext < ' _ ,   ' _ > ,  item :  & hir :: Item < ' _ > )  { 
11091        if_chain !  { 
111-             if  ecx . sess. opts. edition == Edition :: Edition2018 ; 
112-             if  let  ast :: ItemKind :: Use ( use_tree )  = & item. kind; 
92+             if  lcx . sess( ) . opts. edition == Edition :: Edition2018 ; 
93+             if  let  hir :: ItemKind :: Use ( path ,  _kind )  = & item. kind; 
11394            if  let  Some ( mac_attr)  = item
11495                . attrs
11596                . iter( ) 
11697                . find( |attr| attr. ident( ) . map( |s| s. to_string( ) )  == Some ( "macro_use" . to_string( ) ) ) ; 
98+             if  let  Res :: Def ( DefKind :: Mod ,  id)  = path. res; 
11799            then { 
118-                 let  import_path = snippet( ecx,  use_tree. span,  "_" ) ; 
119-                 let  span = mac_attr. span. clone( ) ; 
120-                 self . imports. push( ( import_path. to_string( ) ,  span) ) ; 
100+                 for  kid in lcx. tcx. item_children( id) . iter( )  { 
101+                     if  let  Res :: Def ( DefKind :: Macro ( _mac_type) ,  mac_id)  = kid. res { 
102+                         let  span = mac_attr. span. clone( ) ; 
103+                         println!( "{:#?}" ,  lcx. tcx. def_path_str( mac_id) ) ; 
104+                         self . imports. push( ( lcx. tcx. def_path_str( mac_id) ,  span) ) ; 
105+                     } 
106+                 } 
107+             }  else { 
108+                 if  in_macro( item. span)  { 
109+                     let  call_site = item. span. source_callsite( ) ; 
110+                     let  name = snippet( lcx,  lcx. sess( ) . source_map( ) . span_until_char( call_site,  '!' ) ,  "_" ) ; 
111+                     if  let  Some ( callee)  = item. span. source_callee( )  { 
112+                         if  !self . collected. contains_key( & call_site)  { 
113+                             let  mac = MacroRefData :: new( name. to_string( ) ,  callee. def_site,  lcx) ; 
114+                             self . mac_refs. push( ( call_site,  mac. clone( ) ) ) ; 
115+                             self . collected. insert( call_site,  mac) ; 
116+                             // println!("EXPR {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site)); 
117+                         } 
118+                     } 
119+                 } 
121120            } 
122121        } 
123122    } 
124123
125-     fn  check_expr ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  expr :  & ast :: Expr )  { 
124+     fn  check_expr ( & mut  self ,  lcx :  & LateContext < ' _ ,   ' _ > ,  expr :  & hir :: Expr < ' _ > )  { 
126125        if  in_macro ( expr. span )  { 
127126            let  call_site = expr. span . source_callsite ( ) ; 
128-             let  name = snippet ( ecx ,  ecx . sess . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
127+             let  name = snippet ( lcx ,  lcx . sess ( ) . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
129128            if  let  Some ( callee)  = expr. span . source_callee ( )  { 
130-                 self . collected . entry ( call_site) 
131-                     . or_insert_with ( || { 
132-                         MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  ecx) 
133-                     } ) ; 
129+                 if  !self . collected . contains_key ( & call_site)  { 
130+                     let  mac = MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  lcx) ; 
131+                     self . mac_refs . push ( ( call_site,  mac. clone ( ) ) ) ; 
132+                     self . collected . insert ( call_site,  mac) ; 
133+                     // println!("EXPR {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site)); 
134+                 } 
134135            } 
135136        } 
136137    } 
137-     fn  check_stmt ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  stmt :  & ast :: Stmt )  { 
138+     fn  check_stmt ( & mut  self ,  lcx :  & LateContext < ' _ ,   ' _ > ,  stmt :  & hir :: Stmt < ' _ > )  { 
138139        if  in_macro ( stmt. span )  { 
139140            let  call_site = stmt. span . source_callsite ( ) ; 
140-             let  name = snippet ( ecx ,  ecx . sess . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
141+             let  name = snippet ( lcx ,  lcx . sess ( ) . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
141142            if  let  Some ( callee)  = stmt. span . source_callee ( )  { 
142-                 self . collected . entry ( call_site) 
143-                     . or_insert_with ( || { 
144-                         MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  ecx) 
145-                     } ) ; 
143+                 if  !self . collected . contains_key ( & call_site)  { 
144+                     let  mac = MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  lcx) ; 
145+                     self . mac_refs . push ( ( call_site,  mac. clone ( ) ) ) ; 
146+                     self . collected . insert ( call_site,  mac) ; 
147+                     // println!("STMT {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site)); 
148+                 } 
146149            } 
147150        } 
148151    } 
149-     fn  check_pat ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  pat :  & ast :: Pat )  { 
152+     fn  check_pat ( & mut  self ,  lcx :  & LateContext < ' _ ,   ' _ > ,  pat :  & hir :: Pat < ' _ > )  { 
150153        if  in_macro ( pat. span )  { 
151154            let  call_site = pat. span . source_callsite ( ) ; 
152-             let  name = snippet ( ecx ,  ecx . sess . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
155+             let  name = snippet ( lcx ,  lcx . sess ( ) . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
153156            if  let  Some ( callee)  = pat. span . source_callee ( )  { 
154-                 self . collected . entry ( call_site) 
155-                     . or_insert_with ( || { 
156-                         MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  ecx) 
157-                     } ) ; 
157+                 if  !self . collected . contains_key ( & call_site)  { 
158+                     let  mac = MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  lcx) ; 
159+                     self . mac_refs . push ( ( call_site,  mac. clone ( ) ) ) ; 
160+                     self . collected . insert ( call_site,  mac) ; 
161+                     // println!("PAT {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site)); 
162+                 } 
158163            } 
159164        } 
160165    } 
161-     fn  check_ty ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  ty :  & ast :: Ty )  { 
166+     fn  check_ty ( & mut  self ,  lcx :  & LateContext < ' _ ,   ' _ > ,  ty :  & hir :: Ty < ' _ > )  { 
162167        if  in_macro ( ty. span )  { 
163168            let  call_site = ty. span . source_callsite ( ) ; 
164-             let  name = snippet ( ecx ,  ecx . sess . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
169+             let  name = snippet ( lcx ,  lcx . sess ( ) . source_map ( ) . span_until_char ( call_site,  '!' ) ,  "_" ) ; 
165170            if  let  Some ( callee)  = ty. span . source_callee ( )  { 
166-                 self . collected . entry ( call_site) 
167-                     . or_insert_with ( || { 
168-                         MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  ecx) 
169-                     } ) ; 
171+                 if  !self . collected . contains_key ( & call_site)  { 
172+                     let  mac = MacroRefData :: new ( name. to_string ( ) ,  callee. def_site ,  lcx) ; 
173+                     self . mac_refs . push ( ( call_site,  mac. clone ( ) ) ) ; 
174+                     self . collected . insert ( call_site,  mac) ; 
175+                     // println!("TYPE {:?} {:?}", name, lcx.sess().source_map().span_to_filename(callee.def_site)); 
176+                 } 
170177            } 
171178        } 
172179    } 
173180
174-     fn  check_crate_post ( & mut  self ,  ecx :  & EarlyContext < ' _ > ,  _krate :  & ast:: Crate )  { 
175-         for  ( name,  span)  in  self . imports . iter ( )  { 
176-             let  import_path = self . import_path_mac ( & name) ; 
181+     fn  check_crate_post ( & mut  self ,  lcx :  & LateContext < ' _ ,  ' _ > ,  _krate :  & hir:: Crate < ' _ > )  { 
182+         for  ( import,  span)  in  self . imports . iter ( )  { 
183+ 
184+             let  matched = self . mac_refs . iter ( ) . find ( |( _span,  mac) | !import. ends_with ( & mac. name ) ) . is_some ( ) ; 
185+             if  matched { 
186+                 self . mac_refs . retain ( |( _span,  mac) | !import. ends_with ( & mac. name ) ) ; 
187+                 let  msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition" ; 
188+                 let  help = format ! ( "use {}" ,  import) ; 
189+                 span_lint_and_sugg ( 
190+                     lcx, 
191+                     MACRO_USE_IMPORTS , 
192+                     * span, 
193+                     msg, 
194+                     "remove the attribute and import the macro directly, try" , 
195+                     help, 
196+                     Applicability :: HasPlaceholders , 
197+                 ) 
198+             } 
199+         } 
200+ 
201+         for  ( span,  mac)  in  self . mac_refs . iter ( )  { 
177202            let  msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition" ; 
178-             let  help = format ! ( "use {}" ,  import_path ) ; 
203+             let  help = make_path ( mac ,   "hello" ) ; 
179204            span_lint_and_sugg ( 
180-                 ecx , 
205+                 lcx , 
181206                MACRO_USE_IMPORTS , 
182207                * span, 
183208                msg, 
0 commit comments