11use  proc_macro2:: { Span ,  TokenStream } ; 
22use  quote:: { quote,  quote_spanned,  ToTokens } ; 
33use  std:: collections:: HashMap ; 
4- use  syn:: { parse_quote,  spanned:: Spanned ,  Attribute ,  Generics ,  Ident } ; 
4+ use  syn:: { 
5+     parse:: { Error ,  ParseStream } , 
6+     parse_quote, 
7+     spanned:: Spanned , 
8+     Attribute ,  Generics ,  Ident ,  LitStr ,  Token , 
9+ } ; 
10+ 
11+ mod  kw { 
12+     syn:: custom_keyword!( but_impl_because) ; 
13+ } 
14+ 
15+ fn  parse_skip_reason ( input :  ParseStream < ' _ > )  -> Result < ( ) ,  Error >  { 
16+     input. parse :: < kw:: but_impl_because > ( ) ?; 
17+     input. parse :: < Token ! [ =] > ( ) ?; 
18+     let  reason = input. parse :: < LitStr > ( ) ?; 
19+     if  reason. value ( ) . trim ( ) . is_empty ( )  { 
20+         Err ( Error :: new_spanned ( 
21+             reason, 
22+             "the value of `but_impl_because` must be a non-empty string" , 
23+         ) ) 
24+     }  else  { 
25+         Ok ( ( ) ) 
26+     } 
27+ } 
528
629/// Generate a type parameter with the given `suffix` that does not conflict with 
730/// any of the `existing` generics. 
@@ -31,9 +54,9 @@ fn gen_interner(structure: &mut synstructure::Structure<'_>) -> TokenStream {
3154        } ) 
3255} 
3356
34- /// Returns the `Span` of the  first `#[skip_traversal]` attribute in `attrs`. 
35- fn  find_skip_traversal_attribute ( attrs :  & [ Attribute ] )  -> Option < Span >  { 
36-     attrs. iter ( ) . find ( |& attr| * attr ==  parse_quote !   {  # [ skip_traversal ]   } ) . map ( Spanned :: span ) 
57+ /// Returns the first `#[skip_traversal]` attribute in `attrs`. 
58+ fn  find_skip_traversal_attribute ( attrs :  & [ Attribute ] )  -> Option < & Attribute >  { 
59+     attrs. iter ( ) . find ( |& attr| attr. path . is_ident ( "skip_traversal" ) ) 
3760} 
3861
3962pub  struct  Foldable ; 
@@ -160,23 +183,30 @@ pub fn traversable_derive<T: Traversable>(
160183        structure. add_where_predicate ( parse_quote !  {  Self :  #supertraits } ) ; 
161184    } 
162185
163-     let  body = if  let  Some ( _span)  = find_skip_traversal_attribute ( & ast. attrs )  { 
164-         if  !is_generic { 
165-             // FIXME: spanned error causes ICE: "suggestion must not have overlapping parts" 
166-             return  quote ! ( { 
167-                 :: core:: compile_error!( "non-generic types are automatically skipped where possible" ) ; 
168-             } ) ; 
186+     let  body = if  let  Some ( attr)  = find_skip_traversal_attribute ( & ast. attrs )  { 
187+         if  let  Err ( err)  = attr. parse_args_with ( parse_skip_reason)  { 
188+             return  err. into_compile_error ( ) ; 
169189        } 
190+         // If `is_generic`, it's possible that this no-op impl may not be applicable; but that's fine as it 
191+         // will cause a compilation error forcing removal of the inappropriate `#[skip_traversal]` attribute. 
170192        structure. add_where_predicate ( parse_quote !  {  Self :  #skip_traversal } ) ; 
171193        T :: traverse ( quote !  {  self  } ,  true ) 
194+     }  else  if  !is_generic { 
195+         quote_spanned ! ( ast. ident. span( )  => { 
196+             :: core:: compile_error!( "\  
197+                  traversal of non-generic types are no-ops by default, so explicitly deriving the traversable traits for them is rarely necessary\n \ 
198+                  if the need has arisen to due the appearance of this type in an anonymous tuple, consider replacing that tuple with a named struct\n \ 
199+                  otherwise add `#[skip_traversal(but_impl_because = \" <reason for implementation>\" )]` to this type\ 
200+              ") 
201+         } ) 
172202    }  else  { 
173203        // We add predicates to each generic field type, rather than to our generic type parameters. 
174204        // This results in a "perfect derive" that avoids having to propagate `#[skip_traversal]` annotations 
175205        // into wrapping types, but it can result in trait solver cycles if any type parameters are involved 
176206        // in recursive type definitions; fortunately that is not the case (yet). 
177207        let  mut  predicates = HashMap :: new ( ) ; 
178208        let  arms = structure. each_variant ( |variant| { 
179-             let  skipped_variant_span = find_skip_traversal_attribute ( & variant. ast ( ) . attrs ) ; 
209+             let  skipped_variant_span = find_skip_traversal_attribute ( & variant. ast ( ) . attrs ) . map ( Spanned :: span ) ; 
180210            if  variant. referenced_ty_params ( ) . is_empty ( )  { 
181211                if  let  Some ( span)  = skipped_variant_span { 
182212                    return  quote_spanned ! ( span => { 
@@ -186,7 +216,7 @@ pub fn traversable_derive<T: Traversable>(
186216            } 
187217            T :: arm ( variant,  |bind| { 
188218                let  ast = bind. ast ( ) ; 
189-                 let  skipped_span = skipped_variant_span. or_else ( || find_skip_traversal_attribute ( & ast. attrs ) ) ; 
219+                 let  skipped_span = skipped_variant_span. or_else ( || find_skip_traversal_attribute ( & ast. attrs ) . map ( Spanned :: span ) ) ; 
190220                if  bind. referenced_ty_params ( ) . is_empty ( )  { 
191221                    if  skipped_variant_span. is_none ( )  && let  Some ( span)  = skipped_span { 
192222                        return  quote_spanned ! ( span => { 
0 commit comments