diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 28be1a9fdf8eb..af8f723ff378d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -4,6 +4,7 @@ use std::collections::BTreeMap; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxIndexMap; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Applicability, Diag, DiagMessage, EmissionGuarantee, MultiSpan, listify, msg}; use rustc_hir::def::{CtorKind, Namespace}; use rustc_hir::{ @@ -1309,12 +1310,9 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { && !spans.is_empty() { let mut span: MultiSpan = spans.clone().into(); - err.arg("ty", param_ty.to_string()); - let msg = err.dcx.eagerly_format_to_string( - msg!("`{$ty}` is made to be an `FnOnce` closure here"), - err.args.iter(), - ); - err.remove_arg("ty"); + let msg = msg!("`{$ty}` is made to be an `FnOnce` closure here") + .arg("ty", param_ty.to_string()) + .format(); for sp in spans { span.push_span_label(sp, msg.clone()); } diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 961644b977592..39d210e14a098 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -1,4 +1,5 @@ use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan, SingleLabelManySpans, Subdiagnostic, msg, @@ -763,15 +764,17 @@ pub(crate) struct FormatUnusedArg { // form of diagnostic. impl Subdiagnostic for FormatUnusedArg { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.arg("named", self.named); - let msg = diag.eagerly_format(msg!( - "{$named -> - [true] named argument - *[false] argument - } never used" - )); - diag.remove_arg("named"); - diag.span_label(self.span, msg); + diag.span_label( + self.span, + msg!( + "{$named -> + [true] named argument + *[false] argument + } never used" + ) + .arg("named", self.named) + .format(), + ); } } @@ -946,17 +949,14 @@ pub(crate) struct AsmClobberNoReg { impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AsmClobberNoReg { fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, G> { - // eager translation as `span_labels` takes `AsRef` - let lbl1 = dcx.eagerly_format_to_string(msg!("clobber_abi"), [].into_iter()); - let lbl2 = dcx.eagerly_format_to_string(msg!("generic outputs"), [].into_iter()); Diag::new( dcx, level, msg!("asm with `clobber_abi` must specify explicit registers for outputs"), ) .with_span(self.spans.clone()) - .with_span_labels(self.clobbers, &lbl1) - .with_span_labels(self.spans, &lbl2) + .with_span_labels(self.clobbers, "clobber_abi") + .with_span_labels(self.spans, "generic outputs") } } diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index 89bfdfbd9e579..a7b5bdbf7bdff 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -2,7 +2,9 @@ use std::ffi::CString; use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, msg}; +use rustc_errors::{ + Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, format_diag_message, msg, +}; use rustc_macros::Diagnostic; use rustc_span::Span; @@ -24,7 +26,7 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { let diag: Diag<'_, G> = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); - let message = dcx.eagerly_format_to_string(message.clone(), diag.args.iter()); + let message = format_diag_message(message, &diag.args); Diag::new( dcx, level, diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 13e78259e2680..d64cf8e032935 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -265,6 +265,7 @@ fn build_error_for_const_call<'tcx>( } } CallKind::FnCall { fn_trait_id, self_ty } => { + let kind = ccx.const_kind(); let note = match self_ty.kind() { FnDef(def_id, ..) => { let span = tcx.def_span(*def_id); @@ -274,8 +275,8 @@ fn build_error_for_const_call<'tcx>( Some(errors::NonConstClosureNote::FnDef { span }) } - FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr), - Closure(..) => Some(errors::NonConstClosureNote::Closure), + FnPtr(..) => Some(errors::NonConstClosureNote::FnPtr { kind }), + Closure(..) => Some(errors::NonConstClosureNote::Closure { kind }), _ => None, }; diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 4797b039c9624..07d001e9d847f 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -4,9 +4,10 @@ use std::fmt::Write; use either::Either; use rustc_abi::WrappingRange; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ - Diag, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, - Subdiagnostic, msg, + Diag, DiagArgMap, DiagArgValue, DiagMessage, Diagnostic, EmissionGuarantee, Level, MultiSpan, + Subdiagnostic, format_diag_message, msg, }; use rustc_hir::ConstContext; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -359,14 +360,11 @@ pub struct FrameNote { impl Subdiagnostic for FrameNote { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.arg("times", self.times); - diag.arg("where_", self.where_); - diag.arg("instance", self.instance); let mut span: MultiSpan = self.span.into(); if self.has_label && !self.span.is_dummy() { span.push_span_label(self.span, msg!("the failure occurred here")); } - let msg = diag.eagerly_format(msg!( + let msg = msg!( r#"{$times -> [0] inside {$where_ -> [closure] closure @@ -379,10 +377,11 @@ impl Subdiagnostic for FrameNote { *[other] {""} } ...] }"# - )); - diag.remove_arg("times"); - diag.remove_arg("where_"); - diag.remove_arg("instance"); + ) + .arg("times", self.times) + .arg("where_", self.where_) + .arg("instance", self.instance) + .format(); diag.span_note(span, msg); } } @@ -534,7 +533,7 @@ pub enum NonConstClosureNote { *[other] {""} }s"# )] - FnPtr, + FnPtr { kind: ConstContext }, #[note( r#"closures need an RFC before allowed to be called in {$kind -> [const] constant @@ -543,7 +542,7 @@ pub enum NonConstClosureNote { *[other] {""} }s"# )] - Closure, + Closure { kind: ConstContext }, } #[derive(Subdiagnostic)] @@ -624,7 +623,7 @@ pub trait ReportErrorExt { let mut diag = dcx.struct_allow(DiagMessage::Str(String::new().into())); let message = self.diagnostic_message(); self.add_args(&mut diag); - let s = dcx.eagerly_format_to_string(message, diag.args.iter()); + let s = format_diag_message(&message, &diag.args).into_owned(); diag.cancel(); s }) @@ -1086,12 +1085,12 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } let message = if let Some(path) = self.path { - err.dcx.eagerly_format_to_string( - msg!("constructing invalid value at {$path}"), - [("path".into(), DiagArgValue::Str(path.into()))].iter().map(|(a, b)| (a, b)), + format_diag_message( + &msg!("constructing invalid value at {$path}"), + &DiagArgMap::from_iter([("path".into(), DiagArgValue::Str(path.into()))]), ) } else { - err.dcx.eagerly_format_to_string(msg!("constructing invalid value"), [].into_iter()) + Cow::Borrowed("constructing invalid value") }; err.arg("front_matter", message); @@ -1117,12 +1116,13 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { msg!("in the range {$lo}..={$hi}") }; - let args = [ - ("lo".into(), DiagArgValue::Str(lo.to_string().into())), - ("hi".into(), DiagArgValue::Str(hi.to_string().into())), - ]; - let args = args.iter().map(|(a, b)| (a, b)); - let message = err.dcx.eagerly_format_to_string(msg, args); + let message = format_diag_message( + &msg, + &DiagArgMap::from_iter([ + ("lo".into(), DiagArgValue::Str(lo.to_string().into())), + ("hi".into(), DiagArgValue::Str(hi.to_string().into())), + ]), + ); err.arg("in_range", message); } @@ -1132,19 +1132,18 @@ impl<'tcx> ReportErrorExt for ValidationErrorInfo<'tcx> { } PointerAsInt { expected } | Uninit { expected } => { let msg = match expected { - ExpectedKind::Reference => msg!("expected a reference"), - ExpectedKind::Box => msg!("expected a box"), - ExpectedKind::RawPtr => msg!("expected a raw pointer"), - ExpectedKind::InitScalar => msg!("expected initialized scalar value"), - ExpectedKind::Bool => msg!("expected a boolean"), - ExpectedKind::Char => msg!("expected a unicode scalar value"), - ExpectedKind::Float => msg!("expected a floating point number"), - ExpectedKind::Int => msg!("expected an integer"), - ExpectedKind::FnPtr => msg!("expected a function pointer"), - ExpectedKind::EnumTag => msg!("expected a valid enum tag"), - ExpectedKind::Str => msg!("expected a string"), + ExpectedKind::Reference => "expected a reference", + ExpectedKind::Box => "expected a box", + ExpectedKind::RawPtr => "expected a raw pointer", + ExpectedKind::InitScalar => "expected initialized scalar value", + ExpectedKind::Bool => "expected a boolean", + ExpectedKind::Char => "expected a unicode scalar value", + ExpectedKind::Float => "expected a floating point number", + ExpectedKind::Int => "expected an integer", + ExpectedKind::FnPtr => "expected a function pointer", + ExpectedKind::EnumTag => "expected a valid enum tag", + ExpectedKind::Str => "expected a string", }; - let msg = err.dcx.eagerly_format_to_string(msg, [].into_iter()); err.arg("expected", msg); } InvalidEnumTag { value } diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 0878584c8769c..1653bbdaa36d7 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -1,7 +1,7 @@ use either::{Left, Right}; use rustc_abi::{Align, HasDataLayout, Size, TargetDataLayout}; use rustc_data_structures::debug_assert_matches; -use rustc_errors::{DiagCtxtHandle, msg}; +use rustc_errors::{DiagCtxtHandle, format_diag_message, msg}; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_middle::mir::interpret::{ErrorHandled, InvalidMetaKind, ReportedErrorInfo}; @@ -235,9 +235,9 @@ pub fn format_interp_error<'tcx>(dcx: DiagCtxtHandle<'_>, e: InterpErrorInfo<'tc let mut diag = dcx.struct_allow(""); let msg = e.diagnostic_message(); e.add_args(&mut diag); - let s = dcx.eagerly_format_to_string(msg, diag.args.iter()); + let msg = format_diag_message(&msg, &diag.args).into_owned(); diag.cancel(); - s + msg } impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index cfc697b521fc5..ff212e7c01f0c 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -235,9 +235,6 @@ pub struct DiagInner { pub suggestions: Suggestions, pub args: DiagArgMap, - // This is used to store args and restore them after a subdiagnostic is rendered. - pub reserved_args: DiagArgMap, - /// This is not used for highlighting or rendering any error message. Rather, it can be used /// as a sort key to sort a buffer of diagnostics. By default, it is the primary span of /// `span` if there is one. Otherwise, it is `DUMMY_SP`. @@ -268,7 +265,6 @@ impl DiagInner { children: vec![], suggestions: Suggestions::Enabled(vec![]), args: Default::default(), - reserved_args: Default::default(), sort_span: DUMMY_SP, is_lint: None, long_ty_path: None, @@ -333,14 +329,6 @@ impl DiagInner { self.args.swap_remove(name); } - pub fn store_args(&mut self) { - self.reserved_args = self.args.clone(); - } - - pub fn restore_args(&mut self) { - self.args = std::mem::take(&mut self.reserved_args); - } - pub fn emitted_at_sub_diag(&self) -> Subdiag { let track = format!("-Ztrack-diagnostics: created at {}", self.emitted_at); Subdiag { @@ -1143,16 +1131,6 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self } - /// Fluent variables are not namespaced from each other, so when - /// `Diagnostic`s and `Subdiagnostic`s use the same variable name, - /// one value will clobber the other. Eagerly formatting the - /// diagnostic uses the variables defined right then, before the - /// clobbering occurs. - pub fn eagerly_format(&self, msg: impl Into) -> DiagMessage { - let args = self.args.iter(); - self.dcx.eagerly_format(msg.into(), args) - } - with_fn! { with_span, /// Add a span. pub fn span(&mut self, sp: impl Into) -> &mut Self { @@ -1340,12 +1318,6 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> { self.downgrade_to_delayed_bug(); self.emit() } - - pub fn remove_arg(&mut self, name: &str) { - if let Some(diag) = self.diag.as_mut() { - diag.remove_arg(name); - } - } } /// Destructor bomb: every `Diag` must be consumed (emitted, cancelled, etc.) diff --git a/compiler/rustc_errors/src/formatting.rs b/compiler/rustc_errors/src/formatting.rs index 1e0d9b7f5f92a..7b617031d6c8e 100644 --- a/compiler/rustc_errors/src/formatting.rs +++ b/compiler/rustc_errors/src/formatting.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; pub use rustc_error_messages::FluentArgs; -use rustc_error_messages::{DiagArgMap, langid, register_functions}; +use rustc_error_messages::{DiagArgMap, DiagArgName, IntoDiagArg, langid, register_functions}; use tracing::{debug, trace}; use crate::fluent_bundle::FluentResource; @@ -33,30 +33,72 @@ pub fn format_diag_messages( /// Convert a `DiagMessage` to a string pub fn format_diag_message<'a>(message: &'a DiagMessage, args: &DiagArgMap) -> Cow<'a, str> { - trace!(?message, ?args); - match message { DiagMessage::Str(msg) => Cow::Borrowed(msg), - DiagMessage::Inline(msg) => { - const GENERATED_MSG_ID: &str = "generated_msg"; - let resource = - FluentResource::try_new(format!("{GENERATED_MSG_ID} = {msg}\n")).unwrap(); - let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); - bundle.set_use_isolating(false); - bundle.add_resource(resource).unwrap(); - register_functions(&mut bundle); - let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); - let value = message.value().unwrap(); - let args = to_fluent_args(args.iter()); - - let mut errs = vec![]; - let formatted = bundle.format_pattern(value, Some(&args), &mut errs).to_string(); - debug!(?formatted, ?errs); - if errs.is_empty() { - Cow::Owned(formatted) - } else { - panic!("Fluent errors while formatting message: {errs:?}"); - } - } + DiagMessage::Inline(msg) => format_fluent_str(msg, args), + } +} + +fn format_fluent_str(message: &str, args: &DiagArgMap) -> Cow<'static, str> { + trace!(?message, ?args); + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap(); + let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); + bundle.set_use_isolating(false); + bundle.add_resource(resource).unwrap(); + register_functions(&mut bundle); + let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); + let value = message.value().unwrap(); + let args = to_fluent_args(args.iter()); + + let mut errs = vec![]; + let formatted = bundle.format_pattern(value, Some(&args), &mut errs).to_string(); + debug!(?formatted, ?errs); + if errs.is_empty() { + Cow::Owned(formatted) + } else { + panic!("Fluent errors while formatting message: {errs:?}"); + } +} + +pub trait DiagMessageAddArg { + fn arg(self, name: impl Into, arg: impl IntoDiagArg) -> EagerDiagMessageBuilder; +} + +pub struct EagerDiagMessageBuilder { + fluent_str: Cow<'static, str>, + args: DiagArgMap, +} + +impl DiagMessageAddArg for EagerDiagMessageBuilder { + fn arg( + mut self, + name: impl Into, + arg: impl IntoDiagArg, + ) -> EagerDiagMessageBuilder { + let name = name.into(); + let value = arg.into_diag_arg(&mut None); + debug_assert!( + !self.args.contains_key(&name) || self.args.get(&name) == Some(&value), + "arg {} already exists", + name + ); + self.args.insert(name, value); + self + } +} + +impl DiagMessageAddArg for DiagMessage { + fn arg(self, name: impl Into, arg: impl IntoDiagArg) -> EagerDiagMessageBuilder { + let DiagMessage::Inline(fluent_str) = self else { + panic!("Tried to eagerly format an already formatted message") + }; + EagerDiagMessageBuilder { fluent_str, args: Default::default() }.arg(name, arg) + } +} + +impl EagerDiagMessageBuilder { + pub fn format(self) -> DiagMessage { + DiagMessage::Str(format_fluent_str(&self.fluent_str, &self.args)) } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 7fc3e4a45d0ab..e3c2d93aff1a8 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -66,7 +66,8 @@ use rustc_span::{DUMMY_SP, Span}; use tracing::debug; use crate::emitter::TimingEvent; -use crate::formatting::format_diag_message; +use crate::formatting::DiagMessageAddArg; +pub use crate::formatting::format_diag_message; use crate::timings::TimingRecord; pub mod annotate_snippet_emitter_writer; @@ -482,26 +483,6 @@ impl DiagCtxt { self.inner.borrow_mut().emitter = emitter; } - /// Format `message` eagerly with `args` to `DiagMessage::Eager`. - pub fn eagerly_format<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> DiagMessage { - let inner = self.inner.borrow(); - inner.eagerly_format(message, args) - } - - /// Format `message` eagerly with `args` to `String`. - pub fn eagerly_format_to_string<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> String { - let inner = self.inner.borrow(); - inner.eagerly_format_to_string(message, 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. @@ -1417,33 +1398,6 @@ impl DiagCtxtInner { self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) } - /// Format `message` eagerly with `args` to `DiagMessage::Eager`. - fn eagerly_format<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> DiagMessage { - DiagMessage::Str(Cow::from(self.eagerly_format_to_string(message, args))) - } - - /// Format `message` eagerly with `args` to `String`. - fn eagerly_format_to_string<'a>( - &self, - message: DiagMessage, - args: impl Iterator>, - ) -> String { - let args = args.map(|(name, val)| (name.clone(), val.clone())).collect(); - format_diag_message(&message, &args).to_string() - } - - fn eagerly_format_for_subdiag( - &self, - diag: &DiagInner, - msg: impl Into, - ) -> DiagMessage { - self.eagerly_format(msg.into(), diag.args.iter()) - } - fn flush_delayed(&mut self) { // Stashed diagnostics must be emitted before delayed bugs are flushed. // Otherwise, we might ICE prematurely when errors would have @@ -1493,7 +1447,7 @@ impl DiagCtxtInner { ); } - let mut bug = if decorate { bug.decorate(self) } else { bug.inner }; + let mut bug = if decorate { bug.decorate() } else { bug.inner }; // "Undelay" the delayed bugs into plain bugs. if bug.level != DelayedBug { @@ -1503,11 +1457,9 @@ impl DiagCtxtInner { // We are at the `DiagInner`/`DiagCtxtInner` level rather than // the usual `Diag`/`DiagCtxt` level, so we must augment `bug` // in a lower-level fashion. - bug.arg("level", bug.level); let msg = msg!( "`flushed_delayed` got diagnostic with level {$level}, instead of the expected `DelayedBug`" - ); - let msg = self.eagerly_format_for_subdiag(&bug, msg); // after the `arg` call + ).arg("level", bug.level).format(); bug.sub(Note, msg, bug.span.primary_span().unwrap().into()); } bug.level = Bug; @@ -1542,7 +1494,7 @@ impl DelayedDiagInner { DelayedDiagInner { inner: diagnostic, note: backtrace } } - fn decorate(self, dcx: &DiagCtxtInner) -> DiagInner { + fn decorate(self) -> DiagInner { // We are at the `DiagInner`/`DiagCtxtInner` level rather than the // usual `Diag`/`DiagCtxt` level, so we must construct `diag` in a // lower-level fashion. @@ -1555,10 +1507,10 @@ impl DelayedDiagInner { // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. _ => msg!("delayed at {$emitted_at} - {$note}"), - }; - diag.arg("emitted_at", diag.emitted_at.clone()); - diag.arg("note", self.note); - let msg = dcx.eagerly_format_for_subdiag(&diag, msg); // after the `arg` calls + } + .arg("emitted_at", diag.emitted_at.clone()) + .arg("note", self.note) + .format(); diag.sub(Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into()); diag } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 52f6b126a7d0e..5c6a66403019c 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -987,13 +987,17 @@ impl rustc_errors::Subdiagnostic for CastUnknownPointerSub { fn add_to_diag(self, diag: &mut Diag<'_, G>) { match self { CastUnknownPointerSub::To(span) => { - let msg = diag.eagerly_format(msg!("needs more type information")); + let msg = msg!("needs more type information"); diag.span_label(span, msg); - let msg = diag.eagerly_format(msg!("the type information given here is insufficient to check whether the pointer cast is valid")); + let msg = msg!( + "the type information given here is insufficient to check whether the pointer cast is valid" + ); diag.note(msg); } CastUnknownPointerSub::From(span) => { - let msg = diag.eagerly_format(msg!("the type information given here is insufficient to check whether the pointer cast is valid")); + let msg = msg!( + "the type information given here is insufficient to check whether the pointer cast is valid" + ); diag.span_label(span, msg); } } diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 85b881cddc2b8..54c8c75b88fcb 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -2242,10 +2242,10 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitOutlivesRequirements { EXPLICIT_OUTLIVES_REQUIREMENTS, lint_spans.clone(), BuiltinExplicitOutlives { - count: bound_count, suggestion: BuiltinExplicitOutlivesSuggestion { spans: lint_spans, applicability, + count: bound_count, }, }, ); diff --git a/compiler/rustc_lint/src/if_let_rescope.rs b/compiler/rustc_lint/src/if_let_rescope.rs index 0a754a7af03b2..cc8229b3f387b 100644 --- a/compiler/rustc_lint/src/if_let_rescope.rs +++ b/compiler/rustc_lint/src/if_let_rescope.rs @@ -354,9 +354,8 @@ impl Subdiagnostic for IfLetRescopeRewrite { .chain(repeat_n('}', closing_brackets.count)) .collect(), )); - let msg = diag.eagerly_format(msg!( - "a `match` with a single arm can preserve the drop order up to Edition 2021" - )); + let msg = + msg!("a `match` with a single arm can preserve the drop order up to Edition 2021"); diag.multipart_suggestion_with_style( msg, suggestions, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 5597c5b4c839a..d8b62e81b0cbc 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3,6 +3,7 @@ use std::num::NonZero; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Applicability, Diag, DiagArgValue, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, ElidedLifetimeInPathSubdiag, EmissionGuarantee, Level, MultiSpan, Subdiagnostic, @@ -492,7 +493,6 @@ pub(crate) struct BuiltinKeywordIdents { #[derive(Diagnostic)] #[diag("outlives requirements can be inferred")] pub(crate) struct BuiltinExplicitOutlives { - pub count: usize, #[subdiagnostic] pub suggestion: BuiltinExplicitOutlivesSuggestion, } @@ -509,6 +509,7 @@ pub(crate) struct BuiltinExplicitOutlivesSuggestion { pub spans: Vec, #[applicability] pub applicability: Applicability, + pub count: usize, } #[derive(Diagnostic)] @@ -3628,9 +3629,9 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { } Explicit { lifetime_name, suggestions, optional_alternative } => { - diag.arg("lifetime_name", lifetime_name); - let msg = diag.eagerly_format(msg!("consistently use `{$lifetime_name}`")); - diag.remove_arg("lifetime_name"); + let msg = msg!("consistently use `{$lifetime_name}`") + .arg("lifetime_name", lifetime_name) + .format(); diag.multipart_suggestion_with_style( msg, suggestions, diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index dfc5c7e800c65..11bf904c18e1b 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -64,26 +64,46 @@ fn verify_variables_used(msg_span: Span, message_str: &str, variant: Option<&Var fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> { let mut refs = vec![]; + if let Some(Pattern { elements }) = &msg.value { for elt in elements { - if let PatternElement::Placeable { - expression: Expression::Inline(InlineExpression::VariableReference { id }), - } = elt - { - refs.push(id.name); - } + traverse_pattern(elt, &mut refs); } } for attr in &msg.attributes { for elt in &attr.value.elements { - if let PatternElement::Placeable { - expression: Expression::Inline(InlineExpression::VariableReference { id }), - } = elt - { - refs.push(id.name); + traverse_pattern(elt, &mut refs); + } + } + + fn traverse_pattern<'a>(elem: &PatternElement<&'a str>, refs: &mut Vec<&'a str>) { + match elem { + PatternElement::TextElement { .. } => {} + PatternElement::Placeable { expression } => traverse_expression(expression, refs), + } + } + fn traverse_expression<'a>(expr: &Expression<&'a str>, refs: &mut Vec<&'a str>) { + match expr { + Expression::Select { selector, variants } => { + traverse_inline_expr(selector, refs); + for variant in variants { + for pattern in &variant.value.elements { + traverse_pattern(pattern, refs); + } + } + } + Expression::Inline(expr) => { + traverse_inline_expr(expr, refs); } } } + fn traverse_inline_expr<'a>(elem: &InlineExpression<&'a str>, refs: &mut Vec<&'a str>) { + match elem { + InlineExpression::VariableReference { id } => refs.push(id.name), + _ => {} + } + } + refs } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 6237421322089..3e094ee8d42b6 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -227,9 +227,9 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let ident = format_ident!("{}", ident); // strip `r#` prefix, if present quote! { - #diag.arg( - stringify!(#ident), - #field_binding + sub_args.insert( + stringify!(#ident).into(), + rustc_errors::IntoDiagArg::into_diag_arg(#field_binding, &mut #diag.long_ty_path) ); } } @@ -529,14 +529,25 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } }; - let span_field = self.span_field.value_ref(); + let plain_args: TokenStream = self + .variant + .bindings() + .iter() + .filter(|binding| should_generate_arg(binding.ast())) + .map(|binding| self.generate_field_arg(binding)) + .collect(); + let plain_args = quote! { + let mut sub_args = rustc_errors::DiagArgMap::default(); + #plain_args + }; + let span_field = self.span_field.value_ref(); let diag = &self.parent.diag; let mut calls = TokenStream::new(); for (kind, messages) in kind_messages { let message = format_ident!("__message"); let message_stream = messages.diag_message(Some(self.variant)); - calls.extend(quote! { let #message = #diag.eagerly_format(#message_stream); }); + calls.extend(quote! { let #message = rustc_errors::format_diag_message(&#message_stream, &sub_args); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let call = match kind { @@ -600,19 +611,6 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { calls.extend(call); } - let store_args = quote! { - #diag.store_args(); - }; - let restore_args = quote! { - #diag.restore_args(); - }; - let plain_args: TokenStream = self - .variant - .bindings() - .iter() - .filter(|binding| should_generate_arg(binding.ast())) - .map(|binding| self.generate_field_arg(binding)) - .collect(); let formatting_init = &self.formatting_init; @@ -626,10 +624,10 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { #init #formatting_init #attr_args - #store_args + // #store_args #plain_args #calls - #restore_args + // #restore_args }) } } diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index c8d19f2e920a3..c354279b8ed32 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -5,6 +5,7 @@ use std::rc::Rc; use itertools::Itertools as _; use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::unord::{UnordMap, UnordSet}; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Subdiagnostic, msg}; use rustc_hir::CRATE_HIR_ID; use rustc_hir::def_id::LocalDefId; @@ -524,31 +525,30 @@ struct LocalLabel<'a> { /// A custom `Subdiagnostic` implementation so that the notes are delivered in a specific order impl Subdiagnostic for LocalLabel<'_> { fn add_to_diag(self, diag: &mut rustc_errors::Diag<'_, G>) { - // Because parent uses this field , we need to remove it delay before adding it. - diag.remove_arg("name"); - diag.arg("name", self.name); - diag.remove_arg("is_generated_name"); - diag.arg("is_generated_name", self.is_generated_name); - diag.remove_arg("is_dropped_first_edition_2024"); - diag.arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024); - let msg = diag.eagerly_format(msg!( - "{$is_generated_name -> - [true] this value will be stored in a temporary; let us call it `{$name}` - *[false] `{$name}` calls a custom destructor - }" - )); - diag.span_label(self.span, msg); + diag.span_label( + self.span, + msg!( + "{$is_generated_name -> + [true] this value will be stored in a temporary; let us call it `{$name}` + *[false] `{$name}` calls a custom destructor + }" + ) + .arg("name", self.name) + .arg("is_generated_name", self.is_generated_name) + .format(), + ); for dtor in self.destructors { dtor.add_to_diag(diag); } - let msg = - diag.eagerly_format(msg!( - "{$is_dropped_first_edition_2024 -> - [true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024 - *[false] `{$name}` will be dropped later as of Edition 2024 - }" - )); - diag.span_label(self.span, msg); + diag.span_label(self.span, msg!( + "{$is_dropped_first_edition_2024 -> + [true] up until Edition 2021 `{$name}` is dropped last but will be dropped earlier in Edition 2024 + *[false] `{$name}` will be dropped later as of Edition 2024 + }" + ) + .arg("is_dropped_first_edition_2024", self.is_dropped_first_edition_2024) + .arg("name", self.name) + .format()); } } diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index c1ccb6a298315..f4f7182922050 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1179,6 +1179,7 @@ pub(crate) enum MatchArmBodyWithoutBracesSugg { left: Span, #[suggestion_part(code = " }}")] right: Span, + num_statements: usize, }, #[suggestion( "replace `;` with `,` to end a `match` arm expression", diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e6f73e41f0821..4297086983b40 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3207,6 +3207,7 @@ impl<'a> Parser<'a> { errors::MatchArmBodyWithoutBracesSugg::AddBraces { left: span.shrink_to_lo(), right: span.shrink_to_hi(), + num_statements: stmts.len(), } } else { errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp } diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6c7cc311da2a0..1b1198af41c0e 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength use std::ops::ControlFlow; use itertools::Itertools as _; @@ -579,6 +580,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { errs::GenericParamsFromOuterItemInnerItem { span: *span, descr: kind.descr().to_string(), + is_self, } }), }; diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 54427535c5f6e..63ffdbc37f7d1 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1,4 +1,5 @@ use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, Diagnostic, ElidedLifetimeInPathSubdiag, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, @@ -51,6 +52,7 @@ pub(crate) struct GenericParamsFromOuterItemInnerItem { #[primary_span] pub(crate) span: Span, pub(crate) descr: String, + pub(crate) is_self: bool, } #[derive(Subdiagnostic)] @@ -1364,12 +1366,10 @@ impl Subdiagnostic for FoundItemConfigureOut { let mut multispan: MultiSpan = self.span.into(); match self.item_was { ItemWas::BehindFeature { feature, span } => { - let key = "feature".into(); let value = feature.into_diag_arg(&mut None); - let msg = diag.dcx.eagerly_format_to_string( - msg!("the item is gated behind the `{$feature}` feature"), - [(&key, &value)].into_iter(), - ); + let msg = msg!("the item is gated behind the `{$feature}` feature") + .arg("feature", value) + .format(); multispan.push_span_label(span, msg); } ItemWas::CfgOut { span } => { diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 254a626ce2216..6f63c1d663f13 100644 --- a/compiler/rustc_trait_selection/src/errors.rs +++ b/compiler/rustc_trait_selection/src/errors.rs @@ -1,5 +1,6 @@ use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_errors::codes::*; +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{ Applicability, Diag, DiagCtxtHandle, DiagMessage, DiagStyledString, Diagnostic, EmissionGuarantee, IntoDiagArg, Level, MultiSpan, Subdiagnostic, msg, @@ -450,28 +451,23 @@ impl Subdiagnostic for RegionOriginNote<'_> { requirement, expected_found: Some((expected, found)), } => { - // `RegionOriginNote` can appear multiple times on one diagnostic with different - // `requirement` values. Scope args per-note and eagerly translate to avoid - // cross-note arg collisions. - // See https://github.com/rust-lang/rust/issues/143872 for details. - diag.store_args(); - diag.arg("requirement", requirement); - let msg = diag.eagerly_format(msg!( + let msg = msg!( "...so that the {$requirement -> - [method_compat] method type is compatible with trait - [type_compat] associated type is compatible with trait - [const_compat] const is compatible with trait - [expr_assignable] expression is assignable - [if_else_different] `if` and `else` have incompatible types - [no_else] `if` missing an `else` returns `()` - [fn_main_correct_type] `main` function has the correct type - [fn_lang_correct_type] lang item function has the correct type - [intrinsic_correct_type] intrinsic has the correct type - [method_correct_type] method receiver has the correct type - *[other] types are compatible - }" - )); - diag.restore_args(); + [method_compat] method type is compatible with trait + [type_compat] associated type is compatible with trait + [const_compat] const is compatible with trait + [expr_assignable] expression is assignable + [if_else_different] `if` and `else` have incompatible types + [no_else] `if` missing an `else` returns `()` + [fn_main_correct_type] `main` function has the correct type + [fn_lang_correct_type] lang item function has the correct type + [intrinsic_correct_type] intrinsic has the correct type + [method_correct_type] method receiver has the correct type + *[other] types are compatible + }" + ) + .arg("requirement", requirement) + .format(); label_or_note(diag, span, msg); diag.note_expected_found("", expected, "", found); @@ -480,9 +476,7 @@ impl Subdiagnostic for RegionOriginNote<'_> { // FIXME: this really should be handled at some earlier stage. Our // handling of region checking when type errors are present is // *terrible*. - diag.store_args(); - diag.arg("requirement", requirement); - let msg = diag.eagerly_format(msg!( + let msg = msg!( "...so that {$requirement -> [method_compat] method type is compatible with trait [type_compat] associated type is compatible with trait @@ -496,8 +490,9 @@ impl Subdiagnostic for RegionOriginNote<'_> { [method_correct_type] method receiver has the correct type *[other] types are compatible }" - )); - diag.restore_args(); + ) + .arg("requirement", requirement) + .format(); label_or_note(diag, span, msg); } }; @@ -1174,7 +1169,9 @@ impl Subdiagnostic for ConsiderBorrowingParamHelp { type_param_span .push_span_label(span, msg!("consider borrowing this type parameter in the trait")); } - let msg = diag.eagerly_format(msg!("the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`")); + let msg = msg!( + "the lifetime requirements from the `impl` do not correspond to the requirements in the `trait`" + ); diag.span_help(type_param_span, msg); } } @@ -1218,9 +1215,9 @@ impl Subdiagnostic for DynTraitConstraintSuggestion { self.ident.span, msg!("calling this method introduces the `impl`'s `'static` requirement"), ); - let msg = diag.eagerly_format(msg!("the used `impl` has a `'static` requirement")); + let msg = msg!("the used `impl` has a `'static` requirement"); diag.span_note(multi_span, msg); - let msg = diag.eagerly_format(msg!("consider relaxing the implicit `'static` requirement")); + let msg = msg!("consider relaxing the implicit `'static` requirement"); diag.span_suggestion_verbose( self.span.shrink_to_hi(), msg, @@ -1230,38 +1227,6 @@ impl Subdiagnostic for DynTraitConstraintSuggestion { } } -#[derive(Diagnostic)] -#[diag("{$has_param_name -> - [true] `{$param_name}` - *[false] `fn` parameter -} has {$lifetime_kind -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` -} but calling `{$assoc_item}` introduces an implicit `'static` lifetime requirement", code = E0772)] -pub struct ButCallingIntroduces { - #[label( - "{$has_lifetime -> - [true] lifetime `{$lifetime}` - *[false] an anonymous lifetime `'_` - }" - )] - pub param_ty_span: Span, - #[primary_span] - #[label("...is used and required to live as long as `'static` here because of an implicit lifetime bound on the {$has_impl_path -> - [true] `impl` of `{$impl_path}` - *[false] inherent `impl` - }")] - pub cause_span: Span, - - pub has_param_name: bool, - pub param_name: String, - pub has_lifetime: bool, - pub lifetime: String, - pub assoc_item: Symbol, - pub has_impl_path: bool, - pub impl_path: String, -} - pub struct ReqIntroducedLocations { pub span: MultiSpan, pub spans: Vec, @@ -1283,8 +1248,7 @@ impl Subdiagnostic for ReqIntroducedLocations { ); } self.span.push_span_label(self.cause_span, msg!("because of this returned expression")); - let msg = diag - .eagerly_format(msg!("\"`'static` lifetime requirement introduced by the return type")); + let msg = msg!("\"`'static` lifetime requirement introduced by the return type"); diag.span_note(self.span, msg); } } @@ -1724,8 +1688,7 @@ pub struct SuggestTuplePatternMany { impl Subdiagnostic for SuggestTuplePatternMany { fn add_to_diag(self, diag: &mut Diag<'_, G>) { diag.arg("path", self.path); - let message = - diag.eagerly_format(msg!("try wrapping the pattern in a variant of `{$path}`")); + let message = msg!("try wrapping the pattern in a variant of `{$path}`"); diag.multipart_suggestions( message, self.compatible_variants.into_iter().map(|variant| { diff --git a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs index 5121ca886c1bd..fd943bff3700b 100644 --- a/compiler/rustc_trait_selection/src/errors/note_and_explain.rs +++ b/compiler/rustc_trait_selection/src/errors/note_and_explain.rs @@ -1,3 +1,4 @@ +use rustc_errors::formatting::DiagMessageAddArg; use rustc_errors::{Diag, EmissionGuarantee, IntoDiagArg, Subdiagnostic, msg}; use rustc_hir::def_id::LocalDefId; use rustc_middle::bug; @@ -163,13 +164,7 @@ impl RegionExplanation<'_> { impl Subdiagnostic for RegionExplanation<'_> { fn add_to_diag(self, diag: &mut Diag<'_, G>) { - diag.store_args(); - 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 = diag.eagerly_format(msg!( + let msg = msg!( "{$pref_kind -> *[should_not_happen] [{$pref_kind}] [ref_valid_for] ...the reference is valid for @@ -202,8 +197,14 @@ impl Subdiagnostic for RegionExplanation<'_> { [continues] ... [req_by_binding] {\" \"}as required by this binding }" - )); - diag.restore_args(); + ) + .arg("pref_kind", self.prefix) + .arg("suff_kind", self.suffix) + .arg("desc_kind", self.desc.kind) + .arg("desc_arg", self.desc.arg) + .format(); + + // diag.restore_args(); if let Some(span) = self.desc.span { diag.span_note(span, msg); } else { diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr index 2d90b2a96b015..9149baaa40871 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr @@ -35,12 +35,6 @@ help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Not LL | struct NotIntoDiagArg; | ^^^^^^^^^^^^^^^^^^^^^ = help: normalized in stderr -note: required by a bound in `Diag::<'a, G>::arg` - --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC - ::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC - | - = note: in this macro invocation - = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors