diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 2336186dc71..78e273ef823 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -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, + 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, diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 5f55492edf9..df57a9f183f 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -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)] diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index ef4950ba3af..5a685ae43be 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -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`.