Rollup merge of #151657 - JonathanBrouwer:diag2, r=Kivooeo

Cleanup of `#[derive(Diagnostic)]` attribute parsers

This PR does a lot of refactoring on the implementation of `#[derive(Diagnostic)]`. It should have no observable effect other than error messages for incorrect usage of the attributes. In general, I think the error messages got better.

This PR can be reviewed commit by commit, each commit passes the tests.
- [Convert parse_nested_meta to parse_args_with for #[diagnostic]](https://github.com/rust-lang/rust/pull/151657/changes/9e61014a8a0bb1f1d7911511c303a7ae2a9c2a7d)
  Start parsing `#[diagnostic]` using `syn`'s `parse_args_with` function instead of `parse_nested_meta`. This improves error messages and prepares for the new syntax needed for https://github.com/rust-lang/rust/issues/151366 which cannot be parsed using `parse_args_with`.
- [Convert parse_nested_meta to parse_args_with for #[subdiagnostic]](https://github.com/rust-lang/rust/pull/151657/changes/5d21a21695d56b74ea249f269ee10195251008b7)
  Same as above but for `#[subdiagnostic]`
- [Remove unused no_span option](https://github.com/rust-lang/rust/pull/151657/changes/0bf3f5d51cb853884240792818d81e70daec6ab7)
  Removes the `no_span` option of `#[suggestion]`, which there were no tests for and which seems to have been unused. If needed again in the future, this can be re-added pretty easily, but I find that unlikely.
- [Remove HasFieldMap trait in favour of passing FieldMap directly](https://github.com/rust-lang/rust/pull/151657/changes/2e8347abf4147d2bffe4d7989a21b17ae04cdb57)
  Removes the `HasFieldMap` trait, because I don't really see the point of having a trait "has a field map" if we can just pass the fieldmap itself instead.

r? @Kivooeo
(Thanks for reviewing my PRs so far :3)
This commit is contained in:
Stuart Cook 2026-01-29 22:34:10 +11:00 committed by GitHub
commit 617288eb58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 415 additions and 505 deletions

View file

@ -2,6 +2,7 @@
use proc_macro2::{Ident, Span, TokenStream};
use quote::{format_ident, quote, quote_spanned};
use syn::parse::ParseStream;
use syn::spanned::Spanned;
use syn::{Attribute, Meta, Path, Token, Type, parse_quote};
use synstructure::{BindingInfo, Structure, VariantInfo};
@ -11,7 +12,7 @@ use crate::diagnostics::error::{
DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err,
};
use crate::diagnostics::utils::{
FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind,
build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error,
should_generate_arg, type_is_bool, type_is_unit, type_matches_path,
};
@ -42,19 +43,13 @@ pub(crate) struct DiagnosticDeriveVariantBuilder {
/// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
/// has the actual diagnostic message.
pub slug: SpannedOption<Path>,
pub slug: Option<Path>,
/// Error codes are a optional part of the struct attribute - this is only set to detect
/// multiple specifications.
pub code: SpannedOption<()>,
}
impl HasFieldMap for DiagnosticDeriveVariantBuilder {
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
self.field_map.get(field)
}
}
impl DiagnosticDeriveKind {
/// Call `f` for the struct or for each variant of the enum, returning a `TokenStream` with the
/// tokens from `f` wrapped in an `match` expression. Emits errors for use of derive on unions
@ -111,7 +106,7 @@ impl DiagnosticDeriveKind {
impl DiagnosticDeriveVariantBuilder {
pub(crate) fn primary_message(&self) -> Option<&Path> {
match self.slug.value_ref() {
match self.slug.as_ref() {
None => {
span_err(self.span, "diagnostic slug not specified")
.help(
@ -169,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder {
&self,
attr: &Attribute,
) -> Result<Option<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else {
let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
return Ok(None);
@ -191,7 +186,7 @@ impl DiagnosticDeriveVariantBuilder {
SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(),
});
Ok(Some((subdiag.kind, slug, subdiag.no_span)))
Ok(Some((subdiag.kind, slug, false)))
}
/// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct
@ -209,47 +204,54 @@ impl DiagnosticDeriveVariantBuilder {
let name = attr.path().segments.last().unwrap().ident.to_string();
let name = name.as_str();
let mut first = true;
if name == "diag" {
let mut tokens = TokenStream::new();
attr.parse_nested_meta(|nested| {
let path = &nested.path;
attr.parse_args_with(|input: ParseStream<'_>| {
let mut input = &*input;
let slug_recovery_point = input.fork();
if first && (nested.input.is_empty() || nested.input.peek(Token![,])) {
self.slug.set_once(path.clone(), path.span().unwrap());
first = false;
return Ok(());
let slug = input.parse::<Path>()?;
if input.is_empty() || input.peek(Token![,]) {
self.slug = Some(slug);
} else {
input = &slug_recovery_point;
}
first = false;
let Ok(nested) = nested.value() else {
span_err(
nested.input.span().unwrap(),
"diagnostic slug must be the first argument",
)
.emit();
return Ok(());
};
if path.is_ident("code") {
self.code.set_once((), path.span().unwrap());
let code = nested.parse::<syn::Expr>()?;
tokens.extend(quote! {
diag.code(#code);
});
} else {
span_err(path.span().unwrap(), "unknown argument")
.note("only the `code` parameter is valid after the slug")
while !input.is_empty() {
input.parse::<Token![,]>()?;
// Allow trailing comma
if input.is_empty() {
break;
}
let arg_name: Path = input.parse::<Path>()?;
if input.peek(Token![,]) {
span_err(
arg_name.span().unwrap(),
"diagnostic slug must be the first argument",
)
.emit();
// consume the buffer so we don't have syntax errors from syn
let _ = nested.parse::<TokenStream>();
continue;
}
let arg_name = arg_name.require_ident()?;
input.parse::<Token![=]>()?;
let arg_value = input.parse::<syn::Expr>()?;
match arg_name.to_string().as_str() {
"code" => {
self.code.set_once((), arg_name.span().unwrap());
tokens.extend(quote! {
diag.code(#arg_value);
});
}
_ => {
span_err(arg_name.span().unwrap(), "unknown argument")
.note("only the `code` parameter is valid after the slug")
.emit();
}
}
}
Ok(())
})?;
return Ok(tokens);
}

View file

@ -1,9 +1,10 @@
#![deny(unused_must_use)]
use proc_macro2::TokenStream;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::parse::ParseStream;
use syn::spanned::Spanned;
use syn::{Attribute, Meta, MetaList, Path};
use syn::{Attribute, Meta, MetaList, Path, Token};
use synstructure::{BindingInfo, Structure, VariantInfo};
use super::utils::SubdiagnosticVariant;
@ -11,10 +12,10 @@ use crate::diagnostics::error::{
DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err,
};
use crate::diagnostics::utils::{
AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce,
SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment,
new_code_ident, report_error_if_not_applied_to_applicability,
report_error_if_not_applied_to_span, should_generate_arg,
AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption,
SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident,
report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span,
should_generate_arg,
};
/// The central struct for constructing the `add_to_diag` method from an annotated struct.
@ -142,12 +143,6 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
is_enum: bool,
}
impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
self.fields.get(field)
}
}
/// Provides frequently-needed information about the diagnostic kinds being derived for this type.
#[derive(Clone, Copy, Debug)]
struct KindsStatistics {
@ -187,14 +182,12 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics {
}
impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
fn identify_kind(
&mut self,
) -> Result<Vec<(SubdiagnosticKind, Path, bool)>, DiagnosticDeriveError> {
fn identify_kind(&mut self) -> Result<Vec<(SubdiagnosticKind, Path)>, DiagnosticDeriveError> {
let mut kind_slugs = vec![];
for attr in self.variant.ast().attrs {
let Some(SubdiagnosticVariant { kind, slug, no_span }) =
SubdiagnosticVariant::from_attr(attr, self)?
let Some(SubdiagnosticVariant { kind, slug }) =
SubdiagnosticVariant::from_attr(attr, &self.fields)?
else {
// Some attributes aren't errors - like documentation comments - but also aren't
// subdiagnostics.
@ -213,7 +206,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
);
};
kind_slugs.push((kind, slug, no_span));
kind_slugs.push((kind, slug));
}
Ok(kind_slugs)
@ -437,23 +430,35 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let mut code = None;
list.parse_nested_meta(|nested| {
if nested.path.is_ident("code") {
let code_field = new_code_ident();
let span = nested.path.span().unwrap();
let formatting_init = build_suggestion_code(
&code_field,
nested,
self,
AllowMultipleAlternatives::No,
);
code.set_once((code_field, formatting_init), span);
} else {
span_err(
nested.path.span().unwrap(),
"`code` is the only valid nested attribute",
)
.emit();
list.parse_args_with(|input: ParseStream<'_>| {
while !input.is_empty() {
let arg_name = input.parse::<Ident>()?;
match arg_name.to_string().as_str() {
"code" => {
let code_field = new_code_ident();
let formatting_init = build_suggestion_code(
&code_field,
input,
&self.fields,
AllowMultipleAlternatives::No,
)?;
code.set_once(
(code_field, formatting_init),
arg_name.span().unwrap(),
);
}
_ => {
span_err(
arg_name.span().unwrap(),
"`code` is the only valid nested attribute",
)
.emit();
}
}
if input.is_empty() {
break;
}
input.parse::<Token![,]>()?;
}
Ok(())
})?;
@ -492,8 +497,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
let kind_slugs = self.identify_kind()?;
let kind_stats: KindsStatistics =
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect();
let init = if kind_stats.has_multipart_suggestion {
quote! { let mut suggestions = Vec::new(); }
@ -526,17 +530,13 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
let diag = &self.parent.diag;
let mut calls = TokenStream::new();
for (kind, slug, no_span) in kind_slugs {
for (kind, slug) in kind_slugs {
let message = format_ident!("__message");
calls.extend(
quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); },
);
let name = format_ident!(
"{}{}",
if span_field.is_some() && !no_span { "span_" } else { "" },
kind
);
let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
let call = match kind {
SubdiagnosticKind::Suggestion {
suggestion_kind,
@ -588,9 +588,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
}
}
_ => {
if let Some(span) = span_field
&& !no_span
{
if let Some(span) = span_field {
quote! { #diag.#name(#span, #message); }
} else {
quote! { #diag.#name(#message); }

View file

@ -6,7 +6,7 @@ use std::str::FromStr;
use proc_macro::Span;
use proc_macro2::{Ident, TokenStream};
use quote::{ToTokens, format_ident, quote};
use syn::meta::ParseNestedMeta;
use syn::parse::ParseStream;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::{Attribute, Field, LitStr, Meta, Path, Token, Type, TypeTuple, parenthesized};
@ -261,108 +261,104 @@ impl<T> SetOnce<T> for SpannedOption<T> {
pub(super) type FieldMap = HashMap<String, TokenStream>;
pub(crate) trait HasFieldMap {
/// Returns the binding for the field with the given name, if it exists on the type.
fn get_field_binding(&self, field: &String) -> Option<&TokenStream>;
/// In the strings in the attributes supplied to this macro, we want callers to be able to
/// reference fields in the format string. For example:
///
/// ```ignore (not-usage-example)
/// /// Suggest `==` when users wrote `===`.
/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
/// struct NotJavaScriptEq {
/// #[primary_span]
/// span: Span,
/// lhs: Ident,
/// rhs: Ident,
/// }
/// ```
///
/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
/// `self.rhs`, then generate this call to `format!`:
///
/// ```ignore (not-usage-example)
/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
/// ```
///
/// This function builds the entire call to `format!`.
pub(super) fn build_format(
field_map: &FieldMap,
input: &str,
span: proc_macro2::Span,
) -> TokenStream {
// This set is used later to generate the final format string. To keep builds reproducible,
// the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
// instead of a `HashSet`.
let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
/// In the strings in the attributes supplied to this macro, we want callers to be able to
/// reference fields in the format string. For example:
///
/// ```ignore (not-usage-example)
/// /// Suggest `==` when users wrote `===`.
/// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
/// struct NotJavaScriptEq {
/// #[primary_span]
/// span: Span,
/// lhs: Ident,
/// rhs: Ident,
/// }
/// ```
///
/// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
/// `self.rhs`, then generate this call to `format!`:
///
/// ```ignore (not-usage-example)
/// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
/// ```
///
/// This function builds the entire call to `format!`.
fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream {
// This set is used later to generate the final format string. To keep builds reproducible,
// the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
// instead of a `HashSet`.
let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
// At this point, we can start parsing the format string.
let mut it = input.chars().peekable();
// At this point, we can start parsing the format string.
let mut it = input.chars().peekable();
// Once the start of a format string has been found, process the format string and spit out
// the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
// the next call to `it.next()` retrieves the next character.
while let Some(c) = it.next() {
if c != '{' {
continue;
}
if *it.peek().unwrap_or(&'\0') == '{' {
assert_eq!(it.next().unwrap(), '{');
continue;
}
let mut eat_argument = || -> Option<String> {
let mut result = String::new();
// Format specifiers look like:
//
// format := '{' [ argument ] [ ':' format_spec ] '}' .
//
// Therefore, we only need to eat until ':' or '}' to find the argument.
while let Some(c) = it.next() {
result.push(c);
let next = *it.peek().unwrap_or(&'\0');
if next == '}' {
break;
} else if next == ':' {
// Eat the ':' character.
assert_eq!(it.next().unwrap(), ':');
break;
}
}
// Eat until (and including) the matching '}'
while it.next()? != '}' {
continue;
}
Some(result)
};
if let Some(referenced_field) = eat_argument() {
referenced_fields.insert(referenced_field);
}
// Once the start of a format string has been found, process the format string and spit out
// the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
// the next call to `it.next()` retrieves the next character.
while let Some(c) = it.next() {
if c != '{' {
continue;
}
if *it.peek().unwrap_or(&'\0') == '{' {
assert_eq!(it.next().unwrap(), '{');
continue;
}
let mut eat_argument = || -> Option<String> {
let mut result = String::new();
// Format specifiers look like:
//
// format := '{' [ argument ] [ ':' format_spec ] '}' .
//
// Therefore, we only need to eat until ':' or '}' to find the argument.
while let Some(c) = it.next() {
result.push(c);
let next = *it.peek().unwrap_or(&'\0');
if next == '}' {
break;
} else if next == ':' {
// Eat the ':' character.
assert_eq!(it.next().unwrap(), ':');
break;
}
}
// Eat until (and including) the matching '}'
while it.next()? != '}' {
continue;
}
Some(result)
};
// At this point, `referenced_fields` contains a set of the unique fields that were
// referenced in the format string. Generate the corresponding "x = self.x" format
// string parameters:
let args = referenced_fields.into_iter().map(|field: String| {
let field_ident = format_ident!("{}", field);
let value = match self.get_field_binding(&field) {
Some(value) => value.clone(),
// This field doesn't exist. Emit a diagnostic.
None => {
span_err(
span.unwrap(),
format!("`{field}` doesn't refer to a field on this type"),
)
if let Some(referenced_field) = eat_argument() {
referenced_fields.insert(referenced_field);
}
}
// At this point, `referenced_fields` contains a set of the unique fields that were
// referenced in the format string. Generate the corresponding "x = self.x" format
// string parameters:
let args = referenced_fields.into_iter().map(|field: String| {
let field_ident = format_ident!("{}", field);
let value = match field_map.get(&field) {
Some(value) => value.clone(),
// This field doesn't exist. Emit a diagnostic.
None => {
span_err(span.unwrap(), format!("`{field}` doesn't refer to a field on this type"))
.emit();
quote! {
"{#field}"
}
quote! {
"{#field}"
}
};
quote! {
#field_ident = #value
}
});
};
quote! {
format!(#input #(,#args)*)
#field_ident = #value
}
});
quote! {
format!(#input #(,#args)*)
}
}
@ -428,76 +424,63 @@ pub(super) enum AllowMultipleAlternatives {
}
fn parse_suggestion_values(
nested: ParseNestedMeta<'_>,
nested: ParseStream<'_>,
allow_multiple: AllowMultipleAlternatives,
) -> syn::Result<Vec<LitStr>> {
let values = if let Ok(val) = nested.value() {
vec![val.parse()?]
} else {
let content;
parenthesized!(content in nested.input);
if nested.parse::<Token![=]>().is_ok() {
return Ok(vec![nested.parse::<LitStr>()?]);
}
if let AllowMultipleAlternatives::No = allow_multiple {
let content;
parenthesized!(content in nested);
if let AllowMultipleAlternatives::No = allow_multiple {
span_err(content.span().unwrap(), "expected exactly one string literal for `code = ...`")
.emit();
return Ok(vec![]);
}
let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
Ok(match literals {
Ok(p) if p.is_empty() => {
span_err(
nested.input.span().unwrap(),
"expected exactly one string literal for `code = ...`",
content.span().unwrap(),
"expected at least one string literal for `code(...)`",
)
.emit();
vec![]
} else {
let literals = Punctuated::<LitStr, Token![,]>::parse_terminated(&content);
match literals {
Ok(p) if p.is_empty() => {
span_err(
content.span().unwrap(),
"expected at least one string literal for `code(...)`",
)
.emit();
vec![]
}
Ok(p) => p.into_iter().collect(),
Err(_) => {
span_err(
content.span().unwrap(),
"`code(...)` must contain only string literals",
)
.emit();
vec![]
}
}
}
};
Ok(values)
Ok(p) => p.into_iter().collect(),
Err(_) => {
span_err(content.span().unwrap(), "`code(...)` must contain only string literals")
.emit();
vec![]
}
})
}
/// Constructs the `format!()` invocation(s) necessary for a `#[suggestion*(code = "foo")]` or
/// `#[suggestion*(code("foo", "bar"))]` attribute field
pub(super) fn build_suggestion_code(
code_field: &Ident,
nested: ParseNestedMeta<'_>,
fields: &impl HasFieldMap,
nested: ParseStream<'_>,
fields: &FieldMap,
allow_multiple: AllowMultipleAlternatives,
) -> TokenStream {
let values = match parse_suggestion_values(nested, allow_multiple) {
Ok(x) => x,
Err(e) => return e.into_compile_error(),
};
) -> Result<TokenStream, syn::Error> {
let values = parse_suggestion_values(nested, allow_multiple)?;
if let AllowMultipleAlternatives::Yes = allow_multiple {
Ok(if let AllowMultipleAlternatives::Yes = allow_multiple {
let formatted_strings: Vec<_> = values
.into_iter()
.map(|value| fields.build_format(&value.value(), value.span()))
.map(|value| build_format(fields, &value.value(), value.span()))
.collect();
quote! { let #code_field = [#(#formatted_strings),*].into_iter(); }
} else if let [value] = values.as_slice() {
let formatted_str = fields.build_format(&value.value(), value.span());
let formatted_str = build_format(fields, &value.value(), value.span());
quote! { let #code_field = #formatted_str; }
} else {
// error handled previously
quote! { let #code_field = String::new(); }
}
})
}
/// Possible styles for suggestion subdiagnostics.
@ -605,16 +588,15 @@ pub(super) enum SubdiagnosticKind {
pub(super) struct SubdiagnosticVariant {
pub(super) kind: SubdiagnosticKind,
pub(super) slug: Option<Path>,
pub(super) no_span: bool,
}
impl SubdiagnosticVariant {
/// Constructs a `SubdiagnosticVariant` from a field or type attribute such as `#[note]`,
/// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the
/// `#[error(parser::add_paren)]` or `#[suggestion(code = "...")]`. Returns the
/// `SubdiagnosticKind` and the diagnostic slug, if specified.
pub(super) fn from_attr(
attr: &Attribute,
fields: &impl HasFieldMap,
fields: &FieldMap,
) -> Result<Option<SubdiagnosticVariant>, DiagnosticDeriveError> {
// Always allow documentation comments.
if is_doc_comment(attr) {
@ -694,7 +676,7 @@ impl SubdiagnosticVariant {
| SubdiagnosticKind::HelpOnce
| SubdiagnosticKind::Warn
| SubdiagnosticKind::MultipartSuggestion { .. } => {
return Ok(Some(SubdiagnosticVariant { kind, slug: None, no_span: false }));
return Ok(Some(SubdiagnosticVariant { kind, slug: None }));
}
SubdiagnosticKind::Suggestion { .. } => {
throw_span_err!(span, "suggestion without `code = \"...\"`")
@ -709,112 +691,93 @@ impl SubdiagnosticVariant {
let mut code = None;
let mut suggestion_kind = None;
let mut first = true;
let mut slug = None;
let mut no_span = false;
list.parse_nested_meta(|nested| {
if nested.input.is_empty() || nested.input.peek(Token![,]) {
if first {
slug = Some(nested.path);
} else if nested.path.is_ident("no_span") {
no_span = true;
} else {
span_err(nested.input.span().unwrap(), "a diagnostic slug must be the first argument to the attribute").emit();
list.parse_args_with(|input: ParseStream<'_>| {
let mut is_first = true;
while !input.is_empty() {
let arg_name: Path = input.parse::<Path>()?;
let arg_name_span = arg_name.span().unwrap();
if input.is_empty() || input.parse::<Token![,]>().is_ok() {
if is_first {
slug = Some(arg_name);
is_first = false;
} else {
span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit();
}
continue
}
is_first = false;
first = false;
return Ok(());
}
match (arg_name.require_ident()?.to_string().as_str(), &mut kind) {
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
let code_init = build_suggestion_code(
&code_field,
&input,
fields,
AllowMultipleAlternatives::Yes,
)?;
code.set_once(code_init, arg_name_span);
}
(
"applicability",
SubdiagnosticKind::Suggestion { applicability, .. }
| SubdiagnosticKind::MultipartSuggestion { applicability, .. },
) => {
input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid applicability").emit();
Applicability::Unspecified
});
applicability.set_once(value, span);
}
(
"style",
SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. },
) => {
input.parse::<Token![=]>()?;
let value = input.parse::<LitStr>()?;
first = false;
let value = value.value().parse().unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid suggestion style")
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
.emit();
SuggestionKind::Normal
});
let nested_name = nested.path.segments.last().unwrap().ident.to_string();
let nested_name = nested_name.as_str();
suggestion_kind.set_once(value, span);
}
let path_span = nested.path.span().unwrap();
let val_span = nested.input.span().unwrap();
macro_rules! get_string {
() => {{
let Ok(value) = nested.value().and_then(|x| x.parse::<LitStr>()) else {
span_err(val_span, "expected `= \"xxx\"`").emit();
return Ok(());
};
value
}};
}
let mut has_errors = false;
let input = nested.input;
match (nested_name, &mut kind) {
("code", SubdiagnosticKind::Suggestion { code_field, .. }) => {
let code_init = build_suggestion_code(
code_field,
nested,
fields,
AllowMultipleAlternatives::Yes,
);
code.set_once(code_init, path_span);
}
(
"applicability",
SubdiagnosticKind::Suggestion { applicability, .. }
| SubdiagnosticKind::MultipartSuggestion { applicability, .. },
) => {
let value = get_string!();
let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid applicability").emit();
has_errors = true;
Applicability::Unspecified
});
applicability.set_once(value, span);
}
(
"style",
SubdiagnosticKind::Suggestion { .. }
| SubdiagnosticKind::MultipartSuggestion { .. },
) => {
let value = get_string!();
let value = value.value().parse().unwrap_or_else(|()| {
span_err(value.span().unwrap(), "invalid suggestion style")
.help("valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`")
// Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => {
span_err(arg_name_span, "invalid nested attribute")
.help(
"only `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
has_errors = true;
SuggestionKind::Normal
});
suggestion_kind.set_once(value, span);
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
span_err(arg_name_span, "invalid nested attribute")
.help("only `style` and `applicability` are valid nested attributes")
.emit();
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
_ => {
span_err(arg_name_span, "no nested attribute expected here").emit();
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
}
// Invalid nested attribute
(_, SubdiagnosticKind::Suggestion { .. }) => {
span_err(path_span, "invalid nested attribute")
.help(
"only `no_span`, `style`, `code` and `applicability` are valid nested attributes",
)
.emit();
has_errors = true;
}
(_, SubdiagnosticKind::MultipartSuggestion { .. }) => {
span_err(path_span, "invalid nested attribute")
.help("only `no_span`, `style` and `applicability` are valid nested attributes")
.emit();
has_errors = true;
}
_ => {
span_err(path_span, "only `no_span` is a valid nested attribute").emit();
has_errors = true;
}
if input.is_empty() { break }
input.parse::<Token![,]>()?;
}
if has_errors {
// Consume the rest of the input to avoid spamming errors
let _ = input.parse::<TokenStream>();
}
Ok(())
})?;
@ -851,7 +814,7 @@ impl SubdiagnosticVariant {
| SubdiagnosticKind::Warn => {}
}
Ok(Some(SubdiagnosticVariant { kind, slug, no_span }))
Ok(Some(SubdiagnosticVariant { kind, slug }))
}
}

View file

@ -80,20 +80,17 @@ struct InvalidNestedStructAttr {}
#[derive(Diagnostic)]
#[diag(nonsense("foo"), code = E0123, slug = "foo")]
//~^ ERROR diagnostic slug must be the first argument
//~| ERROR diagnostic slug not specified
//~^ ERROR derive(Diagnostic): diagnostic slug not specified
struct InvalidNestedStructAttr1 {}
#[derive(Diagnostic)]
#[diag(nonsense = "...", code = E0123, slug = "foo")]
//~^ ERROR unknown argument
//~| ERROR diagnostic slug not specified
//~^ ERROR diagnostic slug not specified
struct InvalidNestedStructAttr2 {}
#[derive(Diagnostic)]
#[diag(nonsense = 4, code = E0123, slug = "foo")]
//~^ ERROR unknown argument
//~| ERROR diagnostic slug not specified
//~^ ERROR diagnostic slug not specified
struct InvalidNestedStructAttr3 {}
#[derive(Diagnostic)]
@ -113,7 +110,6 @@ struct WrongPlaceField {
#[diag(no_crate_example, code = E0123)]
#[diag(no_crate_example, code = E0456)]
//~^ ERROR specified multiple times
//~^^ ERROR specified multiple times
struct DiagSpecifiedTwice {}
#[derive(Diagnostic)]
@ -543,7 +539,7 @@ struct LabelWithTrailingPath {
#[diag(no_crate_example, code = E0123)]
struct LabelWithTrailingNameValue {
#[label(no_crate_label, foo = "...")]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
span: Span,
}
@ -551,7 +547,7 @@ struct LabelWithTrailingNameValue {
#[diag(no_crate_example, code = E0123)]
struct LabelWithTrailingList {
#[label(no_crate_label, foo("..."))]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
span: Span,
}
@ -807,7 +803,7 @@ struct SuggestionsInvalidItem {
sub: Span,
}
#[derive(Diagnostic)] //~ ERROR cannot find value `__code_34` in this scope
#[derive(Diagnostic)]
#[diag(no_crate_example)]
struct SuggestionsInvalidLiteral {
#[suggestion(code = 3)]

View file

@ -48,12 +48,6 @@ LL | #[diag(code = E0123)]
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): diagnostic slug must be the first argument
--> $DIR/diagnostic-derive.rs:82:16
|
LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")]
| ^
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:82:1
|
@ -62,32 +56,16 @@ LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")]
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): unknown argument
--> $DIR/diagnostic-derive.rs:88:8
|
LL | #[diag(nonsense = "...", code = E0123, slug = "foo")]
| ^^^^^^^^
|
= note: only the `code` parameter is valid after the slug
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:88:1
--> $DIR/diagnostic-derive.rs:87:1
|
LL | #[diag(nonsense = "...", code = E0123, slug = "foo")]
| ^
|
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): unknown argument
--> $DIR/diagnostic-derive.rs:94:8
|
LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
| ^^^^^^^^
|
= note: only the `code` parameter is valid after the slug
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:94:1
--> $DIR/diagnostic-derive.rs:92:1
|
LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
| ^
@ -95,7 +73,7 @@ LL | #[diag(nonsense = 4, code = E0123, slug = "foo")]
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): unknown argument
--> $DIR/diagnostic-derive.rs:100:40
--> $DIR/diagnostic-derive.rs:97:40
|
LL | #[diag(no_crate_example, code = E0123, slug = "foo")]
| ^^^^
@ -103,55 +81,43 @@ LL | #[diag(no_crate_example, code = E0123, slug = "foo")]
= note: only the `code` parameter is valid after the slug
error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:107:5
--> $DIR/diagnostic-derive.rs:104:5
|
LL | #[suggestion = "bar"]
| ^
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:114:8
|
LL | #[diag(no_crate_example, code = E0456)]
| ^^^^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:113:8
|
LL | #[diag(no_crate_example, code = E0123)]
| ^^^^^^^^^^^^^^^^
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:114:26
--> $DIR/diagnostic-derive.rs:111:26
|
LL | #[diag(no_crate_example, code = E0456)]
| ^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:113:26
--> $DIR/diagnostic-derive.rs:110:26
|
LL | #[diag(no_crate_example, code = E0123)]
| ^^^^
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:120:40
--> $DIR/diagnostic-derive.rs:116:40
|
LL | #[diag(no_crate_example, code = E0123, code = E0456)]
| ^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:120:26
--> $DIR/diagnostic-derive.rs:116:26
|
LL | #[diag(no_crate_example, code = E0123, code = E0456)]
| ^^^^
error: derive(Diagnostic): diagnostic slug must be the first argument
--> $DIR/diagnostic-derive.rs:125:43
--> $DIR/diagnostic-derive.rs:121:26
|
LL | #[diag(no_crate_example, no_crate::example, code = E0123)]
| ^
| ^^^^^^^^
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:130:1
--> $DIR/diagnostic-derive.rs:126:1
|
LL | struct KindNotProvided {}
| ^^^^^^
@ -159,7 +125,7 @@ LL | struct KindNotProvided {}
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:133:1
--> $DIR/diagnostic-derive.rs:129:1
|
LL | #[diag(code = E0123)]
| ^
@ -167,31 +133,31 @@ LL | #[diag(code = E0123)]
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan`
--> $DIR/diagnostic-derive.rs:144:5
--> $DIR/diagnostic-derive.rs:140:5
|
LL | #[primary_span]
| ^
error: derive(Diagnostic): `#[nonsense]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:152:5
--> $DIR/diagnostic-derive.rs:148:5
|
LL | #[nonsense]
| ^
error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
--> $DIR/diagnostic-derive.rs:169:5
--> $DIR/diagnostic-derive.rs:165:5
|
LL | #[label(no_crate_label)]
| ^
error: derive(Diagnostic): `name` doesn't refer to a field on this type
--> $DIR/diagnostic-derive.rs:177:46
--> $DIR/diagnostic-derive.rs:173:46
|
LL | #[suggestion(no_crate_suggestion, code = "{name}")]
| ^^^^^^^^
error: invalid format string: expected `}` but string was terminated
--> $DIR/diagnostic-derive.rs:182:10
--> $DIR/diagnostic-derive.rs:178:10
|
LL | #[derive(Diagnostic)]
| ^^^^^^^^^^ expected `}` in format string
@ -200,7 +166,7 @@ LL | #[derive(Diagnostic)]
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: invalid format string: unmatched `}` found
--> $DIR/diagnostic-derive.rs:192:10
--> $DIR/diagnostic-derive.rs:188:10
|
LL | #[derive(Diagnostic)]
| ^^^^^^^^^^ unmatched `}` in format string
@ -209,47 +175,47 @@ LL | #[derive(Diagnostic)]
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan`
--> $DIR/diagnostic-derive.rs:212:5
--> $DIR/diagnostic-derive.rs:208:5
|
LL | #[label(no_crate_label)]
| ^
error: derive(Diagnostic): suggestion without `code = "..."`
--> $DIR/diagnostic-derive.rs:231:5
--> $DIR/diagnostic-derive.rs:227:5
|
LL | #[suggestion(no_crate_suggestion)]
| ^
error: derive(Diagnostic): invalid nested attribute
--> $DIR/diagnostic-derive.rs:239:18
--> $DIR/diagnostic-derive.rs:235:18
|
LL | #[suggestion(nonsense = "bar")]
| ^^^^^^^^
|
= help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes
= help: only `style`, `code` and `applicability` are valid nested attributes
error: derive(Diagnostic): suggestion without `code = "..."`
--> $DIR/diagnostic-derive.rs:239:5
--> $DIR/diagnostic-derive.rs:235:5
|
LL | #[suggestion(nonsense = "bar")]
| ^
error: derive(Diagnostic): invalid nested attribute
--> $DIR/diagnostic-derive.rs:248:18
--> $DIR/diagnostic-derive.rs:244:18
|
LL | #[suggestion(msg = "bar")]
| ^^^
|
= help: only `no_span`, `style`, `code` and `applicability` are valid nested attributes
= help: only `style`, `code` and `applicability` are valid nested attributes
error: derive(Diagnostic): suggestion without `code = "..."`
--> $DIR/diagnostic-derive.rs:248:5
--> $DIR/diagnostic-derive.rs:244:5
|
LL | #[suggestion(msg = "bar")]
| ^
error: derive(Diagnostic): wrong field type for suggestion
--> $DIR/diagnostic-derive.rs:271:5
--> $DIR/diagnostic-derive.rs:267:5
|
LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")]
| ^
@ -257,79 +223,79 @@ LL | #[suggestion(no_crate_suggestion, code = "This is suggested code")]
= help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:287:24
--> $DIR/diagnostic-derive.rs:283:24
|
LL | suggestion: (Span, Span, Applicability),
| ^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:287:18
--> $DIR/diagnostic-derive.rs:283:18
|
LL | suggestion: (Span, Span, Applicability),
| ^^^^
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:295:33
--> $DIR/diagnostic-derive.rs:291:33
|
LL | suggestion: (Applicability, Applicability, Span),
| ^^^^^^^^^^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:295:18
--> $DIR/diagnostic-derive.rs:291:18
|
LL | suggestion: (Applicability, Applicability, Span),
| ^^^^^^^^^^^^^
error: derive(Diagnostic): `#[label = ...]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:302:5
--> $DIR/diagnostic-derive.rs:298:5
|
LL | #[label = "bar"]
| ^
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:453:5
--> $DIR/diagnostic-derive.rs:449:5
|
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "maybe-incorrect")]
| ^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:455:24
--> $DIR/diagnostic-derive.rs:451:24
|
LL | suggestion: (Span, Applicability),
| ^^^^^^^^^^^^^
error: derive(Diagnostic): invalid applicability
--> $DIR/diagnostic-derive.rs:461:69
--> $DIR/diagnostic-derive.rs:457:69
|
LL | #[suggestion(no_crate_suggestion, code = "...", applicability = "batman")]
| ^^^^^^^^
error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()`
--> $DIR/diagnostic-derive.rs:528:5
--> $DIR/diagnostic-derive.rs:524:5
|
LL | #[help(no_crate_help)]
| ^
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
--> $DIR/diagnostic-derive.rs:537:32
--> $DIR/diagnostic-derive.rs:533:29
|
LL | #[label(no_crate_label, foo)]
| ^
| ^^^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
--> $DIR/diagnostic-derive.rs:545:29
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/diagnostic-derive.rs:541:29
|
LL | #[label(no_crate_label, foo = "...")]
| ^^^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
--> $DIR/diagnostic-derive.rs:553:29
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/diagnostic-derive.rs:549:29
|
LL | #[label(no_crate_label, foo("..."))]
| ^^^
error: derive(Diagnostic): `#[primary_span]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:565:5
--> $DIR/diagnostic-derive.rs:561:5
|
LL | #[primary_span]
| ^
@ -337,13 +303,13 @@ LL | #[primary_span]
= help: the `primary_span` field attribute is not valid for lint diagnostics
error: derive(Diagnostic): `#[error(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:585:1
--> $DIR/diagnostic-derive.rs:581:1
|
LL | #[error(no_crate_example, code = E0123)]
| ^
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:585:1
--> $DIR/diagnostic-derive.rs:581:1
|
LL | #[error(no_crate_example, code = E0123)]
| ^
@ -351,13 +317,13 @@ LL | #[error(no_crate_example, code = E0123)]
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:592:1
--> $DIR/diagnostic-derive.rs:588:1
|
LL | #[warn_(no_crate_example, code = E0123)]
| ^
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:592:1
--> $DIR/diagnostic-derive.rs:588:1
|
LL | #[warn_(no_crate_example, code = E0123)]
| ^
@ -365,13 +331,13 @@ LL | #[warn_(no_crate_example, code = E0123)]
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:599:1
--> $DIR/diagnostic-derive.rs:595:1
|
LL | #[lint(no_crate_example, code = E0123)]
| ^
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:599:1
--> $DIR/diagnostic-derive.rs:595:1
|
LL | #[lint(no_crate_example, code = E0123)]
| ^
@ -379,13 +345,13 @@ LL | #[lint(no_crate_example, code = E0123)]
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:606:1
--> $DIR/diagnostic-derive.rs:602:1
|
LL | #[lint(no_crate_example, code = E0123)]
| ^
error: derive(Diagnostic): diagnostic slug not specified
--> $DIR/diagnostic-derive.rs:606:1
--> $DIR/diagnostic-derive.rs:602:1
|
LL | #[lint(no_crate_example, code = E0123)]
| ^
@ -393,19 +359,19 @@ LL | #[lint(no_crate_example, code = E0123)]
= help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]`
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/diagnostic-derive.rs:615:53
--> $DIR/diagnostic-derive.rs:611:53
|
LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
| ^^^^
|
note: previously specified here
--> $DIR/diagnostic-derive.rs:615:39
--> $DIR/diagnostic-derive.rs:611:39
|
LL | #[suggestion(no_crate_suggestion, code = "...", code = ",,,")]
| ^^^^
error: derive(Diagnostic): wrong types for suggestion
--> $DIR/diagnostic-derive.rs:624:24
--> $DIR/diagnostic-derive.rs:620:24
|
LL | suggestion: (Span, usize),
| ^^^^^
@ -413,7 +379,7 @@ LL | suggestion: (Span, usize),
= help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
error: derive(Diagnostic): wrong types for suggestion
--> $DIR/diagnostic-derive.rs:632:17
--> $DIR/diagnostic-derive.rs:628:17
|
LL | suggestion: (Span,),
| ^^^^^^^
@ -421,13 +387,13 @@ LL | suggestion: (Span,),
= help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`
error: derive(Diagnostic): suggestion without `code = "..."`
--> $DIR/diagnostic-derive.rs:639:5
--> $DIR/diagnostic-derive.rs:635:5
|
LL | #[suggestion(no_crate_suggestion)]
| ^
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:646:1
--> $DIR/diagnostic-derive.rs:642:1
|
LL | #[multipart_suggestion(no_crate_suggestion)]
| ^
@ -435,7 +401,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)]
= help: consider creating a `Subdiagnostic` instead
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:649:1
--> $DIR/diagnostic-derive.rs:645:1
|
LL | #[multipart_suggestion()]
| ^
@ -443,7 +409,7 @@ LL | #[multipart_suggestion()]
= help: consider creating a `Subdiagnostic` instead
error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:653:5
--> $DIR/diagnostic-derive.rs:649:5
|
LL | #[multipart_suggestion(no_crate_suggestion)]
| ^
@ -451,7 +417,7 @@ LL | #[multipart_suggestion(no_crate_suggestion)]
= help: consider creating a `Subdiagnostic` instead
error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:661:1
--> $DIR/diagnostic-derive.rs:657:1
|
LL | #[suggestion(no_crate_suggestion, code = "...")]
| ^
@ -459,7 +425,7 @@ LL | #[suggestion(no_crate_suggestion, code = "...")]
= help: `#[label]` and `#[suggestion]` can only be applied to fields
error: derive(Diagnostic): `#[label]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:670:1
--> $DIR/diagnostic-derive.rs:666:1
|
LL | #[label]
| ^
@ -467,73 +433,73 @@ LL | #[label]
= help: `#[label]` and `#[suggestion]` can only be applied to fields
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:704:5
--> $DIR/diagnostic-derive.rs:700:5
|
LL | #[subdiagnostic(bad)]
| ^
error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:712:5
--> $DIR/diagnostic-derive.rs:708:5
|
LL | #[subdiagnostic = "bad"]
| ^
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:720:5
--> $DIR/diagnostic-derive.rs:716:5
|
LL | #[subdiagnostic(bad, bad)]
| ^
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:728:5
--> $DIR/diagnostic-derive.rs:724:5
|
LL | #[subdiagnostic("bad")]
| ^
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:736:5
--> $DIR/diagnostic-derive.rs:732:5
|
LL | #[subdiagnostic(eager)]
| ^
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:744:5
--> $DIR/diagnostic-derive.rs:740:5
|
LL | #[subdiagnostic(eager)]
| ^
error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:765:5
--> $DIR/diagnostic-derive.rs:761:5
|
LL | #[subdiagnostic(eager)]
| ^
error: derive(Diagnostic): expected at least one string literal for `code(...)`
--> $DIR/diagnostic-derive.rs:796:23
--> $DIR/diagnostic-derive.rs:792:23
|
LL | #[suggestion(code())]
| ^
error: derive(Diagnostic): `code(...)` must contain only string literals
--> $DIR/diagnostic-derive.rs:804:23
--> $DIR/diagnostic-derive.rs:800:23
|
LL | #[suggestion(code(foo))]
| ^^^
error: unexpected token, expected `)`
--> $DIR/diagnostic-derive.rs:804:23
--> $DIR/diagnostic-derive.rs:800:23
|
LL | #[suggestion(code(foo))]
| ^^^
error: expected string literal
--> $DIR/diagnostic-derive.rs:813:25
--> $DIR/diagnostic-derive.rs:809:25
|
LL | #[suggestion(code = 3)]
| ^
error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute
--> $DIR/diagnostic-derive.rs:828:5
--> $DIR/diagnostic-derive.rs:824:5
|
LL | #[suggestion(no_crate_suggestion, code = "")]
| ^
@ -549,13 +515,13 @@ LL | #[nonsense(no_crate_example, code = E0123)]
| ^^^^^^^^
error: cannot find attribute `nonsense` in this scope
--> $DIR/diagnostic-derive.rs:152:7
--> $DIR/diagnostic-derive.rs:148:7
|
LL | #[nonsense]
| ^^^^^^^^
error: cannot find attribute `error` in this scope
--> $DIR/diagnostic-derive.rs:585:3
--> $DIR/diagnostic-derive.rs:581:3
|
LL | #[error(no_crate_example, code = E0123)]
| ^^^^^
@ -567,7 +533,7 @@ LL | struct ErrorAttribute {}
|
error: cannot find attribute `warn_` in this scope
--> $DIR/diagnostic-derive.rs:592:3
--> $DIR/diagnostic-derive.rs:588:3
|
LL | #[warn_(no_crate_example, code = E0123)]
| ^^^^^
@ -579,7 +545,7 @@ LL + #[warn(no_crate_example, code = E0123)]
|
error: cannot find attribute `lint` in this scope
--> $DIR/diagnostic-derive.rs:599:3
--> $DIR/diagnostic-derive.rs:595:3
|
LL | #[lint(no_crate_example, code = E0123)]
| ^^^^
@ -591,7 +557,7 @@ LL + #[link(no_crate_example, code = E0123)]
|
error: cannot find attribute `lint` in this scope
--> $DIR/diagnostic-derive.rs:606:3
--> $DIR/diagnostic-derive.rs:602:3
|
LL | #[lint(no_crate_example, code = E0123)]
| ^^^^
@ -603,7 +569,7 @@ LL + #[link(no_crate_example, code = E0123)]
|
error: cannot find attribute `multipart_suggestion` in this scope
--> $DIR/diagnostic-derive.rs:646:3
--> $DIR/diagnostic-derive.rs:642:3
|
LL | #[multipart_suggestion(no_crate_suggestion)]
| ^^^^^^^^^^^^^^^^^^^^
@ -615,7 +581,7 @@ LL | struct MultipartSuggestion {
|
error: cannot find attribute `multipart_suggestion` in this scope
--> $DIR/diagnostic-derive.rs:649:3
--> $DIR/diagnostic-derive.rs:645:3
|
LL | #[multipart_suggestion()]
| ^^^^^^^^^^^^^^^^^^^^
@ -627,7 +593,7 @@ LL | struct MultipartSuggestion {
|
error: cannot find attribute `multipart_suggestion` in this scope
--> $DIR/diagnostic-derive.rs:653:7
--> $DIR/diagnostic-derive.rs:649:7
|
LL | #[multipart_suggestion(no_crate_suggestion)]
| ^^^^^^^^^^^^^^^^^^^^
@ -640,16 +606,8 @@ error[E0425]: cannot find value `nonsense` in module `crate::fluent_generated`
LL | #[diag(nonsense, code = E0123)]
| ^^^^^^^^ not found in `crate::fluent_generated`
error[E0425]: cannot find value `__code_34` in this scope
--> $DIR/diagnostic-derive.rs:810:10
|
LL | #[derive(Diagnostic)]
| ^^^^^^^^^^ not found in this scope
|
= note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied
--> $DIR/diagnostic-derive.rs:351:12
--> $DIR/diagnostic-derive.rs:347:12
|
LL | #[derive(Diagnostic)]
| ---------- required by a bound introduced by this call
@ -670,7 +628,7 @@ note: required by a bound in `Diag::<'a, G>::arg`
= note: in this macro invocation
= note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 85 previous errors
error: aborting due to 80 previous errors
Some errors have detailed explanations: E0277, E0425.
For more information about an error, try `rustc --explain E0277`.

View file

@ -85,7 +85,7 @@ struct F {
#[derive(Subdiagnostic)]
#[label(bug = "...")]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
//~| ERROR diagnostic slug must be first argument
struct G {
#[primary_span]
@ -95,7 +95,7 @@ struct G {
#[derive(Subdiagnostic)]
#[label("...")]
//~^ ERROR unexpected literal in nested attribute, expected ident
//~^ ERROR expected identifier
struct H {
#[primary_span]
span: Span,
@ -104,7 +104,7 @@ struct H {
#[derive(Subdiagnostic)]
#[label(slug = 4)]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
//~| ERROR diagnostic slug must be first argument
struct J {
#[primary_span]
@ -114,7 +114,7 @@ struct J {
#[derive(Subdiagnostic)]
#[label(slug("..."))]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
//~| ERROR diagnostic slug must be first argument
struct K {
#[primary_span]
@ -133,7 +133,7 @@ struct M {
#[derive(Subdiagnostic)]
#[label(no_crate_example, code = "...")]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
struct N {
#[primary_span]
span: Span,
@ -142,7 +142,7 @@ struct N {
#[derive(Subdiagnostic)]
#[label(no_crate_example, applicability = "machine-applicable")]
//~^ ERROR only `no_span` is a valid nested attribute
//~^ ERROR no nested attribute expected here
struct O {
#[primary_span]
span: Span,
@ -214,7 +214,7 @@ enum T {
enum U {
#[label(code = "...")]
//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute
//~| ERROR only `no_span` is a valid nested attribute
//~| ERROR no nested attribute expected here
A {
#[primary_span]
span: Span,
@ -775,7 +775,7 @@ struct SuggestionStyleInvalid1 {
#[derive(Subdiagnostic)]
#[suggestion(no_crate_example, code = "", style = 42)]
//~^ ERROR expected `= "xxx"`
//~^ ERROR expected string literal
struct SuggestionStyleInvalid2 {
#[primary_span]
sub: Span,
@ -791,8 +791,7 @@ struct SuggestionStyleInvalid3 {
#[derive(Subdiagnostic)]
#[suggestion(no_crate_example, code = "", style("foo"))]
//~^ ERROR expected `= "xxx"`
//~| ERROR expected `,`
//~^ ERROR expected `=`
struct SuggestionStyleInvalid4 {
#[primary_span]
sub: Span,

View file

@ -22,7 +22,7 @@ error: derive(Diagnostic): `#[label = ...]` is not a valid attribute
LL | #[label = "..."]
| ^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/subdiagnostic-derive.rs:87:9
|
LL | #[label(bug = "...")]
@ -34,13 +34,13 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(
LL | #[label(bug = "...")]
| ^
error: unexpected literal in nested attribute, expected ident
error: expected identifier
--> $DIR/subdiagnostic-derive.rs:97:9
|
LL | #[label("...")]
| ^^^^^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/subdiagnostic-derive.rs:106:9
|
LL | #[label(slug = 4)]
@ -52,7 +52,7 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(
LL | #[label(slug = 4)]
| ^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/subdiagnostic-derive.rs:116:9
|
LL | #[label(slug("..."))]
@ -70,13 +70,13 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(
LL | #[label()]
| ^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/subdiagnostic-derive.rs:135:27
|
LL | #[label(no_crate_example, code = "...")]
| ^^^^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/subdiagnostic-derive.rs:144:27
|
LL | #[label(no_crate_example, applicability = "machine-applicable")]
@ -112,7 +112,7 @@ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute
LL | #[bar("...")]
| ^
error: derive(Diagnostic): only `no_span` is a valid nested attribute
error: derive(Diagnostic): no nested attribute expected here
--> $DIR/subdiagnostic-derive.rs:215:13
|
LL | #[label(code = "...")]
@ -175,10 +175,10 @@ LL | | }
| |_^
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
--> $DIR/subdiagnostic-derive.rs:317:44
--> $DIR/subdiagnostic-derive.rs:317:27
|
LL | #[label(no_crate_example, no_crate::example)]
| ^
| ^^^^^^^^
error: derive(Diagnostic): attribute specified multiple times
--> $DIR/subdiagnostic-derive.rs:330:5
@ -292,7 +292,7 @@ error: derive(Diagnostic): invalid nested attribute
LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")]
| ^^^^
|
= help: only `no_span`, `style` and `applicability` are valid nested attributes
= help: only `style` and `applicability` are valid nested attributes
error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields
--> $DIR/subdiagnostic-derive.rs:530:1
@ -381,10 +381,10 @@ LL | #[applicability]
| ^
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
--> $DIR/subdiagnostic-derive.rs:663:34
--> $DIR/subdiagnostic-derive.rs:663:28
|
LL | #[suggestion_part(code("foo"))]
| ^
| ^^^^^
error: unexpected token, expected `)`
--> $DIR/subdiagnostic-derive.rs:663:28
@ -393,10 +393,10 @@ LL | #[suggestion_part(code("foo"))]
| ^^^^^
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
--> $DIR/subdiagnostic-derive.rs:673:41
--> $DIR/subdiagnostic-derive.rs:673:28
|
LL | #[suggestion_part(code("foo", "bar"))]
| ^
| ^^^^^
error: unexpected token, expected `)`
--> $DIR/subdiagnostic-derive.rs:673:28
@ -405,10 +405,10 @@ LL | #[suggestion_part(code("foo", "bar"))]
| ^^^^^
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
--> $DIR/subdiagnostic-derive.rs:683:30
--> $DIR/subdiagnostic-derive.rs:683:28
|
LL | #[suggestion_part(code(3))]
| ^
| ^
error: unexpected token, expected `)`
--> $DIR/subdiagnostic-derive.rs:683:28
@ -417,10 +417,10 @@ LL | #[suggestion_part(code(3))]
| ^
error: derive(Diagnostic): expected exactly one string literal for `code = ...`
--> $DIR/subdiagnostic-derive.rs:693:29
--> $DIR/subdiagnostic-derive.rs:693:28
|
LL | #[suggestion_part(code())]
| ^
| ^
error: expected string literal
--> $DIR/subdiagnostic-derive.rs:702:30
@ -464,32 +464,26 @@ LL | #[suggestion(no_crate_example, code = "", style = "foo")]
|
= help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only`
error: derive(Diagnostic): expected `= "xxx"`
--> $DIR/subdiagnostic-derive.rs:777:49
error: expected string literal
--> $DIR/subdiagnostic-derive.rs:777:51
|
LL | #[suggestion(no_crate_example, code = "", style = 42)]
| ^
| ^^
error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute
--> $DIR/subdiagnostic-derive.rs:785:48
--> $DIR/subdiagnostic-derive.rs:785:43
|
LL | #[suggestion(no_crate_example, code = "", style)]
| ^
| ^^^^^
error: derive(Diagnostic): expected `= "xxx"`
--> $DIR/subdiagnostic-derive.rs:793:48
|
LL | #[suggestion(no_crate_example, code = "", style("foo"))]
| ^
error: expected `,`
error: expected `=`
--> $DIR/subdiagnostic-derive.rs:793:48
|
LL | #[suggestion(no_crate_example, code = "", style("foo"))]
| ^
error: derive(Diagnostic): `#[primary_span]` is not a valid attribute
--> $DIR/subdiagnostic-derive.rs:805:5
--> $DIR/subdiagnostic-derive.rs:804:5
|
LL | #[primary_span]
| ^
@ -498,7 +492,7 @@ LL | #[primary_span]
= help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead
error: derive(Diagnostic): suggestion without `#[primary_span]` field
--> $DIR/subdiagnostic-derive.rs:802:1
--> $DIR/subdiagnostic-derive.rs:801:1
|
LL | #[suggestion(no_crate_example, code = "")]
| ^
@ -557,5 +551,5 @@ error: cannot find attribute `bar` in this scope
LL | #[bar("...")]
| ^^^
error: aborting due to 84 previous errors
error: aborting due to 83 previous errors