From f20c882b8b65f09701ef937b765e93e76682a298 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 23 Sep 2022 12:49:02 +0100 Subject: [PATCH] macros: support diagnostic derive on enums Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 155 +++++-------- .../src/diagnostics/diagnostic_builder.rs | 204 ++++++++++-------- .../src/diagnostics/subdiagnostic.rs | 26 +-- .../rustc_macros/src/diagnostics/utils.rs | 85 ++++++-- .../session-diagnostic/diagnostic-derive.rs | 22 +- .../diagnostic-derive.stderr | 185 ++++++++-------- 6 files changed, 370 insertions(+), 307 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 3b8d9594eb9..b9a283552f7 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -2,10 +2,9 @@ use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveBuilder, DiagnosticDeriveKind}; use crate::diagnostics::error::{span_err, DiagnosticDeriveError}; -use crate::diagnostics::utils::{build_field_mapping, SetOnce}; +use crate::diagnostics::utils::SetOnce; use proc_macro2::TokenStream; use quote::quote; -use syn::spanned::Spanned; use synstructure::Structure; /// The central struct for constructing the `into_diagnostic` method from an annotated struct. @@ -18,13 +17,7 @@ pub(crate) struct DiagnosticDerive<'a> { impl<'a> DiagnosticDerive<'a> { pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a>) -> Self { Self { - builder: DiagnosticDeriveBuilder { - diag, - fields: build_field_mapping(&structure), - kind: DiagnosticDeriveKind::Diagnostic, - code: None, - slug: None, - }, + builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::Diagnostic }, handler, structure, } @@ -33,52 +26,35 @@ pub(crate) fn new(diag: syn::Ident, handler: syn::Ident, structure: Structure<'a pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure, handler, mut builder } = self; - let ast = structure.ast(); - let implementation = { - if let syn::Data::Struct(..) = ast.data { - let preamble = builder.preamble(&structure); - let (attrs, args) = builder.body(&mut structure); + let implementation = builder.each_variant(&mut structure, |mut builder, variant| { + let preamble = builder.preamble(&variant); + let body = builder.body(&variant); - let span = ast.span().unwrap(); - let diag = &builder.diag; - let init = match builder.slug.value() { - None => { - span_err(span, "diagnostic slug not specified") - .help(&format!( - "specify the slug as the first argument to the `#[diag(...)]` attribute, \ - such as `#[diag(typeck::example_error)]`", - )) - .emit(); - return DiagnosticDeriveError::ErrorHandled.to_compile_error(); - } - Some(slug) => { - quote! { - let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug); - } - } - }; - - quote! { - #init - #preamble - match self { - #attrs - } - match self { - #args - } - #diag + let diag = &builder.parent.diag; + let init = match builder.slug.value_ref() { + None => { + span_err(builder.span, "diagnostic slug not specified") + .help(&format!( + "specify the slug as the first argument to the `#[diag(...)]` \ + attribute, such as `#[diag(typeck::example_error)]`", + )) + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - } else { - span_err( - ast.span().unwrap(), - "`#[derive(Diagnostic)]` can only be used on structs", - ) - .emit(); + Some(slug) => { + quote! { + let mut #diag = #handler.struct_diagnostic(rustc_errors::fluent::#slug); + } + } + }; - DiagnosticDeriveError::ErrorHandled.to_compile_error() + quote! { + #init + #preamble + #body + #diag } - }; + }); structure.gen_impl(quote! { gen impl<'__diagnostic_handler_sess, G> @@ -107,13 +83,7 @@ pub(crate) struct LintDiagnosticDerive<'a> { impl<'a> LintDiagnosticDerive<'a> { pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self { Self { - builder: DiagnosticDeriveBuilder { - diag, - fields: build_field_mapping(&structure), - kind: DiagnosticDeriveKind::LintDiagnostic, - code: None, - slug: None, - }, + builder: DiagnosticDeriveBuilder { diag, kind: DiagnosticDeriveKind::LintDiagnostic }, structure, } } @@ -121,54 +91,35 @@ pub(crate) fn new(diag: syn::Ident, structure: Structure<'a>) -> Self { pub(crate) fn into_tokens(self) -> TokenStream { let LintDiagnosticDerive { mut structure, mut builder } = self; - let ast = structure.ast(); - let implementation = { - if let syn::Data::Struct(..) = ast.data { - let preamble = builder.preamble(&structure); - let (attrs, args) = builder.body(&mut structure); + let implementation = builder.each_variant(&mut structure, |mut builder, variant| { + let preamble = builder.preamble(&variant); + let body = builder.body(&variant); - let diag = &builder.diag; - let span = ast.span().unwrap(); - let init = match builder.slug.value() { - None => { - span_err(span, "diagnostic slug not specified") - .help(&format!( - "specify the slug as the first argument to the attribute, such as \ - `#[diag(typeck::example_error)]`", - )) - .emit(); - return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + let diag = &builder.parent.diag; + let init = match builder.slug.value_ref() { + None => { + span_err(builder.span, "diagnostic slug not specified") + .help(&format!( + "specify the slug as the first argument to the attribute, such as \ + `#[diag(typeck::example_error)]`", + )) + .emit(); + return DiagnosticDeriveError::ErrorHandled.to_compile_error(); + } + Some(slug) => { + quote! { + let mut #diag = #diag.build(rustc_errors::fluent::#slug); } - Some(slug) => { - quote! { - let mut #diag = #diag.build(rustc_errors::fluent::#slug); - } - } - }; + } + }; - let implementation = quote! { - #init - #preamble - match self { - #attrs - } - match self { - #args - } - #diag.emit(); - }; - - implementation - } else { - span_err( - ast.span().unwrap(), - "`#[derive(LintDiagnostic)]` can only be used on structs", - ) - .emit(); - - DiagnosticDeriveError::ErrorHandled.to_compile_error() + quote! { + #init + #preamble + #body + #diag.emit(); } - }; + }); let diag = &builder.diag; structure.gen_impl(quote! { diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 2aa292bbce2..38bd986f76f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -1,22 +1,20 @@ #![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_span_err, DiagnosticDeriveError, + invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_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, - FieldInfo, FieldInnerTy, HasFieldMap, SetOnce, + bind_style_of_field, build_field_mapping, report_error_if_not_applied_to_span, + report_type_error, should_generate_set_arg, type_is_unit, type_matches_path, FieldInfo, + FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote}; -use std::collections::HashMap; use syn::{ - parse_quote, spanned::Spanned, Attribute, Field, Meta, MetaList, MetaNameValue, NestedMeta, - Path, Type, + parse_quote, spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path, Type, }; -use synstructure::{BindingInfo, Structure}; +use synstructure::{BindingInfo, Structure, VariantInfo}; /// What kind of diagnostic is being derived - a fatal/error/warning or a lint? #[derive(Copy, Clone, PartialEq, Eq)] @@ -25,19 +23,30 @@ pub(crate) enum DiagnosticDeriveKind { LintDiagnostic, } -/// Tracks persistent information required for building up individual calls to diagnostic methods -/// for generated diagnostic derives - both `Diagnostic` for fatal/errors/warnings and -/// `LintDiagnostic` for lints. +/// Tracks persistent information required for the entire type when building up individual calls to +/// diagnostic methods for generated diagnostic derives - both `Diagnostic` for +/// fatal/errors/warnings and `LintDiagnostic` for lints. pub(crate) struct DiagnosticDeriveBuilder { /// The identifier to use for the generated `DiagnosticBuilder` instance. pub diag: syn::Ident, + /// Kind of diagnostic that should be derived. + pub kind: DiagnosticDeriveKind, +} + +/// Tracks persistent information required for a specific variant when building up individual calls +/// to diagnostic methods for generated diagnostic derives - both `Diagnostic` for +/// fatal/errors/warnings and `LintDiagnostic` for lints. +pub(crate) struct DiagnosticDeriveVariantBuilder<'parent> { + /// The parent builder for the entire type. + pub parent: &'parent DiagnosticDeriveBuilder, + + /// Span of the struct or the enum variant. + pub span: proc_macro::Span, /// Store a map of field name to its corresponding field. This is built on construction of the /// derive builder. - pub fields: HashMap, + pub field_map: FieldMap, - /// Kind of diagnostic that should be derived. - 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: SpannedOption, @@ -46,15 +55,82 @@ pub(crate) struct DiagnosticDeriveBuilder { pub code: SpannedOption<()>, } -impl HasFieldMap for DiagnosticDeriveBuilder { +impl<'a> HasFieldMap for DiagnosticDeriveVariantBuilder<'a> { fn get_field_binding(&self, field: &String) -> Option<&TokenStream> { - self.fields.get(field) + self.field_map.get(field) } } impl DiagnosticDeriveBuilder { - pub fn preamble<'s>(&mut self, structure: &Structure<'s>) -> TokenStream { + /// 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 + /// or attributes on the type itself when input is an enum. + pub fn each_variant<'s, F>(&mut self, structure: &mut Structure<'s>, f: F) -> TokenStream + where + F: for<'a, 'v> Fn(DiagnosticDeriveVariantBuilder<'a>, &VariantInfo<'v>) -> TokenStream, + { let ast = structure.ast(); + let span = ast.span().unwrap(); + match ast.data { + syn::Data::Struct(..) | syn::Data::Enum(..) => (), + syn::Data::Union(..) => { + span_err(span, "diagnostic derives can only be used on structs and enums"); + } + } + + if matches!(ast.data, syn::Data::Enum(..)) { + for attr in &ast.attrs { + span_err( + attr.span().unwrap(), + "unsupported type attribute for diagnostic derive enum", + ) + .emit(); + } + } + + for variant in structure.variants_mut() { + // First, change the binding style of each field based on the code that will be + // generated for the field - e.g. `set_arg` calls needs by-move bindings, whereas + // `set_primary_span` only needs by-ref. + variant.bind_with(|bi| bind_style_of_field(bi.ast()).0); + + // Then, perform a stable sort on bindings which generates code for by-ref bindings + // before code generated for by-move bindings. Any code generated for the by-ref + // bindings which creates a reference to the by-move fields will happen before the + // by-move bindings move those fields and make them inaccessible. + variant.bindings_mut().sort_by_cached_key(|bi| bind_style_of_field(bi.ast())); + } + + let variants = structure.each_variant(|variant| { + let span = match structure.ast().data { + syn::Data::Struct(..) => span, + // There isn't a good way to get the span of the variant, so the variant's + // name will need to do. + _ => variant.ast().ident.span().unwrap(), + }; + let builder = DiagnosticDeriveVariantBuilder { + parent: &self, + span, + field_map: build_field_mapping(variant), + slug: None, + code: None, + }; + f(builder, variant) + }); + + quote! { + match self { + #variants + } + } + } +} + +impl<'a> DiagnosticDeriveVariantBuilder<'a> { + /// Generates calls to `code` and similar functions based on the attributes on the type or + /// variant. + pub fn preamble<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream { + let ast = variant.ast(); let attrs = &ast.attrs; let preamble = attrs.iter().map(|attr| { self.generate_structure_code_for_attr(attr).unwrap_or_else(|v| v.to_compile_error()) @@ -65,68 +141,24 @@ pub fn preamble<'s>(&mut self, structure: &Structure<'s>) -> TokenStream { } } - pub fn body<'s>(&mut self, structure: &mut Structure<'s>) -> (TokenStream, TokenStream) { - // Keep track of which fields need to be handled with a by-move binding. - let mut needs_moved = std::collections::HashSet::new(); - - // Generates calls to `span_label` and similar functions based on the attributes - // on fields. Code for suggestions uses formatting machinery and the value of - // other fields - because any given field can be referenced multiple times, it - // should be accessed through a borrow. When passing fields to `add_subdiagnostic` - // or `set_arg` (which happens below) for Fluent, we want to move the data, so that - // has to happen in a separate pass over the fields. - let attrs = structure - .clone() - .filter(|field_binding| { - let ast = &field_binding.ast(); - !self.needs_move(ast) || { - needs_moved.insert(field_binding.binding.clone()); - false - } - }) - .each(|field_binding| self.generate_field_attrs_code(field_binding)); - - structure.bind_with(|_| synstructure::BindStyle::Move); - // When a field has attributes like `#[label]` or `#[note]` then it doesn't - // need to be passed as an argument to the diagnostic. But when a field has no - // attributes or a `#[subdiagnostic]` attribute then it must be passed as an - // argument to the diagnostic so that it can be referred to by Fluent messages. - let args = structure - .filter(|field_binding| needs_moved.contains(&field_binding.binding)) - .each(|field_binding| self.generate_field_attrs_code(field_binding)); - - (attrs, args) - } - - /// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic - /// call (like `span_label`). - fn should_generate_set_arg(&self, field: &Field) -> bool { - field.attrs.is_empty() - } - - /// Returns `true` if `field` needs to have code generated in the by-move branch of the - /// generated derive rather than the by-ref branch. - fn needs_move(&self, field: &Field) -> bool { - let generates_set_arg = self.should_generate_set_arg(field); - let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); - // FIXME(davidtwco): better support for one field needing to be in the by-move and - // by-ref branches. - let is_subdiagnostic = field - .attrs - .iter() - .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) - .any(|attr| attr == "subdiagnostic"); - - // `set_arg` calls take their argument by-move.. - generates_set_arg - // If this is a `MultiSpan` field then it needs to be moved to be used by any - // attribute.. - || is_multispan - // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is - // unlikely to be `Copy`.. - || is_subdiagnostic + /// Generates calls to `span_label` and similar functions based on the attributes on fields or + /// calls to `set_arg` when no attributes are present. + /// + /// Expects use of `Self::each_variant` which will have sorted bindings so that by-ref bindings + /// (which may create references to by-move bindings) have their code generated first - + /// necessary as code for suggestions uses formatting machinery and the value of other fields + /// (any given field can be referenced multiple times, so must be accessed through a borrow); + /// and when passing fields to `add_subdiagnostic` or `set_arg` for Fluent, fields must be + /// accessed by-move. + pub fn body<'s>(&mut self, variant: &VariantInfo<'s>) -> TokenStream { + let mut body = quote! {}; + for binding in variant.bindings() { + body.extend(self.generate_field_attrs_code(binding)); + } + body } + /// Parse a `SubdiagnosticKind` from an `Attribute`. fn parse_subdiag_attribute( &self, attr: &Attribute, @@ -158,7 +190,7 @@ fn generate_structure_code_for_attr( &mut self, attr: &Attribute, ) -> Result { - let diag = &self.diag; + let diag = &self.parent.diag; let name = attr.path.segments.last().unwrap().ident.to_string(); let name = name.as_str(); @@ -246,8 +278,8 @@ fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> Token let field = binding_info.ast(); let field_binding = &binding_info.binding; - if self.should_generate_set_arg(&field) { - let diag = &self.diag; + if should_generate_set_arg(&field) { + let diag = &self.parent.diag; let ident = field.ident.as_ref().unwrap(); return quote! { #diag.set_arg( @@ -257,7 +289,7 @@ fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> Token }; } - let needs_move = self.needs_move(&field); + let needs_move = bind_style_of_field(&field).is_move(); let inner_ty = FieldInnerTy::from_type(&field.ty); field @@ -303,7 +335,7 @@ fn generate_inner_field_code( info: FieldInfo<'_>, binding: TokenStream, ) -> Result { - let diag = &self.diag; + let diag = &self.parent.diag; let meta = attr.parse_meta()?; if let Meta::Path(_) = meta { @@ -316,7 +348,7 @@ fn generate_inner_field_code( // `set_arg` call will not be generated. return Ok(quote! {}); } - "primary_span" => match self.kind { + "primary_span" => match self.parent.kind { DiagnosticDeriveKind::Diagnostic => { report_error_if_not_applied_to_span(attr, &info)?; @@ -390,7 +422,7 @@ fn add_spanned_subdiagnostic( kind: &Ident, fluent_attr_identifier: Path, ) -> TokenStream { - let diag = &self.diag; + let diag = &self.parent.diag; let fn_name = format_ident!("span_{}", kind); quote! { #diag.#fn_name( @@ -403,7 +435,7 @@ fn add_spanned_subdiagnostic( /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { - let diag = &self.diag; + let diag = &self.parent.diag; quote! { #diag.#kind(rustc_errors::fluent::#fluent_attr_identifier); } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 6545ae086b1..adb4902ebc1 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -1,21 +1,19 @@ #![deny(unused_must_use)] use crate::diagnostics::error::{ - span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err, DiagnosticDeriveError, + invalid_attr, 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, FieldInfo, - FieldInnerTy, HasFieldMap, SetOnce, + build_field_mapping, report_error_if_not_applied_to_applicability, + report_error_if_not_applied_to_span, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, SetOnce, + SpannedOption, SubdiagnosticKind, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; -use std::collections::HashMap; use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, NestedMeta, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; -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> { structure: Structure<'a>, @@ -55,21 +53,11 @@ pub(crate) fn into_tokens(self) -> TokenStream { structure.bind_with(|_| synstructure::BindStyle::Move); let variants_ = structure.each_variant(|variant| { - // Build the mapping of field names to fields. This allows attributes to peek - // values from other fields. - let mut fields_map = HashMap::new(); - for binding in variant.bindings() { - let field = binding.ast(); - if let Some(ident) = &field.ident { - fields_map.insert(ident.to_string(), quote! { #binding }); - } - } - let mut builder = SubdiagnosticDeriveBuilder { diag: &diag, variant, span, - fields: fields_map, + fields: build_field_mapping(variant), span_field: None, applicability: None, has_suggestion_parts: false, @@ -111,7 +99,7 @@ struct SubdiagnosticDeriveBuilder<'a> { /// Store a map of field name to its corresponding field. This is built on construction of the /// derive builder. - fields: HashMap, + fields: FieldMap, /// Identifier for the binding to the `#[primary_span]` field. span_field: SpannedOption, diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index a31bda9ca0d..162699c2868 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -4,12 +4,13 @@ use proc_macro::Span; use proc_macro2::TokenStream; use quote::{format_ident, quote, ToTokens}; +use std::cmp::Ordering; use std::collections::{BTreeSet, HashMap}; use std::fmt; use std::str::FromStr; -use syn::{spanned::Spanned, Attribute, Meta, Type, TypeTuple}; +use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple}; use syn::{MetaList, MetaNameValue, NestedMeta, Path}; -use synstructure::{BindingInfo, Structure}; +use synstructure::{BindStyle, BindingInfo, VariantInfo}; use super::error::invalid_nested_attr; @@ -210,6 +211,8 @@ fn value_ref(&self) -> Option<&T> { } } +pub(super) type FieldMap = HashMap; + 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>; @@ -360,18 +363,13 @@ fn to_tokens(&self, tokens: &mut TokenStream) { /// Build the mapping of field names to fields. This allows attributes to peek values from /// other fields. -pub(crate) fn build_field_mapping<'a>(structure: &Structure<'a>) -> HashMap { - let mut fields_map = HashMap::new(); - - let ast = structure.ast(); - if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data { - for field in fields.iter() { - if let Some(ident) = &field.ident { - fields_map.insert(ident.to_string(), quote! { &self.#ident }); - } +pub(super) fn build_field_mapping<'v>(variant: &VariantInfo<'v>) -> HashMap { + let mut fields_map = FieldMap::new(); + for binding in variant.bindings() { + if let Some(ident) = &binding.ast().ident { + fields_map.insert(ident.to_string(), quote! { #binding }); } } - fields_map } @@ -621,3 +619,66 @@ fn span(&self) -> Option { None } } + +/// Wrapper around `synstructure::BindStyle` which implements `Ord`. +#[derive(PartialEq, Eq)] +pub(super) struct OrderedBindStyle(pub(super) BindStyle); + +impl OrderedBindStyle { + /// Is `BindStyle::Move` or `BindStyle::MoveMut`? + pub(super) fn is_move(&self) -> bool { + matches!(self.0, BindStyle::Move | BindStyle::MoveMut) + } +} + +impl Ord for OrderedBindStyle { + fn cmp(&self, other: &Self) -> Ordering { + match (self.is_move(), other.is_move()) { + // If both `self` and `other` are the same, then ordering is equal. + (true, true) | (false, false) => Ordering::Equal, + // If `self` is not a move then it should be considered less than `other` (so that + // references are sorted first). + (false, _) => Ordering::Less, + // If `self` is a move then it must be greater than `other` (again, so that references + // are sorted first). + (true, _) => Ordering::Greater, + } + } +} + +impl PartialOrd for OrderedBindStyle { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +/// Returns `true` if `field` should generate a `set_arg` call rather than any other diagnostic +/// call (like `span_label`). +pub(super) fn should_generate_set_arg(field: &Field) -> bool { + field.attrs.is_empty() +} + +/// Returns `true` if `field` needs to have code generated in the by-move branch of the +/// generated derive rather than the by-ref branch. +pub(super) fn bind_style_of_field(field: &Field) -> OrderedBindStyle { + let generates_set_arg = should_generate_set_arg(field); + let is_multispan = type_matches_path(&field.ty, &["rustc_errors", "MultiSpan"]); + // FIXME(davidtwco): better support for one field needing to be in the by-move and + // by-ref branches. + let is_subdiagnostic = field + .attrs + .iter() + .map(|attr| attr.path.segments.last().unwrap().ident.to_string()) + .any(|attr| attr == "subdiagnostic"); + + // `set_arg` calls take their argument by-move.. + let needs_move = generates_set_arg + // If this is a `MultiSpan` field then it needs to be moved to be used by any + // attribute.. + || is_multispan + // If this a `#[subdiagnostic]` then it needs to be moved as the other diagnostic is + // unlikely to be `Copy`.. + || is_subdiagnostic; + + OrderedBindStyle(if needs_move { BindStyle::Move } else { BindStyle::Ref }) +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index ad481c14bab..cf04e05095f 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -37,10 +37,12 @@ struct HelloWarn {} #[derive(Diagnostic)] #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] -//~^ ERROR `#[derive(Diagnostic)]` can only be used on structs +//~^ ERROR unsupported type attribute for diagnostic derive enum enum DiagnosticOnEnum { Foo, +//~^ ERROR diagnostic slug not specified Bar, +//~^ ERROR diagnostic slug not specified } #[derive(Diagnostic)] @@ -651,3 +653,21 @@ struct LabelOnStruct { #[primary_span] suggestion: Span, } + +#[derive(Diagnostic)] +enum ExampleEnum { + #[diag(typeck::ambiguous_lifetime_bound)] + Foo { + #[primary_span] + sp: Span, + #[note] + note_sp: Span, + }, + #[diag(typeck::ambiguous_lifetime_bound)] + Bar { + #[primary_span] + sp: Span, + }, + #[diag(typeck::ambiguous_lifetime_bound)] + Baz, +} diff --git a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index 9919b12beaf..68602640a24 100644 --- a/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/src/test/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -1,28 +1,39 @@ -error: `#[derive(Diagnostic)]` can only be used on structs +error: unsupported type attribute for diagnostic derive enum --> $DIR/diagnostic-derive.rs:39:1 | -LL | / #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] -LL | | -LL | | enum DiagnosticOnEnum { -LL | | Foo, -LL | | Bar, -LL | | } - | |_^ +LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: diagnostic slug not specified + --> $DIR/diagnostic-derive.rs:42:5 + | +LL | Foo, + | ^^^ + | + = 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:44:5 + | +LL | Bar, + | ^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[diag = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:48:1 + --> $DIR/diagnostic-derive.rs:50:1 | LL | #[diag = "E0123"] | ^^^^^^^^^^^^^^^^^ error: `#[nonsense(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:53:1 + --> $DIR/diagnostic-derive.rs:55:1 | LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:53:1 + --> $DIR/diagnostic-derive.rs:55:1 | LL | / #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -34,7 +45,7 @@ LL | | struct InvalidStructAttr {} = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(typeck::example_error)]` error: `#[diag("...")]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:60:8 + --> $DIR/diagnostic-derive.rs:62:8 | LL | #[diag("E0123")] | ^^^^^^^ @@ -42,7 +53,7 @@ LL | #[diag("E0123")] = help: a diagnostic slug is required as the first argument error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:60:1 + --> $DIR/diagnostic-derive.rs:62:1 | LL | / #[diag("E0123")] LL | | @@ -53,7 +64,7 @@ LL | | struct InvalidLitNestedAttr {} = 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:71:8 + --> $DIR/diagnostic-derive.rs:73:8 | LL | #[diag(nonsense("foo"), code = "E0123", slug = "foo")] | ^^^^^^^^^^^^^^^ @@ -61,7 +72,7 @@ LL | #[diag(nonsense("foo"), code = "E0123", slug = "foo")] = help: a diagnostic slug is required as the first argument error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:71:1 + --> $DIR/diagnostic-derive.rs:73:1 | LL | / #[diag(nonsense("foo"), code = "E0123", slug = "foo")] LL | | @@ -72,7 +83,7 @@ LL | | struct InvalidNestedStructAttr1 {} = 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:77:8 + --> $DIR/diagnostic-derive.rs:79:8 | LL | #[diag(nonsense = "...", code = "E0123", slug = "foo")] | ^^^^^^^^^^^^^^^^ @@ -80,7 +91,7 @@ LL | #[diag(nonsense = "...", code = "E0123", slug = "foo")] = 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 + --> $DIR/diagnostic-derive.rs:79:42 | LL | #[diag(nonsense = "...", code = "E0123", slug = "foo")] | ^^^^^^^^^^^^ @@ -88,7 +99,7 @@ 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 + --> $DIR/diagnostic-derive.rs:79:1 | LL | / #[diag(nonsense = "...", code = "E0123", slug = "foo")] LL | | @@ -100,13 +111,13 @@ 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:84:8 + --> $DIR/diagnostic-derive.rs:86:8 | LL | #[diag(nonsense = 4, code = "E0123", slug = "foo")] | ^^^^^^^^^^^^ error: `#[diag(slug = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:84:38 + --> $DIR/diagnostic-derive.rs:86:38 | LL | #[diag(nonsense = 4, code = "E0123", slug = "foo")] | ^^^^^^^^^^^^ @@ -114,7 +125,7 @@ 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:84:1 + --> $DIR/diagnostic-derive.rs:86:1 | LL | / #[diag(nonsense = 4, code = "E0123", slug = "foo")] LL | | @@ -126,7 +137,7 @@ 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:91:58 + --> $DIR/diagnostic-derive.rs:93:58 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123", slug = "foo")] | ^^^^^^^^^^^^ @@ -134,49 +145,49 @@ 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:98:5 + --> $DIR/diagnostic-derive.rs:100:5 | LL | #[suggestion = "bar"] | ^^^^^^^^^^^^^^^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:105:8 + --> $DIR/diagnostic-derive.rs:107:8 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:104:8 + --> $DIR/diagnostic-derive.rs:106:8 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:105:49 + --> $DIR/diagnostic-derive.rs:107:49 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456")] | ^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:104:49 + --> $DIR/diagnostic-derive.rs:106:49 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:111:65 + --> $DIR/diagnostic-derive.rs:113:65 | LL | #[diag(typeck::ambiguous_lifetime_bound, code = "E0456", code = "E0457")] | ^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:111:49 + --> $DIR/diagnostic-derive.rs:113: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:116:42 + --> $DIR/diagnostic-derive.rs:118:42 | LL | #[diag(typeck::ambiguous_lifetime_bound, typeck::ambiguous_lifetime_bound, code = "E0456")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -184,7 +195,7 @@ LL | #[diag(typeck::ambiguous_lifetime_bound, typeck::ambiguous_lifetime_bound, = help: diagnostic slug must be the first argument error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:121:1 + --> $DIR/diagnostic-derive.rs:123:1 | LL | struct KindNotProvided {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -192,7 +203,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:124:1 + --> $DIR/diagnostic-derive.rs:126:1 | LL | / #[diag(code = "E0456")] LL | | @@ -202,31 +213,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:135:5 + --> $DIR/diagnostic-derive.rs:137:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ error: `#[nonsense]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:143:5 + --> $DIR/diagnostic-derive.rs:145:5 | LL | #[nonsense] | ^^^^^^^^^^^ error: the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/diagnostic-derive.rs:160:5 + --> $DIR/diagnostic-derive.rs:162:5 | LL | #[label(typeck::label)] | ^^^^^^^^^^^^^^^^^^^^^^^ error: `name` doesn't refer to a field on this type - --> $DIR/diagnostic-derive.rs:168:45 + --> $DIR/diagnostic-derive.rs:170:45 | LL | #[suggestion(typeck::suggestion, code = "{name}")] | ^^^^^^^^ error: invalid format string: expected `'}'` but string was terminated - --> $DIR/diagnostic-derive.rs:173:16 + --> $DIR/diagnostic-derive.rs:175:16 | LL | #[derive(Diagnostic)] | - ^ expected `'}'` in format string @@ -237,7 +248,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:183:15 + --> $DIR/diagnostic-derive.rs:185:15 | LL | #[derive(Diagnostic)] | ^ unmatched `}` in format string @@ -246,19 +257,19 @@ 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:203:5 + --> $DIR/diagnostic-derive.rs:205:5 | LL | #[label(typeck::label)] | ^^^^^^^^^^^^^^^^^^^^^^^ error: suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:221:5 + --> $DIR/diagnostic-derive.rs:223:5 | LL | #[suggestion(typeck::suggestion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[suggestion(nonsense = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:229:18 + --> $DIR/diagnostic-derive.rs:231:18 | LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^^^^^^^^^ @@ -266,13 +277,13 @@ LL | #[suggestion(nonsense = "bar")] = help: only `code` and `applicability` are valid nested attributes error: suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:229:5 + --> $DIR/diagnostic-derive.rs:231:5 | LL | #[suggestion(nonsense = "bar")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[suggestion(msg = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:238:18 + --> $DIR/diagnostic-derive.rs:240:18 | LL | #[suggestion(msg = "bar")] | ^^^^^^^^^^^ @@ -280,13 +291,13 @@ LL | #[suggestion(msg = "bar")] = help: only `code` and `applicability` are valid nested attributes error: suggestion without `code = "..."` - --> $DIR/diagnostic-derive.rs:238:5 + --> $DIR/diagnostic-derive.rs:240:5 | LL | #[suggestion(msg = "bar")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: wrong field type for suggestion - --> $DIR/diagnostic-derive.rs:261:5 + --> $DIR/diagnostic-derive.rs:263:5 | LL | / #[suggestion(typeck::suggestion, code = "This is suggested code")] LL | | @@ -296,55 +307,55 @@ LL | | suggestion: Applicability, = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` error: specified multiple times - --> $DIR/diagnostic-derive.rs:277:24 + --> $DIR/diagnostic-derive.rs:279:24 | LL | suggestion: (Span, Span, Applicability), | ^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:277:18 + --> $DIR/diagnostic-derive.rs:279:18 | LL | suggestion: (Span, Span, Applicability), | ^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:285:33 + --> $DIR/diagnostic-derive.rs:287:33 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:285:18 + --> $DIR/diagnostic-derive.rs:287:18 | LL | suggestion: (Applicability, Applicability, Span), | ^^^^^^^^^^^^^ error: `#[label = ...]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:292:5 + --> $DIR/diagnostic-derive.rs:294:5 | LL | #[label = "bar"] | ^^^^^^^^^^^^^^^^ error: specified multiple times - --> $DIR/diagnostic-derive.rs:443:52 + --> $DIR/diagnostic-derive.rs:445:52 | LL | #[suggestion(typeck::suggestion, code = "...", applicability = "maybe-incorrect")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:445:24 + --> $DIR/diagnostic-derive.rs:447:24 | LL | suggestion: (Span, Applicability), | ^^^^^^^^^^^^^ error: invalid applicability - --> $DIR/diagnostic-derive.rs:451:52 + --> $DIR/diagnostic-derive.rs:453:52 | LL | #[suggestion(typeck::suggestion, code = "...", applicability = "batman")] | ^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[label(foo)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:514:28 + --> $DIR/diagnostic-derive.rs:516:28 | LL | #[label(typeck::label, foo)] | ^^^ @@ -352,19 +363,19 @@ LL | #[label(typeck::label, foo)] = help: a diagnostic slug must be the first argument to the attribute error: `#[label(foo = ...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:522:28 + --> $DIR/diagnostic-derive.rs:524:28 | LL | #[label(typeck::label, foo = "...")] | ^^^^^^^^^^^ error: `#[label(foo(...))]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:530:28 + --> $DIR/diagnostic-derive.rs:532:28 | LL | #[label(typeck::label, foo("..."))] | ^^^^^^^^^^ error: `#[primary_span]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:543:5 + --> $DIR/diagnostic-derive.rs:545:5 | LL | #[primary_span] | ^^^^^^^^^^^^^^^ @@ -372,13 +383,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:563:1 + --> $DIR/diagnostic-derive.rs:565:1 | LL | #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:563:1 + --> $DIR/diagnostic-derive.rs:565:1 | LL | / #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -390,13 +401,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:570:1 + --> $DIR/diagnostic-derive.rs:572:1 | LL | #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:570:1 + --> $DIR/diagnostic-derive.rs:572:1 | LL | / #[warn_(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -408,13 +419,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:577:1 + --> $DIR/diagnostic-derive.rs:579:1 | LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:577:1 + --> $DIR/diagnostic-derive.rs:579:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -426,13 +437,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:584:1 + --> $DIR/diagnostic-derive.rs:586:1 | LL | #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostic slug not specified - --> $DIR/diagnostic-derive.rs:584:1 + --> $DIR/diagnostic-derive.rs:586:1 | LL | / #[lint(typeck::ambiguous_lifetime_bound, code = "E0123")] LL | | @@ -444,19 +455,19 @@ 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 + --> $DIR/diagnostic-derive.rs:595:52 | LL | #[suggestion(typeck::suggestion, code = "...", code = ",,,")] | ^^^^^^^^^^^^ | note: previously specified here - --> $DIR/diagnostic-derive.rs:593:38 + --> $DIR/diagnostic-derive.rs:595:38 | LL | #[suggestion(typeck::suggestion, code = "...", code = ",,,")] | ^^^^^^^^^^^^ error: wrong types for suggestion - --> $DIR/diagnostic-derive.rs:602:24 + --> $DIR/diagnostic-derive.rs:604:24 | LL | suggestion: (Span, usize), | ^^^^^ @@ -464,7 +475,7 @@ 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 + --> $DIR/diagnostic-derive.rs:612:17 | LL | suggestion: (Span,), | ^^^^^^^ @@ -472,13 +483,13 @@ 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 + --> $DIR/diagnostic-derive.rs:619:5 | LL | #[suggestion(typeck::suggestion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:624:1 + --> $DIR/diagnostic-derive.rs:626:1 | LL | #[multipart_suggestion(typeck::suggestion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -486,7 +497,7 @@ 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 + --> $DIR/diagnostic-derive.rs:629:1 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -494,7 +505,7 @@ LL | #[multipart_suggestion()] = help: consider creating a `Subdiagnostic` instead error: `#[multipart_suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:631:5 + --> $DIR/diagnostic-derive.rs:633:5 | LL | #[multipart_suggestion(typeck::suggestion)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -502,7 +513,7 @@ LL | #[multipart_suggestion(typeck::suggestion)] = help: consider creating a `Subdiagnostic` instead error: `#[suggestion(...)]` is not a valid attribute - --> $DIR/diagnostic-derive.rs:639:1 + --> $DIR/diagnostic-derive.rs:641:1 | LL | #[suggestion(typeck::suggestion, code = "...")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -510,7 +521,7 @@ 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 + --> $DIR/diagnostic-derive.rs:650:1 | LL | #[label] | ^^^^^^^^ @@ -518,67 +529,67 @@ 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 + --> $DIR/diagnostic-derive.rs:55:3 | LL | #[nonsense(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^^^^ error: cannot find attribute `nonsense` in this scope - --> $DIR/diagnostic-derive.rs:143:7 + --> $DIR/diagnostic-derive.rs:145:7 | LL | #[nonsense] | ^^^^^^^^ error: cannot find attribute `error` in this scope - --> $DIR/diagnostic-derive.rs:563:3 + --> $DIR/diagnostic-derive.rs:565:3 | LL | #[error(typeck::ambiguous_lifetime_bound, code = "E0123")] | ^^^^^ error: cannot find attribute `warn_` in this scope - --> $DIR/diagnostic-derive.rs:570:3 + --> $DIR/diagnostic-derive.rs:572: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:577:3 + --> $DIR/diagnostic-derive.rs:579: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:584:3 + --> $DIR/diagnostic-derive.rs:586: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 + --> $DIR/diagnostic-derive.rs:626:3 | LL | #[multipart_suggestion(typeck::suggestion)] | ^^^^^^^^^^^^^^^^^^^^ error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:627:3 + --> $DIR/diagnostic-derive.rs:629:3 | LL | #[multipart_suggestion()] | ^^^^^^^^^^^^^^^^^^^^ error: cannot find attribute `multipart_suggestion` in this scope - --> $DIR/diagnostic-derive.rs:631:7 + --> $DIR/diagnostic-derive.rs:633:7 | LL | #[multipart_suggestion(typeck::suggestion)] | ^^^^^^^^^^^^^^^^^^^^ error[E0425]: cannot find value `nonsense` in module `rustc_errors::fluent` - --> $DIR/diagnostic-derive.rs:66:8 + --> $DIR/diagnostic-derive.rs:68:8 | 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:336:10 + --> $DIR/diagnostic-derive.rs:338:10 | LL | #[derive(Diagnostic)] | ^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` @@ -591,7 +602,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 72 previous errors +error: aborting due to 74 previous errors Some errors have detailed explanations: E0277, E0425. For more information about an error, try `rustc --explain E0277`.