1
1
#![ deny( unused_must_use) ]
2
2
3
3
use 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 ,
6
5
} ;
7
6
use crate :: diagnostics:: utils:: {
8
7
build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
@@ -11,9 +10,8 @@ use crate::diagnostics::utils::{
11
10
} ;
12
11
use proc_macro2:: { Ident , Span , TokenStream } ;
13
12
use 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 } ;
17
15
use synstructure:: { BindingInfo , Structure , VariantInfo } ;
18
16
19
17
/// What kind of diagnostic is being derived - a fatal/error/warning or a lint?
@@ -77,7 +75,7 @@ impl DiagnosticDeriveBuilder {
77
75
match ast. data {
78
76
syn:: Data :: Struct ( ..) | syn:: Data :: Enum ( ..) => ( ) ,
79
77
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 ( ) ;
81
79
}
82
80
}
83
81
@@ -160,8 +158,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
160
158
} ;
161
159
162
160
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
165
162
. help( "consider creating a `Subdiagnostic` instead" ) ) ;
166
163
}
167
164
@@ -191,71 +188,44 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
191
188
return Ok ( quote ! { } ) ;
192
189
}
193
190
194
- let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
191
+ let name = attr. path ( ) . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
195
192
let name = name. as_str ( ) ;
196
- let meta = attr. parse_meta ( ) ?;
197
193
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 ;
205
195
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 ;
207
200
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 ( ( ) )
212
205
}
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
- } ;
219
206
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 ( ( ) )
239
212
} ;
240
213
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 > ( ) ;
257
226
}
258
- }
227
+ Ok ( ( ) )
228
+ } ) ?;
259
229
return Ok ( tokens) ;
260
230
}
261
231
@@ -270,7 +240,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
270
240
Ok ( self . add_subdiagnostic ( & fn_ident, slug) )
271
241
}
272
242
SubdiagnosticKind :: Label | SubdiagnosticKind :: Suggestion { .. } => {
273
- throw_invalid_attr ! ( attr, & meta , |diag| diag
243
+ throw_invalid_attr ! ( attr, |diag| diag
274
244
. help( "`#[label]` and `#[suggestion]` can only be applied to fields" ) ) ;
275
245
}
276
246
SubdiagnosticKind :: MultipartSuggestion { .. } => unreachable ! ( ) ,
@@ -309,7 +279,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
309
279
return quote ! { } ;
310
280
}
311
281
312
- let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
282
+ let name = attr. path ( ) . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
313
283
let needs_clone =
314
284
name == "primary_span" && matches ! ( inner_ty, FieldInnerTy :: Vec ( _) ) ;
315
285
let ( binding, needs_destructure) = if needs_clone {
@@ -343,11 +313,10 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
343
313
binding : TokenStream ,
344
314
) -> Result < TokenStream , DiagnosticDeriveError > {
345
315
let diag = & self . parent . diag ;
346
- let meta = attr. parse_meta ( ) ?;
347
316
348
- let ident = & attr. path . segments . last ( ) . unwrap ( ) . ident ;
317
+ let ident = & attr. path ( ) . segments . last ( ) . unwrap ( ) . ident ;
349
318
let name = ident. to_string ( ) ;
350
- match ( & meta, name. as_str ( ) ) {
319
+ match ( & attr . meta , name. as_str ( ) ) {
351
320
// Don't need to do anything - by virtue of the attribute existing, the
352
321
// `set_arg` call will not be generated.
353
322
( Meta :: Path ( _) , "skip_arg" ) => return Ok ( quote ! { } ) ,
@@ -361,7 +330,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
361
330
} ) ;
362
331
}
363
332
DiagnosticDeriveKind :: LintDiagnostic => {
364
- throw_invalid_attr ! ( attr, & meta , |diag| {
333
+ throw_invalid_attr ! ( attr, |diag| {
365
334
diag. help( "the `primary_span` field attribute is not valid for lint diagnostics" )
366
335
} )
367
336
}
@@ -378,26 +347,34 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
378
347
return Ok ( quote ! { #diag. subdiagnostic( #binding) ; } ) ;
379
348
}
380
349
}
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 ! { } ) ;
400
367
}
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) ; } ) ;
401
378
}
402
379
_ => ( ) ,
403
380
}
@@ -432,7 +409,7 @@ impl<'a> DiagnosticDeriveVariantBuilder<'a> {
432
409
code_init,
433
410
} => {
434
411
if let FieldInnerTy :: Vec ( _) = info. ty {
435
- throw_invalid_attr ! ( attr, & meta , |diag| {
412
+ throw_invalid_attr ! ( attr, |diag| {
436
413
diag
437
414
. note( "`#[suggestion(...)]` applied to `Vec` field is ambiguous" )
438
415
. help( "to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]`" )
0 commit comments