11#![ deny( unused_must_use) ]  
22
33use  crate :: diagnostics:: error:: { 
4-     invalid_nested_attr,  span_err,  throw_invalid_attr,  throw_invalid_nested_attr,  throw_span_err, 
5-     DiagnosticDeriveError , 
4+     span_err,  throw_invalid_attr,  throw_span_err,  DiagnosticDeriveError , 
65} ; 
76use  crate :: diagnostics:: utils:: { 
87    build_field_mapping,  is_doc_comment,  report_error_if_not_applied_to_span,  report_type_error, 
@@ -11,9 +10,8 @@ use crate::diagnostics::utils::{
1110} ; 
1211use  proc_macro2:: { Ident ,  Span ,  TokenStream } ; 
1312use  quote:: { format_ident,  quote} ; 
14- use  syn:: { 
15-     parse_quote,  spanned:: Spanned ,  Attribute ,  Meta ,  MetaList ,  MetaNameValue ,  NestedMeta ,  Path ,  Type , 
16- } ; 
13+ use  syn:: Token ; 
14+ use  syn:: { parse_quote,  spanned:: Spanned ,  Attribute ,  Meta ,  Path ,  Type } ; 
1715use  synstructure:: { BindingInfo ,  Structure ,  VariantInfo } ; 
1816
1917/// What kind of diagnostic is being derived - a fatal/error/warning or a lint? 
@@ -77,7 +75,7 @@ impl DiagnosticDeriveBuilder {
7775        match  ast. data  { 
7876            syn:: Data :: Struct ( ..)  | syn:: Data :: Enum ( ..)  => ( ) , 
7977            syn:: Data :: Union ( ..)  => { 
80-                 span_err ( span,  "diagnostic derives can only be used on structs and enums" ) ; 
78+                 span_err ( span,  "diagnostic derives can only be used on structs and enums" ) . emit ( ) ; 
8179            } 
8280        } 
8381
@@ -160,8 +158,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
160158        } ; 
161159
162160        if  let  SubdiagnosticKind :: MultipartSuggestion  {  .. }  = subdiag { 
163-             let  meta = attr. parse_meta ( ) ?; 
164-             throw_invalid_attr ! ( attr,  & meta,  |diag| diag
161+             throw_invalid_attr ! ( attr,  |diag| diag
165162                . help( "consider creating a `Subdiagnostic` instead" ) ) ; 
166163        } 
167164
@@ -191,71 +188,44 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
191188            return  Ok ( quote !  { } ) ; 
192189        } 
193190
194-         let  name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ; 
191+         let  name = attr. path ( ) . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ; 
195192        let  name = name. as_str ( ) ; 
196-         let  meta = attr. parse_meta ( ) ?; 
197193
198-         if  name == "diag"  { 
199-             let  Meta :: List ( MetaList  {  ref  nested,  .. } )  = meta else  { 
200-                 throw_invalid_attr ! ( 
201-                     attr, 
202-                     & meta
203-                 ) ; 
204-             } ; 
194+         let  mut  first = true ; 
205195
206-             let  mut  nested_iter = nested. into_iter ( ) . peekable ( ) ; 
196+         if  name == "diag"  { 
197+             let  mut  tokens = TokenStream :: new ( ) ; 
198+             attr. parse_nested_meta ( |nested| { 
199+                 let  path = & nested. path ; 
207200
208-             match  nested_iter . peek ( )  { 
209-                 Some ( NestedMeta :: Meta ( Meta :: Path ( slug ) ) )  =>  { 
210-                     self . slug . set_once ( slug . clone ( ) ,  slug . span ( ) . unwrap ( ) ) ; 
211-                     nested_iter . next ( ) ; 
201+                  if  first &&  ( nested . input . is_empty ( )  || nested . input . peek ( Token ! [ , ] ) )  { 
202+                      self . slug . set_once ( path . clone ( ) ,  path . span ( ) . unwrap ( ) ) ; 
203+                     first =  false ; 
204+                     return   Ok ( ( ) ) 
212205                } 
213-                 Some ( NestedMeta :: Meta ( Meta :: NameValue  {  .. } ) )  => { } 
214-                 Some ( nested_attr)  => throw_invalid_nested_attr ! ( attr,  nested_attr,  |diag| diag
215-                     . help( "a diagnostic slug is required as the first argument" ) ) , 
216-                 None  => throw_invalid_attr ! ( attr,  & meta,  |diag| diag
217-                     . help( "a diagnostic slug is required as the first argument" ) ) , 
218-             } ; 
219206
220-             // Remaining attributes are optional, only `code = ".."` at the moment. 
221-             let  mut  tokens = TokenStream :: new ( ) ; 
222-             for  nested_attr in  nested_iter { 
223-                 let  ( value,  path)  = match  nested_attr { 
224-                     NestedMeta :: Meta ( Meta :: NameValue ( MetaNameValue  { 
225-                         lit :  syn:: Lit :: Str ( value) , 
226-                         path, 
227-                         ..
228-                     } ) )  => ( value,  path) , 
229-                     NestedMeta :: Meta ( Meta :: Path ( _) )  => { 
230-                         invalid_nested_attr ( attr,  nested_attr) 
231-                             . help ( "diagnostic slug must be the first argument" ) 
232-                             . emit ( ) ; 
233-                         continue ; 
234-                     } 
235-                     _ => { 
236-                         invalid_nested_attr ( attr,  nested_attr) . emit ( ) ; 
237-                         continue ; 
238-                     } 
207+                 first = false ; 
208+ 
209+                 let  Ok ( nested)  = nested. value ( )  else  { 
210+                     span_err ( nested. input . span ( ) . unwrap ( ) ,  "diagnostic slug must be the first argument" ) . emit ( ) ; 
211+                     return  Ok ( ( ) ) 
239212                } ; 
240213
241-                 let  nested_name = path. segments . last ( ) . unwrap ( ) . ident . to_string ( ) ; 
242-                 // Struct attributes are only allowed to be applied once, and the diagnostic 
243-                 // changes will be set in the initialisation code. 
244-                 let  span = value. span ( ) . unwrap ( ) ; 
245-                 match  nested_name. as_str ( )  { 
246-                     "code"  => { 
247-                         self . code . set_once ( ( ) ,  span) ; 
248- 
249-                         let  code = value. value ( ) ; 
250-                         tokens. extend ( quote !  { 
251-                             #diag. code( rustc_errors:: DiagnosticId :: Error ( #code. to_string( ) ) ) ; 
252-                         } ) ; 
253-                     } 
254-                     _ => invalid_nested_attr ( attr,  nested_attr) 
255-                         . help ( "only `code` is a valid nested attributes following the slug" ) 
256-                         . emit ( ) , 
214+                 if  path. is_ident ( "code" )  { 
215+                     self . code . set_once ( ( ) ,  path. span ( ) . unwrap ( ) ) ; 
216+ 
217+                     let  code = nested. parse :: < syn:: LitStr > ( ) ?; 
218+                     tokens. extend ( quote !  { 
219+                         #diag. code( rustc_errors:: DiagnosticId :: Error ( #code. to_string( ) ) ) ; 
220+                     } ) ; 
221+                 }  else  { 
222+                     span_err ( path. span ( ) . unwrap ( ) ,  "unknown argument" ) . note ( "only the `code` parameter is valid after the slug" ) . emit ( ) ; 
223+ 
224+                     // consume the buffer so we don't have syntax errors from syn 
225+                     let  _ = nested. parse :: < TokenStream > ( ) ; 
257226                } 
258-             } 
227+                 Ok ( ( ) ) 
228+             } ) ?; 
259229            return  Ok ( tokens) ; 
260230        } 
261231
@@ -270,7 +240,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
270240                Ok ( self . add_subdiagnostic ( & fn_ident,  slug) ) 
271241            } 
272242            SubdiagnosticKind :: Label  | SubdiagnosticKind :: Suggestion  {  .. }  => { 
273-                 throw_invalid_attr ! ( attr,  & meta ,   |diag| diag
243+                 throw_invalid_attr ! ( attr,  |diag| diag
274244                    . help( "`#[label]` and `#[suggestion]` can only be applied to fields" ) ) ; 
275245            } 
276246            SubdiagnosticKind :: MultipartSuggestion  {  .. }  => unreachable ! ( ) , 
@@ -309,7 +279,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
309279                    return  quote !  { } ; 
310280                } 
311281
312-                 let  name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ; 
282+                 let  name = attr. path ( ) . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ; 
313283                let  needs_clone =
314284                    name == "primary_span"  && matches ! ( inner_ty,  FieldInnerTy :: Vec ( _) ) ; 
315285                let  ( binding,  needs_destructure)  = if  needs_clone { 
@@ -343,11 +313,10 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
343313        binding :  TokenStream , 
344314    )  -> Result < TokenStream ,  DiagnosticDeriveError >  { 
345315        let  diag = & self . parent . diag ; 
346-         let  meta = attr. parse_meta ( ) ?; 
347316
348-         let  ident = & attr. path . segments . last ( ) . unwrap ( ) . ident ; 
317+         let  ident = & attr. path ( ) . segments . last ( ) . unwrap ( ) . ident ; 
349318        let  name = ident. to_string ( ) ; 
350-         match  ( & meta,  name. as_str ( ) )  { 
319+         match  ( & attr . meta ,  name. as_str ( ) )  { 
351320            // Don't need to do anything - by virtue of the attribute existing, the 
352321            // `set_arg` call will not be generated. 
353322            ( Meta :: Path ( _) ,  "skip_arg" )  => return  Ok ( quote !  { } ) , 
@@ -361,7 +330,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
361330                        } ) ; 
362331                    } 
363332                    DiagnosticDeriveKind :: LintDiagnostic  => { 
364-                         throw_invalid_attr ! ( attr,  & meta ,   |diag| { 
333+                         throw_invalid_attr ! ( attr,  |diag| { 
365334                            diag. help( "the `primary_span` field attribute is not valid for lint diagnostics" ) 
366335                        } ) 
367336                    } 
@@ -378,26 +347,34 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
378347                    return  Ok ( quote !  {  #diag. subdiagnostic( #binding) ;  } ) ; 
379348                } 
380349            } 
381-             ( Meta :: List ( MetaList  {  ref  nested,  .. } ) ,  "subdiagnostic" )  => { 
382-                 if  nested. len ( )  == 1 
383-                     && let  Some ( NestedMeta :: Meta ( Meta :: Path ( path) ) )  = nested. first ( ) 
384-                     && path. is_ident ( "eager" )  { 
385-                         let  handler = match  & self . parent . kind  { 
386-                             DiagnosticDeriveKind :: Diagnostic  {  handler }  => handler, 
387-                             DiagnosticDeriveKind :: LintDiagnostic  => { 
388-                                 throw_invalid_attr ! ( attr,  & meta,  |diag| { 
389-                                     diag. help( "eager subdiagnostics are not supported on lints" ) 
390-                                 } ) 
391-                             } 
392-                         } ; 
393-                         return  Ok ( quote !  {  #diag. eager_subdiagnostic( #handler,  #binding) ;  } ) ; 
394-                 }  else  { 
395-                     throw_invalid_attr ! ( attr,  & meta,  |diag| { 
396-                         diag. help( 
397-                             "`eager` is the only supported nested attribute for `subdiagnostic`" , 
398-                         ) 
399-                     } ) 
350+             ( Meta :: List ( meta_list) ,  "subdiagnostic" )  => { 
351+                 let  err = || { 
352+                     span_err ( 
353+                         meta_list. span ( ) . unwrap ( ) , 
354+                         "`eager` is the only supported nested attribute for `subdiagnostic`" , 
355+                     ) 
356+                     . emit ( ) ; 
357+                 } ; 
358+ 
359+                 let  Ok ( p) :  Result < Path ,  _ >  = meta_list. parse_args ( )  else  { 
360+                     err ( ) ; 
361+                     return  Ok ( quote !  { } ) ; 
362+                 } ; 
363+ 
364+                 if  !p. is_ident ( "eager" )  { 
365+                     err ( ) ; 
366+                     return  Ok ( quote !  { } ) ; 
400367                } 
368+ 
369+                 let  handler = match  & self . parent . kind  { 
370+                     DiagnosticDeriveKind :: Diagnostic  {  handler }  => handler, 
371+                     DiagnosticDeriveKind :: LintDiagnostic  => { 
372+                         throw_invalid_attr ! ( attr,  |diag| { 
373+                             diag. help( "eager subdiagnostics are not supported on lints" ) 
374+                         } ) 
375+                     } 
376+                 } ; 
377+                 return  Ok ( quote !  {  #diag. eager_subdiagnostic( #handler,  #binding) ;  } ) ; 
401378            } 
402379            _ => ( ) , 
403380        } 
@@ -432,7 +409,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
432409                code_init, 
433410            }  => { 
434411                if  let  FieldInnerTy :: Vec ( _)  = info. ty  { 
435-                     throw_invalid_attr ! ( attr,  & meta ,   |diag| { 
412+                     throw_invalid_attr ! ( attr,  |diag| { 
436413                        diag
437414                        . note( "`#[suggestion(...)]` applied to `Vec` field is ambiguous" ) 
438415                        . help( "to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`" ) 
0 commit comments