@@ -4,100 +4,16 @@ use crate::diagnostics::error::{
44 span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError ,
55} ;
66use 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 ,
99} ;
1010use proc_macro2:: TokenStream ;
1111use quote:: { format_ident, quote} ;
1212use std:: collections:: HashMap ;
13- use std:: fmt;
14- use std:: str:: FromStr ;
1513use syn:: { spanned:: Spanned , Attribute , Meta , MetaList , MetaNameValue , NestedMeta , Path } ;
1614use synstructure:: { BindingInfo , Structure , VariantInfo } ;
1715
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 } ;
10117
10218/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
10319pub ( crate ) struct SubdiagnosticDerive < ' a > {
@@ -198,8 +114,8 @@ struct SubdiagnosticDeriveBuilder<'a> {
198114
199115 /// Identifier for the binding to the `#[primary_span]` field.
200116 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 .
203119 applicability : SpannedOption < TokenStream > ,
204120
205121 /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error
@@ -219,6 +135,7 @@ struct KindsStatistics {
219135 has_multipart_suggestion : bool ,
220136 all_multipart_suggestions : bool ,
221137 has_normal_suggestion : bool ,
138+ all_applicabilities_static : bool ,
222139}
223140
224141impl < ' a > FromIterator < & ' a SubdiagnosticKind > for KindsStatistics {
@@ -227,8 +144,15 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
227144 has_multipart_suggestion : false ,
228145 all_multipart_suggestions : true ,
229146 has_normal_suggestion : false ,
147+ all_applicabilities_static : true ,
230148 } ;
149+
231150 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+ }
232156 if let SubdiagnosticKind :: MultipartSuggestion { .. } = kind {
233157 ret. has_multipart_suggestion = true ;
234158 } else {
@@ -248,151 +172,22 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
248172 let mut kind_slugs = vec ! [ ] ;
249173
250174 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 ) ?;
308176
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 ( ) ;
370180
371- let Some ( ( slug, _) ) = slug else {
372181 throw_span_err ! (
373- span,
182+ attr . span( ) . unwrap ( ) ,
374183 & format!(
375184 "diagnostic slug must be first argument of a `#[{}(...)]` attribute" ,
376185 name
377186 )
378187 ) ;
379188 } ;
380189
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) ) ;
396191 }
397192
398193 Ok ( kind_slugs)
@@ -510,6 +305,15 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
510305 if kind_stats. has_multipart_suggestion || kind_stats. has_normal_suggestion {
511306 report_error_if_not_applied_to_applicability ( attr, & info) ?;
512307
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+ }
513317 let binding = info. binding . binding . clone ( ) ;
514318 self . applicability . set_once ( quote ! { #binding } , span) ;
515319 } else {
@@ -638,19 +442,20 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
638442 . collect ( ) ;
639443
640444 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 } ) ;
646445
647446 let diag = & self . diag ;
648447 let mut calls = TokenStream :: new ( ) ;
649448 for ( kind, slug) in kind_slugs {
650449 let name = format_ident ! ( "{}{}" , if span_field. is_some( ) { "span_" } else { "" } , kind) ;
651450 let message = quote ! { rustc_errors:: fluent:: #slug } ;
652451 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+
654459 if let Some ( span) = span_field {
655460 let style = suggestion_kind. to_suggestion_style ( ) ;
656461
@@ -660,7 +465,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> {
660465 quote ! { unreachable!( ) ; }
661466 }
662467 }
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+
664475 if !self . has_suggestion_parts {
665476 span_err (
666477 self . span ,
0 commit comments