diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 32d6ba62a0d3e..2aa292bbce2b6 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -1,17 +1,17 @@ #![deny(unused_must_use)] +use super::error::throw_invalid_nested_attr; +use super::utils::{SpannedOption, SubdiagnosticKind}; use crate::diagnostics::error::{ - invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, - DiagnosticDeriveError, + invalid_nested_attr, span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ report_error_if_not_applied_to_span, report_type_error, type_is_unit, type_matches_path, - Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, + FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; use std::collections::HashMap; -use std::str::FromStr; use syn::{ parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, @@ -40,10 +40,10 @@ pub(crate) struct DiagnosticDeriveBuilder { pub kind: DiagnosticDeriveKind, /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - pub slug: Option<(Path, proc_macro::Span)>, + pub slug: SpannedOption, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. - pub code: Option<(String, proc_macro::Span)>, + pub code: SpannedOption<()>, } impl HasFieldMap for DiagnosticDeriveBuilder { @@ -127,6 +127,30 @@ impl DiagnosticDeriveBuilder { || is_subdiagnostic } + fn parse_subdiag_attribute( + &self, + attr: &Attribute, + ) -> Result<(SubdiagnosticKind, Path), DiagnosticDeriveError> { + let (subdiag, slug) = SubdiagnosticKind::from_attr(attr, self)?; + + if let SubdiagnosticKind::MultipartSuggestion { .. } = subdiag { + let meta = attr.parse_meta()?; + throw_invalid_attr!(attr, &meta, |diag| diag + .help("consider creating a `Subdiagnostic` instead")); + } + + let slug = slug.unwrap_or_else(|| match subdiag { + SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, + SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, + SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, + SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, + SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, + SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + }); + + Ok((subdiag, slug)) + } + /// Establishes state in the `DiagnosticDeriveBuilder` resulting from the struct /// attributes like `#[diag(..)]`, such as the slug and error code. Generates /// diagnostic builder calls for setting error code and creating note/help messages. @@ -135,98 +159,64 @@ impl DiagnosticDeriveBuilder { attr: &Attribute, ) -> Result { let diag = &self.diag; - let span = attr.span().unwrap(); let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); let meta = attr.parse_meta()?; - let is_diag = name == "diag"; - - let nested = match meta { - // Most attributes are lists, like `#[diag(..)]` for most cases or - // `#[help(..)]`/`#[note(..)]` when the user is specifying a alternative slug. - Meta::List(MetaList { ref nested, .. }) => nested, - // Subdiagnostics without spans can be applied to the type too, and these are just - // paths: `#[help]`, `#[note]` and `#[warning]` - Meta::Path(_) if !is_diag => { - let fn_name = if name == "warning" { - Ident::new("warn", attr.span()) - } else { - Ident::new(name, attr.span()) - }; - return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::_subdiag::#fn_name); }); - } - _ => throw_invalid_attr!(attr, &meta), - }; - - // Check the kind before doing any further processing so that there aren't misleading - // "no kind specified" errors if there are failures later. - match name { - "error" | "lint" => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("`error` and `lint` have been replaced by `diag`") - }), - "warn_" => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("`warn_` have been replaced by `warning`") - }), - "diag" | "help" | "note" | "warning" => (), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help("only `diag`, `help`, `note` and `warning` are valid attributes") - }), - } + if name == "diag" { + let Meta::List(MetaList { ref nested, .. }) = meta else { + throw_invalid_attr!( + attr, + &meta + ); + }; - // First nested element should always be the path, e.g. `#[diag(typeck::invalid)]` or - // `#[help(typeck::another_help)]`. - let mut nested_iter = nested.into_iter(); - if let Some(nested_attr) = nested_iter.next() { - // Report an error if there are any other list items after the path. - if !is_diag && nested_iter.next().is_some() { - throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "`help`, `note` and `warning` struct attributes can only have one argument", - ) - }); - } + let mut nested_iter = nested.into_iter().peekable(); - match nested_attr { - NestedMeta::Meta(Meta::Path(path)) => { - if is_diag { - self.slug.set_once((path.clone(), span)); - } else { - let fn_name = proc_macro2::Ident::new(name, attr.span()); - return Ok(quote! { #diag.#fn_name(rustc_errors::fluent::#path); }); - } - } - NestedMeta::Meta(meta @ Meta::NameValue(_)) - if is_diag && meta.path().segments.last().unwrap().ident == "code" => - { - // don't error for valid follow-up attributes + match nested_iter.peek() { + Some(NestedMeta::Meta(Meta::Path(slug))) => { + self.slug.set_once(slug.clone(), slug.span().unwrap()); + nested_iter.next(); } - nested_attr => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("first argument of the attribute should be the diagnostic slug") - }), + Some(NestedMeta::Meta(Meta::NameValue { .. })) => {} + Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| diag + .help("a diagnostic slug is required as the first argument")), + None => throw_invalid_attr!(attr, &meta, |diag| diag + .help("a diagnostic slug is required as the first argument")), }; - } - // Remaining attributes are optional, only `code = ".."` at the moment. - let mut tokens = Vec::new(); - for nested_attr in nested_iter { - let meta = match nested_attr { - syn::NestedMeta::Meta(meta) => meta, - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; + // Remaining attributes are optional, only `code = ".."` at the moment. + let mut tokens = TokenStream::new(); + for nested_attr in nested_iter { + let (value, path) = match nested_attr { + NestedMeta::Meta(Meta::NameValue(MetaNameValue { + lit: syn::Lit::Str(value), + path, + .. + })) => (value, path), + NestedMeta::Meta(Meta::Path(_)) => { + invalid_nested_attr(attr, &nested_attr) + .help("diagnostic slug must be the first argument") + .emit(); + continue; + } + _ => { + invalid_nested_attr(attr, &nested_attr).emit(); + continue; + } + }; - let path = meta.path(); - let nested_name = path.segments.last().unwrap().ident.to_string(); - // Struct attributes are only allowed to be applied once, and the diagnostic - // changes will be set in the initialisation code. - if let Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) = &meta { - let span = s.span().unwrap(); + let nested_name = path.segments.last().unwrap().ident.to_string(); + // Struct attributes are only allowed to be applied once, and the diagnostic + // changes will be set in the initialisation code. + let span = value.span().unwrap(); match nested_name.as_str() { "code" => { - self.code.set_once((s.value(), span)); - let code = &self.code.as_ref().map(|(v, _)| v); - tokens.push(quote! { + self.code.set_once((), span); + + let code = value.value(); + tokens.extend(quote! { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); }); } @@ -234,12 +224,22 @@ impl DiagnosticDeriveBuilder { .help("only `code` is a valid nested attributes following the slug") .emit(), } - } else { - invalid_nested_attr(attr, &nested_attr).emit() } + return Ok(tokens); } - Ok(tokens.into_iter().collect()) + let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; + let fn_ident = format_ident!("{}", subdiag); + match subdiag { + SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { + Ok(self.add_subdiagnostic(&fn_ident, slug)) + } + SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { + throw_invalid_attr!(attr, &meta, |diag| diag + .help("`#[label]` and `#[suggestion]` can only be applied to fields")); + } + SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + } } fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { @@ -303,232 +303,83 @@ impl DiagnosticDeriveBuilder { info: FieldInfo<'_>, binding: TokenStream, ) -> Result { - let meta = attr.parse_meta()?; - match meta { - Meta::Path(_) => self.generate_inner_field_code_path(attr, info, binding), - Meta::List(MetaList { .. }) => self.generate_inner_field_code_list(attr, info, binding), - _ => throw_invalid_attr!(attr, &meta), - } - } - - fn generate_inner_field_code_path( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - binding: TokenStream, - ) -> Result { - assert!(matches!(attr.parse_meta()?, Meta::Path(_))); let diag = &self.diag; - let meta = attr.parse_meta()?; - let ident = &attr.path.segments.last().unwrap().ident; - let name = ident.to_string(); - let name = name.as_str(); - match name { - "skip_arg" => { - // Don't need to do anything - by virtue of the attribute existing, the - // `set_arg` call will not be generated. - Ok(quote! {}) - } - "primary_span" => { - match self.kind { + if let Meta::Path(_) = meta { + let ident = &attr.path.segments.last().unwrap().ident; + let name = ident.to_string(); + let name = name.as_str(); + match name { + "skip_arg" => { + // Don't need to do anything - by virtue of the attribute existing, the + // `set_arg` call will not be generated. + return Ok(quote! {}); + } + "primary_span" => match self.kind { DiagnosticDeriveKind::Diagnostic => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(quote! { + return Ok(quote! { #diag.set_span(#binding); - }) + }); } DiagnosticDeriveKind::LintDiagnostic => { throw_invalid_attr!(attr, &meta, |diag| { diag.help("the `primary_span` field attribute is not valid for lint diagnostics") }) } - } + }, + "subdiagnostic" => return Ok(quote! { #diag.subdiagnostic(#binding); }), + _ => {} } - "label" => { + } + + let (subdiag, slug) = self.parse_subdiag_attribute(attr)?; + + let fn_ident = format_ident!("{}", subdiag); + match subdiag { + SubdiagnosticKind::Label => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, ident, parse_quote! { _subdiag::label })) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) } - "note" | "help" | "warning" => { - let warn_ident = Ident::new("warn", Span::call_site()); - let (ident, path) = match name { - "note" => (ident, parse_quote! { _subdiag::note }), - "help" => (ident, parse_quote! { _subdiag::help }), - "warning" => (&warn_ident, parse_quote! { _subdiag::warn }), - _ => unreachable!(), - }; + SubdiagnosticKind::Note | SubdiagnosticKind::Help | SubdiagnosticKind::Warn => { if type_matches_path(&info.ty, &["rustc_span", "Span"]) { - Ok(self.add_spanned_subdiagnostic(binding, ident, path)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) } else if type_is_unit(&info.ty) { - Ok(self.add_subdiagnostic(ident, path)) + Ok(self.add_subdiagnostic(&fn_ident, slug)) } else { report_type_error(attr, "`Span` or `()`")? } } - "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(#binding); }), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` \ - are valid field attributes", - ) - }), - } - } - - fn generate_inner_field_code_list( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - binding: TokenStream, - ) -> Result { - let meta = attr.parse_meta()?; - let Meta::List(MetaList { ref path, ref nested, .. }) = meta else { unreachable!() }; - - let ident = &attr.path.segments.last().unwrap().ident; - let name = path.segments.last().unwrap().ident.to_string(); - let name = name.as_ref(); - match name { - "suggestion" | "suggestion_short" | "suggestion_hidden" | "suggestion_verbose" => { - return self.generate_inner_field_code_suggestion(attr, info); - } - "label" | "help" | "note" | "warning" => (), - _ => throw_invalid_attr!(attr, &meta, |diag| { - diag.help( - "only `label`, `help`, `note`, `warn` or `suggestion{,_short,_hidden,_verbose}` are \ - valid field attributes", - ) - }), - } - - // For `#[label(..)]`, `#[note(..)]` and `#[help(..)]`, the first nested element must be a - // path, e.g. `#[label(typeck::label)]`. - let mut nested_iter = nested.into_iter(); - let msg = match nested_iter.next() { - Some(NestedMeta::Meta(Meta::Path(path))) => path.clone(), - Some(nested_attr) => throw_invalid_nested_attr!(attr, &nested_attr), - None => throw_invalid_attr!(attr, &meta), - }; - - // None of these attributes should have anything following the slug. - if nested_iter.next().is_some() { - throw_invalid_attr!(attr, &meta); - } - - match name { - "label" => { - report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) - } - "note" | "help" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => { - Ok(self.add_spanned_subdiagnostic(binding, ident, msg)) - } - "note" | "help" if type_is_unit(&info.ty) => Ok(self.add_subdiagnostic(ident, msg)), - // `warning` must be special-cased because the attribute `warn` already has meaning and - // so isn't used, despite the diagnostic API being named `warn`. - "warning" if type_matches_path(&info.ty, &["rustc_span", "Span"]) => Ok(self - .add_spanned_subdiagnostic(binding, &Ident::new("warn", Span::call_site()), msg)), - "warning" if type_is_unit(&info.ty) => { - Ok(self.add_subdiagnostic(&Ident::new("warn", Span::call_site()), msg)) - } - "note" | "help" | "warning" => report_type_error(attr, "`Span` or `()`")?, - _ => unreachable!(), - } - } - - fn generate_inner_field_code_suggestion( - &mut self, - attr: &Attribute, - info: FieldInfo<'_>, - ) -> Result { - let diag = &self.diag; - - let mut meta = attr.parse_meta()?; - let Meta::List(MetaList { ref path, ref mut nested, .. }) = meta else { unreachable!() }; - - let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; - - let mut msg = None; - let mut code = None; - - let mut nested_iter = nested.into_iter().peekable(); - if let Some(nested_attr) = nested_iter.peek() { - if let NestedMeta::Meta(Meta::Path(path)) = nested_attr { - msg = Some(path.clone()); - } - }; - // Move the iterator forward if a path was found (don't otherwise so that - // code/applicability can be found or an error emitted). - if msg.is_some() { - let _ = nested_iter.next(); - } - - for nested_attr in nested_iter { - let meta = match nested_attr { - syn::NestedMeta::Meta(ref meta) => meta, - syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr), - }; - - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => { - let span = meta.span().unwrap(); - match nested_name { - "code" => { - let formatted_str = self.build_format(&s.value(), s.span()); - code = Some(formatted_str); - } - "applicability" => { - applicability = match applicability { - Some(v) => { - span_err( - span, - "applicability cannot be set in both the field and \ - attribute", - ) - .emit(); - Some(v) - } - None => match Applicability::from_str(&s.value()) { - Ok(v) => Some(quote! { #v }), - Err(()) => { - span_err(span, "invalid applicability").emit(); - None - } - }, - } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "only `message`, `code` and `applicability` are valid field \ - attributes", - ) - }), - } + SubdiagnosticKind::Suggestion { + suggestion_kind, + applicability: static_applicability, + code, + } => { + let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?; + + if let Some((static_applicability, span)) = static_applicability { + applicability.set_once(quote! { #static_applicability }, span); } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - if matches!(meta, Meta::Path(_)) { - diag.help("a diagnostic slug must be the first argument to the attribute") - } else { - diag - } - }), + + let applicability = applicability + .value() + .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); + let style = suggestion_kind.to_suggestion_style(); + + Ok(quote! { + #diag.span_suggestion_with_style( + #span_field, + rustc_errors::fluent::#slug, + #code, + #applicability, + #style + ); + }) } + SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), } - - let applicability = - applicability.unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); - - let name = path.segments.last().unwrap().ident.to_string(); - let method = format_ident!("span_{}", name); - - let msg = msg.unwrap_or_else(|| parse_quote! { _subdiag::suggestion }); - let msg = quote! { rustc_errors::fluent::#msg }; - let code = code.unwrap_or_else(|| quote! { String::new() }); - - Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); }) } /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug @@ -561,7 +412,7 @@ impl DiagnosticDeriveBuilder { fn span_and_applicability_of_ty( &self, info: FieldInfo<'_>, - ) -> Result<(TokenStream, Option), DiagnosticDeriveError> { + ) -> Result<(TokenStream, SpannedOption), DiagnosticDeriveError> { match &info.ty { // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`. ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => { @@ -573,46 +424,37 @@ impl DiagnosticDeriveBuilder { let mut span_idx = None; let mut applicability_idx = None; + fn type_err(span: &Span) -> Result { + span_err(span.unwrap(), "wrong types for suggestion") + .help( + "`#[suggestion(...)]` on a tuple field must be applied to fields \ + of type `(Span, Applicability)`", + ) + .emit(); + Err(DiagnosticDeriveError::ErrorHandled) + } + for (idx, elem) in tup.elems.iter().enumerate() { if type_matches_path(elem, &["rustc_span", "Span"]) { - if span_idx.is_none() { - span_idx = Some(syn::Index::from(idx)); - } else { - throw_span_err!( - info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more \ - than one `Span`" - ); - } + span_idx.set_once(syn::Index::from(idx), elem.span().unwrap()); } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) { - if applicability_idx.is_none() { - applicability_idx = Some(syn::Index::from(idx)); - } else { - throw_span_err!( - info.span.unwrap(), - "type of field annotated with `#[suggestion(...)]` contains more \ - than one Applicability" - ); - } + applicability_idx.set_once(syn::Index::from(idx), elem.span().unwrap()); + } else { + type_err(&elem.span())?; } } - if let Some(span_idx) = span_idx { - let binding = &info.binding.binding; - let span = quote!(#binding.#span_idx); - let applicability = applicability_idx - .map(|applicability_idx| quote!(#binding.#applicability_idx)) - .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified)); - - return Ok((span, Some(applicability))); - } + let Some((span_idx, _)) = span_idx else { + type_err(&tup.span())?; + }; + let Some((applicability_idx, applicability_span)) = applicability_idx else { + type_err(&tup.span())?; + }; + let binding = &info.binding.binding; + let span = quote!(#binding.#span_idx); + let applicability = quote!(#binding.#applicability_idx); - throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| { - diag.help( - "`#[suggestion(...)]` on a tuple field must be applied to fields of type \ - `(Span, Applicability)`", - ) - }); + Ok((span, Some((applicability, applicability_span)))) } // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error. _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| { diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index bdeca3420bcca..6545ae086b158 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -4,98 +4,17 @@ use crate::diagnostics::error::{ span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError, }; use crate::diagnostics::utils::{ - report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, - Applicability, FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, + report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, FieldInfo, + FieldInnerTy, HasFieldMap, SetOnce, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; use std::collections::HashMap; -use std::fmt; -use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; -/// Which kind of suggestion is being created? -#[derive(Clone, Copy)] -enum SubdiagnosticSuggestionKind { - /// `#[suggestion]` - Normal, - /// `#[suggestion_short]` - Short, - /// `#[suggestion_hidden]` - Hidden, - /// `#[suggestion_verbose]` - Verbose, -} - -impl FromStr for SubdiagnosticSuggestionKind { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "" => Ok(SubdiagnosticSuggestionKind::Normal), - "_short" => Ok(SubdiagnosticSuggestionKind::Short), - "_hidden" => Ok(SubdiagnosticSuggestionKind::Hidden), - "_verbose" => Ok(SubdiagnosticSuggestionKind::Verbose), - _ => Err(()), - } - } -} - -impl SubdiagnosticSuggestionKind { - pub fn to_suggestion_style(&self) -> TokenStream { - match self { - SubdiagnosticSuggestionKind::Normal => { - quote! { rustc_errors::SuggestionStyle::ShowCode } - } - SubdiagnosticSuggestionKind::Short => { - quote! { rustc_errors::SuggestionStyle::HideCodeInline } - } - SubdiagnosticSuggestionKind::Hidden => { - quote! { rustc_errors::SuggestionStyle::HideCodeAlways } - } - SubdiagnosticSuggestionKind::Verbose => { - quote! { rustc_errors::SuggestionStyle::ShowAlways } - } - } - } -} - -/// Which kind of subdiagnostic is being created from a variant? -#[derive(Clone)] -enum SubdiagnosticKind { - /// `#[label(...)]` - Label, - /// `#[note(...)]` - Note, - /// `#[help(...)]` - Help, - /// `#[warning(...)]` - Warn, - /// `#[suggestion{,_short,_hidden,_verbose}]` - Suggestion { suggestion_kind: SubdiagnosticSuggestionKind, code: TokenStream }, - /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` - MultipartSuggestion { suggestion_kind: SubdiagnosticSuggestionKind }, -} - -impl quote::IdentFragment for SubdiagnosticKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - SubdiagnosticKind::Label => write!(f, "label"), - SubdiagnosticKind::Note => write!(f, "note"), - SubdiagnosticKind::Help => write!(f, "help"), - SubdiagnosticKind::Warn => write!(f, "warn"), - SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), - SubdiagnosticKind::MultipartSuggestion { .. } => { - write!(f, "multipart_suggestion_with_style") - } - } - } - - fn span(&self) -> Option { - None - } -} +use super::error::invalid_attr; +use super::utils::{SpannedOption, SubdiagnosticKind}; /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. pub(crate) struct SubdiagnosticDerive<'a> { @@ -195,10 +114,10 @@ struct SubdiagnosticDeriveBuilder<'a> { fields: HashMap, /// Identifier for the binding to the `#[primary_span]` field. - span_field: Option<(proc_macro2::Ident, proc_macro::Span)>, - /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a - /// `rustc_errors::Applicability::*` variant directly. - applicability: Option<(TokenStream, proc_macro::Span)>, + span_field: SpannedOption, + + /// The binding to the `#[applicability]` field, if present. + applicability: SpannedOption, /// Set to true when a `#[suggestion_part]` field is encountered, used to generate an error /// during finalization if still `false`. @@ -217,6 +136,7 @@ struct KindsStatistics { has_multipart_suggestion: bool, all_multipart_suggestions: bool, has_normal_suggestion: bool, + all_applicabilities_static: bool, } impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { @@ -225,8 +145,15 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { has_multipart_suggestion: false, all_multipart_suggestions: true, has_normal_suggestion: false, + all_applicabilities_static: true, }; + for kind in kinds { + if let SubdiagnosticKind::MultipartSuggestion { applicability: None, .. } + | SubdiagnosticKind::Suggestion { applicability: None, .. } = kind + { + ret.all_applicabilities_static = false; + } if let SubdiagnosticKind::MultipartSuggestion { .. } = kind { ret.has_multipart_suggestion = true; } else { @@ -246,129 +173,14 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { let mut kind_slugs = vec![]; for attr in self.variant.ast().attrs { - let span = attr.span().unwrap(); - - let name = attr.path.segments.last().unwrap().ident.to_string(); - let name = name.as_str(); - - let meta = attr.parse_meta()?; - let Meta::List(MetaList { ref nested, .. }) = meta else { - throw_invalid_attr!(attr, &meta); - }; - - let mut kind = match name { - "label" => SubdiagnosticKind::Label, - "note" => SubdiagnosticKind::Note, - "help" => SubdiagnosticKind::Help, - "warning" => SubdiagnosticKind::Warn, - _ => { - if let Some(suggestion_kind) = - name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) - { - SubdiagnosticKind::Suggestion { suggestion_kind, code: TokenStream::new() } - } else if let Some(suggestion_kind) = - name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) - { - SubdiagnosticKind::MultipartSuggestion { suggestion_kind } - } else { - throw_invalid_attr!(attr, &meta); - } - } - }; + let (kind, slug) = SubdiagnosticKind::from_attr(attr, self)?; - let mut slug = None; - let mut code = None; + let Some(slug) = slug else { + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); - let mut nested_iter = nested.into_iter(); - if let Some(nested_attr) = nested_iter.next() { - match nested_attr { - NestedMeta::Meta(Meta::Path(path)) => { - slug.set_once((path.clone(), span)); - } - NestedMeta::Meta(meta @ Meta::NameValue(_)) - if matches!( - meta.path().segments.last().unwrap().ident.to_string().as_str(), - "code" | "applicability" - ) => - { - // Don't error for valid follow-up attributes. - } - nested_attr => { - throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help( - "first argument of the attribute should be the diagnostic \ - slug", - ) - }) - } - }; - } - - for nested_attr in nested_iter { - let meta = match nested_attr { - NestedMeta::Meta(ref meta) => meta, - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; - - let span = meta.span().unwrap(); - let nested_name = meta.path().segments.last().unwrap().ident.to_string(); - let nested_name = nested_name.as_str(); - - let value = match meta { - Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, - Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("a diagnostic slug must be the first argument to the attribute") - }), - _ => throw_invalid_nested_attr!(attr, &nested_attr), - }; - - match nested_name { - "code" => { - if matches!(kind, SubdiagnosticKind::Suggestion { .. }) { - let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once((formatted_str, span)); - } else { - span_err( - span, - &format!( - "`code` is not a valid nested attribute of a `{}` attribute", - name - ), - ) - .emit(); - } - } - "applicability" => { - if matches!( - kind, - SubdiagnosticKind::Suggestion { .. } - | SubdiagnosticKind::MultipartSuggestion { .. } - ) { - let value = - Applicability::from_str(&value.value()).unwrap_or_else(|()| { - span_err(span, "invalid applicability").emit(); - Applicability::Unspecified - }); - self.applicability.set_once((quote! { #value }, span)); - } else { - span_err( - span, - &format!( - "`applicability` is not a valid nested attribute of a `{}` attribute", - name - ) - ).emit(); - } - } - _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { - diag.help("only `code` and `applicability` are valid nested attributes") - }), - } - } - - let Some((slug, _)) = slug else { throw_span_err!( - span, + attr.span().unwrap(), &format!( "diagnostic slug must be first argument of a `#[{}(...)]` attribute", name @@ -376,21 +188,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { ); }; - match kind { - SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { - let Some((code, _)) = code else { - throw_span_err!(span, "suggestion without `code = \"...\"`"); - }; - *code_field = code; - } - SubdiagnosticKind::Label - | SubdiagnosticKind::Note - | SubdiagnosticKind::Help - | SubdiagnosticKind::Warn - | SubdiagnosticKind::MultipartSuggestion { .. } => {} - } - - kind_slugs.push((kind, slug)) + kind_slugs.push((kind, slug)); } Ok(kind_slugs) @@ -474,18 +272,18 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { "skip_arg" => Ok(quote! {}), "primary_span" => { if kind_stats.has_multipart_suggestion { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - diag.help( + invalid_attr(attr, &Meta::Path(path)) + .help( "multipart suggestions use one or more `#[suggestion_part]`s rather \ than one `#[primary_span]`", ) - }) - } - - report_error_if_not_applied_to_span(attr, &info)?; + .emit(); + } else { + report_error_if_not_applied_to_span(attr, &info)?; - let binding = info.binding.binding.clone(); - self.span_field.set_once((binding, span)); + let binding = info.binding.binding.clone(); + self.span_field.set_once(binding, span); + } Ok(quote! {}) } @@ -495,28 +293,39 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { if kind_stats.has_multipart_suggestion { span_err(span, "`#[suggestion_part(...)]` attribute without `code = \"...\"`") .emit(); - Ok(quote! {}) } else { - throw_invalid_attr!(attr, &Meta::Path(path), |diag| { - diag.help( - "`#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead", - ) - }); + invalid_attr(attr, &Meta::Path(path)) + .help( + "`#[suggestion_part(...)]` is only valid in multipart suggestions, \ + use `#[primary_span]` instead", + ) + .emit(); } + + Ok(quote! {}) } "applicability" => { if kind_stats.has_multipart_suggestion || kind_stats.has_normal_suggestion { report_error_if_not_applied_to_applicability(attr, &info)?; + if kind_stats.all_applicabilities_static { + span_err( + span, + "`#[applicability]` has no effect if all `#[suggestion]`/\ + `#[multipart_suggestion]` attributes have a static \ + `applicability = \"...\"`", + ) + .emit(); + } let binding = info.binding.binding.clone(); - self.applicability.set_once((quote! { #binding }, span)); + self.applicability.set_once(quote! { #binding }, span); } else { span_err(span, "`#[applicability]` is only valid on suggestions").emit(); } Ok(quote! {}) } - _ => throw_invalid_attr!(attr, &Meta::Path(path), |diag| { + _ => { let mut span_attrs = vec![]; if kind_stats.has_multipart_suggestion { span_attrs.push("suggestion_part"); @@ -524,11 +333,16 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { if !kind_stats.all_multipart_suggestions { span_attrs.push("primary_span") } - diag.help(format!( - "only `{}`, `applicability` and `skip_arg` are valid field attributes", - span_attrs.join(", ") - )) - }), + + invalid_attr(attr, &Meta::Path(path)) + .help(format!( + "only `{}`, `applicability` and `skip_arg` are valid field attributes", + span_attrs.join(", ") + )) + .emit(); + + Ok(quote! {}) + } } } @@ -577,7 +391,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { match nested_name { "code" => { let formatted_str = self.build_format(&value.value(), value.span()); - code.set_once((formatted_str, span)); + code.set_once(formatted_str, span); } _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { diag.help("`code` is the only valid nested attribute") @@ -635,11 +449,7 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { .map(|binding| self.generate_field_attr_code(binding, kind_stats)) .collect(); - let span_field = self.span_field.as_ref().map(|(span, _)| span); - let applicability = self.applicability.take().map_or_else( - || quote! { rustc_errors::Applicability::Unspecified }, - |(applicability, _)| applicability, - ); + let span_field = self.span_field.value_ref(); let diag = &self.diag; let mut calls = TokenStream::new(); @@ -647,7 +457,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let message = quote! { rustc_errors::fluent::#slug }; let call = match kind { - SubdiagnosticKind::Suggestion { suggestion_kind, code } => { + SubdiagnosticKind::Suggestion { suggestion_kind, applicability, code } => { + let applicability = applicability + .value() + .map(|a| quote! { #a }) + .or_else(|| self.applicability.take().value()) + .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); + if let Some(span) = span_field { let style = suggestion_kind.to_suggestion_style(); @@ -657,7 +473,13 @@ impl<'a> SubdiagnosticDeriveBuilder<'a> { quote! { unreachable!(); } } } - SubdiagnosticKind::MultipartSuggestion { suggestion_kind } => { + SubdiagnosticKind::MultipartSuggestion { suggestion_kind, applicability } => { + let applicability = applicability + .value() + .map(|a| quote! { #a }) + .or_else(|| self.applicability.take().value()) + .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); + if !self.has_suggestion_parts { span_err( self.span, diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index ad9ecd39b9e85..a31bda9ca0def 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -1,12 +1,18 @@ -use crate::diagnostics::error::{span_err, throw_span_err, DiagnosticDeriveError}; +use crate::diagnostics::error::{ + span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError, +}; use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; use std::collections::{BTreeSet, HashMap}; +use std::fmt; use std::str::FromStr; use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple}; +use syn::{MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure}; +use super::error::invalid_nested_attr; + /// Checks whether the type name of `ty` matches `name`. /// /// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or @@ -172,13 +178,17 @@ pub(crate) struct FieldInfo<'a> { /// Small helper trait for abstracting over `Option` fields that contain a value and a `Span` /// for error reporting if they are set more than once. pub(crate) trait SetOnce { - fn set_once(&mut self, _: (T, Span)); + fn set_once(&mut self, value: T, span: Span); fn value(self) -> Option; + fn value_ref(&self) -> Option<&T>; } -impl SetOnce for Option<(T, Span)> { - fn set_once(&mut self, (value, span): (T, Span)) { +/// An [`Option`] that keeps track of the span that caused it to be set; used with [`SetOnce`]. +pub(super) type SpannedOption = Option<(T, Span)>; + +impl SetOnce for SpannedOption { + fn set_once(&mut self, value: T, span: Span) { match self { None => { *self = Some((value, span)); @@ -194,6 +204,10 @@ impl SetOnce for Option<(T, Span)> { fn value(self) -> Option { self.map(|(v, _)| v) } + + fn value_ref(&self) -> Option<&T> { + self.as_ref().map(|(v, _)| v) + } } pub(crate) trait HasFieldMap { @@ -303,6 +317,7 @@ pub(crate) trait HasFieldMap { /// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent /// the user's selection of applicability if specified in an attribute. +#[derive(Clone, Copy)] pub(crate) enum Applicability { MachineApplicable, MaybeIncorrect, @@ -359,3 +374,250 @@ pub(crate) fn build_field_mapping<'a>(structure: &Structure<'a>) -> HashMap Result { + match s { + "" => Ok(SuggestionKind::Normal), + "_short" => Ok(SuggestionKind::Short), + "_hidden" => Ok(SuggestionKind::Hidden), + "_verbose" => Ok(SuggestionKind::Verbose), + _ => Err(()), + } + } +} + +impl SuggestionKind { + pub fn to_suggestion_style(&self) -> TokenStream { + match self { + SuggestionKind::Normal => { + quote! { rustc_errors::SuggestionStyle::ShowCode } + } + SuggestionKind::Short => { + quote! { rustc_errors::SuggestionStyle::HideCodeInline } + } + SuggestionKind::Hidden => { + quote! { rustc_errors::SuggestionStyle::HideCodeAlways } + } + SuggestionKind::Verbose => { + quote! { rustc_errors::SuggestionStyle::ShowAlways } + } + } + } +} + +/// Types of subdiagnostics that can be created using attributes +#[derive(Clone)] +pub(super) enum SubdiagnosticKind { + /// `#[label(...)]` + Label, + /// `#[note(...)]` + Note, + /// `#[help(...)]` + Help, + /// `#[warning(...)]` + Warn, + /// `#[suggestion{,_short,_hidden,_verbose}]` + Suggestion { + suggestion_kind: SuggestionKind, + applicability: SpannedOption, + code: TokenStream, + }, + /// `#[multipart_suggestion{,_short,_hidden,_verbose}]` + MultipartSuggestion { + suggestion_kind: SuggestionKind, + applicability: SpannedOption, + }, +} + +impl SubdiagnosticKind { + /// Constructs a `SubdiagnosticKind` from a field or type attribute such as `#[note]`, + /// `#[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, + ) -> Result<(SubdiagnosticKind, Option), DiagnosticDeriveError> { + let span = attr.span().unwrap(); + + let name = attr.path.segments.last().unwrap().ident.to_string(); + let name = name.as_str(); + + let meta = attr.parse_meta()?; + let mut kind = match name { + "label" => SubdiagnosticKind::Label, + "note" => SubdiagnosticKind::Note, + "help" => SubdiagnosticKind::Help, + "warning" => SubdiagnosticKind::Warn, + _ => { + if let Some(suggestion_kind) = + name.strip_prefix("suggestion").and_then(|s| s.parse().ok()) + { + SubdiagnosticKind::Suggestion { + suggestion_kind, + applicability: None, + code: TokenStream::new(), + } + } else if let Some(suggestion_kind) = + name.strip_prefix("multipart_suggestion").and_then(|s| s.parse().ok()) + { + SubdiagnosticKind::MultipartSuggestion { suggestion_kind, applicability: None } + } else { + throw_invalid_attr!(attr, &meta); + } + } + }; + + let nested = match meta { + Meta::List(MetaList { ref nested, .. }) => { + // An attribute with properties, such as `#[suggestion(code = "...")]` or + // `#[error(some::slug)]` + nested + } + Meta::Path(_) => { + // An attribute without a slug or other properties, such as `#[note]` - return + // without further processing. + // + // Only allow this if there are no mandatory properties, such as `code = "..."` in + // `#[suggestion(...)]` + match kind { + SubdiagnosticKind::Label + | SubdiagnosticKind::Note + | SubdiagnosticKind::Help + | SubdiagnosticKind::Warn + | SubdiagnosticKind::MultipartSuggestion { .. } => return Ok((kind, None)), + SubdiagnosticKind::Suggestion { .. } => { + throw_span_err!(span, "suggestion without `code = \"...\"`") + } + } + } + _ => { + throw_invalid_attr!(attr, &meta) + } + }; + + let mut code = None; + + let mut nested_iter = nested.into_iter().peekable(); + + // Peek at the first nested attribute: if it's a slug path, consume it. + let slug = if let Some(NestedMeta::Meta(Meta::Path(path))) = nested_iter.peek() { + let path = path.clone(); + // Advance the iterator. + nested_iter.next(); + Some(path) + } else { + None + }; + + for nested_attr in nested_iter { + let meta = match nested_attr { + NestedMeta::Meta(ref meta) => meta, + NestedMeta::Lit(_) => { + invalid_nested_attr(attr, &nested_attr).emit(); + continue; + } + }; + + let span = meta.span().unwrap(); + let nested_name = meta.path().segments.last().unwrap().ident.to_string(); + let nested_name = nested_name.as_str(); + + let value = match meta { + Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(value), .. }) => value, + Meta::Path(_) => throw_invalid_nested_attr!(attr, &nested_attr, |diag| { + diag.help("a diagnostic slug must be the first argument to the attribute") + }), + _ => { + invalid_nested_attr(attr, &nested_attr).emit(); + continue; + } + }; + + match (nested_name, &mut kind) { + ("code", SubdiagnosticKind::Suggestion { .. }) => { + let formatted_str = fields.build_format(&value.value(), value.span()); + code.set_once(formatted_str, span); + } + ( + "applicability", + SubdiagnosticKind::Suggestion { ref mut applicability, .. } + | SubdiagnosticKind::MultipartSuggestion { ref mut applicability, .. }, + ) => { + let value = Applicability::from_str(&value.value()).unwrap_or_else(|()| { + span_err(span, "invalid applicability").emit(); + Applicability::Unspecified + }); + applicability.set_once(value, span); + } + + // Invalid nested attribute + (_, SubdiagnosticKind::Suggestion { .. }) => { + invalid_nested_attr(attr, &nested_attr) + .help("only `code` and `applicability` are valid nested attributes") + .emit(); + } + (_, SubdiagnosticKind::MultipartSuggestion { .. }) => { + invalid_nested_attr(attr, &nested_attr) + .help("only `applicability` is a valid nested attributes") + .emit() + } + _ => { + invalid_nested_attr(attr, &nested_attr).emit(); + } + } + } + + match kind { + SubdiagnosticKind::Suggestion { code: ref mut code_field, .. } => { + *code_field = if let Some((code, _)) = code { + code + } else { + span_err(span, "suggestion without `code = \"...\"`").emit(); + quote! { "" } + } + } + SubdiagnosticKind::Label + | SubdiagnosticKind::Note + | SubdiagnosticKind::Help + | SubdiagnosticKind::Warn + | SubdiagnosticKind::MultipartSuggestion { .. } => {} + } + + Ok((kind, slug)) + } +} + +impl quote::IdentFragment for SubdiagnosticKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SubdiagnosticKind::Label => write!(f, "label"), + SubdiagnosticKind::Note => write!(f, "note"), + SubdiagnosticKind::Help => write!(f, "help"), + SubdiagnosticKind::Warn => write!(f, "warn"), + SubdiagnosticKind::Suggestion { .. } => write!(f, "suggestion_with_style"), + SubdiagnosticKind::MultipartSuggestion { .. } => { + write!(f, "multipart_suggestion_with_style") + } + } + } + + fn span(&self) -> Option { + None + } +} diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index dcea11eadcbf1..0a81cde93be9c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -289,7 +289,7 @@ pub enum BadTypePlusSub { #[diag(parser::maybe_recover_from_bad_qpath_stage_2)] struct BadQPathStage2 { #[primary_span] - #[suggestion(applicability = "maybe-incorrect")] + #[suggestion(code = "", applicability = "maybe-incorrect")] span: Span, ty: String, } @@ -298,7 +298,7 @@ struct BadQPathStage2 { #[diag(parser::incorrect_semicolon)] struct IncorrectSemicolon<'a> { #[primary_span] - #[suggestion_short(applicability = "machine-applicable")] + #[suggestion_short(code = "", applicability = "machine-applicable")] span: Span, #[help] opt_help: Option<()>, @@ -309,7 +309,7 @@ struct IncorrectSemicolon<'a> { #[diag(parser::incorrect_use_of_await)] struct IncorrectUseOfAwait { #[primary_span] - #[suggestion(parser::parentheses_suggestion, applicability = "machine-applicable")] + #[suggestion(parser::parentheses_suggestion, code = "", applicability = "machine-applicable")] span: Span, } @@ -329,7 +329,7 @@ struct IncorrectAwait { struct InInTypo { #[primary_span] span: Span, - #[suggestion(applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable")] sugg_span: Span, } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index be67c9e3b82d9..de11bb8901013 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -462,7 +462,7 @@ pub struct LinkSection { pub struct NoMangleForeign { #[label] pub span: Span, - #[suggestion(applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable")] pub attr_span: Span, pub foreign_item_kind: &'static str, } @@ -596,7 +596,7 @@ pub enum UnusedNote { #[derive(LintDiagnostic)] #[diag(passes::unused)] pub struct Unused { - #[suggestion(applicability = "machine-applicable")] + #[suggestion(code = "", applicability = "machine-applicable")] pub attr_span: Span, #[subdiagnostic] pub note: UnusedNote, diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 80ea908288103..ad481c14bab8d 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -76,13 +76,15 @@ struct InvalidNestedStructAttr1 {} #[derive(Diagnostic)] #[diag(nonsense = "...", code = "E0123", slug = "foo")] //~^ ERROR `#[diag(nonsense = ...)]` is not a valid attribute -//~^^ ERROR diagnostic slug not specified +//~| ERROR `#[diag(slug = ...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified struct InvalidNestedStructAttr2 {} #[derive(Diagnostic)] #[diag(nonsense = 4, code = "E0123", slug = "foo")] //~^ ERROR `#[diag(nonsense = ...)]` is not a valid attribute -//~^^ ERROR diagnostic slug not specified +//~| ERROR `#[diag(slug = ...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified struct InvalidNestedStructAttr3 {} #[derive(Diagnostic)] @@ -217,6 +219,7 @@ struct Suggest { #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct SuggestWithoutCode { #[suggestion(typeck::suggestion)] + //~^ ERROR suggestion without `code = "..."` suggestion: (Span, Applicability), } @@ -225,6 +228,7 @@ struct SuggestWithoutCode { struct SuggestWithBadKey { #[suggestion(nonsense = "bar")] //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid attribute + //~| ERROR suggestion without `code = "..."` suggestion: (Span, Applicability), } @@ -233,6 +237,7 @@ struct SuggestWithBadKey { struct SuggestWithShorthandMsg { #[suggestion(msg = "bar")] //~^ ERROR `#[suggestion(msg = ...)]` is not a valid attribute + //~| ERROR suggestion without `code = "..."` suggestion: (Span, Applicability), } @@ -269,16 +274,16 @@ struct SuggestWithSpanOnly { #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct SuggestWithDuplicateSpanAndApplicability { #[suggestion(typeck::suggestion, code = "This is suggested code")] - //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one `Span` suggestion: (Span, Span, Applicability), + //~^ ERROR specified multiple times } #[derive(Diagnostic)] #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct SuggestWithDuplicateApplicabilityAndSpan { #[suggestion(typeck::suggestion, code = "This is suggested code")] - //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one suggestion: (Applicability, Applicability, Span), + //~^ ERROR specified multiple times } #[derive(Diagnostic)] @@ -294,7 +299,7 @@ struct WrongKindOfAnnotation { struct OptionsInErrors { #[label(typeck::label)] label: Option, - #[suggestion(typeck::suggestion)] + #[suggestion(typeck::suggestion, code = "...")] opt_sugg: Option<(Span, Applicability)>, } @@ -436,7 +441,7 @@ struct ErrorWithNoteCustomWrongOrder { #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct ApplicabilityInBoth { #[suggestion(typeck::suggestion, code = "...", applicability = "maybe-incorrect")] - //~^ ERROR applicability cannot be set in both the field and attribute + //~^ ERROR specified multiple times suggestion: (Span, Applicability), } @@ -507,7 +512,7 @@ struct OptUnitField { #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct LabelWithTrailingPath { #[label(typeck::label, foo)] - //~^ ERROR `#[label(...)]` is not a valid attribute + //~^ ERROR `#[label(foo)]` is not a valid attribute span: Span, } @@ -515,7 +520,7 @@ struct LabelWithTrailingPath { #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct LabelWithTrailingNameValue { #[label(typeck::label, foo = "...")] - //~^ ERROR `#[label(...)]` is not a valid attribute + //~^ ERROR `#[label(foo = ...)]` is not a valid attribute span: Span, } @@ -523,7 +528,7 @@ struct LabelWithTrailingNameValue { #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] struct LabelWithTrailingList { #[label(typeck::label, foo("..."))] - //~^ ERROR `#[label(...)]` is not a valid attribute + //~^ ERROR `#[label(foo(...))]` is not a valid attribute span: Span, } @@ -581,3 +586,68 @@ struct LintAttributeOnSessionDiag {} //~| ERROR diagnostic slug not specified //~| ERROR cannot find attribute `lint` in this scope struct LintAttributeOnLintDiag {} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +struct DuplicatedSuggestionCode { + #[suggestion(typeck::suggestion, code = "...", code = ",,,")] + //~^ ERROR specified multiple times + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +struct InvalidTypeInSuggestionTuple { + #[suggestion(typeck::suggestion, code = "...")] + suggestion: (Span, usize), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +struct MissingApplicabilityInSuggestionTuple { + #[suggestion(typeck::suggestion, code = "...")] + suggestion: (Span,), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +struct MissingCodeInSuggestion { + #[suggestion(typeck::suggestion)] + //~^ ERROR suggestion without `code = "..."` + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +#[multipart_suggestion(typeck::suggestion)] +//~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute +//~| ERROR cannot find attribute `multipart_suggestion` in this scope +#[multipart_suggestion()] +//~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute +//~| ERROR cannot find attribute `multipart_suggestion` in this scope +struct MultipartSuggestion { + #[multipart_suggestion(typeck::suggestion)] + //~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute + //~| ERROR cannot find attribute `multipart_suggestion` in this scope + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +#[suggestion(typeck::suggestion, code = "...")] +//~^ ERROR `#[suggestion(...)]` is not a valid attribute +struct SuggestionOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] +#[label] +//~^ ERROR `#[label]` is not a valid attribute +struct LabelOnStruct { + #[primary_span] + suggestion: Span, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index c3972beb51282..9919b12beaf0a 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -20,8 +20,6 @@ error: `#[nonsense(...)]` is not a valid attribute | LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: only `diag`, `help`, `note` and `warning` are valid attributes error: diagnostic slug not specified --> $DIR/diagnostic-derive.rs:53:1 @@ -41,7 +39,7 @@ error: `#[diag("...")]` is not a valid attribute LL | #[diag("E0123")] | ^^^^^^^ | - = help: first argument of the attribute should be the diagnostic slug + = help: a diagnostic slug is required as the first argument error: diagnostic slug not specified --> $DIR/diagnostic-derive.rs:60:1 @@ -60,7 +58,7 @@ error: `#[diag(nonsense(...))]` is not a valid attribute LL | #[diag(nonsense("foo"), code = "E0123", slug = "foo")] | ^^^^^^^^^^^^^^^ | - = help: first argument of the attribute should be the diagnostic slug + = help: a diagnostic slug is required as the first argument error: diagnostic slug not specified --> $DIR/diagnostic-derive.rs:71:1 @@ -79,7 +77,15 @@ error: `#[diag(nonsense = ...)]` is not a valid attribute LL | #[diag(nonsense = "...", code = "E0123", slug = "foo")] | ^^^^^^^^^^^^^^^^ | - = help: first argument of the attribute should be the diagnostic slug + = help: only `code` is a valid nested attributes following the slug + +error: `#[diag(slug = ...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:77:42 + | +LL | #[diag(nonsense = "...", code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^ + | + = help: only `code` is a valid nested attributes following the slug error: diagnostic slug not specified --> $DIR/diagnostic-derive.rs:77:1 @@ -87,32 +93,40 @@ error: diagnostic slug not specified LL | / #[diag(nonsense = "...", code = "E0123", slug = "foo")] LL | | LL | | +LL | | LL | | struct InvalidNestedStructAttr2 {} | |__________________________________^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[diag(nonsense = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:83:8 + --> $DIR/diagnostic-derive.rs:84:8 | LL | #[diag(nonsense = 4, code = "E0123", slug = "foo")] | ^^^^^^^^^^^^ + +error: `#[diag(slug = ...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:84:38 | - = help: first argument of the attribute should be the diagnostic slug +LL | #[diag(nonsense = 4, code = "E0123", slug = "foo")] + | ^^^^^^^^^^^^ + | + = help: only `code` is a valid nested attributes following the slug error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:83:1 + --> $DIR/diagnostic-derive.rs:84:1 | LL | / #[diag(nonsense = 4, code = "E0123", slug = "foo")] LL | | LL | | +LL | | LL | | struct InvalidNestedStructAttr3 {} | |__________________________________^ | = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[diag(slug = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:89:58 + --> $DIR/diagnostic-derive.rs:91:58 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123", slug = "foo")] | ^^^^^^^^^^^^ @@ -120,55 +134,57 @@ LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123", slug = "foo")] = help: only `code` is a valid nested attributes following the slug error: `#[suggestion = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:96:5 + --> $DIR/diagnostic-derive.rs:98:5 | LL | #[suggestion = "bar"] | ^^^^^^^^^^^^^^^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:103:1 + --> $DIR/diagnostic-derive.rs:105:8 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:102:1 + --> $DIR/diagnostic-derive.rs:104:8 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:103:49 + --> $DIR/diagnostic-derive.rs:105:49 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456")] | ^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:102:49 + --> $DIR/diagnostic-derive.rs:104:49 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:109:65 + --> $DIR/diagnostic-derive.rs:111:65 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456", code = "E0457")] | ^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:109:49 + --> $DIR/diagnostic-derive.rs:111:49 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456", code = "E0457")] | ^^^^^^^ error: `#[diag(typeck::ambiguous_lifetime_bound)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:114:42 + --> $DIR/diagnostic-derive.rs:116:42 | LL | #[diag(typeck::ambiguous_lifetime_bound, typeck::ambiguous_lifetime_bound, code = "E0456")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: diagnostic slug must be the first argument error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:119:1 + --> $DIR/diagnostic-derive.rs:121:1 | LL | struct KindNotProvided {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -176,7 +192,7 @@ LL | struct KindNotProvided {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:122:1 + --> $DIR/diagnostic-derive.rs:124:1 | LL | / #[diag(code = "E0456")] LL | | @@ -186,33 +202,31 @@ LL | | struct SlugNotProvided {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:133:5 + --> $DIR/diagnostic-derive.rs:135:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ error: `#[nonsense]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:141:5 + --> $DIR/diagnostic-derive.rs:143:5 | LL | #[nonsense] | ^^^^^^^^^^^ - | - = help: only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes error: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:158:5 + --> $DIR/diagnostic-derive.rs:160:5 | LL | #[label(typeck::label)] | ^^^^^^^^^^^^^^^^^^^^^^^ error: `name` doesn't refer to a field on this type - --> $DIR/diagnostic-derive.rs:166:45 + --> $DIR/diagnostic-derive.rs:168:45 | LL | #[suggestion(typeck::suggestion, code = "{name}")] | ^^^^^^^^ error: invalid format string: expected `'}'` but string was terminated - --> $DIR/diagnostic-derive.rs:171:16 + --> $DIR/diagnostic-derive.rs:173:16 | LL | #[derive(Diagnostic)] | - ^ expected `'}'` in format string @@ -223,7 +237,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:181:15 + --> $DIR/diagnostic-derive.rs:183:15 | LL | #[derive(Diagnostic)] | ^ unmatched `}` in format string @@ -232,29 +246,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: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:201:5 + --> $DIR/diagnostic-derive.rs:203:5 | LL | #[label(typeck::label)] | ^^^^^^^^^^^^^^^^^^^^^^^ +error: suggestion without `code = "..."` + --> $DIR/diagnostic-derive.rs:221:5 + | +LL | #[suggestion(typeck::suggestion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: `#[suggestion(nonsense = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:226:18 + --> $DIR/diagnostic-derive.rs:229:18 | LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^^^^^^^^^ | - = help: only `message`, `code` and `applicability` are valid field attributes + = help: only `code` and `applicability` are valid nested attributes + +error: suggestion without `code = "..."` + --> $DIR/diagnostic-derive.rs:229:5 + | +LL | #[suggestion(nonsense = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[suggestion(msg = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:234:18 + --> $DIR/diagnostic-derive.rs:238:18 | LL | #[suggestion(msg = "bar")] | ^^^^^^^^^^^ | - = help: only `message`, `code` and `applicability` are valid field attributes + = help: only `code` and `applicability` are valid nested attributes + +error: suggestion without `code = "..."` + --> $DIR/diagnostic-derive.rs:238:5 + | +LL | #[suggestion(msg = "bar")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: wrong field type for suggestion - --> $DIR/diagnostic-derive.rs:256:5 + --> $DIR/diagnostic-derive.rs:261:5 | LL | / #[suggestion(typeck::suggestion, code = "This is suggested code")] LL | | @@ -263,60 +295,76 @@ 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:271:5 +error: specified multiple times + --> $DIR/diagnostic-derive.rs:277:24 | -LL | / #[suggestion(typeck::suggestion, code = "This is suggested code")] -LL | | -LL | | suggestion: (Span, Span, Applicability), - | |___________________________________________^ +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive.rs:277:18 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ -error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability - --> $DIR/diagnostic-derive.rs:279:5 +error: specified multiple times + --> $DIR/diagnostic-derive.rs:285:33 | -LL | / #[suggestion(typeck::suggestion, code = "This is suggested code")] -LL | | -LL | | suggestion: (Applicability, Applicability, Span), - | |____________________________________________________^ +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive.rs:285:18 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ error: `#[label = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:287:5 + --> $DIR/diagnostic-derive.rs:292:5 | LL | #[label = "bar"] | ^^^^^^^^^^^^^^^^ -error: applicability cannot be set in both the field and attribute - --> $DIR/diagnostic-derive.rs:438:52 +error: specified multiple times + --> $DIR/diagnostic-derive.rs:443:52 | LL | #[suggestion(typeck::suggestion, code = "...", applicability = "maybe-incorrect")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive.rs:445:24 + | +LL | suggestion: (Span, Applicability), + | ^^^^^^^^^^^^^ error: invalid applicability - --> $DIR/diagnostic-derive.rs:446:52 + --> $DIR/diagnostic-derive.rs:451:52 | LL | #[suggestion(typeck::suggestion, code = "...", applicability = "batman")] | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: `#[label(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:509:5 +error: `#[label(foo)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:514:28 | LL | #[label(typeck::label, foo)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ + | + = help: a diagnostic slug must be the first argument to the attribute -error: `#[label(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:517:5 +error: `#[label(foo = ...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:522:28 | LL | #[label(typeck::label, foo = "...")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^ -error: `#[label(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:525:5 +error: `#[label(foo(...))]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:530:28 | LL | #[label(typeck::label, foo("..."))] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ error: `#[primary_span]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:538:5 + --> $DIR/diagnostic-derive.rs:543:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ @@ -324,15 +372,13 @@ LL | #[primary_span] = help: the `primary_span` field attribute is not valid for lint diagnostics error: `#[error(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:558:1 + --> $DIR/diagnostic-derive.rs:563:1 | LL | #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: `error` and `lint` have been replaced by `diag` error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:558:1 + --> $DIR/diagnostic-derive.rs:563:1 | LL | / #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -344,15 +390,13 @@ LL | | struct ErrorAttribute {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[warn_(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:565:1 + --> $DIR/diagnostic-derive.rs:570:1 | LL | #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: `warn_` have been replaced by `warning` error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:565:1 + --> $DIR/diagnostic-derive.rs:570:1 | LL | / #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -364,15 +408,13 @@ LL | | struct WarnAttribute {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:572:1 + --> $DIR/diagnostic-derive.rs:577:1 | LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: `error` and `lint` have been replaced by `diag` error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:572:1 + --> $DIR/diagnostic-derive.rs:577:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -384,15 +426,13 @@ LL | | struct LintAttributeOnSessionDiag {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[lint(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:579:1 + --> $DIR/diagnostic-derive.rs:584:1 | LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = help: `error` and `lint` have been replaced by `diag` error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:579:1 + --> $DIR/diagnostic-derive.rs:584:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -403,6 +443,80 @@ LL | | struct LintAttributeOnLintDiag {} | = help: specify the slug as the first argument to the attribute, such as `#[diag(typeck::example_error)]` +error: specified multiple times + --> $DIR/diagnostic-derive.rs:593:52 + | +LL | #[suggestion(typeck::suggestion, code = "...", code = ",,,")] + | ^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive.rs:593:38 + | +LL | #[suggestion(typeck::suggestion, code = "...", code = ",,,")] + | ^^^^^^^^^^^^ + +error: wrong types for suggestion + --> $DIR/diagnostic-derive.rs:602:24 + | +LL | suggestion: (Span, usize), + | ^^^^^ + | + = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` + +error: wrong types for suggestion + --> $DIR/diagnostic-derive.rs:610:17 + | +LL | suggestion: (Span,), + | ^^^^^^^ + | + = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` + +error: suggestion without `code = "..."` + --> $DIR/diagnostic-derive.rs:617:5 + | +LL | #[suggestion(typeck::suggestion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:624:1 + | +LL | #[multipart_suggestion(typeck::suggestion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider creating a `Subdiagnostic` instead + +error: `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:627:1 + | +LL | #[multipart_suggestion()] + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider creating a `Subdiagnostic` instead + +error: `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:631:5 + | +LL | #[multipart_suggestion(typeck::suggestion)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: consider creating a `Subdiagnostic` instead + +error: `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:639:1 + | +LL | #[suggestion(typeck::suggestion, code = "...")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: `#[label]` is not a valid attribute + --> $DIR/diagnostic-derive.rs:648:1 + | +LL | #[label] + | ^^^^^^^^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive.rs:53:3 | @@ -410,35 +524,53 @@ LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^ error: cannot find attribute `nonsense` in this scope - --> $DIR/diagnostic-derive.rs:141:7 + --> $DIR/diagnostic-derive.rs:143:7 | LL | #[nonsense] | ^^^^^^^^ error: cannot find attribute `error` in this scope - --> $DIR/diagnostic-derive.rs:558:3 + --> $DIR/diagnostic-derive.rs:563:3 | LL | #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^ error: cannot find attribute `warn_` in this scope - --> $DIR/diagnostic-derive.rs:565:3 + --> $DIR/diagnostic-derive.rs:570:3 | LL | #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^ help: a built-in attribute with a similar name exists: `warn` error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive.rs:572:3 + --> $DIR/diagnostic-derive.rs:577:3 | LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^ help: a built-in attribute with a similar name exists: `link` error: cannot find attribute `lint` in this scope - --> $DIR/diagnostic-derive.rs:579:3 + --> $DIR/diagnostic-derive.rs:584:3 | LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^ help: a built-in attribute with a similar name exists: `link` +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive.rs:624:3 + | +LL | #[multipart_suggestion(typeck::suggestion)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive.rs:627:3 + | +LL | #[multipart_suggestion()] + | ^^^^^^^^^^^^^^^^^^^^ + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive.rs:631:7 + | +LL | #[multipart_suggestion(typeck::suggestion)] + | ^^^^^^^^^^^^^^^^^^^^ + error[E0425]: cannot find value `nonsense` in module `rustc_errors::fluent` --> $DIR/diagnostic-derive.rs:66:8 | @@ -446,7 +578,7 @@ LL | #[diag(nonsense, code = "E0123")] | ^^^^^^^^ not found in `rustc_errors::fluent` error[E0277]: the trait bound `Hello: IntoDiagnosticArg` is not satisfied - --> $DIR/diagnostic-derive.rs:331:10 + --> $DIR/diagnostic-derive.rs:336:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` @@ -459,7 +591,7 @@ LL | arg: impl IntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::set_arg` = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 55 previous errors +error: aborting due to 72 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`. diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 9fbe7b1f4c859..606b3b5e5ebca 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -52,7 +52,7 @@ struct C { #[derive(Subdiagnostic)] #[label] -//~^ ERROR `#[label]` is not a valid attribute +//~^ ERROR diagnostic slug must be first argument struct D { #[primary_span] span: Span, @@ -81,6 +81,7 @@ struct F { #[derive(Subdiagnostic)] #[label(bug = "...")] //~^ ERROR `#[label(bug = ...)]` is not a valid attribute +//~| ERROR diagnostic slug must be first argument struct G { #[primary_span] span: Span, @@ -90,6 +91,7 @@ struct G { #[derive(Subdiagnostic)] #[label("...")] //~^ ERROR `#[label("...")]` is not a valid attribute +//~| ERROR diagnostic slug must be first argument struct H { #[primary_span] span: Span, @@ -99,6 +101,7 @@ struct H { #[derive(Subdiagnostic)] #[label(slug = 4)] //~^ ERROR `#[label(slug = ...)]` is not a valid attribute +//~| ERROR diagnostic slug must be first argument struct J { #[primary_span] span: Span, @@ -108,6 +111,7 @@ struct J { #[derive(Subdiagnostic)] #[label(slug("..."))] //~^ ERROR `#[label(slug(...))]` is not a valid attribute +//~| ERROR diagnostic slug must be first argument struct K { #[primary_span] span: Span, @@ -135,7 +139,7 @@ struct M { #[derive(Subdiagnostic)] #[label(parser::add_paren, code = "...")] -//~^ ERROR `code` is not a valid nested attribute of a `label` attribute +//~^ ERROR `#[label(code = ...)]` is not a valid attribute struct N { #[primary_span] span: Span, @@ -144,7 +148,7 @@ struct N { #[derive(Subdiagnostic)] #[label(parser::add_paren, applicability = "machine-applicable")] -//~^ ERROR `applicability` is not a valid nested attribute of a `label` attribute +//~^ ERROR `#[label(applicability = ...)]` is not a valid attribute struct O { #[primary_span] span: Span, @@ -216,6 +220,7 @@ enum T { enum U { #[label(code = "...")] //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute + //~| ERROR `#[label(code = ...)]` is not a valid attribute A { #[primary_span] span: Span, @@ -531,7 +536,7 @@ struct BA { #[derive(Subdiagnostic)] #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] //~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields -//~| ERROR `code` is not a valid nested attribute of a `multipart_suggestion` attribute +//~| ERROR `#[multipart_suggestion(code = ...)]` is not a valid attribute struct BBa { var: String, } @@ -612,10 +617,9 @@ struct BG { #[derive(Subdiagnostic)] #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] -//~^ NOTE previously specified here struct BH { #[applicability] - //~^ ERROR specified multiple times + //~^ ERROR `#[applicability]` has no effect appl: Applicability, #[suggestion_part(code = "(")] first: Span, diff --git a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 0a0247e898088..171b89e657d81 100644 --- a/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -8,7 +8,7 @@ LL | | var: String, LL | | } | |_^ -error: `#[label]` is not a valid attribute +error: diagnostic slug must be first argument of a `#[label(...)]` attribute --> $DIR/subdiagnostic-derive.rs:54:1 | LL | #[label] @@ -31,101 +31,123 @@ error: `#[label(bug = ...)]` is not a valid attribute | LL | #[label(bug = "...")] | ^^^^^^^^^^^ + +error: diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive.rs:82:1 | - = help: first argument of the attribute should be the diagnostic slug +LL | #[label(bug = "...")] + | ^^^^^^^^^^^^^^^^^^^^^ error: `#[label("...")]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:91:9 + --> $DIR/subdiagnostic-derive.rs:92:9 | LL | #[label("...")] | ^^^^^ + +error: diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive.rs:92:1 | - = help: first argument of the attribute should be the diagnostic slug +LL | #[label("...")] + | ^^^^^^^^^^^^^^^ error: `#[label(slug = ...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:100:9 + --> $DIR/subdiagnostic-derive.rs:102:9 | LL | #[label(slug = 4)] | ^^^^^^^^ + +error: diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive.rs:102:1 | - = help: first argument of the attribute should be the diagnostic slug +LL | #[label(slug = 4)] + | ^^^^^^^^^^^^^^^^^^ error: `#[label(slug(...))]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:109:9 + --> $DIR/subdiagnostic-derive.rs:112:9 | LL | #[label(slug("..."))] | ^^^^^^^^^^^ + +error: diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive.rs:112:1 | - = help: first argument of the attribute should be the diagnostic slug +LL | #[label(slug("..."))] + | ^^^^^^^^^^^^^^^^^^^^^ error: diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:128:1 + --> $DIR/subdiagnostic-derive.rs:132:1 | LL | #[label()] | ^^^^^^^^^^ -error: `code` is not a valid nested attribute of a `label` attribute - --> $DIR/subdiagnostic-derive.rs:137:28 +error: `#[label(code = ...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:141:28 | LL | #[label(parser::add_paren, code = "...")] | ^^^^^^^^^^^^ -error: `applicability` is not a valid nested attribute of a `label` attribute - --> $DIR/subdiagnostic-derive.rs:146:28 +error: `#[label(applicability = ...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:150:28 | LL | #[label(parser::add_paren, applicability = "machine-applicable")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:155:1 + --> $DIR/subdiagnostic-derive.rs:159:1 | LL | #[foo] | ^^^^^^ error: `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:169:5 + --> $DIR/subdiagnostic-derive.rs:173:5 | LL | #[bar] | ^^^^^^ error: `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:181:5 + --> $DIR/subdiagnostic-derive.rs:185:5 | LL | #[bar = "..."] | ^^^^^^^^^^^^^^ error: `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:193:5 + --> $DIR/subdiagnostic-derive.rs:197:5 | LL | #[bar = 4] | ^^^^^^^^^^ error: `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:205:5 + --> $DIR/subdiagnostic-derive.rs:209:5 | LL | #[bar("...")] | ^^^^^^^^^^^^^ +error: `#[label(code = ...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:221:13 + | +LL | #[label(code = "...")] + | ^^^^^^^^^^^^ + error: diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:217:5 + --> $DIR/subdiagnostic-derive.rs:221:5 | LL | #[label(code = "...")] | ^^^^^^^^^^^^^^^^^^^^^^ error: subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:234:5 + --> $DIR/subdiagnostic-derive.rs:239:5 | LL | B { | ^ error: the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:246:5 + --> $DIR/subdiagnostic-derive.rs:251:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ error: label without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:243:1 + --> $DIR/subdiagnostic-derive.rs:248:1 | LL | / #[label(parser::add_paren)] LL | | @@ -137,13 +159,13 @@ LL | | } | |_^ error: `#[applicability]` is only valid on suggestions - --> $DIR/subdiagnostic-derive.rs:256:5 + --> $DIR/subdiagnostic-derive.rs:261:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:266:5 + --> $DIR/subdiagnostic-derive.rs:271:5 | LL | #[bar] | ^^^^^^ @@ -151,13 +173,13 @@ LL | #[bar] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:277:5 + --> $DIR/subdiagnostic-derive.rs:282:5 | LL | #[bar = "..."] | ^^^^^^^^^^^^^^ error: `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:288:5 + --> $DIR/subdiagnostic-derive.rs:293:5 | LL | #[bar("...")] | ^^^^^^^^^^^^^ @@ -165,7 +187,7 @@ LL | #[bar("...")] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: unexpected unsupported untagged union - --> $DIR/subdiagnostic-derive.rs:304:1 + --> $DIR/subdiagnostic-derive.rs:309:1 | LL | / union AC { LL | | @@ -175,7 +197,7 @@ LL | | } | |_^ error: `#[label(parser::add_paren)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:319:28 + --> $DIR/subdiagnostic-derive.rs:324:28 | LL | #[label(parser::add_paren, parser::add_paren)] | ^^^^^^^^^^^^^^^^^ @@ -183,67 +205,67 @@ LL | #[label(parser::add_paren, parser::add_paren)] = help: a diagnostic slug must be the first argument to the attribute error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:332:5 + --> $DIR/subdiagnostic-derive.rs:337:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:329:5 + --> $DIR/subdiagnostic-derive.rs:334:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ error: subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:338:8 + --> $DIR/subdiagnostic-derive.rs:343:8 | LL | struct AG { | ^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:375:47 + --> $DIR/subdiagnostic-derive.rs:380:47 | LL | #[suggestion(parser::add_paren, code = "...", code = "...")] | ^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:375:33 + --> $DIR/subdiagnostic-derive.rs:380:33 | LL | #[suggestion(parser::add_paren, code = "...", code = "...")] | ^^^^^^^^^^^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:393:5 + --> $DIR/subdiagnostic-derive.rs:398:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:390:5 + --> $DIR/subdiagnostic-derive.rs:395:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:403:5 + --> $DIR/subdiagnostic-derive.rs:408:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ error: suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:416:1 + --> $DIR/subdiagnostic-derive.rs:421:1 | LL | #[suggestion(parser::add_paren)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: invalid applicability - --> $DIR/subdiagnostic-derive.rs:426:46 + --> $DIR/subdiagnostic-derive.rs:431:46 | LL | #[suggestion(parser::add_paren, code ="...", applicability = "foo")] | ^^^^^^^^^^^^^^^^^^^^^ error: suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:444:1 + --> $DIR/subdiagnostic-derive.rs:449:1 | LL | / #[suggestion(parser::add_paren, code = "...")] LL | | @@ -253,25 +275,25 @@ LL | | } | |_^ error: unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:458:1 + --> $DIR/subdiagnostic-derive.rs:463:1 | LL | #[label] | ^^^^^^^^ error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:478:39 + --> $DIR/subdiagnostic-derive.rs:483:39 | LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] | ^^^^^^^ error: `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:497:43 + --> $DIR/subdiagnostic-derive.rs:502:43 | LL | #[suggestion(parser::add_paren, code ="{var}", applicability = "machine-applicable")] | ^^^^^^^ error: `#[suggestion_part]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:520:5 + --> $DIR/subdiagnostic-derive.rs:525:5 | LL | #[suggestion_part] | ^^^^^^^^^^^^^^^^^^ @@ -279,7 +301,7 @@ LL | #[suggestion_part] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead error: `#[suggestion_part(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:523:5 + --> $DIR/subdiagnostic-derive.rs:528:5 | LL | #[suggestion_part(code = "...")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -287,7 +309,7 @@ LL | #[suggestion_part(code = "...")] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions error: suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:517:1 + --> $DIR/subdiagnostic-derive.rs:522:1 | LL | / #[suggestion(parser::add_paren, code = "...")] LL | | @@ -298,14 +320,16 @@ LL | | var: String, LL | | } | |_^ -error: `code` is not a valid nested attribute of a `multipart_suggestion` attribute - --> $DIR/subdiagnostic-derive.rs:532:43 +error: `#[multipart_suggestion(code = ...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive.rs:537:43 | LL | #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] | ^^^^^^^^^^^^ + | + = help: only `applicability` is a valid nested attributes error: multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:532:1 + --> $DIR/subdiagnostic-derive.rs:537:1 | LL | / #[multipart_suggestion(parser::add_paren, code = "...", applicability = "machine-applicable")] LL | | @@ -316,19 +340,19 @@ LL | | } | |_^ error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:542:5 + --> $DIR/subdiagnostic-derive.rs:547:5 | LL | #[suggestion_part] | ^^^^^^^^^^^^^^^^^^ error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:550:5 + --> $DIR/subdiagnostic-derive.rs:555:5 | LL | #[suggestion_part()] | ^^^^^^^^^^^^^^^^^^^^ error: `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:559:5 + --> $DIR/subdiagnostic-derive.rs:564:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ @@ -336,7 +360,7 @@ LL | #[primary_span] = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` error: multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:556:1 + --> $DIR/subdiagnostic-derive.rs:561:1 | LL | / #[multipart_suggestion(parser::add_paren)] LL | | @@ -348,19 +372,19 @@ LL | | } | |_^ error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:567:5 + --> $DIR/subdiagnostic-derive.rs:572:5 | LL | #[suggestion_part] | ^^^^^^^^^^^^^^^^^^ error: `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:570:5 + --> $DIR/subdiagnostic-derive.rs:575:5 | LL | #[suggestion_part()] | ^^^^^^^^^^^^^^^^^^^^ error: `#[suggestion_part(foo = ...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:573:23 + --> $DIR/subdiagnostic-derive.rs:578:23 | LL | #[suggestion_part(foo = "bar")] | ^^^^^^^^^^^ @@ -368,40 +392,34 @@ LL | #[suggestion_part(foo = "bar")] = help: `code` is the only valid nested attribute error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:576:5 + --> $DIR/subdiagnostic-derive.rs:581:5 | LL | #[suggestion_part(code = "...")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:579:5 + --> $DIR/subdiagnostic-derive.rs:584:5 | LL | #[suggestion_part()] | ^^^^^^^^^^^^^^^^^^^^ error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:587:37 + --> $DIR/subdiagnostic-derive.rs:592:37 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^^^^^^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:587:23 + --> $DIR/subdiagnostic-derive.rs:592:23 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^^^^^^^^^ -error: specified multiple times - --> $DIR/subdiagnostic-derive.rs:617:5 +error: `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` + --> $DIR/subdiagnostic-derive.rs:621:5 | LL | #[applicability] | ^^^^^^^^^^^^^^^^ - | -note: previously specified here - --> $DIR/subdiagnostic-derive.rs:614:43 - | -LL | #[multipart_suggestion(parser::add_paren, applicability = "machine-applicable")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: cannot find attribute `foo` in this scope --> $DIR/subdiagnostic-derive.rs:63:3 @@ -410,59 +428,59 @@ LL | #[foo] | ^^^ error: cannot find attribute `foo` in this scope - --> $DIR/subdiagnostic-derive.rs:155:3 + --> $DIR/subdiagnostic-derive.rs:159:3 | LL | #[foo] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:169:7 + --> $DIR/subdiagnostic-derive.rs:173:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:181:7 + --> $DIR/subdiagnostic-derive.rs:185:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:193:7 + --> $DIR/subdiagnostic-derive.rs:197:7 | LL | #[bar = 4] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:205:7 + --> $DIR/subdiagnostic-derive.rs:209:7 | LL | #[bar("...")] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:266:7 + --> $DIR/subdiagnostic-derive.rs:271:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:277:7 + --> $DIR/subdiagnostic-derive.rs:282:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:288:7 + --> $DIR/subdiagnostic-derive.rs:293:7 | LL | #[bar("...")] | ^^^ error[E0425]: cannot find value `slug` in module `rustc_errors::fluent` - --> $DIR/subdiagnostic-derive.rs:118:9 + --> $DIR/subdiagnostic-derive.rs:122:9 | LL | #[label(slug)] | ^^^^ not found in `rustc_errors::fluent` -error: aborting due to 63 previous errors +error: aborting due to 68 previous errors For more information about this error, try `rustc --explain E0425`.