macros: reuse SetOnce trait in diagnostic derive

`SetOnce` trait was introduced in the subdiagnostic derive to simplify
the code a little bit, re-use it in the diagnostic derive too.

Signed-off-by: David Wood <david.wood@huawei.com>
This commit is contained in:
David Wood 2022-04-27 04:28:21 +01:00
parent 36a396ce51
commit 2647a4812c
3 changed files with 102 additions and 96 deletions

View File

@ -5,7 +5,8 @@ use crate::diagnostics::error::{
SessionDiagnosticDeriveError,
};
use crate::diagnostics::utils::{
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo, HasFieldMap,
option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, FieldInfo,
HasFieldMap, SetOnce,
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
@ -240,7 +241,7 @@ struct SessionDiagnosticDeriveBuilder {
slug: Option<(String, proc_macro::Span)>,
/// Error codes are a optional part of the struct attribute - this is only set to detect
/// multiple specifications.
code: Option<proc_macro::Span>,
code: Option<(String, proc_macro::Span)>,
}
impl HasFieldMap for SessionDiagnosticDeriveBuilder {
@ -306,7 +307,7 @@ impl SessionDiagnosticDeriveBuilder {
diag.help("only `error` and `warning` are valid attributes")
}),
};
self.set_kind_once(kind, span)?;
self.kind.set_once((kind, span));
let mut tokens = Vec::new();
for nested_attr in nested {
@ -321,12 +322,17 @@ impl SessionDiagnosticDeriveBuilder {
// Struct attributes are only allowed to be applied once, and the diagnostic
// changes will be set in the initialisation code.
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
let span = s.span().unwrap();
match nested_name.as_str() {
"slug" => {
self.set_slug_once(s.value(), s.span().unwrap());
self.slug.set_once((s.value(), span));
}
"code" => {
tokens.push(self.set_code_once(s.value(), s.span().unwrap()));
self.code.set_once((s.value(), span));
let (diag, code) = (&self.diag, &self.code.as_ref().map(|(v, _)| v));
tokens.push(quote! {
#diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
});
}
_ => invalid_nested_attr(attr, &nested_attr)
.help("only `slug` and `code` are valid nested attributes")
@ -340,61 +346,6 @@ impl SessionDiagnosticDeriveBuilder {
Ok(tokens.drain(..).collect())
}
#[must_use]
fn set_kind_once(
&mut self,
kind: SessionDiagnosticKind,
span: proc_macro::Span,
) -> Result<(), SessionDiagnosticDeriveError> {
match self.kind {
None => {
self.kind = Some((kind, span));
Ok(())
}
Some((prev_kind, prev_span)) => {
let existing = prev_kind.descr();
let current = kind.descr();
let msg = if current == existing {
format!("`{}` specified multiple times", existing)
} else {
format!("`{}` specified when `{}` was already specified", current, existing)
};
throw_span_err!(span, &msg, |diag| diag
.span_note(prev_span, "previously specified here"));
}
}
}
fn set_code_once(&mut self, code: String, span: proc_macro::Span) -> TokenStream {
match self.code {
None => {
self.code = Some(span);
}
Some(prev_span) => {
span_err(span, "`code` specified multiple times")
.span_note(prev_span, "previously specified here")
.emit();
}
}
let diag = &self.diag;
quote! { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); }
}
fn set_slug_once(&mut self, slug: String, span: proc_macro::Span) {
match self.slug {
None => {
self.slug = Some((slug, span));
}
Some((_, prev_span)) => {
span_err(span, "`slug` specified multiple times")
.span_note(prev_span, "previously specified here")
.emit();
}
}
}
fn generate_field_attr_code(
&mut self,
attr: &syn::Attribute,

View File

@ -90,21 +90,28 @@ struct WrongPlaceField {
#[derive(SessionDiagnostic)]
#[error(code = "E0123", slug = "foo")]
#[error(code = "E0456", slug = "bar")] //~ ERROR `error` specified multiple times
#[error(code = "E0456", slug = "bar")]
//~^ ERROR specified multiple times
//~^^ ERROR specified multiple times
//~^^^ ERROR specified multiple times
struct ErrorSpecifiedTwice {}
#[derive(SessionDiagnostic)]
#[error(code = "E0123", slug = "foo")]
#[warning(code = "E0293", slug = "bar")]
//~^ ERROR `warning` specified when `error` was already specified
//~^ ERROR specified multiple times
//~^^ ERROR specified multiple times
//~^^^ ERROR specified multiple times
struct WarnSpecifiedAfterError {}
#[derive(SessionDiagnostic)]
#[error(code = "E0456", code = "E0457", slug = "bar")] //~ ERROR `code` specified multiple times
#[error(code = "E0456", code = "E0457", slug = "bar")]
//~^ ERROR specified multiple times
struct CodeSpecifiedTwice {}
#[derive(SessionDiagnostic)]
#[error(code = "E0456", slug = "foo", slug = "bar")] //~ ERROR `slug` specified multiple times
#[error(code = "E0456", slug = "foo", slug = "bar")]
//~^ ERROR specified multiple times
struct SlugSpecifiedTwice {}
#[derive(SessionDiagnostic)]

View File

@ -86,7 +86,7 @@ LL | #[suggestion = "bar"]
|
= help: only `label`, `note` and `help` are valid field attributes
error: `error` specified multiple times
error: specified multiple times
--> $DIR/diagnostic-derive.rs:93:1
|
LL | #[error(code = "E0456", slug = "bar")]
@ -98,44 +98,92 @@ note: previously specified here
LL | #[error(code = "E0123", slug = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `warning` specified when `error` was already specified
--> $DIR/diagnostic-derive.rs:98:1
error: specified multiple times
--> $DIR/diagnostic-derive.rs:93:16
|
LL | #[error(code = "E0456", slug = "bar")]
| ^^^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:92:16
|
LL | #[error(code = "E0123", slug = "foo")]
| ^^^^^^^
error: specified multiple times
--> $DIR/diagnostic-derive.rs:93:32
|
LL | #[error(code = "E0456", slug = "bar")]
| ^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:92:32
|
LL | #[error(code = "E0123", slug = "foo")]
| ^^^^^
error: specified multiple times
--> $DIR/diagnostic-derive.rs:101:1
|
LL | #[warning(code = "E0293", slug = "bar")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:97:1
--> $DIR/diagnostic-derive.rs:100:1
|
LL | #[error(code = "E0123", slug = "foo")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: `code` specified multiple times
--> $DIR/diagnostic-derive.rs:103:32
error: specified multiple times
--> $DIR/diagnostic-derive.rs:101:18
|
LL | #[warning(code = "E0293", slug = "bar")]
| ^^^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:100:16
|
LL | #[error(code = "E0123", slug = "foo")]
| ^^^^^^^
error: specified multiple times
--> $DIR/diagnostic-derive.rs:101:34
|
LL | #[warning(code = "E0293", slug = "bar")]
| ^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:100:32
|
LL | #[error(code = "E0123", slug = "foo")]
| ^^^^^
error: specified multiple times
--> $DIR/diagnostic-derive.rs:108:32
|
LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
| ^^^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:103:16
--> $DIR/diagnostic-derive.rs:108:16
|
LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
| ^^^^^^^
error: `slug` specified multiple times
--> $DIR/diagnostic-derive.rs:107:46
error: specified multiple times
--> $DIR/diagnostic-derive.rs:113:46
|
LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
| ^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:107:32
--> $DIR/diagnostic-derive.rs:113:32
|
LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
| ^^^^^
error: diagnostic kind not specified
--> $DIR/diagnostic-derive.rs:111:1
--> $DIR/diagnostic-derive.rs:118:1
|
LL | struct KindNotProvided {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
@ -143,7 +191,7 @@ LL | struct KindNotProvided {}
= help: use the `#[error(...)]` attribute to create an error
error: `slug` not specified
--> $DIR/diagnostic-derive.rs:114:1
--> $DIR/diagnostic-derive.rs:121:1
|
LL | / #[error(code = "E0456")]
LL | | struct SlugNotProvided {}
@ -152,13 +200,13 @@ LL | | struct SlugNotProvided {}
= help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
error: the `#[primary_span]` attribute can only be applied to fields of type `Span`
--> $DIR/diagnostic-derive.rs:124:5
--> $DIR/diagnostic-derive.rs:131:5
|
LL | #[primary_span]
| ^^^^^^^^^^^^^^^
error: `#[nonsense]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:132:5
--> $DIR/diagnostic-derive.rs:139:5
|
LL | #[nonsense]
| ^^^^^^^^^^^
@ -166,19 +214,19 @@ LL | #[nonsense]
= help: only `skip_arg`, `primary_span`, `label`, `note` and `help` are valid field attributes
error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
--> $DIR/diagnostic-derive.rs:149:5
--> $DIR/diagnostic-derive.rs:156:5
|
LL | #[label = "bar"]
| ^^^^^^^^^^^^^^^^
error: `name` doesn't refer to a field on this type
--> $DIR/diagnostic-derive.rs:157:42
--> $DIR/diagnostic-derive.rs:164:42
|
LL | #[suggestion(message = "bar", code = "{name}")]
| ^^^^^^^^
error: invalid format string: expected `'}'` but string was terminated
--> $DIR/diagnostic-derive.rs:162:16
--> $DIR/diagnostic-derive.rs:169:16
|
LL | #[derive(SessionDiagnostic)]
| - ^ expected `'}'` in format string
@ -189,7 +237,7 @@ LL | #[derive(SessionDiagnostic)]
= note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid format string: unmatched `}` found
--> $DIR/diagnostic-derive.rs:172:15
--> $DIR/diagnostic-derive.rs:179:15
|
LL | #[derive(SessionDiagnostic)]
| ^ unmatched `}` in format string
@ -198,13 +246,13 @@ LL | #[derive(SessionDiagnostic)]
= note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
--> $DIR/diagnostic-derive.rs:192:5
--> $DIR/diagnostic-derive.rs:199:5
|
LL | #[label = "bar"]
| ^^^^^^^^^^^^^^^^
error: `#[suggestion(nonsense = ...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:217:18
--> $DIR/diagnostic-derive.rs:224:18
|
LL | #[suggestion(nonsense = "bar")]
| ^^^^^^^^^^^^^^^^
@ -212,7 +260,7 @@ LL | #[suggestion(nonsense = "bar")]
= help: only `message` and `code` are valid field attributes
error: `#[suggestion(msg = ...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:225:18
--> $DIR/diagnostic-derive.rs:232:18
|
LL | #[suggestion(msg = "bar")]
| ^^^^^^^^^^^
@ -220,7 +268,7 @@ LL | #[suggestion(msg = "bar")]
= help: only `message` and `code` are valid field attributes
error: wrong field type for suggestion
--> $DIR/diagnostic-derive.rs:247:5
--> $DIR/diagnostic-derive.rs:254:5
|
LL | / #[suggestion(message = "bar", code = "This is suggested code")]
LL | |
@ -230,7 +278,7 @@ LL | | suggestion: Applicability,
= help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
error: type of field annotated with `#[suggestion(...)]` contains more than one `Span`
--> $DIR/diagnostic-derive.rs:262:5
--> $DIR/diagnostic-derive.rs:269:5
|
LL | / #[suggestion(message = "bar", code = "This is suggested code")]
LL | |
@ -238,7 +286,7 @@ LL | | suggestion: (Span, Span, Applicability),
| |___________________________________________^
error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability
--> $DIR/diagnostic-derive.rs:270:5
--> $DIR/diagnostic-derive.rs:277:5
|
LL | / #[suggestion(message = "bar", code = "This is suggested code")]
LL | |
@ -246,7 +294,7 @@ LL | | suggestion: (Applicability, Applicability, Span),
| |____________________________________________________^
error: `#[label(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:278:5
--> $DIR/diagnostic-derive.rs:285:5
|
LL | #[label("bar")]
| ^^^^^^^^^^^^^^^
@ -254,25 +302,25 @@ LL | #[label("bar")]
= help: only `suggestion{,_short,_hidden,_verbose}` are valid field attributes
error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
--> $DIR/diagnostic-derive.rs:399:1
--> $DIR/diagnostic-derive.rs:406:1
|
LL | #[help]
| ^^^^^^^
error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
--> $DIR/diagnostic-derive.rs:407:1
--> $DIR/diagnostic-derive.rs:414:1
|
LL | #[help = "bar"]
| ^^^^^^^^^^^^^^^
error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
--> $DIR/diagnostic-derive.rs:415:1
--> $DIR/diagnostic-derive.rs:422:1
|
LL | #[note]
| ^^^^^^^
error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
--> $DIR/diagnostic-derive.rs:423:1
--> $DIR/diagnostic-derive.rs:430:1
|
LL | #[note = "bar"]
| ^^^^^^^^^^^^^^^
@ -284,13 +332,13 @@ LL | #[nonsense(code = "E0123", slug = "foo")]
| ^^^^^^^^
error: cannot find attribute `nonsense` in this scope
--> $DIR/diagnostic-derive.rs:132:7
--> $DIR/diagnostic-derive.rs:139:7
|
LL | #[nonsense]
| ^^^^^^^^
error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
--> $DIR/diagnostic-derive.rs:322:10
--> $DIR/diagnostic-derive.rs:329:10
|
LL | struct Hello {}
| ------------ method `into_diagnostic_arg` not found for this
@ -300,6 +348,6 @@ LL | #[derive(SessionDiagnostic)]
|
= note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 37 previous errors
error: aborting due to 41 previous errors
For more information about this error, try `rustc --explain E0599`.