Skip to content

Commit e5d9371

Browse files
committed
macros: allow setting applicability in attribute
In the initial implementation of the `SessionSubdiagnostic`, the `Applicability` of a suggestion can be set both as a field and as part of the attribute, this commit adds the same support to the original `SessionDiagnostic` derive. Signed-off-by: David Wood <[email protected]>
1 parent e8ee0d7 commit e5d9371

File tree

5 files changed

+120
-54
lines changed

5 files changed

+120
-54
lines changed

compiler/rustc_macros/src/diagnostics/diagnostic.rs

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@ use crate::diagnostics::error::{
55
SessionDiagnosticDeriveError,
66
};
77
use crate::diagnostics::utils::{
8-
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo,
9-
HasFieldMap, SetOnce,
8+
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability,
9+
FieldInfo, HasFieldMap, SetOnce,
1010
};
1111
use proc_macro2::TokenStream;
1212
use quote::{format_ident, quote};
1313
use std::collections::HashMap;
14+
use std::str::FromStr;
1415
use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type};
1516
use synstructure::Structure;
1617

@@ -430,7 +431,7 @@ impl SessionDiagnosticDeriveBuilder {
430431
}),
431432
};
432433

433-
let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
434+
let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
434435

435436
let mut msg = None;
436437
let mut code = None;
@@ -445,6 +446,7 @@ impl SessionDiagnosticDeriveBuilder {
445446
let nested_name = nested_name.as_str();
446447
match meta {
447448
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
449+
let span = meta.span().unwrap();
448450
match nested_name {
449451
"message" => {
450452
msg = Some(s.value());
@@ -453,9 +455,27 @@ impl SessionDiagnosticDeriveBuilder {
453455
let formatted_str = self.build_format(&s.value(), s.span());
454456
code = Some(formatted_str);
455457
}
458+
"applicability" => {
459+
applicability = match applicability {
460+
Some(v) => {
461+
span_err(
462+
span,
463+
"applicability cannot be set in both the field and attribute"
464+
).emit();
465+
Some(v)
466+
}
467+
None => match Applicability::from_str(&s.value()) {
468+
Ok(v) => Some(quote! { #v }),
469+
Err(()) => {
470+
span_err(span, "invalid applicability").emit();
471+
None
472+
}
473+
},
474+
}
475+
}
456476
_ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
457477
diag.help(
458-
"only `message` and `code` are valid field attributes",
478+
"only `message`, `code` and `applicability` are valid field attributes",
459479
)
460480
}),
461481
}
@@ -464,6 +484,9 @@ impl SessionDiagnosticDeriveBuilder {
464484
}
465485
}
466486

487+
let applicability = applicability
488+
.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
489+
467490
let method = format_ident!("span_{}", name);
468491

469492
let slug = self
@@ -475,7 +498,7 @@ impl SessionDiagnosticDeriveBuilder {
475498
let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
476499
let code = code.unwrap_or_else(|| quote! { String::new() });
477500

478-
Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
501+
Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); })
479502
}
480503
_ => throw_invalid_attr!(attr, &meta),
481504
}
@@ -505,12 +528,12 @@ impl SessionDiagnosticDeriveBuilder {
505528
fn span_and_applicability_of_ty(
506529
&self,
507530
info: FieldInfo<'_>,
508-
) -> Result<(TokenStream, TokenStream), SessionDiagnosticDeriveError> {
531+
) -> Result<(TokenStream, Option<TokenStream>), SessionDiagnosticDeriveError> {
509532
match &info.ty {
510533
// If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
511534
ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
512535
let binding = &info.binding.binding;
513-
Ok((quote!(*#binding), quote!(rustc_errors::Applicability::Unspecified)))
536+
Ok((quote!(*#binding), None))
514537
}
515538
// If `ty` is `(Span, Applicability)` then return tokens accessing those.
516539
Type::Tuple(tup) => {
@@ -546,7 +569,7 @@ impl SessionDiagnosticDeriveBuilder {
546569
.map(|applicability_idx| quote!(#binding.#applicability_idx))
547570
.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
548571

549-
return Ok((span, applicability));
572+
return Ok((span, Some(applicability)));
550573
}
551574

552575
throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {

compiler/rustc_macros/src/diagnostics/subdiagnostic.rs

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::diagnostics::error::{
66
};
77
use crate::diagnostics::utils::{
88
option_inner_ty, report_error_if_not_applied_to_applicability,
9-
report_error_if_not_applied_to_span, FieldInfo, HasFieldMap, SetOnce,
9+
report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce,
1010
};
1111
use proc_macro2::TokenStream;
1212
use quote::{format_ident, quote};
@@ -16,48 +16,6 @@ use std::str::FromStr;
1616
use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue};
1717
use synstructure::{BindingInfo, Structure, VariantInfo};
1818

19-
/// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent
20-
/// the user's selection of applicability if specified in an attribute.
21-
enum Applicability {
22-
MachineApplicable,
23-
MaybeIncorrect,
24-
HasPlaceholders,
25-
Unspecified,
26-
}
27-
28-
impl FromStr for Applicability {
29-
type Err = ();
30-
31-
fn from_str(s: &str) -> Result<Self, Self::Err> {
32-
match s {
33-
"machine-applicable" => Ok(Applicability::MachineApplicable),
34-
"maybe-incorrect" => Ok(Applicability::MaybeIncorrect),
35-
"has-placeholders" => Ok(Applicability::HasPlaceholders),
36-
"unspecified" => Ok(Applicability::Unspecified),
37-
_ => Err(()),
38-
}
39-
}
40-
}
41-
42-
impl quote::ToTokens for Applicability {
43-
fn to_tokens(&self, tokens: &mut TokenStream) {
44-
tokens.extend(match self {
45-
Applicability::MachineApplicable => {
46-
quote! { rustc_errors::Applicability::MachineApplicable }
47-
}
48-
Applicability::MaybeIncorrect => {
49-
quote! { rustc_errors::Applicability::MaybeIncorrect }
50-
}
51-
Applicability::HasPlaceholders => {
52-
quote! { rustc_errors::Applicability::HasPlaceholders }
53-
}
54-
Applicability::Unspecified => {
55-
quote! { rustc_errors::Applicability::Unspecified }
56-
}
57-
});
58-
}
59-
}
60-
6119
/// Which kind of suggestion is being created?
6220
#[derive(Clone, Copy)]
6321
enum SubdiagnosticSuggestionKind {

compiler/rustc_macros/src/diagnostics/utils.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use proc_macro::Span;
33
use proc_macro2::TokenStream;
44
use quote::{format_ident, quote};
55
use std::collections::BTreeSet;
6+
use std::str::FromStr;
67
use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
78
use synstructure::BindingInfo;
89

@@ -222,3 +223,45 @@ pub(crate) trait HasFieldMap {
222223
}
223224
}
224225
}
226+
227+
/// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent
228+
/// the user's selection of applicability if specified in an attribute.
229+
pub(crate) enum Applicability {
230+
MachineApplicable,
231+
MaybeIncorrect,
232+
HasPlaceholders,
233+
Unspecified,
234+
}
235+
236+
impl FromStr for Applicability {
237+
type Err = ();
238+
239+
fn from_str(s: &str) -> Result<Self, Self::Err> {
240+
match s {
241+
"machine-applicable" => Ok(Applicability::MachineApplicable),
242+
"maybe-incorrect" => Ok(Applicability::MaybeIncorrect),
243+
"has-placeholders" => Ok(Applicability::HasPlaceholders),
244+
"unspecified" => Ok(Applicability::Unspecified),
245+
_ => Err(()),
246+
}
247+
}
248+
}
249+
250+
impl quote::ToTokens for Applicability {
251+
fn to_tokens(&self, tokens: &mut TokenStream) {
252+
tokens.extend(match self {
253+
Applicability::MachineApplicable => {
254+
quote! { rustc_errors::Applicability::MachineApplicable }
255+
}
256+
Applicability::MaybeIncorrect => {
257+
quote! { rustc_errors::Applicability::MaybeIncorrect }
258+
}
259+
Applicability::HasPlaceholders => {
260+
quote! { rustc_errors::Applicability::HasPlaceholders }
261+
}
262+
Applicability::Unspecified => {
263+
quote! { rustc_errors::Applicability::Unspecified }
264+
}
265+
});
266+
}
267+
}

src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,3 +433,33 @@ struct ErrorWithNoteWrongOrder {
433433
struct ErrorWithNoteCustomWrongOrder {
434434
val: String,
435435
}
436+
437+
#[derive(SessionDiagnostic)]
438+
#[error(code = "E0123", slug = "foo")]
439+
struct ApplicabilityInBoth {
440+
#[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
441+
//~^ ERROR applicability cannot be set in both the field and attribute
442+
suggestion: (Span, Applicability),
443+
}
444+
445+
#[derive(SessionDiagnostic)]
446+
#[error(code = "E0123", slug = "foo")]
447+
struct InvalidApplicability {
448+
#[suggestion(message = "bar", code = "...", applicability = "batman")]
449+
//~^ ERROR invalid applicability
450+
suggestion: Span,
451+
}
452+
453+
#[derive(SessionDiagnostic)]
454+
#[error(code = "E0123", slug = "foo")]
455+
struct ValidApplicability {
456+
#[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
457+
suggestion: Span,
458+
}
459+
460+
#[derive(SessionDiagnostic)]
461+
#[error(code = "E0123", slug = "foo")]
462+
struct NoApplicability {
463+
#[suggestion(message = "bar", code = "...")]
464+
suggestion: Span,
465+
}

src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,15 @@ error: `#[suggestion(nonsense = ...)]` is not a valid attribute
257257
LL | #[suggestion(nonsense = "bar")]
258258
| ^^^^^^^^^^^^^^^^
259259
|
260-
= help: only `message` and `code` are valid field attributes
260+
= help: only `message`, `code` and `applicability` are valid field attributes
261261

262262
error: `#[suggestion(msg = ...)]` is not a valid attribute
263263
--> $DIR/diagnostic-derive.rs:232:18
264264
|
265265
LL | #[suggestion(msg = "bar")]
266266
| ^^^^^^^^^^^
267267
|
268-
= help: only `message` and `code` are valid field attributes
268+
= help: only `message`, `code` and `applicability` are valid field attributes
269269

270270
error: wrong field type for suggestion
271271
--> $DIR/diagnostic-derive.rs:254:5
@@ -325,6 +325,18 @@ error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
325325
LL | #[note = "bar"]
326326
| ^^^^^^^^^^^^^^^
327327

328+
error: applicability cannot be set in both the field and attribute
329+
--> $DIR/diagnostic-derive.rs:440:49
330+
|
331+
LL | #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
332+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
333+
334+
error: invalid applicability
335+
--> $DIR/diagnostic-derive.rs:448:49
336+
|
337+
LL | #[suggestion(message = "bar", code = "...", applicability = "batman")]
338+
| ^^^^^^^^^^^^^^^^^^^^^^^^
339+
328340
error: cannot find attribute `nonsense` in this scope
329341
--> $DIR/diagnostic-derive.rs:51:3
330342
|
@@ -348,6 +360,6 @@ LL | #[derive(SessionDiagnostic)]
348360
|
349361
= note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
350362

351-
error: aborting due to 41 previous errors
363+
error: aborting due to 43 previous errors
352364

353365
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)