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,39 @@ 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 :: < TokenStream > ( ) ?;
218+ tokens. extend ( quote ! {
219+ #diag. code( rustc_errors:: DiagnosticId :: Error ( #code. to_string( ) ) ) ;
220+ } ) ;
257221 }
258- }
222+ Ok ( ( ) )
223+ } ) ?;
259224 return Ok ( tokens) ;
260225 }
261226
@@ -270,7 +235,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
270235 Ok ( self . add_subdiagnostic ( & fn_ident, slug) )
271236 }
272237 SubdiagnosticKind :: Label | SubdiagnosticKind :: Suggestion { .. } => {
273- throw_invalid_attr ! ( attr, & meta , |diag| diag
238+ throw_invalid_attr ! ( attr, |diag| diag
274239 . help( "`#[label]` and `#[suggestion]` can only be applied to fields" ) ) ;
275240 }
276241 SubdiagnosticKind :: MultipartSuggestion { .. } => unreachable ! ( ) ,
@@ -309,7 +274,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
309274 return quote ! { } ;
310275 }
311276
312- let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
277+ let name = attr. path ( ) . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
313278 let needs_clone =
314279 name == "primary_span" && matches ! ( inner_ty, FieldInnerTy :: Vec ( _) ) ;
315280 let ( binding, needs_destructure) = if needs_clone {
@@ -343,11 +308,10 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
343308 binding : TokenStream ,
344309 ) -> Result < TokenStream , DiagnosticDeriveError > {
345310 let diag = & self . parent . diag ;
346- let meta = attr. parse_meta ( ) ?;
347311
348- let ident = & attr. path . segments . last ( ) . unwrap ( ) . ident ;
312+ let ident = & attr. path ( ) . segments . last ( ) . unwrap ( ) . ident ;
349313 let name = ident. to_string ( ) ;
350- match ( & meta, name. as_str ( ) ) {
314+ match ( & attr . meta , name. as_str ( ) ) {
351315 // Don't need to do anything - by virtue of the attribute existing, the
352316 // `set_arg` call will not be generated.
353317 ( Meta :: Path ( _) , "skip_arg" ) => return Ok ( quote ! { } ) ,
@@ -361,7 +325,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
361325 } ) ;
362326 }
363327 DiagnosticDeriveKind :: LintDiagnostic => {
364- throw_invalid_attr ! ( attr, & meta , |diag| {
328+ throw_invalid_attr ! ( attr, |diag| {
365329 diag. help( "the `primary_span` field attribute is not valid for lint diagnostics" )
366330 } )
367331 }
@@ -378,26 +342,34 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
378342 return Ok ( quote ! { #diag. subdiagnostic( #binding) ; } ) ;
379343 }
380344 }
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- } )
345+ ( Meta :: List ( meta_list) , "subdiagnostic" ) => {
346+ let err = || {
347+ span_err (
348+ meta_list. span ( ) . unwrap ( ) ,
349+ "`eager` is the only supported nested attribute for `subdiagnostic`" ,
350+ )
351+ . emit ( ) ;
352+ } ;
353+
354+ let Ok ( p) : Result < Path , _ > = meta_list. parse_args ( ) else {
355+ err ( ) ;
356+ return Ok ( quote ! { } ) ;
357+ } ;
358+
359+ if !p. is_ident ( "eager" ) {
360+ err ( ) ;
361+ return Ok ( quote ! { } ) ;
400362 }
363+
364+ let handler = match & self . parent . kind {
365+ DiagnosticDeriveKind :: Diagnostic { handler } => handler,
366+ DiagnosticDeriveKind :: LintDiagnostic => {
367+ throw_invalid_attr ! ( attr, |diag| {
368+ diag. help( "eager subdiagnostics are not supported on lints" )
369+ } )
370+ }
371+ } ;
372+ return Ok ( quote ! { #diag. eager_subdiagnostic( #handler, #binding) ; } ) ;
401373 }
402374 _ => ( ) ,
403375 }
@@ -432,7 +404,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
432404 code_init,
433405 } => {
434406 if let FieldInnerTy :: Vec ( _) = info. ty {
435- throw_invalid_attr ! ( attr, & meta , |diag| {
407+ throw_invalid_attr ! ( attr, |diag| {
436408 diag
437409 . note( "`#[suggestion(...)]` applied to `Vec` field is ambiguous" )
438410 . help( "to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`" )
0 commit comments