From 190fc6a2eff1a63c51d3106e8b28a71249abd766 Mon Sep 17 00:00:00 2001 From: David Wood Date: Mon, 12 Feb 2024 14:50:47 +0000 Subject: [PATCH 1/4] macros: implement raw fluent structs w/out translation Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 24 +++- .../src/diagnostics/diagnostic_builder.rs | 116 +++++++++++++----- .../src/diagnostics/subdiagnostic.rs | 16 ++- .../rustc_macros/src/diagnostics/utils.rs | 18 ++- compiler/rustc_macros/src/lib.rs | 2 + compiler/rustc_parse/messages.ftl | 4 - compiler/rustc_parse/src/errors.rs | 4 +- .../session-diagnostic/diagnostic-derive.rs | 13 ++ .../subdiagnostic-derive.rs | 28 +++++ 9 files changed, 173 insertions(+), 52 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 027c97330b117..91129dadcf6af 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -2,7 +2,7 @@ use std::cell::RefCell; -use crate::diagnostics::diagnostic_builder::DiagnosticDeriveKind; +use crate::diagnostics::diagnostic_builder::{DiagnosticDeriveKind, SlugOrRawFluent}; use crate::diagnostics::error::{span_err, DiagnosticDeriveError}; use crate::diagnostics::utils::SetOnce; use proc_macro2::TokenStream; @@ -38,7 +38,7 @@ impl<'a> DiagnosticDerive<'a> { .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - Some(slug) + Some(SlugOrRawFluent::Slug(slug)) if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { @@ -48,7 +48,7 @@ impl<'a> DiagnosticDerive<'a> { .emit(); return DiagnosticDeriveError::ErrorHandled.to_compile_error(); } - Some(slug) => { + Some(SlugOrRawFluent::Slug(slug)) => { slugs.borrow_mut().push(slug.clone()); quote! { let mut diag = rustc_errors::DiagnosticBuilder::new( @@ -58,6 +58,15 @@ impl<'a> DiagnosticDerive<'a> { ); } } + Some(SlugOrRawFluent::RawFluent(raw)) => { + quote! { + let mut diag = rustc_errors::DiagnosticBuilder::new( + dcx, + level, + #raw, + ); + } + } }; let formatting_init = &builder.formatting_init; @@ -136,7 +145,7 @@ impl<'a> LintDiagnosticDerive<'a> { .emit(); DiagnosticDeriveError::ErrorHandled.to_compile_error() } - Some(slug) + Some(SlugOrRawFluent::Slug(slug)) if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { @@ -146,12 +155,17 @@ impl<'a> LintDiagnosticDerive<'a> { .emit(); DiagnosticDeriveError::ErrorHandled.to_compile_error() } - Some(slug) => { + Some(SlugOrRawFluent::Slug(slug)) => { slugs.borrow_mut().push(slug.clone()); quote! { crate::fluent_generated::#slug.into() } } + Some(SlugOrRawFluent::RawFluent(raw)) => { + quote! { + #raw + } + } } }); diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index ae481efb263df..294cd700104e1 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -10,8 +10,8 @@ use crate::diagnostics::utils::{ }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; -use syn::Token; use syn::{parse_quote, spanned::Spanned, Attribute, Meta, Path, Type}; +use syn::{LitStr, Token}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; @@ -23,6 +23,18 @@ pub(crate) enum DiagnosticDeriveKind { LintDiagnostic, } +/// Temporary type while both slugs and raw Fluent messages are supported. +pub(crate) enum SlugOrRawFluent { + /// Path to item corresponding to the slug of the diagnostic's message from the Fluent resource. + /// + /// e.g. `#[diag(foo_bar)]` + Slug(Path), + /// Literal string containing the Fluent message. + /// + /// e.g. `#[diag_raw(message = "raw fluent content")]` + RawFluent(LitStr), +} + /// 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. @@ -42,7 +54,7 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - pub slug: SpannedOption, + pub slug: SpannedOption, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. @@ -143,7 +155,7 @@ impl DiagnosticDeriveVariantBuilder { fn parse_subdiag_attribute( &self, attr: &Attribute, - ) -> Result, DiagnosticDeriveError> { + ) -> Result, DiagnosticDeriveError> { let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. @@ -155,13 +167,15 @@ impl DiagnosticDeriveVariantBuilder { .help("consider creating a `Subdiagnostic` instead")); } - let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind { - 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!(), + let slug = subdiag.slug.unwrap_or_else(|| { + SlugOrRawFluent::Slug(match subdiag.kind { + 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(Some((subdiag.kind, slug, subdiag.no_span))) @@ -184,13 +198,16 @@ impl DiagnosticDeriveVariantBuilder { let mut first = true; - if name == "diag" { + if name == "diag" || name == "diag_raw" { let mut tokens = TokenStream::new(); attr.parse_nested_meta(|nested| { let path = &nested.path; - if first && (nested.input.is_empty() || nested.input.peek(Token![,])) { - self.slug.set_once(path.clone(), path.span().unwrap()); + if first + && name == "diag" + && (nested.input.is_empty() || nested.input.peek(Token![,])) + { + self.slug.set_once(SlugOrRawFluent::Slug(path.clone()), path.span().unwrap()); first = false; return Ok(()); } @@ -206,7 +223,13 @@ impl DiagnosticDeriveVariantBuilder { return Ok(()); }; - if path.is_ident("code") { + if path.is_ident("message") { + self.slug.set_once( + SlugOrRawFluent::RawFluent(nested.parse::()?), + path.span().unwrap(), + ); + return Ok(()); + } else if path.is_ident("code") { self.code.set_once((), path.span().unwrap()); let code = nested.parse::()?; @@ -388,42 +411,67 @@ impl DiagnosticDeriveVariantBuilder { let style = suggestion_kind.to_suggestion_style(); self.formatting_init.extend(code_init); - Ok(quote! { - diag.span_suggestions_with_style( - #span_field, - crate::fluent_generated::#slug, - #code_field, - #applicability, - #style - ); - }) + match slug { + SlugOrRawFluent::Slug(slug) => Ok(quote! { + diag.span_suggestions_with_style( + #span_field, + crate::fluent_generated::#slug, + #code_field, + #applicability, + #style + ); + }), + SlugOrRawFluent::RawFluent(raw) => Ok(quote! { + diag.span_suggestions_with_style( + #span_field, + #raw, + #code_field, + #applicability, + #style + ); + }), + } } SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), } } /// Adds a spanned subdiagnostic by generating a `diag.span_$kind` call with the current slug - /// and `fluent_attr_identifier`. + /// and the message. fn add_spanned_subdiagnostic( &self, field_binding: TokenStream, kind: &Ident, - fluent_attr_identifier: Path, + content: SlugOrRawFluent, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); - quote! { - diag.#fn_name( - #field_binding, - crate::fluent_generated::#fluent_attr_identifier - ); + match content { + SlugOrRawFluent::Slug(fluent_attr_identifier) => { + quote! { + diag.#fn_name( + #field_binding, + crate::fluent_generated::#fluent_attr_identifier + ); + } + } + SlugOrRawFluent::RawFluent(raw) => { + quote! { diag.#fn_name(#field_binding, #raw); } + } } } /// 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 { - quote! { - diag.#kind(crate::fluent_generated::#fluent_attr_identifier); + /// and the message. + fn add_subdiagnostic(&self, kind: &Ident, content: SlugOrRawFluent) -> TokenStream { + match content { + SlugOrRawFluent::Slug(fluent_attr_identifier) => { + quote! { + diag.#kind(crate::fluent_generated::#fluent_attr_identifier); + } + } + SlugOrRawFluent::RawFluent(raw) => { + quote! { diag.#kind(#raw); } + } } } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 3a5f289559e6a..04ee8b1540e4a 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -14,6 +14,7 @@ use quote::{format_ident, quote}; use syn::{spanned::Spanned, Attribute, Meta, MetaList, Path}; use synstructure::{BindingInfo, Structure, VariantInfo}; +use super::diagnostic_builder::SlugOrRawFluent; use super::utils::SubdiagnosticVariant; /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. @@ -180,7 +181,7 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { fn identify_kind( &mut self, - ) -> Result, DiagnosticDeriveError> { + ) -> Result, DiagnosticDeriveError> { let mut kind_slugs = vec![]; for attr in self.variant.ast().attrs { @@ -513,9 +514,16 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug, no_span) in kind_slugs { let message = format_ident!("__message"); - calls.extend( - quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); }, - ); + match slug { + SlugOrRawFluent::Slug(slug) => { + calls.extend( + quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); }, + ); + } + SlugOrRawFluent::RawFluent(raw) => { + calls.extend(quote! { let #message = #f(#diag, #raw); }); + } + } let name = format_ident!( "{}{}", diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 4684306e23592..02e5c7ae76111 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -1,3 +1,4 @@ +use crate::diagnostics::diagnostic_builder::SlugOrRawFluent; use crate::diagnostics::error::{ span_err, throw_invalid_attr, throw_span_err, DiagnosticDeriveError, }; @@ -10,7 +11,7 @@ use std::fmt; use std::str::FromStr; use syn::meta::ParseNestedMeta; use syn::punctuated::Punctuated; -use syn::{parenthesized, LitStr, Path, Token}; +use syn::{parenthesized, LitStr, Token}; use syn::{spanned::Spanned, Attribute, Field, Meta, Type, TypeTuple}; use synstructure::{BindingInfo, VariantInfo}; @@ -599,7 +600,7 @@ pub(super) enum SubdiagnosticKind { pub(super) struct SubdiagnosticVariant { pub(super) kind: SubdiagnosticKind, - pub(super) slug: Option, + pub(super) slug: Option, pub(super) no_span: bool, } @@ -707,7 +708,7 @@ impl SubdiagnosticVariant { list.parse_nested_meta(|nested| { if nested.input.is_empty() || nested.input.peek(Token![,]) { if first { - slug = Some(nested.path); + slug = Some(SlugOrRawFluent::Slug(nested.path)); } else if nested.path.is_ident("no_span") { no_span = true; } else { @@ -740,6 +741,17 @@ impl SubdiagnosticVariant { let input = nested.input; match (nested_name, &mut kind) { + ("message", _) => { + let Ok(value) = nested.value() else { + span_err( + nested.input.span().unwrap(), + "diagnostic message must have a value", + ) + .emit(); + return Ok(()); + }; + slug = Some(SlugOrRawFluent::RawFluent(value.parse::()?)); + }, ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( code_field, diff --git a/compiler/rustc_macros/src/lib.rs b/compiler/rustc_macros/src/lib.rs index 619f93c8a533a..6231b202e0672 100644 --- a/compiler/rustc_macros/src/lib.rs +++ b/compiler/rustc_macros/src/lib.rs @@ -93,6 +93,7 @@ decl_derive!( [Diagnostic, attributes( // struct attributes diag, + diag_raw, help, note, warning, @@ -110,6 +111,7 @@ decl_derive!( [LintDiagnostic, attributes( // struct attributes diag, + diag_raw, help, note, warning, diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 7c2ecf34c1754..04778c3c6e19d 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -510,10 +510,6 @@ parse_maybe_recover_from_bad_qpath_stage_2 = parse_maybe_recover_from_bad_type_plus = expected a path on the left-hand side of `+`, not `{$ty}` -parse_maybe_report_ambiguous_plus = - ambiguous `+` in a type - .suggestion = use parentheses to disambiguate - parse_meta_bad_delim = wrong meta list delimiters parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 674f7218ea6d0..8b9c4df2f6ed2 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -16,11 +16,11 @@ use crate::fluent_generated as fluent; use crate::parser::{ForbiddenLetReason, TokenDescription}; #[derive(Diagnostic)] -#[diag(parse_maybe_report_ambiguous_plus)] +#[diag_raw(message = "ambiguous `+` in a type")] pub(crate) struct AmbiguousPlus { pub sum_ty: String, #[primary_span] - #[suggestion(code = "({sum_ty})")] + #[suggestion(message = "use parentheses to disambiguate", code = "({sum_ty})")] pub span: Span, } diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 6cc6fdfc0eb2b..a6904fbaa4792 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -828,3 +828,16 @@ struct SuggestionOnVec { //~^ ERROR `#[suggestion(...)]` is not a valid attribute sub: Vec, } + +#[derive(Diagnostic)] +#[diag_raw(message = "raw diagnostic message")] +struct RawDiag { + #[note(message = "raw note")] + x: Span, + #[help(message = "raw help")] + y: Span, + #[label(message = "raw label")] + z: Span, + #[suggestion(message = "raw label", code = "")] + a: Span, +} diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index 74cf91db7a7c5..505cc82ddb447 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -827,3 +827,31 @@ struct PrimarySpanOnVec { //~| NOTE there must be exactly one primary span sub: Vec, } + +#[derive(Diagnostic)] +#[help(message = "raw diagnostic message")] +struct RawHelp { + #[primary_span] + x: Span, +} + +#[derive(Diagnostic)] +#[note(message = "raw diagnostic message")] +struct RawNote { + #[primary_span] + x: Span, +} + +#[derive(Diagnostic)] +#[suggestion(message = "raw diagnostic message", code = "")] +struct RawSuggestion { + #[primary_span] + x: Span, +} + +#[derive(Diagnostic)] +#[label(message = "raw diagnostic message")] +struct RawLabel { + #[primary_span] + x: Span, +} From f25428700019f5be379431633ffebc73a7191bcd Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 13 Feb 2024 11:02:02 +0000 Subject: [PATCH 2/4] macros: raw fluent translation with args Signed-off-by: David Wood --- compiler/rustc_error_messages/src/lib.rs | 4 +- compiler/rustc_errors/src/lib.rs | 12 ++ compiler/rustc_errors/src/translation.rs | 152 +++++++++++------- .../src/diagnostics/diagnostic.rs | 17 +- .../src/diagnostics/diagnostic_builder.rs | 34 +++- .../src/diagnostics/subdiagnostic.rs | 2 +- compiler/rustc_parse/messages.ftl | 7 - compiler/rustc_parse/src/errors.rs | 8 +- 8 files changed, 155 insertions(+), 81 deletions(-) diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index f91b6655f6399..3808379f26ec8 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -38,12 +38,12 @@ pub type FluentBundle = IntoDynSyncSend>; #[cfg(not(parallel_compiler))] -fn new_bundle(locales: Vec) -> FluentBundle { +pub fn new_bundle(locales: Vec) -> FluentBundle { IntoDynSyncSend(fluent_bundle::bundle::FluentBundle::new(locales)) } #[cfg(parallel_compiler)] -fn new_bundle(locales: Vec) -> FluentBundle { +pub fn new_bundle(locales: Vec) -> FluentBundle { IntoDynSyncSend(fluent_bundle::bundle::FluentBundle::new_concurrent(locales)) } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 064ea8d75163e..76a579d77720c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -17,6 +17,7 @@ #![feature(error_reporter)] #![feature(extract_if)] #![feature(generic_nonzero)] +#![feature(lazy_cell)] #![feature(let_chains)] #![feature(negative_impls)] #![feature(never_type)] @@ -657,6 +658,17 @@ impl DiagCtxt { inner.eagerly_translate_to_string(message, args) } + pub fn raw_translate<'a>( + &self, + slug: &str, + raw: &str, + args: impl Iterator>, + ) -> String { + let inner = self.inner.borrow(); + let args = crate::translation::to_fluent_args(args); + inner.emitter.raw_translate_message(slug, raw, &args) + } + // This is here to not allow mutation of flags; // as of this writing it's used in Session::consider_optimizing and // in tests in rustc_interface. diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 5f074dbbbad30..0d8a7ff14ee30 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -1,12 +1,19 @@ use crate::error::{TranslateError, TranslateErrorKind}; use crate::snippet::Style; -use crate::{DiagnosticArg, DiagnosticMessage, FluentBundle}; +use crate::{DiagnosticArg, DiagnosticMessage}; use rustc_data_structures::sync::Lrc; +use rustc_error_messages::fluent_bundle::FluentResource; pub use rustc_error_messages::FluentArgs; +use rustc_error_messages::{langid, new_bundle, FluentBundle}; use std::borrow::Cow; use std::env; use std::error::Report; +#[cfg(not(parallel_compiler))] +use std::cell::LazyCell as Lazy; +#[cfg(parallel_compiler)] +use std::sync::LazyLock as Lazy; + /// Convert diagnostic arguments (a rustc internal type that exists to implement /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. /// @@ -59,72 +66,101 @@ pub trait Translate { message: &'a DiagnosticMessage, args: &'a FluentArgs<'_>, ) -> Result, TranslateError<'_>> { - trace!(?message, ?args); + let fallback = |translator: &'a Self| translator.fallback_fluent_bundle(); let (identifier, attr) = match message { DiagnosticMessage::Str(msg) | DiagnosticMessage::Translated(msg) => { return Ok(Cow::Borrowed(msg)); } DiagnosticMessage::FluentIdentifier(identifier, attr) => (identifier, attr), }; - let translate_with_bundle = - |bundle: &'a FluentBundle| -> Result, TranslateError<'_>> { - let message = bundle - .get_message(identifier) - .ok_or(TranslateError::message(identifier, args))?; - let value = match attr { - Some(attr) => message - .get_attribute(attr) - .ok_or(TranslateError::attribute(identifier, args, attr))? - .value(), - None => message.value().ok_or(TranslateError::value(identifier, args))?, - }; - debug!(?message, ?value); - - let mut errs = vec![]; - let translated = bundle.format_pattern(value, Some(args), &mut errs); - debug!(?translated, ?errs); - if errs.is_empty() { - Ok(translated) - } else { - Err(TranslateError::fluent(identifier, args, errs)) - } + translate_message(self, fallback, identifier, attr.as_ref(), args) + } + + /// Translate a raw Fluent string. + fn raw_translate_message<'t: 'msg, 'msg>( + &'t self, + slug: &'msg str, + raw: &'msg str, + args: &'msg FluentArgs<'_>, + ) -> String { + let fallback = Lazy::new(move || { + let res = FluentResource::try_new(format!("{slug} = {raw}")) + .expect("failed to parse fallback fluent resource"); + let mut bundle = new_bundle(vec![langid!("en-US")]); + // FIXME(davidtwco): get this value from the option + bundle.set_use_isolating(false); + bundle.add_resource(res).expect("adding resource"); + bundle + }); + let fallback = |_| &*fallback; + let slug = Cow::Borrowed(slug); + let translated = translate_message(self, fallback, &slug, None, args); + translated.map_err(Report::new).unwrap().to_string() + } +} + +fn translate_message<'t: 'bundle, 'bundle: 'msg, 'msg, T: Translate + ?Sized>( + translator: &'t T, + fallback: impl Fn(&'t T) -> &'bundle FluentBundle, + identifier: &'msg Cow<'msg, str>, + attr: Option<&'msg Cow<'msg, str>>, + args: &'msg FluentArgs<'msg>, +) -> Result, TranslateError<'msg>> { + let translate_with_bundle = + |bundle: &'bundle FluentBundle| -> Result, TranslateError<'msg>> { + let message = + bundle.get_message(identifier).ok_or(TranslateError::message(identifier, args))?; + let value = match attr { + Some(attr) => message + .get_attribute(attr) + .ok_or(TranslateError::attribute(identifier, args, attr))? + .value(), + None => message.value().ok_or(TranslateError::value(identifier, args))?, }; + debug!(?message, ?value); - try { - match self.fluent_bundle().map(|b| translate_with_bundle(b)) { - // The primary bundle was present and translation succeeded - Some(Ok(t)) => t, - - // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely - // just that the primary bundle doesn't contain the message being translated, so - // proceed to the fallback bundle. - Some(Err( - primary @ TranslateError::One { - kind: TranslateErrorKind::MessageMissing, .. - }, - )) => translate_with_bundle(self.fallback_fluent_bundle()) - .map_err(|fallback| primary.and(fallback))?, - - // Always yeet out for errors on debug (unless - // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows - // local runs of the test suites, of builds with debug assertions, to test the - // behaviour in a normal build). - Some(Err(primary)) - if cfg!(debug_assertions) - && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() => - { - do yeet primary - } - - // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so - // just hide it and try with the fallback bundle. - Some(Err(primary)) => translate_with_bundle(self.fallback_fluent_bundle()) - .map_err(|fallback| primary.and(fallback))?, - - // The primary bundle is missing, proceed to the fallback bundle - None => translate_with_bundle(self.fallback_fluent_bundle()) - .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(args), &mut errs); + debug!(?translated, ?errs); + if errs.is_empty() { + Ok(translated) + } else { + Err(TranslateError::fluent(identifier, args, errs)) } + }; + + try { + match translator.fluent_bundle().map(|b| translate_with_bundle(b)) { + // The primary bundle was present and translation succeeded + Some(Ok(t)) => t, + + // If `translate_with_bundle` returns `Err` with the primary bundle, this is likely + // just that the primary bundle doesn't contain the message being translated, so + // proceed to the fallback bundle. + Some(Err( + primary @ TranslateError::One { kind: TranslateErrorKind::MessageMissing, .. }, + )) => translate_with_bundle(fallback(translator)) + .map_err(|fallback| primary.and(fallback))?, + + // Always yeet out for errors on debug (unless + // `RUSTC_TRANSLATION_NO_DEBUG_ASSERT` is set in the environment - this allows + // local runs of the test suites, of builds with debug assertions, to test the + // behaviour in a normal build). + Some(Err(primary)) + if cfg!(debug_assertions) + && env::var("RUSTC_TRANSLATION_NO_DEBUG_ASSERT").is_err() => + { + do yeet primary + } + + // ..otherwise, for end users, an error about this wouldn't be useful or actionable, so + // just hide it and try with the fallback bundle. + Some(Err(primary)) => translate_with_bundle(fallback(translator)) + .map_err(|fallback| primary.and(fallback))?, + + // The primary bundle is missing, proceed to the fallback bundle + None => translate_with_bundle(fallback(translator)) + .map_err(|fallback| TranslateError::primary(identifier, args).and(fallback))?, } } } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 91129dadcf6af..481dd029887bb 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -28,6 +28,18 @@ impl<'a> DiagnosticDerive<'a> { let preamble = builder.preamble(variant); let body = builder.body(variant); + let args = if matches!(builder.slug.value_ref(), Some(SlugOrRawFluent::RawFluent(_))) { + let args = builder.args; + quote! { + use rustc_data_structures::fx::FxHashMap; + use rustc_errors::{DiagnosticArgName, DiagnosticArgValue, IntoDiagnosticArg}; + let mut args: FxHashMap = Default::default(); + #(#args)* + } + } else { + quote! {} + }; + let init = match builder.slug.value_ref() { None => { span_err(builder.span, "diagnostic slug not specified") @@ -60,10 +72,11 @@ impl<'a> DiagnosticDerive<'a> { } Some(SlugOrRawFluent::RawFluent(raw)) => { quote! { + let raw = dcx.raw_translate("foo", #raw, args.iter()); let mut diag = rustc_errors::DiagnosticBuilder::new( dcx, level, - #raw, + raw, ); } } @@ -71,6 +84,7 @@ impl<'a> DiagnosticDerive<'a> { let formatting_init = &builder.formatting_init; quote! { + #args #init #formatting_init #preamble @@ -87,6 +101,7 @@ impl<'a> DiagnosticDerive<'a> { where G: rustc_errors::EmissionGuarantee { + #[allow(rustc::potential_query_instability)] #[track_caller] fn into_diagnostic( self, diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 294cd700104e1..d3b49731be22e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -59,6 +59,8 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. pub code: SpannedOption<()>, + + pub args: Vec, } impl HasFieldMap for DiagnosticDeriveVariantBuilder { @@ -109,6 +111,7 @@ impl DiagnosticDeriveKind { formatting_init: TokenStream::new(), slug: None, code: None, + args: Vec::new(), }; f(builder, variant) }); @@ -275,11 +278,19 @@ impl DiagnosticDeriveVariantBuilder { let ident = field.ident.as_ref().unwrap(); let ident = format_ident!("{}", ident); // strip `r#` prefix, if present - quote! { - diag.arg( - stringify!(#ident), - #field_binding - ); + if matches!(self.slug.value_ref(), Some(SlugOrRawFluent::RawFluent(_))) { + // Cloning should not be necessary once there are no `diag.arg` calls. + self.args.push(quote! { + args.insert(stringify!(#ident).into(), #field_binding.clone().into_diagnostic_arg()); + }); + quote! {} + } else { + quote! { + diag.arg( + stringify!(#ident), + #field_binding + ); + } } } @@ -422,9 +433,10 @@ impl DiagnosticDeriveVariantBuilder { ); }), SlugOrRawFluent::RawFluent(raw) => Ok(quote! { + let raw = dcx.raw_translate("foo", #raw, args.iter()); diag.span_suggestions_with_style( #span_field, - #raw, + raw, #code_field, #applicability, #style @@ -455,7 +467,10 @@ impl DiagnosticDeriveVariantBuilder { } } SlugOrRawFluent::RawFluent(raw) => { - quote! { diag.#fn_name(#field_binding, #raw); } + quote! { + let raw = dcx.raw_translate("foo", #raw, args.iter()); + diag.#fn_name(#field_binding, raw); + } } } } @@ -470,7 +485,10 @@ impl DiagnosticDeriveVariantBuilder { } } SlugOrRawFluent::RawFluent(raw) => { - quote! { diag.#kind(#raw); } + quote! { + let raw = dcx.raw_translate("foo", #raw, args.iter()); + diag.#kind(raw); + } } } } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 04ee8b1540e4a..9a1b2bbde0b7a 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -521,7 +521,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { ); } SlugOrRawFluent::RawFluent(raw) => { - calls.extend(quote! { let #message = #f(#diag, #raw); }); + calls.extend(quote! { let #message = #raw; }); } } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 04778c3c6e19d..80f31c27acceb 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -180,8 +180,6 @@ parse_expect_eq_instead_of_eqeq = expected `=`, found `==` parse_expect_label_found_ident = expected a label, found an identifier .suggestion = labels start with a tick -parse_expect_path = expected a path - parse_expected_binding_left_of_at = left-hand side of `@` must be a binding .label_lhs = interpreted as a pattern, not a binding .label_rhs = also a pattern @@ -274,8 +272,6 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet parse_fn_trait_missing_paren = `Fn` bounds require arguments in parentheses .add_paren = add the missing parentheses -parse_forgot_paren = perhaps you forgot parentheses? - parse_found_expr_would_be_stmt = expected expression, found `{$token}` .label = expected expression @@ -507,9 +503,6 @@ parse_maybe_recover_from_bad_qpath_stage_2 = missing angle brackets in associated item path .suggestion = types that don't start with an identifier need to be surrounded with angle brackets in qualified paths -parse_maybe_recover_from_bad_type_plus = - expected a path on the left-hand side of `+`, not `{$ty}` - parse_meta_bad_delim = wrong meta list delimiters parse_meta_bad_delim_suggestion = the delimiters should be `(` and `)` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8b9c4df2f6ed2..62a8470196df5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -25,7 +25,7 @@ pub(crate) struct AmbiguousPlus { } #[derive(Diagnostic)] -#[diag(parse_maybe_recover_from_bad_type_plus, code = E0178)] +#[diag_raw(message = "expected a path on the left-hand side of `+`, not `{$ty}`", code = E0178)] pub(crate) struct BadTypePlus { pub ty: String, #[primary_span] @@ -37,7 +37,7 @@ pub(crate) struct BadTypePlus { #[derive(Subdiagnostic)] pub(crate) enum BadTypePlusSub { #[suggestion( - parse_add_paren, + message = "try adding parentheses", code = "{sum_with_parens}", applicability = "machine-applicable" )] @@ -46,12 +46,12 @@ pub(crate) enum BadTypePlusSub { #[primary_span] span: Span, }, - #[label(parse_forgot_paren)] + #[label(message = "perhaps you forgot parentheses?")] ForgotParen { #[primary_span] span: Span, }, - #[label(parse_expect_path)] + #[label(message = "expected a path")] ExpectPath { #[primary_span] span: Span, From 2edb5e25bb31ae9d1679870942ef28196d6bbd1b Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 14 Feb 2024 10:48:12 +0000 Subject: [PATCH 3/4] macros: compute slugs with raw fluent diags Signed-off-by: David Wood --- .../src/diagnostics/diagnostic.rs | 8 +-- .../src/diagnostics/diagnostic_builder.rs | 51 +++++++++++++------ compiler/rustc_macros/src/diagnostics/mod.rs | 2 + .../src/diagnostics/subdiagnostic.rs | 17 +++++-- .../rustc_macros/src/diagnostics/tests.rs | 7 +++ .../rustc_macros/src/diagnostics/utils.rs | 24 ++++++++- 6 files changed, 85 insertions(+), 24 deletions(-) create mode 100644 compiler/rustc_macros/src/diagnostics/tests.rs diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 481dd029887bb..070a6bd1174ed 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -28,7 +28,7 @@ impl<'a> DiagnosticDerive<'a> { let preamble = builder.preamble(variant); let body = builder.body(variant); - let args = if matches!(builder.slug.value_ref(), Some(SlugOrRawFluent::RawFluent(_))) { + let args = if matches!(builder.slug.value_ref(), Some(SlugOrRawFluent::RawFluent(_, _))) { let args = builder.args; quote! { use rustc_data_structures::fx::FxHashMap; @@ -70,9 +70,9 @@ impl<'a> DiagnosticDerive<'a> { ); } } - Some(SlugOrRawFluent::RawFluent(raw)) => { + Some(SlugOrRawFluent::RawFluent(slug, raw)) => { quote! { - let raw = dcx.raw_translate("foo", #raw, args.iter()); + let raw = dcx.raw_translate(#slug, #raw, args.iter()); let mut diag = rustc_errors::DiagnosticBuilder::new( dcx, level, @@ -176,7 +176,7 @@ impl<'a> LintDiagnosticDerive<'a> { crate::fluent_generated::#slug.into() } } - Some(SlugOrRawFluent::RawFluent(raw)) => { + Some(SlugOrRawFluent::RawFluent(_, raw)) => { quote! { #raw } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index d3b49731be22e..d3f7d4593ab0e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -5,8 +5,8 @@ use crate::diagnostics::error::{ }; use crate::diagnostics::utils::{ build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, - should_generate_arg, type_is_bool, type_is_unit, type_matches_path, FieldInfo, FieldInnerTy, - FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, + should_generate_arg, slugify, type_is_bool, type_is_unit, type_matches_path, FieldInfo, + FieldInnerTy, FieldMap, HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; @@ -29,10 +29,10 @@ pub(crate) enum SlugOrRawFluent { /// /// e.g. `#[diag(foo_bar)]` Slug(Path), - /// Literal string containing the Fluent message. + /// Literal string containing the Fluent message and its computed slug. /// /// e.g. `#[diag_raw(message = "raw fluent content")]` - RawFluent(LitStr), + RawFluent(String, LitStr), } /// Tracks persistent information required for a specific variant when building up individual calls @@ -60,6 +60,7 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// multiple specifications. pub code: SpannedOption<()>, + pub generated_slug: String, pub args: Vec, } @@ -96,8 +97,14 @@ impl DiagnosticDeriveKind { } } + let generated_slug = slugify(&structure.ast().ident.to_string()); structure.bind_with(|_| synstructure::BindStyle::Move); let variants = structure.each_variant(|variant| { + let mut generated_slug = generated_slug.clone(); + if matches!(ast.data, syn::Data::Enum(..)) { + generated_slug.push_str("_"); + generated_slug.push_str(&slugify(&variant.ast().ident.to_string())); + } 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 @@ -111,6 +118,7 @@ impl DiagnosticDeriveKind { formatting_init: TokenStream::new(), slug: None, code: None, + generated_slug, args: Vec::new(), }; f(builder, variant) @@ -157,9 +165,10 @@ impl DiagnosticDeriveVariantBuilder { /// Parse a `SubdiagnosticKind` from an `Attribute`. fn parse_subdiag_attribute( &self, + generated_slug: &str, attr: &Attribute, ) -> Result, DiagnosticDeriveError> { - let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, self)? else { + let Some(subdiag) = SubdiagnosticVariant::from_attr(generated_slug, attr, self)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(None); @@ -228,7 +237,10 @@ impl DiagnosticDeriveVariantBuilder { if path.is_ident("message") { self.slug.set_once( - SlugOrRawFluent::RawFluent(nested.parse::()?), + SlugOrRawFluent::RawFluent( + self.generated_slug.clone(), + nested.parse::()?, + ), path.span().unwrap(), ); return Ok(()); @@ -252,7 +264,9 @@ impl DiagnosticDeriveVariantBuilder { return Ok(tokens); } - let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else { + let Some((subdiag, slug, _no_span)) = + self.parse_subdiag_attribute(&self.generated_slug, attr)? + else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(quote! {}); @@ -278,7 +292,7 @@ impl DiagnosticDeriveVariantBuilder { let ident = field.ident.as_ref().unwrap(); let ident = format_ident!("{}", ident); // strip `r#` prefix, if present - if matches!(self.slug.value_ref(), Some(SlugOrRawFluent::RawFluent(_))) { + if matches!(self.slug.value_ref(), Some(SlugOrRawFluent::RawFluent(_, _))) { // Cloning should not be necessary once there are no `diag.arg` calls. self.args.push(quote! { args.insert(stringify!(#ident).into(), #field_binding.clone().into_diagnostic_arg()); @@ -370,7 +384,14 @@ impl DiagnosticDeriveVariantBuilder { _ => (), } - let Some((subdiag, slug, _no_span)) = self.parse_subdiag_attribute(attr)? else { + let mut generated_slug = self.generated_slug.clone(); + if let Some(field_name) = &info.binding.ast().ident { + generated_slug.push_str("_"); + generated_slug.push_str(&slugify(&field_name.to_string())); + } + let Some((subdiag, slug, _no_span)) = + self.parse_subdiag_attribute(&generated_slug, attr)? + else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. return Ok(quote! {}); @@ -432,8 +453,8 @@ impl DiagnosticDeriveVariantBuilder { #style ); }), - SlugOrRawFluent::RawFluent(raw) => Ok(quote! { - let raw = dcx.raw_translate("foo", #raw, args.iter()); + SlugOrRawFluent::RawFluent(slug, raw) => Ok(quote! { + let raw = dcx.raw_translate(#slug, #raw, args.iter()); diag.span_suggestions_with_style( #span_field, raw, @@ -466,9 +487,9 @@ impl DiagnosticDeriveVariantBuilder { ); } } - SlugOrRawFluent::RawFluent(raw) => { + SlugOrRawFluent::RawFluent(slug, raw) => { quote! { - let raw = dcx.raw_translate("foo", #raw, args.iter()); + let raw = dcx.raw_translate(#slug, #raw, args.iter()); diag.#fn_name(#field_binding, raw); } } @@ -484,9 +505,9 @@ impl DiagnosticDeriveVariantBuilder { diag.#kind(crate::fluent_generated::#fluent_attr_identifier); } } - SlugOrRawFluent::RawFluent(raw) => { + SlugOrRawFluent::RawFluent(slug, raw) => { quote! { - let raw = dcx.raw_translate("foo", #raw, args.iter()); + let raw = dcx.raw_translate(#slug, #raw, args.iter()); diag.#kind(raw); } } diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 33dffe6998a1e..79348f02835cd 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -2,6 +2,8 @@ mod diagnostic; mod diagnostic_builder; mod error; mod subdiagnostic; +#[cfg(test)] +mod tests; mod utils; use diagnostic::{DiagnosticDerive, LintDiagnosticDerive}; diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 9a1b2bbde0b7a..b7ac9f3048208 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -6,8 +6,8 @@ use crate::diagnostics::error::{ use crate::diagnostics::utils::{ build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident, report_error_if_not_applied_to_applicability, report_error_if_not_applied_to_span, - should_generate_arg, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, HasFieldMap, - SetOnce, SpannedOption, SubdiagnosticKind, + should_generate_arg, slugify, AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, + HasFieldMap, SetOnce, SpannedOption, SubdiagnosticKind, }; use proc_macro2::TokenStream; use quote::{format_ident, quote}; @@ -61,8 +61,14 @@ impl SubdiagnosticDeriveBuilder { } } + let generated_slug = slugify(&structure.ast().ident.to_string()); structure.bind_with(|_| synstructure::BindStyle::Move); let variants_ = structure.each_variant(|variant| { + let mut generated_slug = generated_slug.clone(); + if matches!(ast.data, syn::Data::Enum(..)) { + generated_slug.push_str("_"); + generated_slug.push_str(&slugify(&variant.ast().ident.to_string())); + } let mut builder = SubdiagnosticDeriveVariantBuilder { parent: &self, variant, @@ -73,6 +79,7 @@ impl SubdiagnosticDeriveBuilder { applicability: None, has_suggestion_parts: false, is_enum, + generated_slug, }; builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); @@ -132,6 +139,8 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// Set to true when this variant is an enum variant rather than just the body of a struct. is_enum: bool, + + generated_slug: String, } impl<'parent, 'a> HasFieldMap for SubdiagnosticDeriveVariantBuilder<'parent, 'a> { @@ -186,7 +195,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { for attr in self.variant.ast().attrs { let Some(SubdiagnosticVariant { kind, slug, no_span }) = - SubdiagnosticVariant::from_attr(attr, self)? + SubdiagnosticVariant::from_attr(&self.generated_slug, attr, self)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. @@ -520,7 +529,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); }, ); } - SlugOrRawFluent::RawFluent(raw) => { + SlugOrRawFluent::RawFluent(_, raw) => { calls.extend(quote! { let #message = #raw; }); } } diff --git a/compiler/rustc_macros/src/diagnostics/tests.rs b/compiler/rustc_macros/src/diagnostics/tests.rs new file mode 100644 index 0000000000000..8a811955920e2 --- /dev/null +++ b/compiler/rustc_macros/src/diagnostics/tests.rs @@ -0,0 +1,7 @@ +use crate::diagnostics::utils::slugify; + +#[test] +fn slugs() { + assert_eq!(slugify("ExampleStructName"), "example_struct_name"); + assert_eq!(slugify("foo-bar"), "foo_bar"); +} diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 02e5c7ae76111..b3c323e34154b 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -609,6 +609,7 @@ impl SubdiagnosticVariant { /// `#[error(parser::add_paren, no_span)]` or `#[suggestion(code = "...")]`. Returns the /// `SubdiagnosticKind` and the diagnostic slug, if specified. pub(super) fn from_attr( + generated_slug: &str, attr: &Attribute, fields: &impl HasFieldMap, ) -> Result, DiagnosticDeriveError> { @@ -750,7 +751,7 @@ impl SubdiagnosticVariant { .emit(); return Ok(()); }; - slug = Some(SlugOrRawFluent::RawFluent(value.parse::()?)); + slug = Some(SlugOrRawFluent::RawFluent(generated_slug.to_string(), value.parse::()?)); }, ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( @@ -885,3 +886,24 @@ pub(super) fn should_generate_arg(field: &Field) -> bool { pub(super) fn is_doc_comment(attr: &Attribute) -> bool { attr.path().segments.last().unwrap().ident == "doc" } + +pub(super) fn slugify(inpt: &str) -> String { + let mut slug = Vec::with_capacity(inpt.len()); + for c in inpt.chars() { + match c { + 'A'..='Z' => { + if !slug.is_empty() { + slug.push('_'); + } + slug.extend(char::to_lowercase(c)); + } + '-' => { + slug.push('_'); + } + _ => { + slug.push(c); + } + } + } + slug.into_iter().collect() +} From bc616952e270c6ac326f41e91de337b20d9b4792 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 15 Feb 2024 15:44:20 +0000 Subject: [PATCH 4/4] macros: raw translation of subdiagnostics Signed-off-by: David Wood --- compiler/rustc_ast_lowering/src/errors.rs | 8 +- compiler/rustc_ast_passes/src/errors.rs | 10 +- compiler/rustc_builtin_macros/src/errors.rs | 13 ++- compiler/rustc_errors/src/diagnostic.rs | 28 ++---- .../rustc_errors/src/diagnostic_builder.rs | 23 ++++- compiler/rustc_errors/src/diagnostic_impls.rs | 6 +- compiler/rustc_errors/src/lib.rs | 74 ++++++++------- compiler/rustc_hir_typeck/src/errors.rs | 25 +++-- compiler/rustc_hir_typeck/src/expr.rs | 6 +- compiler/rustc_infer/src/errors/mod.rs | 56 +++++------ .../src/errors/note_and_explain.rs | 12 +-- .../nice_region_error/different_lifetimes.rs | 3 +- .../nice_region_error/static_impl_trait.rs | 6 +- .../src/infer/error_reporting/note.rs | 92 ++++++++++++------- compiler/rustc_lint/src/errors.rs | 6 +- compiler/rustc_lint/src/lints.rs | 42 ++++----- .../src/diagnostics/subdiagnostic.rs | 60 +++++++++--- compiler/rustc_mir_build/src/errors.rs | 10 +- compiler/rustc_parse/messages.ftl | 1 - compiler/rustc_parse/src/errors.rs | 18 ++-- .../rustc_parse/src/parser/diagnostics.rs | 8 +- compiler/rustc_parse/src/parser/expr.rs | 9 +- compiler/rustc_passes/src/errors.rs | 5 +- compiler/rustc_pattern_analysis/src/errors.rs | 15 +-- compiler/rustc_trait_selection/src/errors.rs | 6 +- 25 files changed, 296 insertions(+), 246 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 274e6b7458c61..a0ee87da92214 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,6 +1,4 @@ -use rustc_errors::{ - codes::*, AddToDiagnostic, Diagnostic, DiagnosticArgFromDisplay, SubdiagnosticMessageOp, -}; +use rustc_errors::{codes::*, AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticArgFromDisplay}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -40,8 +38,8 @@ pub struct InvalidAbi { pub struct InvalidAbiReason(pub &'static str); -impl AddToDiagnostic for InvalidAbiReason { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for InvalidAbiReason { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { #[allow(rustc::untranslatable_diagnostic)] diag.note(self.0); } diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs index 9662c73ca8532..2e5d0b5a90262 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,7 +1,7 @@ //! Errors emitted by ast_passes. use rustc_ast::ParamKindOrd; -use rustc_errors::{codes::*, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -371,8 +371,8 @@ pub struct ArgsBeforeConstraint { pub struct EmptyLabelManySpans(pub Vec); // The derive for `Vec` does multiple calls to `span_label`, adding commas between each -impl AddToDiagnostic for EmptyLabelManySpans { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for EmptyLabelManySpans { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.span_labels(self.0, ""); } } @@ -728,8 +728,8 @@ pub struct StableFeature { pub since: Symbol, } -impl AddToDiagnostic for StableFeature { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for StableFeature { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.arg("name", self.name); diag.arg("since", self.since); diag.help(fluent::ast_passes_stable_since); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 8d2e06bf30dac..b33c643e4645c 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1,6 +1,6 @@ use rustc_errors::{ codes::*, AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee, - IntoDiagnostic, Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp, + IntoDiagnostic, Level, MultiSpan, SingleLabelManySpans, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -610,11 +610,14 @@ pub(crate) struct FormatUnusedArg { // Allow the singular form to be a subdiagnostic of the multiple-unused // form of diagnostic. -impl AddToDiagnostic for FormatUnusedArg { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for FormatUnusedArg { + fn add_to_diagnostic(self, dcx: &'a DiagCtxt, diag: &mut Diagnostic) { diag.arg("named", self.named); - let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into()); - diag.span_label(self.span, msg); + let message = dcx.eagerly_translate( + crate::fluent_generated::builtin_macros_format_unused_arg, + diag.args(), + ); + diag.span_label(self.span, message); } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 034636bea4818..1d4817724b02b 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,7 +1,7 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level, - MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, + CodeSuggestion, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, + Level, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; @@ -66,18 +66,12 @@ impl Into> for DiagnosticArgValue { /// Trait implemented by error types. This should not be implemented manually. Instead, use /// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic]. #[rustc_diagnostic_item = "AddToDiagnostic"] -pub trait AddToDiagnostic +pub trait AddToDiagnostic<'a> where Self: Sized, { /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diagnostic(self, diag: &mut Diagnostic) { - self.add_to_diagnostic_with(diag, |_, m| m); - } - - /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used - /// (to optionally perform eager translation). - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F); + fn add_to_diagnostic(self, dcx: &'a DiagCtxt, diag: &mut Diagnostic); } pub trait SubdiagnosticMessageOp = @@ -855,16 +849,12 @@ impl Diagnostic { /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages /// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of /// interpolated variables). - pub fn subdiagnostic( + pub fn subdiagnostic<'a>( &mut self, - dcx: &crate::DiagCtxt, - subdiagnostic: impl AddToDiagnostic, + dcx: &'a crate::DiagCtxt, + subdiagnostic: impl AddToDiagnostic<'a>, ) -> &mut Self { - subdiagnostic.add_to_diagnostic_with(self, |diag, msg| { - let args = diag.args(); - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - dcx.eagerly_translate(msg, args) - }); + subdiagnostic.add_to_diagnostic(dcx, self); self } @@ -911,7 +901,7 @@ impl Diagnostic { /// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by /// combining it with the primary message of the diagnostic (if translatable, otherwise it just /// passes the user's string along). - pub(crate) fn subdiagnostic_message_to_diagnostic_message( + pub fn subdiagnostic_message_to_diagnostic_message( &self, attr: impl Into, ) -> DiagnosticMessage { diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 0572df69ca947..0952dce758991 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -403,10 +403,25 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { forward!((arg, with_arg)( name: impl Into>, arg: impl IntoDiagnosticArg, )); - forward!((subdiagnostic, with_subdiagnostic)( - dcx: &DiagCtxt, - subdiagnostic: impl crate::AddToDiagnostic, - )); + + /// See [`Diagnostic::subdiagnostic()`]. + pub fn subdiagnostic( + &mut self, + dcx: &'a DiagCtxt, + subdiagnostic: impl crate::AddToDiagnostic<'a>, + ) -> &mut Self { + self.diag.as_mut().unwrap().subdiagnostic(dcx, subdiagnostic); + self + } + /// See [`Diagnostic::subdiagnostic()`]. + pub fn with_subdiagnostic( + mut self, + dcx: &'a DiagCtxt, + subdiagnostic: impl crate::AddToDiagnostic<'a>, + ) -> Self { + self.diag.as_mut().unwrap().subdiagnostic(dcx, subdiagnostic); + self + } } impl Debug for DiagnosticBuilder<'_, G> { diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index eaf75539f59b9..9b58aa284d2c5 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -2,7 +2,7 @@ use crate::diagnostic::DiagnosticLocation; use crate::{fluent_generated as fluent, AddToDiagnostic}; use crate::{ DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, ErrCode, IntoDiagnostic, - IntoDiagnosticArg, Level, SubdiagnosticMessageOp, + IntoDiagnosticArg, Level, }; use rustc_ast as ast; use rustc_ast_pretty::pprust; @@ -298,8 +298,8 @@ pub struct SingleLabelManySpans { pub spans: Vec, pub label: &'static str, } -impl AddToDiagnostic for SingleLabelManySpans { - fn add_to_diagnostic_with(self, diag: &mut crate::Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for SingleLabelManySpans { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut crate::Diagnostic) { diag.span_labels(self.spans, self.label); } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 76a579d77720c..20cfd0c5387a0 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1532,26 +1532,46 @@ impl DiagCtxtInner { self.emit_diagnostic(Diagnostic::new(Note, note2)); } - let mut bug = - if backtrace || self.ice_file.is_none() { bug.decorate(self) } else { bug.inner }; - - // "Undelay" the delayed bugs (into plain `Bug`s). - if bug.level != DelayedBug { - // NOTE(eddyb) not panicking here because we're already producing - // an ICE, and the more information the merrier. - let subdiag = InvalidFlushedDelayedDiagnosticLevel { - span: bug.span.primary_span().unwrap(), - level: bug.level, - }; - // FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it - // just uses `DiagCtxtInner` functions. - subdiag.add_to_diagnostic_with(&mut bug, |diag, msg| { - let args = diag.args(); - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - self.eagerly_translate(msg, args) - }); - } - bug.level = Bug; + // This is a bit gnarly, but the `AddToDiagnostic` type uses a `DiagCtxt` to trigger + // translation. It could take an emitter or a `DiagCtxtInner` but it would make + // every other use of `AddToDiagnostic` more complex. This function is only invoked + // during the `Drop` of `DiagCtxtInner`, so we temporarily get a `T` from the `&mut T` + // here so we can put this back into a lock and back into a `DiagCtxt` to call the + // functions from `AddToDiagnostic`. + // + // FIXME(davidtwco): `AddToDiagnostic` should eventually be merged with + // `IntoDiagnostic`, and then the context determined by an associated type, depending + // on the needs of the specific diagnostic - once this is done, we can make these + // specific diagnostics take an emitter directly. + let bug = unsafe { + let old_dcx = std::ptr::read(self); + let (new_dcx, bug) = std::panic::catch_unwind(panic::AssertUnwindSafe(|| { + let dcx = DiagCtxt { inner: Lock::new(old_dcx) }; + + let mut bug = if backtrace || self.ice_file.is_none() { + bug.decorate(&dcx) + } else { + bug.inner + }; + + // "Undelay" the delayed bugs (into plain `Bug`s). + if bug.level != DelayedBug { + // NOTE(eddyb) not panicking here because we're already producing + // an ICE, and the more information the merrier. + let subdiag = InvalidFlushedDelayedDiagnosticLevel { + span: bug.span.primary_span().unwrap(), + level: bug.level, + }; + subdiag.add_to_diagnostic(&dcx, &mut bug); + } + bug.level = Bug; + + (dcx.inner.into_inner(), bug) + })) + .unwrap_or_else(|_| std::process::abort()); + std::ptr::write(self, new_dcx); + bug + }; self.emit_diagnostic(bug); } @@ -1583,15 +1603,7 @@ impl DelayedDiagnostic { DelayedDiagnostic { inner: diagnostic, note: backtrace } } - fn decorate(mut self, dcx: &DiagCtxtInner) -> Diagnostic { - // FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it - // just uses `DiagCtxtInner` functions. - let subdiag_with = |diag: &mut Diagnostic, msg| { - let args = diag.args(); - let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); - dcx.eagerly_translate(msg, args) - }; - + fn decorate(mut self, dcx: &DiagCtxt) -> Diagnostic { match self.note.status() { BacktraceStatus::Captured => { let inner = &self.inner; @@ -1600,7 +1612,7 @@ impl DelayedDiagnostic { emitted_at: inner.emitted_at.clone(), note: self.note, }; - subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with); + subdiag.add_to_diagnostic(dcx, &mut self.inner); } // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. @@ -1611,7 +1623,7 @@ impl DelayedDiagnostic { emitted_at: inner.emitted_at.clone(), note: self.note, }; - subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with); + subdiag.add_to_diagnostic(dcx, &mut self.inner); } } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 1af0b75bd23c0..744029974b88f 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -3,8 +3,8 @@ use std::borrow::Cow; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg, - MultiSpan, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticArgValue, + IntoDiagnosticArg, MultiSpan, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; @@ -194,8 +194,8 @@ pub struct TypeMismatchFruTypo { pub expr: Option, } -impl AddToDiagnostic for TypeMismatchFruTypo { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for TypeMismatchFruTypo { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); // Only explain that `a ..b` is a range if it's split up @@ -369,8 +369,8 @@ pub struct RemoveSemiForCoerce { pub semi: Span, } -impl AddToDiagnostic for RemoveSemiForCoerce { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for RemoveSemiForCoerce { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut multispan: MultiSpan = self.semi.into(); multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr); multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret); @@ -541,18 +541,15 @@ pub enum CastUnknownPointerSub { From(Span), } -impl AddToDiagnostic for CastUnknownPointerSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for CastUnknownPointerSub { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { CastUnknownPointerSub::To(span) => { - let msg = f(diag, crate::fluent_generated::hir_typeck_label_to); - diag.span_label(span, msg); - let msg = f(diag, crate::fluent_generated::hir_typeck_note); - diag.note(msg); + diag.span_label(span, crate::fluent_generated::hir_typeck_label_to); + diag.note(crate::fluent_generated::hir_typeck_note); } CastUnknownPointerSub::From(span) => { - let msg = f(diag, crate::fluent_generated::hir_typeck_label_from); - diag.span_label(span, msg); + diag.span_label(span, crate::fluent_generated::hir_typeck_label_from); } } } diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index a946d59ff2b68..38e0cc83fdcb7 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -26,8 +26,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::unord::UnordMap; use rustc_errors::{ - codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic, - DiagnosticBuilder, ErrorGuaranteed, StashKey, + codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder, + ErrorGuaranteed, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -2611,7 +2611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We know by construction that `.await` is either on Rust 2015 // or results in `ExprKind::Await`. Suggest switching the edition to 2018. err.note("to `.await` a `Future`, switch to Rust 2018 or later"); - HelpUseLatestEdition::new().add_to_diagnostic(&mut err); + err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new()); } err.emit() diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 8bfc05d6a96db..13d404a6a8a83 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,7 +1,7 @@ use hir::GenericParamKind; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, - DiagnosticStyledString, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticMessage, + DiagnosticStyledString, IntoDiagnosticArg, MultiSpan, }; use rustc_hir as hir; use rustc_hir::FnRetTy; @@ -224,8 +224,8 @@ pub enum RegionOriginNote<'a> { }, } -impl AddToDiagnostic for RegionOriginNote<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for RegionOriginNote<'_> { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut label_or_note = |span, msg: DiagnosticMessage| { let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let expanded_sub_count = diag.children.iter().filter(|d| !d.span.is_dummy()).count(); @@ -285,8 +285,8 @@ pub enum LifetimeMismatchLabels { }, } -impl AddToDiagnostic for LifetimeMismatchLabels { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for LifetimeMismatchLabels { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { diag.span_label(param_span, fluent::infer_declared_different); @@ -329,8 +329,8 @@ pub struct AddLifetimeParamsSuggestion<'a> { pub add_note: bool, } -impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for AddLifetimeParamsSuggestion<'_> { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut mk_suggestion = || { let ( hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. }, @@ -427,8 +427,8 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { pub binding_span: Span, } -impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diagnostic_with(mut self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for IntroducesStaticBecauseUnmetLifetimeReq { + fn add_to_diagnostic(mut self, _: &'a DiagCtxt, diag: &mut Diagnostic) { self.unmet_requirements .push_span_label(self.binding_span, fluent::infer_msl_introduces_static); diag.span_note(self.unmet_requirements, fluent::infer_msl_unmet_req); @@ -742,15 +742,13 @@ pub struct ConsiderBorrowingParamHelp { pub spans: Vec, } -impl AddToDiagnostic for ConsiderBorrowingParamHelp { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for ConsiderBorrowingParamHelp { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut type_param_span: MultiSpan = self.spans.clone().into(); for &span in &self.spans { - // Seems like we can't call f() here as Into is required type_param_span.push_span_label(span, fluent::infer_tid_consider_borrowing); } - let msg = f(diag, fluent::infer_tid_param_help.into()); - diag.span_help(type_param_span, msg); + diag.span_help(type_param_span, fluent::infer_tid_param_help); } } @@ -783,17 +781,15 @@ pub struct DynTraitConstraintSuggestion { pub ident: Ident, } -impl AddToDiagnostic for DynTraitConstraintSuggestion { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for DynTraitConstraintSuggestion { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut multi_span: MultiSpan = vec![self.span].into(); multi_span.push_span_label(self.span, fluent::infer_dtcs_has_lifetime_req_label); multi_span.push_span_label(self.ident.span, fluent::infer_dtcs_introduces_requirement); - let msg = f(diag, fluent::infer_dtcs_has_req_note.into()); - diag.span_note(multi_span, msg); - let msg = f(diag, fluent::infer_dtcs_suggestion.into()); + diag.span_note(multi_span, fluent::infer_dtcs_has_req_note); diag.span_suggestion_verbose( self.span.shrink_to_hi(), - msg, + fluent::infer_dtcs_suggestion, " + '_", Applicability::MaybeIncorrect, ); @@ -826,8 +822,8 @@ pub struct ReqIntroducedLocations { pub add_label: bool, } -impl AddToDiagnostic for ReqIntroducedLocations { - fn add_to_diagnostic_with(mut self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for ReqIntroducedLocations { + fn add_to_diagnostic(mut self, _: &'a DiagCtxt, diag: &mut Diagnostic) { for sp in self.spans { self.span.push_span_label(sp, fluent::infer_ril_introduced_here); } @@ -836,8 +832,7 @@ impl AddToDiagnostic for ReqIntroducedLocations { self.span.push_span_label(self.fn_decl_span, fluent::infer_ril_introduced_by); } self.span.push_span_label(self.cause_span, fluent::infer_ril_because_of); - let msg = f(diag, fluent::infer_ril_static_introduced_by.into()); - diag.span_note(self.span, msg); + diag.span_note(self.span, fluent::infer_ril_static_introduced_by); } } @@ -845,8 +840,8 @@ pub struct MoreTargeted { pub ident: Symbol, } -impl AddToDiagnostic for MoreTargeted { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _f: F) { +impl<'a> AddToDiagnostic<'a> for MoreTargeted { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.code(E0772); diag.primary_message(fluent::infer_more_targeted); diag.arg("ident", self.ident); @@ -1264,12 +1259,11 @@ pub struct SuggestTuplePatternMany { pub compatible_variants: Vec, } -impl AddToDiagnostic for SuggestTuplePatternMany { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for SuggestTuplePatternMany { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.arg("path", self.path); - let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); diag.multipart_suggestions( - message, + crate::fluent_generated::infer_stp_wrap_many, self.compatible_variants.into_iter().map(|variant| { vec![ (self.cause_span.shrink_to_lo(), format!("{variant}(")), diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index a59a4df77296d..45fd7a968af1a 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,6 +1,6 @@ use crate::fluent_generated as fluent; use crate::infer::error_reporting::nice_region_error::find_anon_type; -use rustc_errors::{AddToDiagnostic, Diagnostic, IntoDiagnosticArg, SubdiagnosticMessageOp}; +use rustc_errors::{AddToDiagnostic, DiagCtxt, Diagnostic, IntoDiagnosticArg}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -159,18 +159,18 @@ impl RegionExplanation<'_> { } } -impl AddToDiagnostic for RegionExplanation<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl<'a> AddToDiagnostic<'a> for RegionExplanation<'_> { + fn add_to_diagnostic(self, dcx: &'a DiagCtxt, diag: &mut Diagnostic) { diag.arg("pref_kind", self.prefix); diag.arg("suff_kind", self.suffix); diag.arg("desc_kind", self.desc.kind); diag.arg("desc_arg", self.desc.arg); - let msg = f(diag, fluent::infer_region_explanation.into()); + let message = dcx.eagerly_translate(fluent::infer_region_explanation, diag.args()); if let Some(span) = self.desc.span { - diag.span_note(span, msg); + diag.span_note(span, message); } else { - diag.note(msg); + diag.note(message); } } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index adb3267d5bee7..e1bf43ca564cb 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -11,7 +11,6 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::SubregionOrigin; use crate::infer::TyCtxt; -use rustc_errors::AddToDiagnostic; use rustc_errors::{Diagnostic, ErrorGuaranteed}; use rustc_hir::Ty; use rustc_middle::ty::Region; @@ -145,5 +144,5 @@ pub fn suggest_adding_lifetime_params<'tcx>( err: &mut Diagnostic, ) { let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false }; - suggestion.add_to_diagnostic(err); + err.subdiagnostic(tcx.dcx(), suggestion); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs index c8fd4e3a69286..ac66866517935 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -9,7 +9,7 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; use rustc_data_structures::fx::FxIndexSet; -use rustc_errors::{AddToDiagnostic, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{walk_ty, Visitor}; use rustc_hir::{ @@ -235,7 +235,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { // Provide a more targeted error code and description. let retarget_subdiag = MoreTargeted { ident }; - retarget_subdiag.add_to_diagnostic(&mut err); + err.subdiagnostic(self.tcx().dcx(), retarget_subdiag); } let arg = match param.param.pat.simple_ident() { @@ -533,7 +533,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { hir_v.visit_ty(self_ty); for &span in &traits { let subdiag = DynTraitConstraintSuggestion { span, ident }; - subdiag.add_to_diagnostic(err); + err.subdiagnostic(self.tcx().dcx(), subdiag); suggested = true; } } diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs index 50ac6235debc0..bedaea6c6d63f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/note.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -5,7 +5,7 @@ use crate::errors::{ use crate::fluent_generated as fluent; use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; use crate::infer::{self, SubregionOrigin}; -use rustc_errors::{AddToDiagnostic, Diagnostic, DiagnosticBuilder}; +use rustc_errors::{Diagnostic, DiagnosticBuilder}; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_middle::traits::ObligationCauseCode; use rustc_middle::ty::error::TypeError; @@ -17,58 +17,82 @@ use super::ObligationCauseAsDiagArg; impl<'tcx> TypeErrCtxt<'_, 'tcx> { pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { match *origin { - infer::Subtype(ref trace) => RegionOriginNote::WithRequirement { - span: trace.cause.span, - requirement: ObligationCauseAsDiagArg(trace.cause.clone()), - expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), + infer::Subtype(ref trace) => { + err.subdiagnostic( + self.dcx(), + RegionOriginNote::WithRequirement { + span: trace.cause.span, + requirement: ObligationCauseAsDiagArg(trace.cause.clone()), + expected_found: self.values_str(trace.values).map(|(e, f, _)| (e, f)), + }, + ); } - .add_to_diagnostic(err), infer::Reborrow(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err) + err.subdiagnostic( + self.dcx(), + RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }, + ); } infer::RelateObjectBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }, + ); } infer::ReferenceOutlivesReferent(ty, span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_reference_outlives_referent, - name: &self.ty_to_string(ty), - continues: false, - } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::WithName { + span, + msg: fluent::infer_reference_outlives_referent, + name: &self.ty_to_string(ty), + continues: false, + }, + ); } infer::RelateParamBound(span, ty, opt_span) => { - RegionOriginNote::WithName { - span, - msg: fluent::infer_relate_param_bound, - name: &self.ty_to_string(ty), - continues: opt_span.is_some(), - } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::WithName { + span, + msg: fluent::infer_relate_param_bound, + name: &self.ty_to_string(ty), + continues: opt_span.is_some(), + }, + ); if let Some(span) = opt_span { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }, + ); } } infer::RelateRegionParamBound(span) => { - RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }, + ); } infer::CompareImplItemObligation { span, .. } => { - RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::Plain { + span, + msg: fluent::infer_compare_impl_item_obligation, + }, + ); } infer::CheckAssociatedTypeBounds { ref parent, .. } => { self.note_region_origin(err, parent); } infer::AscribeUserTypeProvePredicate(span) => { - RegionOriginNote::Plain { - span, - msg: fluent::infer_ascribe_user_type_prove_predicate, - } - .add_to_diagnostic(err); + err.subdiagnostic( + self.dcx(), + RegionOriginNote::Plain { + span, + msg: fluent::infer_ascribe_user_type_prove_predicate, + }, + ); } } } diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 21d4b6fa65b5b..f1de54e20e6fe 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,5 +1,5 @@ use crate::fluent_generated as fluent; -use rustc_errors::{codes::*, AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{codes::*, AddToDiagnostic, DiagCtxt, Diagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; @@ -23,8 +23,8 @@ pub enum OverruledAttributeSub { CommandLineSource, } -impl AddToDiagnostic for OverruledAttributeSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for OverruledAttributeSub { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(fluent::lint_default_source); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c204c67fc1f7c..0b87e3d9c931f 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -5,8 +5,8 @@ use std::num::NonZero; use crate::errors::RequestedLevel; use crate::fluent_generated as fluent; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DecorateLint, Diagnostic, DiagnosticBuilder, - DiagnosticMessage, DiagnosticStyledString, SubdiagnosticMessageOp, SuggestionStyle, + codes::*, AddToDiagnostic, Applicability, DecorateLint, DiagCtxt, Diagnostic, + DiagnosticBuilder, DiagnosticMessage, DiagnosticStyledString, SuggestionStyle, }; use rustc_hir::def_id::DefId; use rustc_macros::{LintDiagnostic, Subdiagnostic}; @@ -267,8 +267,8 @@ pub struct SuggestChangingAssocTypes<'a, 'b> { pub ty: &'a rustc_hir::Ty<'b>, } -impl AddToDiagnostic for SuggestChangingAssocTypes<'_, '_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for SuggestChangingAssocTypes<'_, '_> { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { // Access to associates types should use `::Assoc`, which does not need a // bound. Let's see if this type does that. @@ -319,8 +319,8 @@ pub struct BuiltinTypeAliasGenericBoundsSuggestion { pub suggestions: Vec<(Span, String)>, } -impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for BuiltinTypeAliasGenericBoundsSuggestion { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.multipart_suggestion( fluent::lint_suggestion, self.suggestions, @@ -423,7 +423,7 @@ impl<'a> DecorateLint<'a, ()> for BuiltinUnpermittedTypeInit<'_> { fluent::lint_builtin_unpermitted_type_init_label_suggestion, ); } - self.sub.add_to_diagnostic(diag); + diag.subdiagnostic(diag.dcx, self.sub); } fn msg(&self) -> DiagnosticMessage { @@ -436,8 +436,8 @@ pub struct BuiltinUnpermittedTypeInitSub { pub err: InitError, } -impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for BuiltinUnpermittedTypeInitSub { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut err = self.err; loop { if let Some(span) = err.span { @@ -487,8 +487,8 @@ pub struct BuiltinClashingExternSub<'a> { pub found: Ty<'a>, } -impl AddToDiagnostic for BuiltinClashingExternSub<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for BuiltinClashingExternSub<'_> { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let mut expected_str = DiagnosticStyledString::new(); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); let mut found_str = DiagnosticStyledString::new(); @@ -765,8 +765,8 @@ pub struct HiddenUnicodeCodepointsDiagLabels { pub spans: Vec<(char, Span)>, } -impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for HiddenUnicodeCodepointsDiagLabels { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { for (c, span) in self.spans { diag.span_label(span, format!("{c:?}")); } @@ -779,8 +779,8 @@ pub enum HiddenUnicodeCodepointsDiagSub { } // Used because of multiple multipart_suggestion and note -impl AddToDiagnostic for HiddenUnicodeCodepointsDiagSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for HiddenUnicodeCodepointsDiagSub { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { diag.multipart_suggestion_with_style( @@ -927,8 +927,8 @@ pub struct NonBindingLetSub { pub is_assign_desugar: bool, } -impl AddToDiagnostic for NonBindingLetSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for NonBindingLetSub { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; if can_suggest_binding { @@ -1207,8 +1207,8 @@ pub enum NonSnakeCaseDiagSub { SuggestionAndNote { span: Span }, } -impl AddToDiagnostic for NonSnakeCaseDiagSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for NonSnakeCaseDiagSub { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { NonSnakeCaseDiagSub::Label { span } => { diag.span_label(span, fluent::lint_label); @@ -1400,8 +1400,8 @@ pub enum OverflowingBinHexSign { Negative, } -impl AddToDiagnostic for OverflowingBinHexSign { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for OverflowingBinHexSign { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { OverflowingBinHexSign::Positive => { diag.note(fluent::lint_positive_note); diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index b7ac9f3048208..81807ab69d77e 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -19,15 +19,15 @@ use super::utils::SubdiagnosticVariant; /// The central struct for constructing the `add_to_diagnostic` method from an annotated struct. pub(crate) struct SubdiagnosticDeriveBuilder { + dcx: syn::Ident, diag: syn::Ident, - f: syn::Ident, } impl SubdiagnosticDeriveBuilder { pub(crate) fn new() -> Self { + let dcx = format_ident!("dcx"); let diag = format_ident!("diag"); - let f = format_ident!("f"); - Self { diag, f } + Self { dcx, diag } } pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream { @@ -80,6 +80,7 @@ impl SubdiagnosticDeriveBuilder { has_suggestion_parts: false, is_enum, generated_slug, + args: Vec::new(), }; builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); @@ -91,14 +92,16 @@ impl SubdiagnosticDeriveBuilder { } }; + let dcx = &self.dcx; let diag = &self.diag; - let f = &self.f; let ret = structure.gen_impl(quote! { - gen impl rustc_errors::AddToDiagnostic for @Self { - fn add_to_diagnostic_with<__F>(self, #diag: &mut rustc_errors::Diagnostic, #f: __F) - where - __F: rustc_errors::SubdiagnosticMessageOp, - { + gen impl<'_ctxt> rustc_errors::AddToDiagnostic<'_ctxt> for @Self { + #[allow(rustc::potential_query_instability)] + fn add_to_diagnostic( + self, + #dcx: &'_ctxt rustc_errors::DiagCtxt, + #diag: &mut rustc_errors::Diagnostic + ) { #implementation } } @@ -140,6 +143,7 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> { /// Set to true when this variant is an enum variant rather than just the body of a struct. is_enum: bool, + pub args: Vec, generated_slug: String, } @@ -231,6 +235,10 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let ident = field.ident.as_ref().unwrap(); let ident = format_ident!("{}", ident); // strip `r#` prefix, if present + self.args.push(quote! { + args.insert(stringify!(#ident).into(), #field_binding.clone().into_diagnostic_arg()); + }); + quote! { #diag.arg( stringify!(#ident), @@ -518,19 +526,28 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let span_field = self.span_field.value_ref(); + let mut any_raw_fluent = false; + + let dcx = &self.parent.dcx; let diag = &self.parent.diag; - let f = &self.parent.f; let mut calls = TokenStream::new(); for (kind, slug, no_span) in kind_slugs { let message = format_ident!("__message"); match slug { SlugOrRawFluent::Slug(slug) => { - calls.extend( - quote! { let #message = #f(#diag, crate::fluent_generated::#slug.into()); }, - ); + calls.extend(quote! { + let args_ = #diag.args(); + let msg: rustc_errors::SubdiagnosticMessage = crate::fluent_generated::#slug.into(); + let msg = #diag.subdiagnostic_message_to_diagnostic_message(msg); + let #message = #dcx.eagerly_translate_to_string(msg, args_); + }); } - SlugOrRawFluent::RawFluent(_, raw) => { - calls.extend(quote! { let #message = #raw; }); + SlugOrRawFluent::RawFluent(slug, raw) => { + any_raw_fluent = true; + calls.extend(quote! { + let raw = #dcx.raw_translate(#slug, #raw, args.iter()); + let #message = raw; + }); } } @@ -611,8 +628,21 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { .map(|binding| self.generate_field_arg(binding)) .collect(); + let args = if any_raw_fluent { + let args = &self.args; + quote! { + use rustc_data_structures::fx::FxHashMap; + use rustc_errors::{DiagnosticArgName, DiagnosticArgValue, IntoDiagnosticArg}; + let mut args: FxHashMap = Default::default(); + #(#args)* + } + } else { + quote! {} + }; + let formatting_init = &self.formatting_init; Ok(quote! { + #args #init #formatting_init #attr_args diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 0bc5fe6ef89ac..7fb0ad79a93ee 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -2,7 +2,7 @@ use crate::fluent_generated as fluent; use rustc_errors::DiagnosticArgValue; use rustc_errors::{ codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp, + IntoDiagnostic, Level, MultiSpan, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::{self, Ty}; @@ -419,8 +419,8 @@ pub struct UnsafeNotInheritedLintNote { pub body_span: Span, } -impl AddToDiagnostic for UnsafeNotInheritedLintNote { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for UnsafeNotInheritedLintNote { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body); let body_start = self.body_span.shrink_to_lo(); let body_end = self.body_span.shrink_to_hi(); @@ -862,8 +862,8 @@ pub struct Variant { pub span: Span, } -impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a, 'tcx> AddToDiagnostic<'a> for AdtDefinedHere<'tcx> { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.arg("ty", self.ty); let mut spans = MultiSpan::from(self.adt_def_span); diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 80f31c27acceb..1fa45efac92f1 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -707,7 +707,6 @@ parse_sugg_add_semi = add `;` here parse_sugg_change_inner_attr_to_outer = to annotate the {$item}, change the attribute from inner to outer style parse_sugg_change_this_to_semi = change this to `;` -parse_sugg_escape_identifier = escape `{$ident_name}` to use it as an identifier parse_sugg_remove_comma = remove this comma parse_sugg_remove_leading_vert_in_pattern = remove the `|` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 62a8470196df5..f2f17671a16e9 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -4,7 +4,7 @@ use rustc_ast::token::Token; use rustc_ast::{Path, Visibility}; use rustc_errors::{ codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - IntoDiagnostic, Level, SubdiagnosticMessageOp, + IntoDiagnostic, Level, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::errors::ExprParenthesesNeeded; @@ -1007,7 +1007,7 @@ pub(crate) struct InvalidMetaItemSuggQuoteIdent { #[derive(Subdiagnostic)] #[suggestion( - parse_sugg_escape_identifier, + message = "escape `{$ident_name}` to use it as an identifier", style = "verbose", applicability = "maybe-incorrect", code = "r#" @@ -1101,17 +1101,17 @@ impl<'a> IntoDiagnostic<'a> for ExpectedIdentifier { diag.arg("token", self.token); if let Some(sugg) = self.suggest_raw { - sugg.add_to_diagnostic(&mut diag); + diag.subdiagnostic(dcx, sugg); } - ExpectedIdentifierFound::new(token_descr, self.span).add_to_diagnostic(&mut diag); + diag.subdiagnostic(dcx, ExpectedIdentifierFound::new(token_descr, self.span)); if let Some(sugg) = self.suggest_remove_comma { - sugg.add_to_diagnostic(&mut diag); + diag.subdiagnostic(dcx, sugg); } if let Some(help) = self.help_cannot_start_number { - help.add_to_diagnostic(&mut diag); + diag.subdiagnostic(dcx, help); } diag @@ -1162,7 +1162,7 @@ impl<'a> IntoDiagnostic<'a> for ExpectedSemi { diag.span_label(unexpected_token_label, fluent::parse_label_unexpected_token); } - self.sugg.add_to_diagnostic(&mut diag); + diag.subdiagnostic(dcx, self.sugg); diag } @@ -1474,8 +1474,8 @@ pub(crate) struct FnTraitMissingParen { pub machine_applicable: bool, } -impl AddToDiagnostic for FnTraitMissingParen { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for FnTraitMissingParen { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); let applicability = if self.machine_applicable { Applicability::MachineApplicable diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 659716548d95d..c74569afb61d8 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -34,8 +34,8 @@ use rustc_ast::{ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; use rustc_errors::{ - pluralize, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - ErrorGuaranteed, FatalError, PErr, PResult, + pluralize, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError, + PErr, PResult, }; use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::source_map::Spanned; @@ -665,7 +665,7 @@ impl<'a> Parser<'a> { { err.note("you may be trying to write a c-string literal"); err.note("c-string literals require Rust 2021 or later"); - HelpUseLatestEdition::new().add_to_diagnostic(&mut err); + err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new()); } // `pub` may be used for an item or `pub(crate)` @@ -1267,7 +1267,7 @@ impl<'a> Parser<'a> { Ok(_) => { if self.token == token::Eq { let sugg = SuggAddMissingLetStmt { span: prev_span }; - sugg.add_to_diagnostic(err); + err.subdiagnostic(self.dcx(), sugg); } } Err(e) => { diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 20b9581f2ef27..e511b68839808 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -25,9 +25,7 @@ use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLim use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind}; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey, -}; +use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, PResult, StashKey}; use rustc_lexer::unescape::unescape_char; use rustc_macros::Subdiagnostic; use rustc_session::errors::{report_lit_error, ExprParenthesesNeeded}; @@ -3437,10 +3435,11 @@ impl<'a> Parser<'a> { let mut recover_async = false; let in_if_guard = self.restrictions.contains(Restrictions::IN_IF_GUARD); + let dcx = self.dcx(); let mut async_block_err = |e: &mut Diagnostic, span: Span| { recover_async = true; - errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e); - errors::HelpUseLatestEdition::new().add_to_diagnostic(e); + e.subdiagnostic(dcx, errors::AsyncBlockIn2015 { span }); + e.subdiagnostic(dcx, errors::HelpUseLatestEdition::new()); }; while self.token != token::CloseDelim(close_delim) { diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a732bdbca5115..f0182a81f3d03 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -8,7 +8,6 @@ use rustc_ast::Label; use rustc_errors::{ codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, DiagnosticSymbolList, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, - SubdiagnosticMessageOp, }; use rustc_hir::{self as hir, ExprKind, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -1753,8 +1752,8 @@ pub struct UnusedVariableStringInterp { pub hi: Span, } -impl AddToDiagnostic for UnusedVariableStringInterp { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for UnusedVariableStringInterp { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation); diag.multipart_suggestion( crate::fluent_generated::passes_string_interpolation_only_works, diff --git a/compiler/rustc_pattern_analysis/src/errors.rs b/compiler/rustc_pattern_analysis/src/errors.rs index 2dffdc9846c22..f2f6ef790382a 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,4 +1,3 @@ -use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::thir::Pat; use rustc_middle::ty::Ty; @@ -56,22 +55,14 @@ pub struct OverlappingRangeEndpoints<'tcx> { pub overlap: Vec>, } +#[derive(Subdiagnostic)] +#[label(message = "this range overlaps on `{$range}`...")] pub struct Overlap<'tcx> { + #[primary_span] pub span: Span, pub range: Pat<'tcx>, } -impl<'tcx> AddToDiagnostic for Overlap<'tcx> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { - let Overlap { span, range } = self; - - // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` - // does not support `#[subdiagnostic(eager)]`... - let message = format!("this range overlaps on `{range}`..."); - diag.span_label(span, message); - } -} - #[derive(LintDiagnostic)] #[diag(pattern_analysis_non_exhaustive_omitted_pattern)] #[help] diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 407fff03e1588..0dc83af029b4c 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,7 +1,7 @@ use crate::fluent_generated as fluent; use rustc_errors::{ codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp, + EmissionGuarantee, IntoDiagnostic, Level, }; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; @@ -101,8 +101,8 @@ pub enum AdjustSignatureBorrow { RemoveBorrow { remove_borrow: Vec<(Span, String)> }, } -impl AddToDiagnostic for AdjustSignatureBorrow { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { +impl<'a> AddToDiagnostic<'a> for AdjustSignatureBorrow { + fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diag.arg("len", to_borrow.len());