@@ -4,100 +4,16 @@ use crate::diagnostics::error::{
4
4
span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError ,
5
5
} ;
6
6
use crate :: diagnostics:: utils:: {
7
- report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
8
- Applicability , FieldInfo , FieldInnerTy , HasFieldMap , SetOnce ,
7
+ report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo ,
8
+ FieldInnerTy , HasFieldMap , SetOnce ,
9
9
} ;
10
10
use proc_macro2:: TokenStream ;
11
11
use quote:: { format_ident, quote} ;
12
12
use std:: collections:: HashMap ;
13
- use std:: fmt;
14
- use std:: str:: FromStr ;
15
13
use syn:: { spanned:: Spanned , Attribute , Meta , MetaList , MetaNameValue , NestedMeta , Path } ;
16
14
use synstructure:: { BindingInfo , Structure , VariantInfo } ;
17
15
18
- use super :: utils:: SpannedOption ;
19
-
20
- /// Which kind of suggestion is being created?
21
- #[ derive( Clone , Copy ) ]
22
- enum SubdiagnosticSuggestionKind {
23
- /// `#[suggestion]`
24
- Normal ,
25
- /// `#[suggestion_short]`
26
- Short ,
27
- /// `#[suggestion_hidden]`
28
- Hidden ,
29
- /// `#[suggestion_verbose]`
30
- Verbose ,
31
- }
32
-
33
- impl FromStr for SubdiagnosticSuggestionKind {
34
- type Err = ( ) ;
35
-
36
- fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
37
- match s {
38
- "" => Ok ( SubdiagnosticSuggestionKind :: Normal ) ,
39
- "_short" => Ok ( SubdiagnosticSuggestionKind :: Short ) ,
40
- "_hidden" => Ok ( SubdiagnosticSuggestionKind :: Hidden ) ,
41
- "_verbose" => Ok ( SubdiagnosticSuggestionKind :: Verbose ) ,
42
- _ => Err ( ( ) ) ,
43
- }
44
- }
45
- }
46
-
47
- impl SubdiagnosticSuggestionKind {
48
- pub fn to_suggestion_style ( & self ) -> TokenStream {
49
- match self {
50
- SubdiagnosticSuggestionKind :: Normal => {
51
- quote ! { rustc_errors:: SuggestionStyle :: ShowCode }
52
- }
53
- SubdiagnosticSuggestionKind :: Short => {
54
- quote ! { rustc_errors:: SuggestionStyle :: HideCodeInline }
55
- }
56
- SubdiagnosticSuggestionKind :: Hidden => {
57
- quote ! { rustc_errors:: SuggestionStyle :: HideCodeAlways }
58
- }
59
- SubdiagnosticSuggestionKind :: Verbose => {
60
- quote ! { rustc_errors:: SuggestionStyle :: ShowAlways }
61
- }
62
- }
63
- }
64
- }
65
-
66
- /// Which kind of subdiagnostic is being created from a variant?
67
- #[ derive( Clone ) ]
68
- enum SubdiagnosticKind {
69
- /// `#[label(...)]`
70
- Label ,
71
- /// `#[note(...)]`
72
- Note ,
73
- /// `#[help(...)]`
74
- Help ,
75
- /// `#[warning(...)]`
76
- Warn ,
77
- /// `#[suggestion{,_short,_hidden,_verbose}]`
78
- Suggestion { suggestion_kind : SubdiagnosticSuggestionKind , code : TokenStream } ,
79
- /// `#[multipart_suggestion{,_short,_hidden,_verbose}]`
80
- MultipartSuggestion { suggestion_kind : SubdiagnosticSuggestionKind } ,
81
- }
82
-
83
- impl quote:: IdentFragment for SubdiagnosticKind {
84
- fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
85
- match self {
86
- SubdiagnosticKind :: Label => write ! ( f, "label" ) ,
87
- SubdiagnosticKind :: Note => write ! ( f, "note" ) ,
88
- SubdiagnosticKind :: Help => write ! ( f, "help" ) ,
89
- SubdiagnosticKind :: Warn => write ! ( f, "warn" ) ,
90
- SubdiagnosticKind :: Suggestion { .. } => write ! ( f, "suggestion_with_style" ) ,
91
- SubdiagnosticKind :: MultipartSuggestion { .. } => {
92
- write ! ( f, "multipart_suggestion_with_style" )
93
- }
94
- }
95
- }
96
-
97
- fn span ( & self ) -> Option < proc_macro2:: Span > {
98
- None
99
- }
100
- }
16
+ use super :: utils:: { SpannedOption , SubdiagnosticKind } ;
101
17
102
18
/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
103
19
pub ( crate ) struct SubdiagnosticDerive < ' a > {
@@ -198,8 +114,8 @@ struct SubdiagnosticDeriveBuilder<'a> {
198
114
199
115
/// Identifier for the binding to the `#[primary_span]` field.
200
116
span_field : SpannedOption < proc_macro2:: Ident > ,
201
- /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
202
- /// `rustc_errors::Applicability::*` variant directly .
117
+
118
+ /// The binding to the `#[applicability]` field, if present .
203
119
applicability : SpannedOption < TokenStream > ,
204
120
205
121
/// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
@@ -219,6 +135,7 @@ struct KindsStatistics {
219
135
has_multipart_suggestion : bool ,
220
136
all_multipart_suggestions : bool ,
221
137
has_normal_suggestion : bool ,
138
+ all_applicabilities_static : bool ,
222
139
}
223
140
224
141
impl < ' a > FromIterator < & ' a SubdiagnosticKind > for KindsStatistics {
@@ -227,8 +144,15 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
227
144
has_multipart_suggestion : false ,
228
145
all_multipart_suggestions : true ,
229
146
has_normal_suggestion : false ,
147
+ all_applicabilities_static : true ,
230
148
} ;
149
+
231
150
for kind in kinds {
151
+ if let SubdiagnosticKind :: MultipartSuggestion { applicability : None , .. }
152
+ | SubdiagnosticKind :: Suggestion { applicability : None , .. } = kind
153
+ {
154
+ ret. all_applicabilities_static = false ;
155
+ }
232
156
if let SubdiagnosticKind :: MultipartSuggestion { .. } = kind {
233
157
ret. has_multipart_suggestion = true ;
234
158
} else {
@@ -248,151 +172,22 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
248
172
let mut kind_slugs = vec ! [ ] ;
249
173
250
174
for attr in self . variant . ast ( ) . attrs {
251
- let span = attr. span ( ) . unwrap ( ) ;
252
-
253
- let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
254
- let name = name. as_str ( ) ;
255
-
256
- let meta = attr. parse_meta ( ) ?;
257
- let Meta :: List ( MetaList { ref nested, .. } ) = meta else {
258
- throw_invalid_attr ! ( attr, & meta) ;
259
- } ;
260
-
261
- let mut kind = match name {
262
- "label" => SubdiagnosticKind :: Label ,
263
- "note" => SubdiagnosticKind :: Note ,
264
- "help" => SubdiagnosticKind :: Help ,
265
- "warning" => SubdiagnosticKind :: Warn ,
266
- _ => {
267
- if let Some ( suggestion_kind) =
268
- name. strip_prefix ( "suggestion" ) . and_then ( |s| s. parse ( ) . ok ( ) )
269
- {
270
- SubdiagnosticKind :: Suggestion { suggestion_kind, code : TokenStream :: new ( ) }
271
- } else if let Some ( suggestion_kind) =
272
- name. strip_prefix ( "multipart_suggestion" ) . and_then ( |s| s. parse ( ) . ok ( ) )
273
- {
274
- SubdiagnosticKind :: MultipartSuggestion { suggestion_kind }
275
- } else {
276
- throw_invalid_attr ! ( attr, & meta) ;
277
- }
278
- }
279
- } ;
280
-
281
- let mut slug = None ;
282
- let mut code = None ;
283
-
284
- let mut nested_iter = nested. into_iter ( ) ;
285
- if let Some ( nested_attr) = nested_iter. next ( ) {
286
- match nested_attr {
287
- NestedMeta :: Meta ( Meta :: Path ( path) ) => {
288
- slug. set_once ( path. clone ( ) , span) ;
289
- }
290
- NestedMeta :: Meta ( meta @ Meta :: NameValue ( _) )
291
- if matches ! (
292
- meta. path( ) . segments. last( ) . unwrap( ) . ident. to_string( ) . as_str( ) ,
293
- "code" | "applicability"
294
- ) =>
295
- {
296
- // Don't error for valid follow-up attributes.
297
- }
298
- nested_attr => {
299
- throw_invalid_nested_attr ! ( attr, & nested_attr, |diag| {
300
- diag. help(
301
- "first argument of the attribute should be the diagnostic \
302
- slug",
303
- )
304
- } )
305
- }
306
- } ;
307
- }
175
+ let ( kind, slug) = SubdiagnosticKind :: from_attr ( attr, self ) ?;
308
176
309
- for nested_attr in nested_iter {
310
- let meta = match nested_attr {
311
- NestedMeta :: Meta ( ref meta) => meta,
312
- _ => throw_invalid_nested_attr ! ( attr, & nested_attr) ,
313
- } ;
314
-
315
- let span = meta. span ( ) . unwrap ( ) ;
316
- let nested_name = meta. path ( ) . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
317
- let nested_name = nested_name. as_str ( ) ;
318
-
319
- let value = match meta {
320
- Meta :: NameValue ( MetaNameValue { lit : syn:: Lit :: Str ( value) , .. } ) => value,
321
- Meta :: Path ( _) => throw_invalid_nested_attr ! ( attr, & nested_attr, |diag| {
322
- diag. help( "a diagnostic slug must be the first argument to the attribute" )
323
- } ) ,
324
- _ => throw_invalid_nested_attr ! ( attr, & nested_attr) ,
325
- } ;
326
-
327
- match nested_name {
328
- "code" => {
329
- if matches ! ( kind, SubdiagnosticKind :: Suggestion { .. } ) {
330
- let formatted_str = self . build_format ( & value. value ( ) , value. span ( ) ) ;
331
- code. set_once ( formatted_str, span) ;
332
- } else {
333
- span_err (
334
- span,
335
- & format ! (
336
- "`code` is not a valid nested attribute of a `{}` attribute" ,
337
- name
338
- ) ,
339
- )
340
- . emit ( ) ;
341
- }
342
- }
343
- "applicability" => {
344
- if matches ! (
345
- kind,
346
- SubdiagnosticKind :: Suggestion { .. }
347
- | SubdiagnosticKind :: MultipartSuggestion { .. }
348
- ) {
349
- let value =
350
- Applicability :: from_str ( & value. value ( ) ) . unwrap_or_else ( |( ) | {
351
- span_err ( span, "invalid applicability" ) . emit ( ) ;
352
- Applicability :: Unspecified
353
- } ) ;
354
- self . applicability . set_once ( quote ! { #value } , span) ;
355
- } else {
356
- span_err (
357
- span,
358
- & format ! (
359
- "`applicability` is not a valid nested attribute of a `{}` attribute" ,
360
- name
361
- )
362
- ) . emit ( ) ;
363
- }
364
- }
365
- _ => throw_invalid_nested_attr ! ( attr, & nested_attr, |diag| {
366
- diag. help( "only `code` and `applicability` are valid nested attributes" )
367
- } ) ,
368
- }
369
- }
177
+ let Some ( slug) = slug else {
178
+ let name = attr. path . segments . last ( ) . unwrap ( ) . ident . to_string ( ) ;
179
+ let name = name. as_str ( ) ;
370
180
371
- let Some ( ( slug, _) ) = slug else {
372
181
throw_span_err ! (
373
- span,
182
+ attr . span( ) . unwrap ( ) ,
374
183
& format!(
375
184
"diagnostic slug must be first argument of a `#[{}(...)]` attribute" ,
376
185
name
377
186
)
378
187
) ;
379
188
} ;
380
189
381
- match kind {
382
- SubdiagnosticKind :: Suggestion { code : ref mut code_field, .. } => {
383
- let Some ( ( code, _) ) = code else {
384
- throw_span_err ! ( span, "suggestion without `code = \" ...\" `" ) ;
385
- } ;
386
- * code_field = code;
387
- }
388
- SubdiagnosticKind :: Label
389
- | SubdiagnosticKind :: Note
390
- | SubdiagnosticKind :: Help
391
- | SubdiagnosticKind :: Warn
392
- | SubdiagnosticKind :: MultipartSuggestion { .. } => { }
393
- }
394
-
395
- kind_slugs. push ( ( kind, slug) )
190
+ kind_slugs. push ( ( kind, slug) ) ;
396
191
}
397
192
398
193
Ok ( kind_slugs)
@@ -510,6 +305,15 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
510
305
if kind_stats. has_multipart_suggestion || kind_stats. has_normal_suggestion {
511
306
report_error_if_not_applied_to_applicability ( attr, & info) ?;
512
307
308
+ if kind_stats. all_applicabilities_static {
309
+ span_err (
310
+ span,
311
+ "`#[applicability]` has no effect if all `#[suggestion]`/\
312
+ `#[multipart_suggestion]` attributes have a static \
313
+ `applicability = \" ...\" `",
314
+ )
315
+ . emit ( ) ;
316
+ }
513
317
let binding = info. binding . binding . clone ( ) ;
514
318
self . applicability . set_once ( quote ! { #binding } , span) ;
515
319
} else {
@@ -638,19 +442,20 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
638
442
. collect ( ) ;
639
443
640
444
let span_field = self . span_field . value_ref ( ) ;
641
- let applicability = self
642
- . applicability
643
- . take ( )
644
- . value ( )
645
- . unwrap_or_else ( || quote ! { rustc_errors:: Applicability :: Unspecified } ) ;
646
445
647
446
let diag = & self . diag ;
648
447
let mut calls = TokenStream :: new ( ) ;
649
448
for ( kind, slug) in kind_slugs {
650
449
let name = format_ident ! ( "{}{}" , if span_field. is_some( ) { "span_" } else { "" } , kind) ;
651
450
let message = quote ! { rustc_errors:: fluent:: #slug } ;
652
451
let call = match kind {
653
- SubdiagnosticKind :: Suggestion { suggestion_kind, code } => {
452
+ SubdiagnosticKind :: Suggestion { suggestion_kind, applicability, code } => {
453
+ let applicability = applicability
454
+ . value ( )
455
+ . map ( |a| quote ! { #a } )
456
+ . or_else ( || self . applicability . take ( ) . value ( ) )
457
+ . unwrap_or_else ( || quote ! { rustc_errors:: Applicability :: Unspecified } ) ;
458
+
654
459
if let Some ( span) = span_field {
655
460
let style = suggestion_kind. to_suggestion_style ( ) ;
656
461
@@ -660,7 +465,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
660
465
quote ! { unreachable!( ) ; }
661
466
}
662
467
}
663
- SubdiagnosticKind :: MultipartSuggestion { suggestion_kind } => {
468
+ SubdiagnosticKind :: MultipartSuggestion { suggestion_kind, applicability } => {
469
+ let applicability = applicability
470
+ . value ( )
471
+ . map ( |a| quote ! { #a } )
472
+ . or_else ( || self . applicability . take ( ) . value ( ) )
473
+ . unwrap_or_else ( || quote ! { rustc_errors:: Applicability :: Unspecified } ) ;
474
+
664
475
if !self . has_suggestion_parts {
665
476
span_err (
666
477
self . span ,
0 commit comments