@@ -2,7 +2,7 @@ use crate::utils::{in_macro, snippet, span_lint_and_sugg};
22use hir:: def:: { DefKind , Res } ;
33use if_chain:: if_chain;
44use rustc_ast:: ast;
5- use rustc_data_structures:: fx:: FxHashSet ;
5+ use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
66use rustc_errors:: Applicability ;
77use rustc_hir as hir;
88use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
@@ -38,7 +38,7 @@ pub struct MacroRefData {
3838}
3939
4040impl MacroRefData {
41- pub fn new ( name : & str , callee : Span , cx : & LateContext < ' _ , ' _ > ) -> Self {
41+ pub fn new ( name : String , callee : Span , cx : & LateContext < ' _ , ' _ > ) -> Self {
4242 let mut path = cx. sess ( ) . source_map ( ) . span_to_filename ( callee) . to_string ( ) ;
4343
4444 // std lib paths are <::std::module::file type>
@@ -50,7 +50,7 @@ impl MacroRefData {
5050 path = path. split ( ' ' ) . next ( ) . unwrap ( ) . to_string ( ) ;
5151 }
5252 Self {
53- name : name . to_string ( ) ,
53+ name,
5454 path,
5555 }
5656 }
@@ -69,128 +69,265 @@ pub struct MacroUseImports {
6969impl_lint_pass ! ( MacroUseImports => [ MACRO_USE_IMPORTS ] ) ;
7070
7171impl MacroUseImports {
72- fn push_unique_macro ( & mut self , cx : & LateContext < ' _ , ' _ > , name : & str , call_site : Span , callee : Span ) {
73- if !self . collected . contains ( & call_site) {
74- let name = if name. contains ( "::" ) {
75- name. split ( "::" ) . last ( ) . unwrap ( ) . to_string ( )
76- } else {
77- name. to_string ( )
78- } ;
79-
80- self . mac_refs . push ( MacroRefData :: new ( & name, callee, cx) ) ;
81- self . collected . insert ( call_site) ;
72+ fn push_unique_macro ( & mut self , cx : & LateContext < ' _ , ' _ > , span : Span ) {
73+ let call_site = span. source_callsite ( ) ;
74+ let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
75+ if let Some ( callee) = span. source_callee ( ) {
76+ if !self . collected . contains ( & call_site) {
77+ let name = if name. contains ( "::" ) {
78+ name. split ( "::" ) . last ( ) . unwrap ( ) . to_string ( )
79+ } else {
80+ name. to_string ( )
81+ } ;
82+
83+ self . mac_refs . push ( MacroRefData :: new ( name, callee. def_site , cx) ) ;
84+ self . collected . insert ( call_site) ;
85+ }
8286 }
8387 }
8488
85- fn push_unique_macro_pat_ty ( & mut self , cx : & LateContext < ' _ , ' _ > , name : & str , call_site : Span , callee : Span ) {
86- if !self . collected . contains ( & call_site) {
87- self . mac_refs . push ( MacroRefData :: new ( & name, callee, cx) ) ;
88- self . collected . insert ( call_site) ;
89+ fn push_unique_macro_pat_ty ( & mut self , cx : & LateContext < ' _ , ' _ > , span : Span ) {
90+ let call_site = span. source_callsite ( ) ;
91+ let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
92+ if let Some ( callee) = span. source_callee ( ) {
93+ if !self . collected . contains ( & call_site) {
94+ self . mac_refs . push ( MacroRefData :: new ( name. to_string ( ) , callee. def_site , cx) ) ;
95+ self . collected . insert ( call_site) ;
96+ }
8997 }
9098 }
9199}
92100
93101impl < ' l , ' txc > LateLintPass < ' l , ' txc > for MacroUseImports {
94102 fn check_item ( & mut self , cx : & LateContext < ' _ , ' _ > , item : & hir:: Item < ' _ > ) {
95103 if_chain ! {
96- if cx. sess( ) . opts. edition == Edition :: Edition2018 ;
97- if let hir:: ItemKind :: Use ( path, _kind) = & item. kind;
98- if let Some ( mac_attr) = item
99- . attrs
100- . iter( )
101- . find( |attr| attr. ident( ) . map( |s| s. to_string( ) ) == Some ( "macro_use" . to_string( ) ) ) ;
102- if let Res :: Def ( DefKind :: Mod , id) = path. res;
103- then {
104- for kid in cx. tcx. item_children( id) . iter( ) {
105- if let Res :: Def ( DefKind :: Macro ( _mac_type) , mac_id) = kid. res {
106- let span = mac_attr. span;
107- self . imports. push( ( cx. tcx. def_path_str( mac_id) , span) ) ;
108- }
109- }
110- } else {
111- if in_macro( item. span) {
112- let call_site = item. span. source_callsite( ) ;
113- let name = snippet( cx, cx. sess( ) . source_map( ) . span_until_char( call_site, '!' ) , "_" ) ;
114- if let Some ( callee) = item. span. source_callee( ) {
115- if !self . collected. contains( & call_site) {
116- self . mac_refs. push( MacroRefData :: new( & name, callee. def_site, cx) ) ;
117- self . collected. insert( call_site) ;
118- }
119- }
104+ if cx. sess( ) . opts. edition == Edition :: Edition2018 ;
105+ if let hir:: ItemKind :: Use ( path, _kind) = & item. kind;
106+ if let Some ( mac_attr) = item
107+ . attrs
108+ . iter( )
109+ . find( |attr| attr. ident( ) . map( |s| s. to_string( ) ) == Some ( "macro_use" . to_string( ) ) ) ;
110+ if let Res :: Def ( DefKind :: Mod , id) = path. res;
111+ then {
112+ for kid in cx. tcx. item_children( id) . iter( ) {
113+ if let Res :: Def ( DefKind :: Macro ( _mac_type) , mac_id) = kid. res {
114+ let span = mac_attr. span;
115+ self . imports. push( ( cx. tcx. def_path_str( mac_id) , span) ) ;
120116 }
117+ }
118+ } else {
119+ if in_macro( item. span) {
120+ self . push_unique_macro_pat_ty( cx, item. span) ;
121+ }
121122 }
122123 }
123124 }
124125 fn check_attribute ( & mut self , cx : & LateContext < ' _ , ' _ > , attr : & ast:: Attribute ) {
125126 if in_macro ( attr. span ) {
126- let call_site = attr. span . source_callsite ( ) ;
127- let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
128- if let Some ( callee) = attr. span . source_callee ( ) {
129- self . push_unique_macro ( cx, & name, call_site, callee. def_site ) ;
130- }
127+ self . push_unique_macro ( cx, attr. span ) ;
131128 }
132129 }
133130 fn check_expr ( & mut self , cx : & LateContext < ' _ , ' _ > , expr : & hir:: Expr < ' _ > ) {
134131 if in_macro ( expr. span ) {
135- let call_site = expr. span . source_callsite ( ) ;
136- let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
137- if let Some ( callee) = expr. span . source_callee ( ) {
138- self . push_unique_macro ( cx, & name, call_site, callee. def_site ) ;
139- }
132+ self . push_unique_macro ( cx, expr. span ) ;
140133 }
141134 }
142135 fn check_stmt ( & mut self , cx : & LateContext < ' _ , ' _ > , stmt : & hir:: Stmt < ' _ > ) {
143136 if in_macro ( stmt. span ) {
144- let call_site = stmt. span . source_callsite ( ) ;
145- let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
146- if let Some ( callee) = stmt. span . source_callee ( ) {
147- self . push_unique_macro ( cx, & name, call_site, callee. def_site ) ;
148- }
137+ self . push_unique_macro ( cx, stmt. span ) ;
149138 }
150139 }
151140 fn check_pat ( & mut self , cx : & LateContext < ' _ , ' _ > , pat : & hir:: Pat < ' _ > ) {
152141 if in_macro ( pat. span ) {
153- let call_site = pat. span . source_callsite ( ) ;
154- let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
155- if let Some ( callee) = pat. span . source_callee ( ) {
156- self . push_unique_macro_pat_ty ( cx, & name, call_site, callee. def_site ) ;
157- }
142+ self . push_unique_macro_pat_ty ( cx, pat. span ) ;
158143 }
159144 }
160145 fn check_ty ( & mut self , cx : & LateContext < ' _ , ' _ > , ty : & hir:: Ty < ' _ > ) {
161146 if in_macro ( ty. span ) {
162- let call_site = ty. span . source_callsite ( ) ;
163- let name = snippet ( cx, cx. sess ( ) . source_map ( ) . span_until_char ( call_site, '!' ) , "_" ) ;
164- if let Some ( callee) = ty. span . source_callee ( ) {
165- self . push_unique_macro_pat_ty ( cx, & name, call_site, callee. def_site ) ;
166- }
147+ self . push_unique_macro_pat_ty ( cx, ty. span ) ;
167148 }
168149 }
169150
170151 fn check_crate_post ( & mut self , cx : & LateContext < ' _ , ' _ > , _krate : & hir:: Crate < ' _ > ) {
152+ let mut import_map = FxHashMap :: default ( ) ;
171153 for ( import, span) in & self . imports {
172- let matched = self . mac_refs . iter ( ) . any ( |mac| import. ends_with ( & mac. name ) ) ;
154+ let found_idx = self . mac_refs . iter ( ) . position ( |mac| import. ends_with ( & mac. name ) ) ;
155+
156+ if let Some ( idx) = found_idx {
157+ let _ = self . mac_refs . remove ( idx) ;
158+ proccess_macro_path ( * span, import, & mut import_map) ;
159+ }
160+ }
161+ println ! ( "{:#?}" , import_map) ;
162+ let mut imports = vec ! [ ] ;
163+ for ( root, rest) in import_map {
164+ let mut path = format ! ( "use {}::" , root) ;
165+ let mut s = None ;
166+ let mut count = 1 ;
167+ let rest_len = rest. len ( ) ;
168+ if rest_len > 1 {
169+ path. push_str ( "{" ) ;
170+ }
171+ for m in & rest {
172+ println ! ( "{} => {:?}" , root, m) ;
173+ if count == 1 {
174+ s = Some ( m. span ( ) ) ;
175+ }
176+
177+ let comma = if rest_len == count { "" } else { ", " } ;
178+ match m {
179+ ModPath :: Item { item, .. } => {
180+ path. push_str ( & format ! ( "{}{}" , item, comma) ) ;
181+ }
182+ ModPath :: Nested { names, item, span } => {
183+ let nested = rest. iter ( )
184+ // filter "self" out
185+ . filter ( |other_m| other_m != & m)
186+ // this matches the first path segment and filters non ModPath::Nested items
187+ . filter ( |other_m| other_m. matches ( 0 , m) )
188+ . collect :: < Vec < _ > > ( ) ;
189+
190+ println ! ( "{:#?}" , nested) ;
191+
192+ if nested. is_empty ( ) {
193+ path. push_str ( & format ! ( "{}::{}{}" , names. join( "::" ) . to_string( ) , item, comma) )
194+ } else {
195+ // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2}
196+ let mod_path = if names. len ( ) - 1 > 0 {
197+ ModPath :: Nested { names : names. clone ( ) , item : item. to_string ( ) , span : * span, }
198+ } else {
199+ ModPath :: Item { item : names[ 0 ] . to_string ( ) , span : * span, }
200+ } ;
201+ let names = recursive_path_push ( mod_path, comma, & rest, String :: new ( ) ) ;
202+ path. push_str ( & format ! ( "{}::{{{}}}{}" , names, item, comma) )
203+ }
204+ }
205+ }
206+ count += 1 ;
207+ }
208+ if rest_len > 1 {
209+ path. push_str ( "};" ) ;
210+ }
211+ if let Some ( span) = s {
212+ imports. push ( ( span, path) )
213+ }
214+ }
173215
174- if matched {
175- self . mac_refs . retain ( |mac| !import. ends_with ( & mac. name ) ) ;
176- let msg = "`macro_use` attributes are no longer needed in the Rust 2018 edition" ;
216+ if !self . mac_refs . is_empty ( ) {
217+ // TODO if not empty we found one we could not make a suggestion for
218+ // such as std::prelude::v1 or something else I haven't thought of.
219+ // If we defer the calling of span_lint_and_sugg we can make a decision about its
220+ // applicability?
221+ } else {
222+ for ( span, import) in imports {
177223 let help = format ! ( "use {}" , import) ;
178224 span_lint_and_sugg (
179225 cx,
180226 MACRO_USE_IMPORTS ,
181- * span,
182- msg ,
227+ span,
228+ "`macro_use` attributes are no longer needed in the Rust 2018 edition" ,
183229 "remove the attribute and import the macro directly, try" ,
184230 help,
185231 Applicability :: MaybeIncorrect ,
186232 )
187233 }
188234 }
189- if !self . mac_refs . is_empty ( ) {
190- // TODO if not empty we found one we could not make a suggestion for
191- // such as std::prelude::v1 or something else I haven't thought of.
192- // If we defer the calling of span_lint_and_sugg we can make a decision about its
193- // applicability?
235+ }
236+ }
237+
238+ #[ derive( Debug , PartialEq ) ]
239+ enum ModPath {
240+ Item { item : String , span : Span , } ,
241+ Nested { names : Vec < String > , item : String , span : Span , } ,
242+ }
243+
244+ impl ModPath {
245+ fn span ( & self ) -> Span {
246+ match self {
247+ Self :: Item { span, .. } => * span,
248+ Self :: Nested { span, .. } => * span,
249+ }
250+ }
251+
252+ fn item ( & self ) -> & str {
253+ match self {
254+ Self :: Item { item, .. } => item,
255+ Self :: Nested { item, .. } => item,
256+ }
257+ }
258+
259+ fn matches ( & self , idx : usize , other : & ModPath ) -> bool {
260+ match ( self , other) {
261+ ( Self :: Item { item, .. } , Self :: Item { item : other_item, .. } ) => item == other_item,
262+ ( Self :: Nested { names, .. } , Self :: Nested { names : other_names, .. } ) => {
263+ match ( names. get ( idx) , other_names. get ( idx) ) {
264+ ( Some ( seg) , Some ( other_seg) ) => seg == other_seg,
265+ ( _, _) => false ,
266+ }
267+ }
268+ ( _, _) => false ,
269+ }
270+ }
271+ }
272+
273+ fn proccess_macro_path ( span : Span , import : & str , import_map : & mut FxHashMap < String , Vec < ModPath > > ) {
274+ let mut mod_path = import. split ( "::" ) . collect :: < Vec < _ > > ( ) ;
275+
276+ if mod_path. len ( ) == 2 {
277+ let item_list = import_map. entry ( mod_path[ 0 ] . to_string ( ) )
278+ . or_insert ( vec ! [ ] ) ;
279+
280+ if !item_list. iter ( ) . any ( |mods| mods. item ( ) == mod_path[ 1 ] ) {
281+ item_list. push ( ModPath :: Item {
282+ item : mod_path[ 1 ] . to_string ( ) ,
283+ span,
284+ } ) ;
285+ }
286+ } else if mod_path. len ( ) > 2 {
287+ let first = mod_path. remove ( 0 ) ;
288+ let name = mod_path. remove ( mod_path. len ( ) - 1 ) ;
289+
290+ let nested = ModPath :: Nested {
291+ names : mod_path. into_iter ( ) . map ( ToString :: to_string) . collect ( ) ,
292+ item : name. to_string ( ) ,
293+ span,
294+ } ;
295+ import_map. entry ( first. to_string ( ) )
296+ . or_insert ( vec ! [ ] )
297+ . push ( nested) ;
298+ } else {
299+ unreachable ! ( "test to see if code path hit TODO REMOVE" )
300+ }
301+ }
302+
303+ fn recursive_path_push ( module : ModPath , comma : & str , rest : & [ ModPath ] , mut path : String ) -> String {
304+ match & module {
305+ ModPath :: Item { item, .. } => {
306+ path. push_str ( & format ! ( "{}{}" , item, comma) ) ;
307+ }
308+ ModPath :: Nested { names, item, span } => {
309+ let nested = rest. iter ( )
310+ // filter "self" out
311+ . filter ( |other_m| other_m != & & module)
312+ // this matches the first path segment and filters non ModPath::Nested items
313+ . filter ( |other_m| other_m. matches ( 0 , & module) )
314+ . collect :: < Vec < _ > > ( ) ;
315+
316+ println ! ( "{:#?}" , nested) ;
317+
318+ if nested. is_empty ( ) {
319+ path. push_str ( & format ! ( "{}::{}{}" , names. join( "::" ) . to_string( ) , item, comma) )
320+ } else {
321+ // use mod_a::{mod_b::{one, two}, mod_c::item, item1, item2}
322+ let mod_path = if names. len ( ) - 1 > 0 {
323+ ModPath :: Nested { names : names. clone ( ) , item : item. to_string ( ) , span : * span, }
324+ } else {
325+ ModPath :: Item { item : names[ 0 ] . to_string ( ) , span : * span, }
326+ } ;
327+ let names = recursive_path_push ( mod_path, comma, rest, path. to_string ( ) ) ;
328+ // path.push_str(&format!("{}{}", item, comma));
329+ }
194330 }
195331 }
332+ path
196333}
0 commit comments