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.
6467 collected : 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