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