1
Fork 0

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, SessionDiagnosticDeriveError,
}; };
use crate::diagnostics::utils::{ 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 proc_macro2::TokenStream;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
@ -240,7 +241,7 @@ struct SessionDiagnosticDeriveBuilder {
slug: Option<(String, proc_macro::Span)>, slug: Option<(String, proc_macro::Span)>,
/// Error codes are a optional part of the struct attribute - this is only set to detect /// Error codes are a optional part of the struct attribute - this is only set to detect
/// multiple specifications. /// multiple specifications.
code: Option<proc_macro::Span>, code: Option<(String, proc_macro::Span)>,
} }
impl HasFieldMap for SessionDiagnosticDeriveBuilder { impl HasFieldMap for SessionDiagnosticDeriveBuilder {
@ -306,7 +307,7 @@ impl SessionDiagnosticDeriveBuilder {
diag.help("only `error` and `warning` are valid attributes") 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(); let mut tokens = Vec::new();
for nested_attr in nested { for nested_attr in nested {
@ -321,12 +322,17 @@ impl SessionDiagnosticDeriveBuilder {
// Struct attributes are only allowed to be applied once, and the diagnostic // Struct attributes are only allowed to be applied once, and the diagnostic
// changes will be set in the initialisation code. // changes will be set in the initialisation code.
Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
let span = s.span().unwrap();
match nested_name.as_str() { match nested_name.as_str() {
"slug" => { "slug" => {
self.set_slug_once(s.value(), s.span().unwrap()); self.slug.set_once((s.value(), span));
} }
"code" => { "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) _ => invalid_nested_attr(attr, &nested_attr)
.help("only `slug` and `code` are valid nested attributes") .help("only `slug` and `code` are valid nested attributes")
@ -340,61 +346,6 @@ impl SessionDiagnosticDeriveBuilder {
Ok(tokens.drain(..).collect()) 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( fn generate_field_attr_code(
&mut self, &mut self,
attr: &syn::Attribute, attr: &syn::Attribute,

View file

@ -90,21 +90,28 @@ struct WrongPlaceField {
#[derive(SessionDiagnostic)] #[derive(SessionDiagnostic)]
#[error(code = "E0123", slug = "foo")] #[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 {} struct ErrorSpecifiedTwice {}
#[derive(SessionDiagnostic)] #[derive(SessionDiagnostic)]
#[error(code = "E0123", slug = "foo")] #[error(code = "E0123", slug = "foo")]
#[warning(code = "E0293", slug = "bar")] #[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 {} struct WarnSpecifiedAfterError {}
#[derive(SessionDiagnostic)] #[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 {} struct CodeSpecifiedTwice {}
#[derive(SessionDiagnostic)] #[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 {} struct SlugSpecifiedTwice {}
#[derive(SessionDiagnostic)] #[derive(SessionDiagnostic)]

View file

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