From b18f3e11fa9c2d6fb8a6c4807229bebc3608d3ac Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 1 Feb 2024 10:13:24 +1100 Subject: Prefer `DiagnosticBuilder` over `Diagnostic` in diagnostic modifiers. There are lots of functions that modify a diagnostic. This can be via a `&mut Diagnostic` or a `&mut DiagnosticBuilder`, because the latter type wraps the former and impls `DerefMut`. This commit converts all the `&mut Diagnostic` occurrences to `&mut DiagnosticBuilder`. This is a step towards greatly simplifying `Diagnostic`. Some of the relevant function are made generic, because they deal with both errors and warnings. No function bodies are changed, because all the modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. --- compiler/rustc_errors/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 064ea8d7516..8643596b446 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1745,9 +1745,9 @@ impl Level { } // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. -pub fn add_elided_lifetime_in_path_suggestion( +pub fn add_elided_lifetime_in_path_suggestion( source_map: &SourceMap, - diag: &mut DiagnosticBuilder<'_, E>, + diag: &mut DiagnosticBuilder<'_, G>, n: usize, path_span: Span, incl_angl_brckt: bool, -- cgit 1.4.1-3-g733a5 From f6f87798439e2ce7861da761b444fe0978335ed9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 6 Feb 2024 16:44:30 +1100 Subject: Reduce capabilities of `Diagnostic`. Currently many diagnostic modifier methods are available on both `Diagnostic` and `DiagnosticBuilder`. This commit removes most of them from `Diagnostic`. To minimize the diff size, it keeps them within `diagnostic.rs` but changes the surrounding `impl Diagnostic` block to `impl DiagnosticBuilder`. (I intend to move things around later, to give a more sensible code layout.) `Diagnostic` keeps a few methods that it still needs, like `sub`, `arg`, and `replace_args`. The `forward!` macro, which defined two additional methods per call (e.g. `note` and `with_note`), is replaced by the `with_fn!` macro, which defines one additional method per call (e.g. `with_note`). It's now also only used when necessary -- not all modifier methods currently need a `with_*` form. (New ones can be easily added as necessary.) All this also requires changing `trait AddToDiagnostic` so its methods take `DiagnosticBuilder` instead of `Diagnostic`, which leads to many mechanical changes. `SubdiagnosticMessageOp` gains a type parameter `G`. There are three subdiagnostics -- `DelayedAtWithoutNewline`, `DelayedAtWithNewline`, and `InvalidFlushedDelayedDiagnosticLevel` -- that are created within the diagnostics machinery and appended to external diagnostics. These are handled at the `Diagnostic` level, which means it's now hard to construct them via `derive(Diagnostic)`, so instead we construct them by hand. This has no effect on what they look like when printed. There are lots of new `allow` markers for `untranslatable_diagnostics` and `diagnostics_outside_of_impl`. This is because `#[rustc_lint_diagnostics]` annotations were present on the `Diagnostic` modifier methods, but missing from the `DiagnosticBuilder` modifier methods. They're now present. --- compiler/rustc_ast_lowering/src/errors.rs | 9 +- compiler/rustc_ast_passes/src/errors.rs | 17 +- compiler/rustc_ast_passes/src/feature_gate.rs | 3 + .../src/diagnostics/region_errors.rs | 15 ++ compiler/rustc_borrowck/src/lib.rs | 2 + compiler/rustc_builtin_macros/src/errors.rs | 10 +- compiler/rustc_codegen_ssa/src/back/write.rs | 4 +- .../src/transform/check_consts/ops.rs | 7 + compiler/rustc_errors/src/diagnostic.rs | 212 +++++++++++++++------ compiler/rustc_errors/src/diagnostic_builder.rs | 191 ++----------------- compiler/rustc_errors/src/diagnostic_impls.rs | 30 +-- compiler/rustc_errors/src/emitter.rs | 2 +- compiler/rustc_errors/src/lib.rs | 82 ++++---- compiler/rustc_expand/src/base.rs | 2 + compiler/rustc_expand/src/config.rs | 1 + compiler/rustc_expand/src/proc_macro_server.rs | 9 +- compiler/rustc_hir_typeck/src/errors.rs | 24 ++- compiler/rustc_infer/src/errors/mod.rs | 59 ++++-- .../rustc_infer/src/errors/note_and_explain.rs | 11 +- compiler/rustc_lint/src/context/diagnostics.rs | 3 + compiler/rustc_lint/src/errors.rs | 10 +- compiler/rustc_lint/src/levels.rs | 3 + compiler/rustc_lint/src/lints.rs | 66 +++++-- .../rustc_macros/src/diagnostics/subdiagnostic.rs | 10 +- compiler/rustc_metadata/src/errors.rs | 4 + compiler/rustc_mir_build/src/errors.rs | 14 +- compiler/rustc_mir_transform/src/errors.rs | 3 + compiler/rustc_monomorphize/src/errors.rs | 1 + compiler/rustc_parse/src/errors.rs | 8 +- compiler/rustc_passes/src/check_const.rs | 9 +- compiler/rustc_passes/src/errors.rs | 11 +- compiler/rustc_pattern_analysis/src/errors.rs | 8 +- compiler/rustc_session/src/config.rs | 3 + compiler/rustc_trait_selection/src/errors.rs | 10 +- src/tools/clippy/clippy_utils/src/sugg.rs | 4 +- tests/ui-fulldeps/internal-lints/diagnostics.rs | 13 +- .../ui-fulldeps/internal-lints/diagnostics.stderr | 8 +- .../diagnostic-derive-doc-comment-field.stderr | 9 +- .../session-diagnostic/diagnostic-derive.rs | 4 +- .../session-diagnostic/diagnostic-derive.stderr | 6 +- 40 files changed, 502 insertions(+), 395 deletions(-) (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_ast_lowering/src/errors.rs b/compiler/rustc_ast_lowering/src/errors.rs index 274e6b7458c..834409da675 100644 --- a/compiler/rustc_ast_lowering/src/errors.rs +++ b/compiler/rustc_ast_lowering/src/errors.rs @@ -1,5 +1,6 @@ use rustc_errors::{ - codes::*, AddToDiagnostic, Diagnostic, DiagnosticArgFromDisplay, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, DiagnosticArgFromDisplay, DiagnosticBuilder, EmissionGuarantee, + SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -41,7 +42,11 @@ pub struct InvalidAbi { pub struct InvalidAbiReason(pub &'static str); impl AddToDiagnostic for InvalidAbiReason { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { #[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 9662c73ca85..e5153c89790 100644 --- a/compiler/rustc_ast_passes/src/errors.rs +++ b/compiler/rustc_ast_passes/src/errors.rs @@ -1,7 +1,10 @@ //! Errors emitted by ast_passes. use rustc_ast::ParamKindOrd; -use rustc_errors::{codes::*, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{ + codes::*, AddToDiagnostic, Applicability, DiagnosticBuilder, EmissionGuarantee, + SubdiagnosticMessageOp, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -372,7 +375,11 @@ 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) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.span_labels(self.0, ""); } } @@ -729,7 +736,11 @@ pub struct StableFeature { } impl AddToDiagnostic for StableFeature { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.arg("name", self.name); diag.arg("since", self.since); diag.help(fluent::ast_passes_stable_since); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 409aef9185d..1b0dd9acc37 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -22,6 +22,9 @@ macro_rules! gate { }}; ($visitor:expr, $feature:ident, $span:expr, $explain:expr, $help:expr) => {{ if !$visitor.features.$feature && !$span.allows_unstable(sym::$feature) { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] feature_err(&$visitor.sess, sym::$feature, $span, $explain).with_help($help).emit(); } }}; diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index 1b88d5046d9..50d22881c3e 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -251,6 +251,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { hrtb_bounds.iter().for_each(|bound| { let Trait(PolyTraitRef { trait_ref, span: trait_span, .. }, _) = bound else { return; }; + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.span_note( *trait_span, "due to current limitations in the borrow checker, this implies a `'static` lifetime" @@ -421,6 +424,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { /// ``` /// /// Here we would be invoked with `fr = 'a` and `outlived_fr = 'b`. + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] pub(crate) fn report_region_error( &mut self, fr: RegionVid, @@ -685,12 +691,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { borrowck_errors::borrowed_data_escapes_closure(self.infcx.tcx, *span, escapes_from); if let Some((Some(outlived_fr_name), outlived_fr_span)) = outlived_fr_name_and_span { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.span_label( outlived_fr_span, format!("`{outlived_fr_name}` declared here, outside of the {escapes_from} body",), ); } + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] if let Some((Some(fr_name), fr_span)) = fr_name_and_span { diag.span_label( fr_span, @@ -714,6 +726,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> { let outlived_fr_region_name = self.give_region_a_name(errci.outlived_fr).unwrap(); outlived_fr_region_name.highlight_region_name(&mut diag); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.span_label( *span, format!( diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 6c2a511538d..dbaa9e5bcfa 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -2497,6 +2497,8 @@ mod diags { } for (_, (mut diag, count)) in std::mem::take(&mut self.diags.buffered_mut_errors) { if count > 10 { + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] diag.note(format!("...and {} other attempted mutable borrows", count - 10)); } self.diags.buffered_diags.push(BufferedDiag::Error(diag)); diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index 8d2e06bf30d..f304a37be85 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, + codes::*, AddToDiagnostic, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, + Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{symbol::Ident, Span, Symbol}; @@ -611,7 +611,11 @@ 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) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.arg("named", self.named); let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into()); diag.span_label(self.span, msg); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 24fdd013509..7a981217b52 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -1856,9 +1856,7 @@ impl SharedEmitterMain { Ok(SharedEmitterMessage::Diagnostic(diag)) => { let dcx = sess.dcx(); let mut d = rustc_errors::Diagnostic::new_with_messages(diag.lvl, diag.msgs); - if let Some(code) = diag.code { - d.code(code); - } + d.code = diag.code; // may be `None`, that's ok d.replace_args(diag.args); dcx.emit_diagnostic(d); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs index 25ddd5e85f9..5b4bbf8510b 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/ops.rs @@ -93,6 +93,9 @@ pub struct FnCallNonConst<'tcx> { } impl<'tcx> NonConstOp<'tcx> for FnCallNonConst<'tcx> { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, _: Span) -> DiagnosticBuilder<'tcx> { let FnCallNonConst { caller, callee, args, span, call_source, feature } = *self; let ConstCx { tcx, param_env, .. } = *ccx; @@ -321,6 +324,8 @@ impl<'tcx> NonConstOp<'tcx> for FnCallUnstable { .dcx() .create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) }); + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] if ccx.is_const_stable_const_fn() { err.help("const-stable functions can only call other const-stable functions"); } else if ccx.tcx.sess.is_nightly_build() { @@ -591,6 +596,8 @@ impl<'tcx> NonConstOp<'tcx> for StaticAccess { span, format!("referencing statics in {}s is unstable", ccx.const_kind(),), ); + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] err .note("`static` and `const` variables can refer to other `const` variables. A `const` variable, however, cannot refer to a `static` variable.") .help("to fix this, the value can be extracted to a `const` and then used."); diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 034636bea48..57610635ee6 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -12,6 +12,7 @@ use rustc_span::{Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; +use std::ops::{Deref, DerefMut}; use std::panic::Location; /// Error type for `Diagnostic`'s `suggestions` field, indicating that @@ -71,17 +72,21 @@ where Self: Sized, { /// Add a subdiagnostic to an existing diagnostic. - fn add_to_diagnostic(self, diag: &mut Diagnostic) { + fn add_to_diagnostic(self, diag: &mut DiagnosticBuilder<'_, G>) { 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_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ); } -pub trait SubdiagnosticMessageOp = - Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage; +pub trait SubdiagnosticMessageOp = + Fn(&mut DiagnosticBuilder<'_, G>, SubdiagnosticMessage) -> SubdiagnosticMessage; /// Trait implemented by lint types. This should not be implemented manually. Instead, use /// `#[derive(LintDiagnostic)]` -- see [rustc_macros::LintDiagnostic]. @@ -93,6 +98,10 @@ pub trait DecorateLint<'a, G: EmissionGuarantee> { fn msg(&self) -> DiagnosticMessage; } +/// The main part of a diagnostic. Note that `DiagnosticBuilder`, which wraps +/// this type, is used for most operations, and should be used instead whenever +/// possible. This type should only be used when `DiagnosticBuilder`'s lifetime +/// causes difficulties, e.g. when storing diagnostics within `DiagCtxt`. #[must_use] #[derive(Clone, Debug, Encodable, Decodable)] pub struct Diagnostic { @@ -289,6 +298,90 @@ impl Diagnostic { } } + // See comment on `DiagnosticBuilder::subdiagnostic_message_to_diagnostic_message`. + pub(crate) fn subdiagnostic_message_to_diagnostic_message( + &self, + attr: impl Into, + ) -> DiagnosticMessage { + let msg = + self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); + msg.with_subdiagnostic_message(attr.into()) + } + + pub(crate) fn sub( + &mut self, + level: Level, + message: impl Into, + span: MultiSpan, + ) { + let sub = SubDiagnostic { + level, + messages: vec![( + self.subdiagnostic_message_to_diagnostic_message(message), + Style::NoStyle, + )], + span, + }; + self.children.push(sub); + } + + pub(crate) fn arg(&mut self, name: impl Into, arg: impl IntoDiagnosticArg) { + self.args.insert(name.into(), arg.into_diagnostic_arg()); + } + + pub fn args(&self) -> impl Iterator> { + self.args.iter() + } + + pub fn replace_args(&mut self, args: FxIndexMap) { + self.args = args; + } +} + +/// `DiagnosticBuilder` impls many `&mut self -> &mut Self` methods. Each one +/// modifies an existing diagnostic, either in a standalone fashion, e.g. +/// `err.code(code);`, or in a chained fashion to make multiple modifications, +/// e.g. `err.code(code).span(span);`. +/// +/// This macro creates an equivalent `self -> Self` method, with a `with_` +/// prefix. This can be used in a chained fashion when making a new diagnostic, +/// e.g. `let err = struct_err(msg).with_code(code);`, or emitting a new +/// diagnostic, e.g. `struct_err(msg).with_code(code).emit();`. +/// +/// Although the latter method can be used to modify an existing diagnostic, +/// e.g. `err = err.with_code(code);`, this should be avoided because the former +/// method gives shorter code, e.g. `err.code(code);`. +/// +/// Note: the `with_` methods are added only when needed. If you want to use +/// one and it's not defined, feel free to add it. +/// +/// Note: any doc comments must be within the `with_fn!` call. +macro_rules! with_fn { + { + $with_f:ident, + $(#[$attrs:meta])* + pub fn $f:ident(&mut $self:ident, $($name:ident: $ty:ty),* $(,)?) -> &mut Self { + $($body:tt)* + } + } => { + // The original function. + $(#[$attrs])* + #[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")] + pub fn $f(&mut $self, $($name: $ty),*) -> &mut Self { + $($body)* + } + + // The `with_*` variant. + $(#[$attrs])* + #[doc = concat!("See [`DiagnosticBuilder::", stringify!($f), "()`].")] + pub fn $with_f(mut $self, $($name: $ty),*) -> Self { + $self.$f($($name),*); + $self + } + }; +} + +impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Delay emission of this diagnostic as a bug. /// /// This can be useful in contexts where an error indicates a bug but @@ -309,6 +402,7 @@ impl Diagnostic { self.level = Level::DelayedBug; } + with_fn! { with_span_label, /// Appends a labeled span to the diagnostic. /// /// Labels are used to convey additional context for the diagnostic's primary span. They will @@ -323,10 +417,12 @@ impl Diagnostic { /// primary. #[rustc_lint_diagnostics] pub fn span_label(&mut self, span: Span, label: impl Into) -> &mut Self { - self.span.push_span_label(span, self.subdiagnostic_message_to_diagnostic_message(label)); + let msg = self.subdiagnostic_message_to_diagnostic_message(label); + self.span.push_span_label(span, msg); self - } + } } + with_fn! { with_span_labels, /// Labels all the given spans with the provided label. /// See [`Self::span_label()`] for more information. pub fn span_labels(&mut self, spans: impl IntoIterator, label: &str) -> &mut Self { @@ -334,7 +430,7 @@ impl Diagnostic { self.span_label(span, label.to_string()); } self - } + } } pub fn replace_span_with(&mut self, after: Span, keep_label: bool) -> &mut Self { let before = self.span.clone(); @@ -412,39 +508,40 @@ impl Diagnostic { self } + with_fn! { with_note, /// Add a note attached to this diagnostic. #[rustc_lint_diagnostics] pub fn note(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::Note, msg, MultiSpan::new()); self - } + } } fn highlighted_note(&mut self, msg: Vec) -> &mut Self { self.sub_with_highlights(Level::Note, msg, MultiSpan::new()); self } - /// Prints the span with a note above it. - /// This is like [`Diagnostic::note()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::note()`], but it's only printed once. pub fn note_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceNote, msg, MultiSpan::new()); self } + with_fn! { with_span_note, /// Prints the span with a note above it. - /// This is like [`Diagnostic::note()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::note()`], but it gets its own span. #[rustc_lint_diagnostics] - pub fn span_note>( + pub fn span_note( &mut self, - sp: S, + sp: impl Into, msg: impl Into, ) -> &mut Self { self.sub(Level::Note, msg, sp.into()); self - } + } } /// Prints the span with a note above it. - /// This is like [`Diagnostic::note()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::note_once()`], but it gets its own span. pub fn span_note_once>( &mut self, sp: S, @@ -454,15 +551,16 @@ impl Diagnostic { self } + with_fn! { with_warn, /// Add a warning attached to this diagnostic. #[rustc_lint_diagnostics] pub fn warn(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::Warning, msg, MultiSpan::new()); self - } + } } /// Prints the span with a warning above it. - /// This is like [`Diagnostic::warn()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::warn()`], but it gets its own span. #[rustc_lint_diagnostics] pub fn span_warn>( &mut self, @@ -473,15 +571,15 @@ impl Diagnostic { self } + with_fn! { with_help, /// Add a help message attached to this diagnostic. #[rustc_lint_diagnostics] pub fn help(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::Help, msg, MultiSpan::new()); self - } + } } - /// Prints the span with a help above it. - /// This is like [`Diagnostic::help()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::help()`], but it's only printed once. pub fn help_once(&mut self, msg: impl Into) -> &mut Self { self.sub(Level::OnceHelp, msg, MultiSpan::new()); self @@ -494,7 +592,7 @@ impl Diagnostic { } /// Prints the span with some help above it. - /// This is like [`Diagnostic::help()`], but it gets its own span. + /// This is like [`DiagnosticBuilder::help()`], but it gets its own span. #[rustc_lint_diagnostics] pub fn span_help>( &mut self, @@ -531,6 +629,7 @@ impl Diagnostic { } } + with_fn! { with_multipart_suggestion, /// Show a suggestion that has multiple parts to it. /// In other words, multiple changes need to be applied as part of this suggestion. pub fn multipart_suggestion( @@ -545,7 +644,7 @@ impl Diagnostic { applicability, SuggestionStyle::ShowCode, ) - } + } } /// Show a suggestion that has multiple parts to it, always as it's own subdiagnostic. /// In other words, multiple changes need to be applied as part of this suggestion. @@ -562,7 +661,8 @@ impl Diagnostic { SuggestionStyle::ShowAlways, ) } - /// [`Diagnostic::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. + + /// [`DiagnosticBuilder::multipart_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn multipart_suggestion_with_style( &mut self, msg: impl Into, @@ -619,6 +719,7 @@ impl Diagnostic { ) } + with_fn! { with_span_suggestion, /// Prints out a message with a suggested edit of the code. /// /// In case of short messages and a simple suggestion, rustc displays it as a label: @@ -651,9 +752,9 @@ impl Diagnostic { SuggestionStyle::ShowCode, ); self - } + } } - /// [`Diagnostic::span_suggestion()`] but you can set the [`SuggestionStyle`]. + /// [`DiagnosticBuilder::span_suggestion()`] but you can set the [`SuggestionStyle`]. pub fn span_suggestion_with_style( &mut self, sp: Span, @@ -677,6 +778,7 @@ impl Diagnostic { self } + with_fn! { with_span_suggestion_verbose, /// Always show the suggested change. pub fn span_suggestion_verbose( &mut self, @@ -693,10 +795,11 @@ impl Diagnostic { SuggestionStyle::ShowAlways, ); self - } + } } + with_fn! { with_span_suggestions, /// Prints out a message with multiple suggested edits of the code. - /// See also [`Diagnostic::span_suggestion()`]. + /// See also [`DiagnosticBuilder::span_suggestion()`]. pub fn span_suggestions( &mut self, sp: Span, @@ -711,9 +814,8 @@ impl Diagnostic { applicability, SuggestionStyle::ShowCode, ) - } + } } - /// [`Diagnostic::span_suggestions()`] but you can set the [`SuggestionStyle`]. pub fn span_suggestions_with_style( &mut self, sp: Span, @@ -743,7 +845,7 @@ impl Diagnostic { /// Prints out a message with multiple suggested edits of the code, where each edit consists of /// multiple parts. - /// See also [`Diagnostic::multipart_suggestion()`]. + /// See also [`DiagnosticBuilder::multipart_suggestion()`]. pub fn multipart_suggestions( &mut self, msg: impl Into, @@ -785,6 +887,7 @@ impl Diagnostic { self } + with_fn! { with_span_suggestion_short, /// Prints out a message with a suggested edit of the code. If the suggestion is presented /// inline, it will only show the message and not the suggestion. /// @@ -804,7 +907,7 @@ impl Diagnostic { SuggestionStyle::HideCodeInline, ); self - } + } } /// Prints out a message for a suggestion without showing the suggested code. /// @@ -829,6 +932,7 @@ impl Diagnostic { self } + with_fn! { with_tool_only_span_suggestion, /// Adds a suggestion to the JSON output that will not be shown in the CLI. /// /// This is intended to be used for suggestions that are *very* obvious in what the changes @@ -849,7 +953,7 @@ impl Diagnostic { SuggestionStyle::CompletelyHidden, ); self - } + } } /// Add a subdiagnostic from a type that implements `Subdiagnostic` (see /// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages @@ -868,45 +972,45 @@ impl Diagnostic { self } - pub fn span>(&mut self, sp: S) -> &mut Self { + with_fn! { with_span, + /// Add a span. + pub fn span(&mut self, sp: impl Into) -> &mut Self { self.span = sp.into(); if let Some(span) = self.span.primary_span() { self.sort_span = span; } self - } + } } pub fn is_lint(&mut self, name: String, has_future_breakage: bool) -> &mut Self { self.is_lint = Some(IsLint { name, has_future_breakage }); self } + with_fn! { with_code, + /// Add an error code. pub fn code(&mut self, code: ErrCode) -> &mut Self { self.code = Some(code); self - } + } } + with_fn! { with_primary_message, + /// Add a primary message. pub fn primary_message(&mut self, msg: impl Into) -> &mut Self { self.messages[0] = (msg.into(), Style::NoStyle); self - } - - pub fn args(&self) -> impl Iterator> { - self.args.iter() - } + } } + with_fn! { with_arg, + /// Add an argument. pub fn arg( &mut self, name: impl Into, arg: impl IntoDiagnosticArg, ) -> &mut Self { - self.args.insert(name.into(), arg.into_diagnostic_arg()); + self.deref_mut().arg(name, arg); self - } - - pub fn replace_args(&mut self, args: FxIndexMap) { - self.args = args; - } + } } /// 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 @@ -915,9 +1019,7 @@ impl Diagnostic { &self, attr: impl Into, ) -> DiagnosticMessage { - let msg = - self.messages.iter().map(|(msg, _)| msg).next().expect("diagnostic with no messages"); - msg.with_subdiagnostic_message(attr.into()) + self.deref().subdiagnostic_message_to_diagnostic_message(attr) } /// Convenience function for internal use, clients should use one of the @@ -925,15 +1027,7 @@ impl Diagnostic { /// /// Used by `proc_macro_server` for implementing `server::Diagnostic`. pub fn sub(&mut self, level: Level, message: impl Into, span: MultiSpan) { - let sub = SubDiagnostic { - level, - messages: vec![( - self.subdiagnostic_message_to_diagnostic_message(message), - Style::NoStyle, - )], - span, - }; - self.children.push(sub); + self.deref_mut().sub(level, message, span); } /// Convenience function for internal use, clients should use one of the @@ -946,7 +1040,9 @@ impl Diagnostic { let sub = SubDiagnostic { level, messages, span }; self.children.push(sub); } +} +impl Diagnostic { /// Fields used for Hash, and PartialEq trait fn keys( &self, diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs index 0572df69ca9..3a6a494af95 100644 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ b/compiler/rustc_errors/src/diagnostic_builder.rs @@ -1,14 +1,8 @@ -use crate::diagnostic::IntoDiagnosticArg; -use crate::{DiagCtxt, Level, MultiSpan, StashKey}; use crate::{ - Diagnostic, DiagnosticMessage, DiagnosticStyledString, ErrCode, ErrorGuaranteed, ExplicitBug, - SubdiagnosticMessage, + DiagCtxt, Diagnostic, DiagnosticMessage, ErrorGuaranteed, ExplicitBug, Level, StashKey, }; -use rustc_lint_defs::Applicability; use rustc_span::source_map::Spanned; - use rustc_span::Span; -use std::borrow::Cow; use std::fmt::{self, Debug}; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -35,6 +29,11 @@ where } /// Used for emitting structured error messages and other diagnostic information. +/// Wraps a `Diagnostic`, adding some useful things. +/// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check +/// that it has been emitted or cancelled. +/// - The `EmissionGuarantee`, which determines the type returned from `emit`. +/// /// Each constructed `DiagnosticBuilder` must be consumed by a function such as /// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a /// `DiagnosticBuilder` is dropped without being consumed by one of these @@ -56,9 +55,11 @@ pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { /// often used as a return value, especially within the frequently-used /// `PResult` type. In theory, return value optimization (RVO) should avoid /// unnecessary copying. In practice, it does not (at the time of writing). - diag: Option>, + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) diag: Option>, - _marker: PhantomData, + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) _marker: PhantomData, } // Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted @@ -88,18 +89,21 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { /// Takes the diagnostic. For use by methods that consume the /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the /// only code that will be run on `self`. - fn take_diag(&mut self) -> Diagnostic { + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) fn take_diag(&mut self) -> Diagnostic { Box::into_inner(self.diag.take().unwrap()) } /// Most `emit_producing_guarantee` functions use this as a starting point. - fn emit_producing_nothing(mut self) { + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) fn emit_producing_nothing(mut self) { let diag = self.take_diag(); self.dcx.emit_diagnostic(diag); } /// `ErrorGuaranteed::emit_producing_guarantee` uses this. - fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { let diag = self.take_diag(); // The only error levels that produce `ErrorGuaranteed` are @@ -168,40 +172,6 @@ impl EmissionGuarantee for rustc_span::fatal_error::FatalError { } } -/// `DiagnosticBuilder` impls `DerefMut`, which allows access to the fields and -/// methods of the embedded `Diagnostic`. However, that doesn't allow method -/// chaining at the `DiagnosticBuilder` level. Each use of this macro defines -/// two builder methods at that level, both of which wrap the equivalent method -/// in `Diagnostic`. -/// - A `&mut self -> &mut Self` method, with the same name as the underlying -/// `Diagnostic` method. It is mostly to modify existing diagnostics, either -/// in a standalone fashion, e.g. `err.code(code)`, or in a chained fashion -/// to make multiple modifications, e.g. `err.code(code).span(span)`. -/// - A `self -> Self` method, which has a `with_` prefix added. -/// It is mostly used in a chained fashion when producing a new diagnostic, -/// e.g. `let err = struct_err(msg).with_code(code)`, or when emitting a new -/// diagnostic , e.g. `struct_err(msg).with_code(code).emit()`. -/// -/// Although the latter method can be used to modify an existing diagnostic, -/// e.g. `err = err.with_code(code)`, this should be avoided because the former -/// method gives shorter code, e.g. `err.code(code)`. -macro_rules! forward { - ( - ($f:ident, $with_f:ident)($($name:ident: $ty:ty),* $(,)?) - ) => { - #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")] - pub fn $f(&mut self, $($name: $ty),*) -> &mut Self { - self.diag.as_mut().unwrap().$f($($name),*); - self - } - #[doc = concat!("See [`Diagnostic::", stringify!($f), "()`].")] - pub fn $with_f(mut self, $($name: $ty),*) -> Self { - self.diag.as_mut().unwrap().$f($($name),*); - self - } - }; -} - impl Deref for DiagnosticBuilder<'_, G> { type Target = Diagnostic; @@ -278,135 +248,6 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { self.downgrade_to_delayed_bug(); self.emit() } - - forward!((span_label, with_span_label)( - span: Span, - label: impl Into, - )); - forward!((span_labels, with_span_labels)( - spans: impl IntoIterator, - label: &str, - )); - forward!((note_expected_found, with_note_expected_found)( - expected_label: &dyn fmt::Display, - expected: DiagnosticStyledString, - found_label: &dyn fmt::Display, - found: DiagnosticStyledString, - )); - forward!((note_expected_found_extra, with_note_expected_found_extra)( - expected_label: &dyn fmt::Display, - expected: DiagnosticStyledString, - found_label: &dyn fmt::Display, - found: DiagnosticStyledString, - expected_extra: &dyn fmt::Display, - found_extra: &dyn fmt::Display, - )); - forward!((note, with_note)( - msg: impl Into, - )); - forward!((note_once, with_note_once)( - msg: impl Into, - )); - forward!((span_note, with_span_note)( - sp: impl Into, - msg: impl Into, - )); - forward!((span_note_once, with_span_note_once)( - sp: impl Into, - msg: impl Into, - )); - forward!((warn, with_warn)( - msg: impl Into, - )); - forward!((span_warn, with_span_warn)( - sp: impl Into, - msg: impl Into, - )); - forward!((help, with_help)( - msg: impl Into, - )); - forward!((help_once, with_help_once)( - msg: impl Into, - )); - forward!((span_help, with_span_help_once)( - sp: impl Into, - msg: impl Into, - )); - forward!((multipart_suggestion, with_multipart_suggestion)( - msg: impl Into, - suggestion: Vec<(Span, String)>, - applicability: Applicability, - )); - forward!((multipart_suggestion_verbose, with_multipart_suggestion_verbose)( - msg: impl Into, - suggestion: Vec<(Span, String)>, - applicability: Applicability, - )); - forward!((tool_only_multipart_suggestion, with_tool_only_multipart_suggestion)( - msg: impl Into, - suggestion: Vec<(Span, String)>, - applicability: Applicability, - )); - forward!((span_suggestion, with_span_suggestion)( - sp: Span, - msg: impl Into, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((span_suggestions, with_span_suggestions)( - sp: Span, - msg: impl Into, - suggestions: impl IntoIterator, - applicability: Applicability, - )); - forward!((multipart_suggestions, with_multipart_suggestions)( - msg: impl Into, - suggestions: impl IntoIterator>, - applicability: Applicability, - )); - forward!((span_suggestion_short, with_span_suggestion_short)( - sp: Span, - msg: impl Into, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((span_suggestion_verbose, with_span_suggestion_verbose)( - sp: Span, - msg: impl Into, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((span_suggestion_hidden, with_span_suggestion_hidden)( - sp: Span, - msg: impl Into, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((tool_only_span_suggestion, with_tool_only_span_suggestion)( - sp: Span, - msg: impl Into, - suggestion: impl ToString, - applicability: Applicability, - )); - forward!((primary_message, with_primary_message)( - msg: impl Into, - )); - forward!((span, with_span)( - sp: impl Into, - )); - forward!((is_lint, with_is_lint)( - name: String, has_future_breakage: bool, - )); - forward!((code, with_code)( - code: ErrCode, - )); - forward!((arg, with_arg)( - name: impl Into>, arg: impl IntoDiagnosticArg, - )); - forward!((subdiagnostic, with_subdiagnostic)( - dcx: &DiagCtxt, - subdiagnostic: impl crate::AddToDiagnostic, - )); } impl Debug for DiagnosticBuilder<'_, G> { diff --git a/compiler/rustc_errors/src/diagnostic_impls.rs b/compiler/rustc_errors/src/diagnostic_impls.rs index eaf75539f59..bc1e81642ff 100644 --- a/compiler/rustc_errors/src/diagnostic_impls.rs +++ b/compiler/rustc_errors/src/diagnostic_impls.rs @@ -299,7 +299,11 @@ pub struct SingleLabelManySpans { pub label: &'static str, } impl AddToDiagnostic for SingleLabelManySpans { - fn add_to_diagnostic_with(self, diag: &mut crate::Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { diag.span_labels(self.spans, self.label); } } @@ -312,23 +316,6 @@ pub struct ExpectedLifetimeParameter { pub count: usize, } -#[derive(Subdiagnostic)] -#[note(errors_delayed_at_with_newline)] -pub struct DelayedAtWithNewline { - #[primary_span] - pub span: Span, - pub emitted_at: DiagnosticLocation, - pub note: Backtrace, -} -#[derive(Subdiagnostic)] -#[note(errors_delayed_at_without_newline)] -pub struct DelayedAtWithoutNewline { - #[primary_span] - pub span: Span, - pub emitted_at: DiagnosticLocation, - pub note: Backtrace, -} - impl IntoDiagnosticArg for DiagnosticLocation { fn into_diagnostic_arg(self) -> DiagnosticArgValue { DiagnosticArgValue::Str(Cow::from(self.to_string())) @@ -341,13 +328,6 @@ impl IntoDiagnosticArg for Backtrace { } } -#[derive(Subdiagnostic)] -#[note(errors_invalid_flushed_delayed_diagnostic_level)] -pub struct InvalidFlushedDelayedDiagnosticLevel { - #[primary_span] - pub span: Span, - pub level: Level, -} impl IntoDiagnosticArg for Level { fn into_diagnostic_arg(self) -> DiagnosticArgValue { DiagnosticArgValue::Str(Cow::from(self.to_string())) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index e09c041c1d0..df94b69004b 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -599,7 +599,7 @@ impl Emitter for SilentEmitter { fn emit_diagnostic(&mut self, mut diag: Diagnostic) { if diag.level == Level::Fatal { - diag.note(self.fatal_note.clone()); + diag.sub(Level::Note, self.fatal_note.clone(), MultiSpan::new()); self.fatal_dcx.emit_diagnostic(diag); } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 8643596b446..73cda64f1cc 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -46,7 +46,7 @@ pub use diagnostic_builder::{ }; pub use diagnostic_impls::{ DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, - IndicateAnonymousLifetime, InvalidFlushedDelayedDiagnosticLevel, SingleLabelManySpans, + IndicateAnonymousLifetime, SingleLabelManySpans, }; pub use emitter::ColorConfig; pub use rustc_error_messages::{ @@ -62,7 +62,6 @@ pub use snippet::Style; // See https://github.com/rust-lang/rust/pull/115393. pub use termcolor::{Color, ColorSpec, WriteColor}; -use crate::diagnostic_impls::{DelayedAtWithNewline, DelayedAtWithoutNewline}; use emitter::{is_case_difference, DynEmitter, Emitter, HumanEmitter}; use registry::Registry; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; @@ -1395,9 +1394,8 @@ impl DiagCtxtInner { }; diagnostic.children.extract_if(already_emitted_sub).for_each(|_| {}); if already_emitted { - diagnostic.note( - "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`", - ); + let msg = "duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`"; + diagnostic.sub(Level::Note, msg, MultiSpan::new()); } if is_error { @@ -1483,6 +1481,16 @@ impl DiagCtxtInner { self.emitter.translate_message(&message, &args).map_err(Report::new).unwrap().to_string() } + fn eagerly_translate_for_subdiag( + &self, + diag: &Diagnostic, + msg: impl Into, + ) -> SubdiagnosticMessage { + let args = diag.args(); + let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); + self.eagerly_translate(msg, args) + } + fn flush_delayed(&mut self) { if self.delayed_bugs.is_empty() { return; @@ -1527,17 +1535,14 @@ impl DiagCtxtInner { 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) - }); + // + // We are at the `Diagnostic`/`DiagCtxtInner` level rather than + // the usual `DiagnosticBuilder`/`DiagCtxt` level, so we must + // augment `bug` in a lower-level fashion. + bug.arg("level", bug.level); + let msg = crate::fluent_generated::errors_invalid_flushed_delayed_diagnostic_level; + let msg = self.eagerly_translate_for_subdiag(&bug, msg); // after the `arg` call + bug.sub(Level::Note, msg, bug.span.primary_span().unwrap().into()); } bug.level = Bug; @@ -1571,39 +1576,22 @@ 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) - }; - - match self.note.status() { - BacktraceStatus::Captured => { - let inner = &self.inner; - let subdiag = DelayedAtWithNewline { - span: inner.span.primary_span().unwrap_or(DUMMY_SP), - emitted_at: inner.emitted_at.clone(), - note: self.note, - }; - subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with); - } + fn decorate(self, dcx: &DiagCtxtInner) -> Diagnostic { + // We are at the `Diagnostic`/`DiagCtxtInner` level rather than the + // usual `DiagnosticBuilder`/`DiagCtxt` level, so we must construct + // `diag` in a lower-level fashion. + let mut diag = self.inner; + let msg = match self.note.status() { + BacktraceStatus::Captured => crate::fluent_generated::errors_delayed_at_with_newline, // Avoid the needless newline when no backtrace has been captured, // the display impl should just be a single line. - _ => { - let inner = &self.inner; - let subdiag = DelayedAtWithoutNewline { - span: inner.span.primary_span().unwrap_or(DUMMY_SP), - emitted_at: inner.emitted_at.clone(), - note: self.note, - }; - subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with); - } - } - - self.inner + _ => crate::fluent_generated::errors_delayed_at_without_newline, + }; + diag.arg("emitted_at", diag.emitted_at.clone()); + diag.arg("note", self.note); + let msg = dcx.eagerly_translate_for_subdiag(&diag, msg); // after the `arg` calls + diag.sub(Level::Note, msg, diag.span.primary_span().unwrap_or(DUMMY_SP).into()); + diag } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 09144523658..3226362ad10 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1176,6 +1176,8 @@ impl<'a> ExtCtxt<'a> { for (span, notes) in self.expansions.iter() { let mut db = self.dcx().create_note(errors::TraceMacro { span: *span }); for note in notes { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] db.note(note.clone()); } db.emit(); diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 781186764fa..435135d1959 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -384,6 +384,7 @@ impl<'a> StripUnconfigured<'a> { ); if attr.is_doc_comment() { + #[allow(rustc::untranslatable_diagnostic)] err.help("`///` is for documentation comments. For a plain comment, use `//`."); } diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 8f31b5801da..b80ecbc9c65 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -10,7 +10,7 @@ use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; -use rustc_errors::{ErrorGuaranteed, MultiSpan, PResult}; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed, MultiSpan, PResult}; use rustc_parse::lexer::nfc_normalize; use rustc_parse::parse_stream_from_source_str; use rustc_session::parse::ParseSess; @@ -509,13 +509,14 @@ impl server::FreeFunctions for Rustc<'_, '_> { } fn emit_diagnostic(&mut self, diagnostic: Diagnostic) { - let mut diag = - rustc_errors::Diagnostic::new(diagnostic.level.to_internal(), diagnostic.message); + let message = rustc_errors::DiagnosticMessage::from(diagnostic.message); + let mut diag: DiagnosticBuilder<'_, rustc_errors::ErrorGuaranteed> = + DiagnosticBuilder::new(&self.sess().dcx, diagnostic.level.to_internal(), message); diag.span(MultiSpan::from_spans(diagnostic.spans)); for child in diagnostic.children { diag.sub(child.level.to_internal(), child.message, MultiSpan::from_spans(child.spans)); } - self.sess().dcx.emit_diagnostic(diag); + diag.emit(); } } diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 1af0b75bd23..f609d0f7e8f 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, DiagnosticArgValue, DiagnosticBuilder, + EmissionGuarantee, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_middle::ty::Ty; @@ -195,7 +195,11 @@ pub struct TypeMismatchFruTypo { } impl AddToDiagnostic for TypeMismatchFruTypo { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); // Only explain that `a ..b` is a range if it's split up @@ -370,7 +374,11 @@ pub struct RemoveSemiForCoerce { } impl AddToDiagnostic for RemoveSemiForCoerce { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { 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,8 +549,12 @@ pub enum CastUnknownPointerSub { From(Span), } -impl AddToDiagnostic for CastUnknownPointerSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { +impl rustc_errors::AddToDiagnostic for CastUnknownPointerSub { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { match self { CastUnknownPointerSub::To(span) => { let msg = f(diag, crate::fluent_generated::hir_typeck_label_to); diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 8bfc05d6a96..f29ba70be98 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,7 +1,8 @@ use hir::GenericParamKind; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticMessage, - DiagnosticStyledString, IntoDiagnosticArg, MultiSpan, SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagnosticBuilder, DiagnosticMessage, + DiagnosticStyledString, EmissionGuarantee, IntoDiagnosticArg, MultiSpan, + SubdiagnosticMessageOp, }; use rustc_hir as hir; use rustc_hir::FnRetTy; @@ -225,7 +226,11 @@ pub enum RegionOriginNote<'a> { } impl AddToDiagnostic for RegionOriginNote<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { 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(); @@ -286,7 +291,11 @@ pub enum LifetimeMismatchLabels { } impl AddToDiagnostic for LifetimeMismatchLabels { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { diag.span_label(param_span, fluent::infer_declared_different); @@ -330,7 +339,11 @@ pub struct AddLifetimeParamsSuggestion<'a> { } impl AddToDiagnostic for AddLifetimeParamsSuggestion<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut mk_suggestion = || { let ( hir::Ty { kind: hir::TyKind::Ref(lifetime_sub, _), .. }, @@ -428,7 +441,11 @@ pub struct IntroducesStaticBecauseUnmetLifetimeReq { } impl AddToDiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { - fn add_to_diagnostic_with(mut self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + mut self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { 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); @@ -743,7 +760,11 @@ pub struct ConsiderBorrowingParamHelp { } impl AddToDiagnostic for ConsiderBorrowingParamHelp { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { 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 @@ -784,7 +805,11 @@ pub struct DynTraitConstraintSuggestion { } impl AddToDiagnostic for DynTraitConstraintSuggestion { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { 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); @@ -827,7 +852,11 @@ pub struct ReqIntroducedLocations { } impl AddToDiagnostic for ReqIntroducedLocations { - fn add_to_diagnostic_with(mut self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with>( + mut self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { for sp in self.spans { self.span.push_span_label(sp, fluent::infer_ril_introduced_here); } @@ -846,7 +875,11 @@ pub struct MoreTargeted { } impl AddToDiagnostic for MoreTargeted { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _f: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.code(E0772); diag.primary_message(fluent::infer_more_targeted); diag.arg("ident", self.ident); @@ -1265,7 +1298,11 @@ pub struct SuggestTuplePatternMany { } impl AddToDiagnostic for SuggestTuplePatternMany { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.arg("path", self.path); let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); diag.multipart_suggestions( diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index a59a4df7729..c272aa63b08 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,6 +1,9 @@ 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, DiagnosticBuilder, EmissionGuarantee, IntoDiagnosticArg, + SubdiagnosticMessageOp, +}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -160,7 +163,11 @@ impl RegionExplanation<'_> { } impl AddToDiagnostic for RegionExplanation<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, f: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.arg("pref_kind", self.prefix); diag.arg("suff_kind", self.suffix); diag.arg("desc_kind", self.desc.kind); diff --git a/compiler/rustc_lint/src/context/diagnostics.rs b/compiler/rustc_lint/src/context/diagnostics.rs index 5af2b6daec1..86434002e2c 100644 --- a/compiler/rustc_lint/src/context/diagnostics.rs +++ b/compiler/rustc_lint/src/context/diagnostics.rs @@ -1,3 +1,6 @@ +#![allow(rustc::diagnostic_outside_of_impl)] +#![allow(rustc::untranslatable_diagnostic)] + use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_errors::{add_elided_lifetime_in_path_suggestion, DiagnosticBuilder}; use rustc_errors::{Applicability, SuggestionStyle}; diff --git a/compiler/rustc_lint/src/errors.rs b/compiler/rustc_lint/src/errors.rs index 21d4b6fa65b..bcff20fc260 100644 --- a/compiler/rustc_lint/src/errors.rs +++ b/compiler/rustc_lint/src/errors.rs @@ -1,5 +1,7 @@ use crate::fluent_generated as fluent; -use rustc_errors::{codes::*, AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{ + codes::*, AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, SubdiagnosticMessageOp, +}; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_session::lint::Level; use rustc_span::{Span, Symbol}; @@ -24,7 +26,11 @@ pub enum OverruledAttributeSub { } impl AddToDiagnostic for OverruledAttributeSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { OverruledAttributeSub::DefaultSource { id } => { diag.note(fluent::lint_default_source); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 40fb12b2107..c1a083bde8d 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1062,6 +1062,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if self.lint_added_lints { let lint = builtin::UNKNOWN_LINTS; let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] lint_level( self.sess, lint, diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index c204c67fc1f..e4a7eb1d953 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, DiagnosticBuilder, DiagnosticMessage, + DiagnosticStyledString, EmissionGuarantee, SubdiagnosticMessageOp, SuggestionStyle, }; use rustc_hir::def_id::DefId; use rustc_macros::{LintDiagnostic, Subdiagnostic}; @@ -267,17 +267,21 @@ 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, 'b> AddToDiagnostic for SuggestChangingAssocTypes<'a, 'b> { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { // Access to associates types should use `::Assoc`, which does not need a // bound. Let's see if this type does that. // We use a HIR visitor to walk the type. use rustc_hir::intravisit::{self, Visitor}; - struct WalkAssocTypes<'a> { - err: &'a mut Diagnostic, + struct WalkAssocTypes<'a, 'b, G: EmissionGuarantee> { + err: &'a mut DiagnosticBuilder<'b, G>, } - impl Visitor<'_> for WalkAssocTypes<'_> { + impl<'a, 'b, G: EmissionGuarantee> Visitor<'_> for WalkAssocTypes<'a, 'b, G> { fn visit_qpath( &mut self, qpath: &rustc_hir::QPath<'_>, @@ -320,7 +324,11 @@ pub struct BuiltinTypeAliasGenericBoundsSuggestion { } impl AddToDiagnostic for BuiltinTypeAliasGenericBoundsSuggestion { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.multipart_suggestion( fluent::lint_suggestion, self.suggestions, @@ -437,7 +445,11 @@ pub struct BuiltinUnpermittedTypeInitSub { } impl AddToDiagnostic for BuiltinUnpermittedTypeInitSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut err = self.err; loop { if let Some(span) = err.span { @@ -488,7 +500,11 @@ pub struct BuiltinClashingExternSub<'a> { } impl AddToDiagnostic for BuiltinClashingExternSub<'_> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let mut expected_str = DiagnosticStyledString::new(); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); let mut found_str = DiagnosticStyledString::new(); @@ -766,7 +782,11 @@ pub struct HiddenUnicodeCodepointsDiagLabels { } impl AddToDiagnostic for HiddenUnicodeCodepointsDiagLabels { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { for (c, span) in self.spans { diag.span_label(span, format!("{c:?}")); } @@ -780,7 +800,11 @@ 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) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { HiddenUnicodeCodepointsDiagSub::Escape { spans } => { diag.multipart_suggestion_with_style( @@ -928,7 +952,11 @@ pub struct NonBindingLetSub { } impl AddToDiagnostic for NonBindingLetSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; if can_suggest_binding { @@ -1208,7 +1236,11 @@ pub enum NonSnakeCaseDiagSub { } impl AddToDiagnostic for NonSnakeCaseDiagSub { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { NonSnakeCaseDiagSub::Label { span } => { diag.span_label(span, fluent::lint_label); @@ -1401,7 +1433,11 @@ pub enum OverflowingBinHexSign { } impl AddToDiagnostic for OverflowingBinHexSign { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { 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 3a5f289559e..323614c222f 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -87,9 +87,13 @@ impl SubdiagnosticDeriveBuilder { 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, + fn add_to_diagnostic_with<__G, __F>( + self, + #diag: &mut rustc_errors::DiagnosticBuilder<'_, __G>, + #f: __F + ) where + __G: rustc_errors::EmissionGuarantee, + __F: rustc_errors::SubdiagnosticMessageOp<__G>, { #implementation } diff --git a/compiler/rustc_metadata/src/errors.rs b/compiler/rustc_metadata/src/errors.rs index d17bf0cf708..7e0a4fb72d4 100644 --- a/compiler/rustc_metadata/src/errors.rs +++ b/compiler/rustc_metadata/src/errors.rs @@ -505,6 +505,8 @@ impl IntoDiagnostic<'_, G> for MultipleCandidates { diag.code(E0464); diag.span(self.span); for (i, candidate) in self.candidates.iter().enumerate() { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] diag.note(format!("candidate #{}: {}", i + 1, candidate.display())); } diag @@ -601,6 +603,8 @@ impl IntoDiagnostic<'_, G> for InvalidMetadataFiles { diag.code(E0786); diag.span(self.span); for crate_rejection in self.crate_rejections { + // FIXME: make this translatable + #[allow(rustc::untranslatable_diagnostic)] diag.note(crate_rejection); } diag diff --git a/compiler/rustc_mir_build/src/errors.rs b/compiler/rustc_mir_build/src/errors.rs index 0bc5fe6ef89..2a42dae289b 100644 --- a/compiler/rustc_mir_build/src/errors.rs +++ b/compiler/rustc_mir_build/src/errors.rs @@ -1,7 +1,7 @@ use crate::fluent_generated as fluent; use rustc_errors::DiagnosticArgValue; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -420,7 +420,11 @@ pub struct UnsafeNotInheritedLintNote { } impl AddToDiagnostic for UnsafeNotInheritedLintNote { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { 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(); @@ -863,7 +867,11 @@ pub struct Variant { } impl<'tcx> AddToDiagnostic for AdtDefinedHere<'tcx> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { diag.arg("ty", self.ty); let mut spans = MultiSpan::from(self.adt_def_span); diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index ff4918df9a2..af2d7d4946f 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -87,6 +87,9 @@ pub(crate) struct RequiresUnsafeDetail { } impl RequiresUnsafeDetail { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] fn add_subdiagnostics(&self, diag: &mut DiagnosticBuilder<'_, G>) { use UnsafetyViolationDetails::*; match self.violation { diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index bd89874b5cc..e4c30679146 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -56,6 +56,7 @@ impl IntoDiagnostic<'_, G> for UnusedGenericParamsHint { // FIXME: I can figure out how to do a label with a fluent string with a fixed message, // or a label with a dynamic value in a hard-coded string, but I haven't figured out // how to combine the two. 😢 + #[allow(rustc::untranslatable_diagnostic)] diag.span_label(span, format!("generic parameter `{name}` is unused")); } diag diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 674f7218ea6..fde67ac089a 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use rustc_ast::token::Token; use rustc_ast::{Path, Visibility}; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, IntoDiagnostic, Level, SubdiagnosticMessageOp, }; use rustc_macros::{Diagnostic, Subdiagnostic}; @@ -1475,7 +1475,11 @@ pub(crate) struct FnTraitMissingParen { } impl AddToDiagnostic for FnTraitMissingParen { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { 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_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs index 3676eb92a3f..02792491f5e 100644 --- a/compiler/rustc_passes/src/check_const.rs +++ b/compiler/rustc_passes/src/check_const.rs @@ -156,9 +156,12 @@ impl<'tcx> CheckConstVisitor<'tcx> { // is a pretty narrow case, however. if tcx.sess.is_nightly_build() { for gate in missing_secondary { - let note = - format!("add `#![feature({gate})]` to the crate attributes to enable",); - err.help(note); + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] + err.help(format!( + "add `#![feature({gate})]` to the crate attributes to enable" + )); } } diff --git a/compiler/rustc_passes/src/errors.rs b/compiler/rustc_passes/src/errors.rs index a732bdbca51..982def54d30 100644 --- a/compiler/rustc_passes/src/errors.rs +++ b/compiler/rustc_passes/src/errors.rs @@ -6,9 +6,8 @@ use std::{ use crate::fluent_generated as fluent; use rustc_ast::Label; use rustc_errors::{ - codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticBuilder, - DiagnosticSymbolList, EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, - SubdiagnosticMessageOp, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, DiagnosticSymbolList, + EmissionGuarantee, IntoDiagnostic, Level, MultiSpan, SubdiagnosticMessageOp, }; use rustc_hir::{self as hir, ExprKind, Target}; use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; @@ -1754,7 +1753,11 @@ pub struct UnusedVariableStringInterp { } impl AddToDiagnostic for UnusedVariableStringInterp { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { 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 2dffdc9846c..27619b74a66 100644 --- a/compiler/rustc_pattern_analysis/src/errors.rs +++ b/compiler/rustc_pattern_analysis/src/errors.rs @@ -1,4 +1,4 @@ -use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessageOp}; +use rustc_errors::{AddToDiagnostic, DiagnosticBuilder, EmissionGuarantee, SubdiagnosticMessageOp}; use rustc_macros::{LintDiagnostic, Subdiagnostic}; use rustc_middle::thir::Pat; use rustc_middle::ty::Ty; @@ -62,7 +62,11 @@ pub struct Overlap<'tcx> { } impl<'tcx> AddToDiagnostic for Overlap<'tcx> { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _: F, + ) { let Overlap { span, range } = self; // FIXME(mejrs) unfortunately `#[derive(LintDiagnostic)]` diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index ef0512bf686..0a330da87b0 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2468,6 +2468,9 @@ pub fn parse_externs( )); let adjusted_name = name.replace('-', "_"); if is_ascii_ident(&adjusted_name) { + // FIXME: make this translatable + #[allow(rustc::diagnostic_outside_of_impl)] + #[allow(rustc::untranslatable_diagnostic)] error.help(format!( "consider replacing the dashes with underscores: `{adjusted_name}`" )); diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs index 407fff03e15..10911c0d002 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, + codes::*, AddToDiagnostic, Applicability, DiagCtxt, DiagnosticBuilder, EmissionGuarantee, + IntoDiagnostic, Level, SubdiagnosticMessageOp, }; use rustc_macros::Diagnostic; use rustc_middle::ty::{self, ClosureKind, PolyTraitRef, Ty}; @@ -102,7 +102,11 @@ pub enum AdjustSignatureBorrow { } impl AddToDiagnostic for AdjustSignatureBorrow { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + _f: F, + ) { match self { AdjustSignatureBorrow::Borrow { to_borrow } => { diag.arg("len", to_borrow.len()); diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index c86362c427c..b355e66b7b1 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -683,7 +683,7 @@ fn indentation(cx: &T, span: Span) -> Option { }) } -/// Convenience extension trait for `Diagnostic`. +/// Convenience extension trait for `DiagnosticBuilder`. pub trait DiagnosticExt { /// Suggests to add an attribute to an item. /// @@ -731,7 +731,7 @@ pub trait DiagnosticExt { fn suggest_remove_item(&mut self, cx: &T, item: Span, msg: &str, applicability: Applicability); } -impl DiagnosticExt for rustc_errors::Diagnostic { +impl DiagnosticExt for rustc_errors::DiagnosticBuilder<'_, ()> { fn suggest_item_with_attr( &mut self, cx: &T, diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.rs b/tests/ui-fulldeps/internal-lints/diagnostics.rs index 5a2099865d6..dcf948d2a88 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.rs +++ b/tests/ui-fulldeps/internal-lints/diagnostics.rs @@ -55,8 +55,11 @@ impl<'a, G: EmissionGuarantee> IntoDiagnostic<'a, G> for TranslatableInIntoDiagn pub struct UntranslatableInAddToDiagnostic; impl AddToDiagnostic for UntranslatableInAddToDiagnostic { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) - { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.note("untranslatable diagnostic"); //~^ ERROR diagnostics should be created using translatable messages } @@ -65,7 +68,11 @@ impl AddToDiagnostic for UntranslatableInAddToDiagnostic { pub struct TranslatableInAddToDiagnostic; impl AddToDiagnostic for TranslatableInAddToDiagnostic { - fn add_to_diagnostic_with(self, diag: &mut Diagnostic, _: F) { + fn add_to_diagnostic_with>( + self, + diag: &mut DiagnosticBuilder<'_, G>, + f: F, + ) { diag.note(crate::fluent_generated::no_crate_note); } } diff --git a/tests/ui-fulldeps/internal-lints/diagnostics.stderr b/tests/ui-fulldeps/internal-lints/diagnostics.stderr index 108b7c8ea9c..a69a71bf50e 100644 --- a/tests/ui-fulldeps/internal-lints/diagnostics.stderr +++ b/tests/ui-fulldeps/internal-lints/diagnostics.stderr @@ -11,13 +11,13 @@ LL | #![deny(rustc::untranslatable_diagnostic)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:60:14 + --> $DIR/diagnostics.rs:63:14 | LL | diag.note("untranslatable diagnostic"); | ^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:74:21 + --> $DIR/diagnostics.rs:81:21 | LL | let _diag = dcx.struct_err(crate::fluent_generated::no_crate_example); | ^^^^^^^^^^ @@ -29,13 +29,13 @@ LL | #![deny(rustc::diagnostic_outside_of_impl)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: diagnostics should only be created in `IntoDiagnostic`/`AddToDiagnostic` impls - --> $DIR/diagnostics.rs:77:21 + --> $DIR/diagnostics.rs:84:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ error: diagnostics should be created using translatable messages - --> $DIR/diagnostics.rs:77:21 + --> $DIR/diagnostics.rs:84:21 | LL | let _diag = dcx.struct_err("untranslatable diagnostic"); | ^^^^^^^^^^ 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 f07b69326b0..0d61e15b0f1 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 @@ -8,9 +8,9 @@ LL | arg: NotIntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` | = help: normalized in stderr -note: required by a bound in `DiagnosticBuilder::<'a, G>::arg` - --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC - = note: this error originates in the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `rustc_errors::diagnostic::>::arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: the trait bound `NotIntoDiagnosticArg: IntoDiagnosticArg` is not satisfied --> $DIR/diagnostic-derive-doc-comment-field.rs:46:10 @@ -22,8 +22,9 @@ LL | arg: NotIntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` | = help: normalized in stderr -note: required by a bound in `Diagnostic::arg` +note: required by a bound in `rustc_errors::diagnostic::>::arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + = 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 diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs index 6cc6fdfc0eb..856f32fafa0 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.rs @@ -1,8 +1,8 @@ // check-fail // Tests error conditions for specifying diagnostics using #[derive(Diagnostic)] - // normalize-stderr-test "the following other types implement trait `IntoDiagnosticArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr" -// normalize-stderr-test "diagnostic_builder\.rs:[0-9]+:[0-9]+" -> "diagnostic_builder.rs:LL:CC" +// normalize-stderr-test "(COMPILER_DIR/.*\.rs):[0-9]+:[0-9]+" -> "$1:LL:CC" + // The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, // changing the output of this test. Since Diagnostic is strictly internal to the compiler // the test is just ignored on stable and beta: diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index f2dbc718c76..ddbb3c6df26 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -628,9 +628,9 @@ LL | other: Hello, | ^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` | = help: normalized in stderr -note: required by a bound in `DiagnosticBuilder::<'a, G>::arg` - --> $COMPILER_DIR/rustc_errors/src/diagnostic_builder.rs:LL:CC - = note: this error originates in the macro `forward` (in Nightly builds, run with -Z macro-backtrace for more info) +note: required by a bound in `rustc_errors::diagnostic::>::arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 86 previous errors -- cgit 1.4.1-3-g733a5 From 8f20a54c6d4440ec58f78549878b9baabbf7f218 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 21 Feb 2024 09:37:30 +1100 Subject: Merge `diagnostic_builder.rs` into `diagnostic.rs`. Because: - `diagnostic_builder.rs` is small (282 lines), - `Diagnostic` and `DiagnosticBuilder` are closely related types, and - there's already an `impl DiagnosticBuilder` block in `diagnostic.rs`. At the same time, reorder a few of things already in `diagnostic.rs`, e.g. move `struct Diagnostic` just before `impl Diagnostic`. This commit only moves code around. There are no functional changes. --- compiler/rustc_errors/src/diagnostic.rs | 429 +++++++++++++++++---- compiler/rustc_errors/src/diagnostic_builder.rs | 282 -------------- compiler/rustc_errors/src/lib.rs | 10 +- .../diagnostic-derive-doc-comment-field.stderr | 4 +- .../session-diagnostic/diagnostic-derive.stderr | 2 +- 5 files changed, 355 insertions(+), 372 deletions(-) delete mode 100644 compiler/rustc_errors/src/diagnostic_builder.rs (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 57610635ee6..686a3dc66a1 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -1,19 +1,22 @@ use crate::snippet::Style; use crate::{ - CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level, - MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, + CodeSuggestion, DiagCtxt, DiagnosticMessage, ErrCode, ErrorGuaranteed, ExplicitBug, Level, + MultiSpan, StashKey, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle, }; use rustc_data_structures::fx::FxIndexMap; use rustc_error_messages::fluent_value_from_str_list_sep_by_and; use rustc_error_messages::FluentValue; use rustc_lint_defs::{Applicability, LintExpectationId}; +use rustc_span::source_map::Spanned; use rustc_span::symbol::Symbol; use rustc_span::{Span, DUMMY_SP}; use std::borrow::Cow; use std::fmt::{self, Debug}; use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -use std::panic::Location; +use std::panic; +use std::thread::panicking; /// Error type for `Diagnostic`'s `suggestions` field, indicating that /// `.disable_suggestions()` was called on the `Diagnostic`. @@ -40,6 +43,86 @@ pub enum DiagnosticArgValue { StrListSepByAnd(Vec>), } +/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" +/// (or "proof") token that the emission happened. +pub trait EmissionGuarantee: Sized { + /// This exists so that bugs and fatal errors can both result in `!` (an + /// abort) when emitted, but have different aborting behaviour. + type EmitResult = Self; + + /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each + /// `impl` of `EmissionGuarantee`, to make it impossible to create a value + /// of `Self::EmitResult` without actually performing the emission. + #[track_caller] + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult; +} + +impl EmissionGuarantee for ErrorGuaranteed { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_error_guaranteed() + } +} + +impl EmissionGuarantee for () { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + } +} + +/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for +/// bug diagnostics. +#[derive(Copy, Clone)] +pub struct BugAbort; + +impl EmissionGuarantee for BugAbort { + type EmitResult = !; + + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + panic::panic_any(ExplicitBug); + } +} + +/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for +/// fatal diagnostics. +#[derive(Copy, Clone)] +pub struct FatalAbort; + +impl EmissionGuarantee for FatalAbort { + type EmitResult = !; + + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + crate::FatalError.raise() + } +} + +impl EmissionGuarantee for rustc_span::fatal_error::FatalError { + fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { + db.emit_producing_nothing(); + rustc_span::fatal_error::FatalError + } +} + +/// Trait implemented by error types. This is rarely implemented manually. Instead, use +/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. +#[rustc_diagnostic_item = "IntoDiagnostic"] +pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { + /// Write out as a diagnostic out of `DiagCtxt`. + #[must_use] + fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>; +} + +impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned +where + T: IntoDiagnostic<'a, G>, + G: EmissionGuarantee, +{ + fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { + self.node.into_diagnostic(dcx, level).with_span(self.span) + } +} + /// Converts a value of a type into a `DiagnosticArg` (typically a field of an `IntoDiagnostic` /// struct). Implemented as a custom trait rather than `From` so that it is implemented on the type /// being converted rather than on `DiagnosticArgValue`, which enables types from other `rustc_*` @@ -98,36 +181,6 @@ pub trait DecorateLint<'a, G: EmissionGuarantee> { fn msg(&self) -> DiagnosticMessage; } -/// The main part of a diagnostic. Note that `DiagnosticBuilder`, which wraps -/// this type, is used for most operations, and should be used instead whenever -/// possible. This type should only be used when `DiagnosticBuilder`'s lifetime -/// causes difficulties, e.g. when storing diagnostics within `DiagCtxt`. -#[must_use] -#[derive(Clone, Debug, Encodable, Decodable)] -pub struct Diagnostic { - // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, - // outside of what methods in this crate themselves allow. - pub(crate) level: Level, - - pub messages: Vec<(DiagnosticMessage, Style)>, - pub code: Option, - pub span: MultiSpan, - pub children: Vec, - pub suggestions: Result, SuggestionsDisabled>, - args: FxIndexMap, - - /// 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`. - pub sort_span: Span, - - pub is_lint: Option, - - /// With `-Ztrack_diagnostics` enabled, - /// we print where in rustc this error was emitted. - pub(crate) emitted_at: DiagnosticLocation, -} - #[derive(Clone, Debug, Encodable, Decodable)] pub struct DiagnosticLocation { file: Cow<'static, str>, @@ -138,7 +191,7 @@ pub struct DiagnosticLocation { impl DiagnosticLocation { #[track_caller] fn caller() -> Self { - let loc = Location::caller(); + let loc = panic::Location::caller(); DiagnosticLocation { file: loc.file().into(), line: loc.line(), col: loc.column() } } } @@ -157,15 +210,6 @@ pub struct IsLint { has_future_breakage: bool, } -/// A "sub"-diagnostic attached to a parent diagnostic. -/// For example, a note attached to an error. -#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] -pub struct SubDiagnostic { - pub level: Level, - pub messages: Vec<(DiagnosticMessage, Style)>, - pub span: MultiSpan, -} - #[derive(Debug, PartialEq, Eq)] pub struct DiagnosticStyledString(pub Vec); @@ -215,6 +259,36 @@ impl StringPart { } } +/// The main part of a diagnostic. Note that `DiagnosticBuilder`, which wraps +/// this type, is used for most operations, and should be used instead whenever +/// possible. This type should only be used when `DiagnosticBuilder`'s lifetime +/// causes difficulties, e.g. when storing diagnostics within `DiagCtxt`. +#[must_use] +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct Diagnostic { + // NOTE(eddyb) this is private to disallow arbitrary after-the-fact changes, + // outside of what methods in this crate themselves allow. + pub(crate) level: Level, + + pub messages: Vec<(DiagnosticMessage, Style)>, + pub code: Option, + pub span: MultiSpan, + pub children: Vec, + pub suggestions: Result, SuggestionsDisabled>, + args: FxIndexMap, + + /// 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`. + pub sort_span: Span, + + pub is_lint: Option, + + /// With `-Ztrack_diagnostics` enabled, + /// we print where in rustc this error was emitted. + pub(crate) emitted_at: DiagnosticLocation, +} + impl Diagnostic { #[track_caller] pub fn new>(level: Level, message: M) -> Self { @@ -336,6 +410,120 @@ impl Diagnostic { pub fn replace_args(&mut self, args: FxIndexMap) { self.args = args; } + + /// Fields used for Hash, and PartialEq trait. + fn keys( + &self, + ) -> ( + &Level, + &[(DiagnosticMessage, Style)], + &Option, + &MultiSpan, + &[SubDiagnostic], + &Result, SuggestionsDisabled>, + Vec<(&DiagnosticArgName, &DiagnosticArgValue)>, + &Option, + ) { + ( + &self.level, + &self.messages, + &self.code, + &self.span, + &self.children, + &self.suggestions, + self.args().collect(), + // omit self.sort_span + &self.is_lint, + // omit self.emitted_at + ) + } +} + +impl Hash for Diagnostic { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + self.keys().hash(state); + } +} + +impl PartialEq for Diagnostic { + fn eq(&self, other: &Self) -> bool { + self.keys() == other.keys() + } +} + +/// A "sub"-diagnostic attached to a parent diagnostic. +/// For example, a note attached to an error. +#[derive(Clone, Debug, PartialEq, Hash, Encodable, Decodable)] +pub struct SubDiagnostic { + pub level: Level, + pub messages: Vec<(DiagnosticMessage, Style)>, + pub span: MultiSpan, +} + +/// Used for emitting structured error messages and other diagnostic information. +/// Wraps a `Diagnostic`, adding some useful things. +/// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check +/// that it has been emitted or cancelled. +/// - The `EmissionGuarantee`, which determines the type returned from `emit`. +/// +/// Each constructed `DiagnosticBuilder` must be consumed by a function such as +/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a +/// `DiagnosticBuilder` is dropped without being consumed by one of these +/// functions. +/// +/// If there is some state in a downstream crate you would like to +/// access in the methods of `DiagnosticBuilder` here, consider +/// extending `DiagCtxtFlags`. +#[must_use] +pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { + pub dcx: &'a DiagCtxt, + + /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is + /// consumed via `emit`, `cancel`, etc. At that point it is consumed and + /// replaced with `None`. Then `drop` checks that it is `None`; if not, it + /// panics because a diagnostic was built but not used. + /// + /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is + /// often used as a return value, especially within the frequently-used + /// `PResult` type. In theory, return value optimization (RVO) should avoid + /// unnecessary copying. In practice, it does not (at the time of writing). + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) diag: Option>, + + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) _marker: PhantomData, +} + +// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted +// twice, which would be bad. +impl !Clone for DiagnosticBuilder<'_, G> {} + +rustc_data_structures::static_assert_size!( + DiagnosticBuilder<'_, ()>, + 2 * std::mem::size_of::() +); + +impl Deref for DiagnosticBuilder<'_, G> { + type Target = Diagnostic; + + fn deref(&self) -> &Diagnostic { + self.diag.as_ref().unwrap() + } +} + +impl DerefMut for DiagnosticBuilder<'_, G> { + fn deref_mut(&mut self) -> &mut Diagnostic { + self.diag.as_mut().unwrap() + } +} + +impl Debug for DiagnosticBuilder<'_, G> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.diag.fmt(f) + } } /// `DiagnosticBuilder` impls many `&mut self -> &mut Self` methods. Each one @@ -382,6 +570,20 @@ macro_rules! with_fn { } impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { + #[rustc_lint_diagnostics] + #[track_caller] + pub fn new>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self { + Self::new_diagnostic(dcx, Diagnostic::new(level, message)) + } + + /// Creates a new `DiagnosticBuilder` with an already constructed + /// diagnostic. + #[track_caller] + pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self { + debug!("Created new diagnostic"); + Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } + } + /// Delay emission of this diagnostic as a bug. /// /// This can be useful in contexts where an error indicates a bug but @@ -1040,48 +1242,115 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { let sub = SubDiagnostic { level, messages, span }; self.children.push(sub); } -} -impl Diagnostic { - /// Fields used for Hash, and PartialEq trait - fn keys( - &self, - ) -> ( - &Level, - &[(DiagnosticMessage, Style)], - &Option, - &MultiSpan, - &[SubDiagnostic], - &Result, SuggestionsDisabled>, - Vec<(&DiagnosticArgName, &DiagnosticArgValue)>, - &Option, - ) { - ( - &self.level, - &self.messages, - &self.code, - &self.span, - &self.children, - &self.suggestions, - self.args().collect(), - // omit self.sort_span - &self.is_lint, - // omit self.emitted_at - ) + /// Takes the diagnostic. For use by methods that consume the + /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the + /// only code that will be run on `self`. + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) fn take_diag(&mut self) -> Diagnostic { + Box::into_inner(self.diag.take().unwrap()) } -} -impl Hash for Diagnostic { - fn hash(&self, state: &mut H) - where - H: Hasher, - { - self.keys().hash(state); + /// Most `emit_producing_guarantee` functions use this as a starting point. + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) fn emit_producing_nothing(mut self) { + let diag = self.take_diag(); + self.dcx.emit_diagnostic(diag); + } + + /// `ErrorGuaranteed::emit_producing_guarantee` uses this. + // FIXME(nnethercote) Make private once this moves to diagnostic.rs. + pub(crate) fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { + let diag = self.take_diag(); + + // The only error levels that produce `ErrorGuaranteed` are + // `Error` and `DelayedBug`. But `DelayedBug` should never occur here + // because delayed bugs have their level changed to `Bug` when they are + // actually printed, so they produce an ICE. + // + // (Also, even though `level` isn't `pub`, the whole `Diagnostic` could + // be overwritten with a new one thanks to `DerefMut`. So this assert + // protects against that, too.) + assert!( + matches!(diag.level, Level::Error | Level::DelayedBug), + "invalid diagnostic level ({:?})", + diag.level, + ); + + let guar = self.dcx.emit_diagnostic(diag); + guar.unwrap() + } + + /// Emit and consume the diagnostic. + #[track_caller] + pub fn emit(self) -> G::EmitResult { + G::emit_producing_guarantee(self) + } + + /// Emit the diagnostic unless `delay` is true, + /// in which case the emission will be delayed as a bug. + /// + /// See `emit` and `delay_as_bug` for details. + #[track_caller] + pub fn emit_unless(mut self, delay: bool) -> G::EmitResult { + if delay { + self.downgrade_to_delayed_bug(); + } + self.emit() + } + + /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or + /// cancelled or it will panic when dropped). + pub fn cancel(mut self) { + self.diag = None; + drop(self); + } + + /// Stashes diagnostic for possible later improvement in a different, + /// later stage of the compiler. The diagnostic can be accessed with + /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. + pub fn stash(mut self, span: Span, key: StashKey) { + self.dcx.stash_diagnostic(span, key, self.take_diag()); + } + + /// Delay emission of this diagnostic as a bug. + /// + /// This can be useful in contexts where an error indicates a bug but + /// typically this only happens when other compilation errors have already + /// happened. In those cases this can be used to defer emission of this + /// diagnostic as a bug in the compiler only if no other errors have been + /// emitted. + /// + /// In the meantime, though, callsites are required to deal with the "bug" + /// locally in whichever way makes the most sense. + #[track_caller] + pub fn delay_as_bug(mut self) -> G::EmitResult { + self.downgrade_to_delayed_bug(); + self.emit() } } -impl PartialEq for Diagnostic { - fn eq(&self, other: &Self) -> bool { - self.keys() == other.keys() +/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted, +/// cancelled, etc.) or we emit a bug. +impl Drop for DiagnosticBuilder<'_, G> { + fn drop(&mut self) { + match self.diag.take() { + Some(diag) if !panicking() => { + self.dcx.emit_diagnostic(Diagnostic::new( + Level::Bug, + DiagnosticMessage::from("the following error was constructed but not emitted"), + )); + self.dcx.emit_diagnostic(*diag); + panic!("error was constructed but not emitted"); + } + _ => {} + } } } + +#[macro_export] +macro_rules! struct_span_code_err { + ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({ + $dcx.struct_span_err($span, format!($($message)*)).with_code($code) + }) +} diff --git a/compiler/rustc_errors/src/diagnostic_builder.rs b/compiler/rustc_errors/src/diagnostic_builder.rs deleted file mode 100644 index 3a6a494af95..00000000000 --- a/compiler/rustc_errors/src/diagnostic_builder.rs +++ /dev/null @@ -1,282 +0,0 @@ -use crate::{ - DiagCtxt, Diagnostic, DiagnosticMessage, ErrorGuaranteed, ExplicitBug, Level, StashKey, -}; -use rustc_span::source_map::Spanned; -use rustc_span::Span; -use std::fmt::{self, Debug}; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use std::panic; -use std::thread::panicking; - -/// Trait implemented by error types. This is rarely implemented manually. Instead, use -/// `#[derive(Diagnostic)]` -- see [rustc_macros::Diagnostic]. -#[rustc_diagnostic_item = "IntoDiagnostic"] -pub trait IntoDiagnostic<'a, G: EmissionGuarantee = ErrorGuaranteed> { - /// Write out as a diagnostic out of `DiagCtxt`. - #[must_use] - fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G>; -} - -impl<'a, T, G> IntoDiagnostic<'a, G> for Spanned -where - T: IntoDiagnostic<'a, G>, - G: EmissionGuarantee, -{ - fn into_diagnostic(self, dcx: &'a DiagCtxt, level: Level) -> DiagnosticBuilder<'a, G> { - self.node.into_diagnostic(dcx, level).with_span(self.span) - } -} - -/// Used for emitting structured error messages and other diagnostic information. -/// Wraps a `Diagnostic`, adding some useful things. -/// - The `dcx` field, allowing it to (a) emit itself, and (b) do a drop check -/// that it has been emitted or cancelled. -/// - The `EmissionGuarantee`, which determines the type returned from `emit`. -/// -/// Each constructed `DiagnosticBuilder` must be consumed by a function such as -/// `emit`, `cancel`, `delay_as_bug`, or `into_diagnostic`. A panic occurrs if a -/// `DiagnosticBuilder` is dropped without being consumed by one of these -/// functions. -/// -/// If there is some state in a downstream crate you would like to -/// access in the methods of `DiagnosticBuilder` here, consider -/// extending `DiagCtxtFlags`. -#[must_use] -pub struct DiagnosticBuilder<'a, G: EmissionGuarantee = ErrorGuaranteed> { - pub dcx: &'a DiagCtxt, - - /// Why the `Option`? It is always `Some` until the `DiagnosticBuilder` is - /// consumed via `emit`, `cancel`, etc. At that point it is consumed and - /// replaced with `None`. Then `drop` checks that it is `None`; if not, it - /// panics because a diagnostic was built but not used. - /// - /// Why the Box? `Diagnostic` is a large type, and `DiagnosticBuilder` is - /// often used as a return value, especially within the frequently-used - /// `PResult` type. In theory, return value optimization (RVO) should avoid - /// unnecessary copying. In practice, it does not (at the time of writing). - // FIXME(nnethercote) Make private once this moves to diagnostic.rs. - pub(crate) diag: Option>, - - // FIXME(nnethercote) Make private once this moves to diagnostic.rs. - pub(crate) _marker: PhantomData, -} - -// Cloning a `DiagnosticBuilder` is a recipe for a diagnostic being emitted -// twice, which would be bad. -impl !Clone for DiagnosticBuilder<'_, G> {} - -rustc_data_structures::static_assert_size!( - DiagnosticBuilder<'_, ()>, - 2 * std::mem::size_of::() -); - -/// Trait for types that `DiagnosticBuilder::emit` can return as a "guarantee" -/// (or "proof") token that the emission happened. -pub trait EmissionGuarantee: Sized { - /// This exists so that bugs and fatal errors can both result in `!` (an - /// abort) when emitted, but have different aborting behaviour. - type EmitResult = Self; - - /// Implementation of `DiagnosticBuilder::emit`, fully controlled by each - /// `impl` of `EmissionGuarantee`, to make it impossible to create a value - /// of `Self::EmitResult` without actually performing the emission. - #[track_caller] - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult; -} - -impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { - /// Takes the diagnostic. For use by methods that consume the - /// DiagnosticBuilder: `emit`, `cancel`, etc. Afterwards, `drop` is the - /// only code that will be run on `self`. - // FIXME(nnethercote) Make private once this moves to diagnostic.rs. - pub(crate) fn take_diag(&mut self) -> Diagnostic { - Box::into_inner(self.diag.take().unwrap()) - } - - /// Most `emit_producing_guarantee` functions use this as a starting point. - // FIXME(nnethercote) Make private once this moves to diagnostic.rs. - pub(crate) fn emit_producing_nothing(mut self) { - let diag = self.take_diag(); - self.dcx.emit_diagnostic(diag); - } - - /// `ErrorGuaranteed::emit_producing_guarantee` uses this. - // FIXME(nnethercote) Make private once this moves to diagnostic.rs. - pub(crate) fn emit_producing_error_guaranteed(mut self) -> ErrorGuaranteed { - let diag = self.take_diag(); - - // The only error levels that produce `ErrorGuaranteed` are - // `Error` and `DelayedBug`. But `DelayedBug` should never occur here - // because delayed bugs have their level changed to `Bug` when they are - // actually printed, so they produce an ICE. - // - // (Also, even though `level` isn't `pub`, the whole `Diagnostic` could - // be overwritten with a new one thanks to `DerefMut`. So this assert - // protects against that, too.) - assert!( - matches!(diag.level, Level::Error | Level::DelayedBug), - "invalid diagnostic level ({:?})", - diag.level, - ); - - let guar = self.dcx.emit_diagnostic(diag); - guar.unwrap() - } -} - -impl EmissionGuarantee for ErrorGuaranteed { - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_error_guaranteed() - } -} - -impl EmissionGuarantee for () { - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - } -} - -/// Marker type which enables implementation of `create_bug` and `emit_bug` functions for -/// bug diagnostics. -#[derive(Copy, Clone)] -pub struct BugAbort; - -impl EmissionGuarantee for BugAbort { - type EmitResult = !; - - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - panic::panic_any(ExplicitBug); - } -} - -/// Marker type which enables implementation of `create_fatal` and `emit_fatal` functions for -/// fatal diagnostics. -#[derive(Copy, Clone)] -pub struct FatalAbort; - -impl EmissionGuarantee for FatalAbort { - type EmitResult = !; - - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - crate::FatalError.raise() - } -} - -impl EmissionGuarantee for rustc_span::fatal_error::FatalError { - fn emit_producing_guarantee(db: DiagnosticBuilder<'_, Self>) -> Self::EmitResult { - db.emit_producing_nothing(); - rustc_span::fatal_error::FatalError - } -} - -impl Deref for DiagnosticBuilder<'_, G> { - type Target = Diagnostic; - - fn deref(&self) -> &Diagnostic { - self.diag.as_ref().unwrap() - } -} - -impl DerefMut for DiagnosticBuilder<'_, G> { - fn deref_mut(&mut self) -> &mut Diagnostic { - self.diag.as_mut().unwrap() - } -} - -impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> { - #[rustc_lint_diagnostics] - #[track_caller] - pub fn new>(dcx: &'a DiagCtxt, level: Level, message: M) -> Self { - Self::new_diagnostic(dcx, Diagnostic::new(level, message)) - } - - /// Creates a new `DiagnosticBuilder` with an already constructed - /// diagnostic. - #[track_caller] - pub(crate) fn new_diagnostic(dcx: &'a DiagCtxt, diag: Diagnostic) -> Self { - debug!("Created new diagnostic"); - Self { dcx, diag: Some(Box::new(diag)), _marker: PhantomData } - } - - /// Emit and consume the diagnostic. - #[track_caller] - pub fn emit(self) -> G::EmitResult { - G::emit_producing_guarantee(self) - } - - /// Emit the diagnostic unless `delay` is true, - /// in which case the emission will be delayed as a bug. - /// - /// See `emit` and `delay_as_bug` for details. - #[track_caller] - pub fn emit_unless(mut self, delay: bool) -> G::EmitResult { - if delay { - self.downgrade_to_delayed_bug(); - } - self.emit() - } - - /// Cancel and consume the diagnostic. (A diagnostic must either be emitted or - /// cancelled or it will panic when dropped). - pub fn cancel(mut self) { - self.diag = None; - drop(self); - } - - /// Stashes diagnostic for possible later improvement in a different, - /// later stage of the compiler. The diagnostic can be accessed with - /// the provided `span` and `key` through [`DiagCtxt::steal_diagnostic()`]. - pub fn stash(mut self, span: Span, key: StashKey) { - self.dcx.stash_diagnostic(span, key, self.take_diag()); - } - - /// Delay emission of this diagnostic as a bug. - /// - /// This can be useful in contexts where an error indicates a bug but - /// typically this only happens when other compilation errors have already - /// happened. In those cases this can be used to defer emission of this - /// diagnostic as a bug in the compiler only if no other errors have been - /// emitted. - /// - /// In the meantime, though, callsites are required to deal with the "bug" - /// locally in whichever way makes the most sense. - #[track_caller] - pub fn delay_as_bug(mut self) -> G::EmitResult { - self.downgrade_to_delayed_bug(); - self.emit() - } -} - -impl Debug for DiagnosticBuilder<'_, G> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.diag.fmt(f) - } -} - -/// Destructor bomb: every `DiagnosticBuilder` must be consumed (emitted, -/// cancelled, etc.) or we emit a bug. -impl Drop for DiagnosticBuilder<'_, G> { - fn drop(&mut self) { - match self.diag.take() { - Some(diag) if !panicking() => { - self.dcx.emit_diagnostic(Diagnostic::new( - Level::Bug, - DiagnosticMessage::from("the following error was constructed but not emitted"), - )); - self.dcx.emit_diagnostic(*diag); - panic!("error was constructed but not emitted"); - } - _ => {} - } - } -} - -#[macro_export] -macro_rules! struct_span_code_err { - ($dcx:expr, $span:expr, $code:expr, $($message:tt)*) => ({ - $dcx.struct_span_err($span, format!($($message)*)).with_code($code) - }) -} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 73cda64f1cc..052d9b3a783 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -37,12 +37,9 @@ extern crate self as rustc_errors; pub use codes::*; pub use diagnostic::{ - AddToDiagnostic, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName, - DiagnosticArgValue, DiagnosticStyledString, IntoDiagnosticArg, StringPart, SubDiagnostic, - SubdiagnosticMessageOp, -}; -pub use diagnostic_builder::{ - BugAbort, DiagnosticBuilder, EmissionGuarantee, FatalAbort, IntoDiagnostic, + AddToDiagnostic, BugAbort, DecorateLint, Diagnostic, DiagnosticArg, DiagnosticArgName, + DiagnosticArgValue, DiagnosticBuilder, DiagnosticStyledString, EmissionGuarantee, FatalAbort, + IntoDiagnostic, IntoDiagnosticArg, StringPart, SubDiagnostic, SubdiagnosticMessageOp, }; pub use diagnostic_impls::{ DiagnosticArgFromDisplay, DiagnosticSymbolList, ExpectedLifetimeParameter, @@ -87,7 +84,6 @@ use Level::*; pub mod annotate_snippet_emitter_writer; pub mod codes; mod diagnostic; -mod diagnostic_builder; mod diagnostic_impls; pub mod emitter; pub mod error; 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 0d61e15b0f1..7844e60c654 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 @@ -8,7 +8,7 @@ LL | arg: NotIntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` | = help: normalized in stderr -note: required by a bound in `rustc_errors::diagnostic::>::arg` +note: required by a bound in `DiagnosticBuilder::<'a, G>::arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) @@ -22,7 +22,7 @@ LL | arg: NotIntoDiagnosticArg, | ^^^^^^^^^^^^^^^^^^^^ the trait `IntoDiagnosticArg` is not implemented for `NotIntoDiagnosticArg` | = help: normalized in stderr -note: required by a bound in `rustc_errors::diagnostic::>::arg` +note: required by a bound in `DiagnosticBuilder::<'a, G>::arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr index ddbb3c6df26..8732629db47 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive.stderr @@ -628,7 +628,7 @@ LL | other: Hello, | ^^^^^ the trait `IntoDiagnosticArg` is not implemented for `Hello` | = help: normalized in stderr -note: required by a bound in `rustc_errors::diagnostic::>::arg` +note: required by a bound in `DiagnosticBuilder::<'a, G>::arg` --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -- cgit 1.4.1-3-g733a5 From 1e16927ef37eb841d6a38b5e61438931d20c43fb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 16 Feb 2024 10:35:53 +1100 Subject: Remove an out-of-date comment. --- compiler/rustc_errors/src/lib.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 052d9b3a783..64b88adedbb 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1271,7 +1271,6 @@ impl DiagCtxtInner { fn emit_stashed_diagnostics(&mut self) { let has_errors = !self.err_guars.is_empty(); for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { - // Decrement the count tracking the stash; emitting will increment it. if diag.is_error() { if diag.is_lint.is_none() { self.stashed_err_count -= 1; -- cgit 1.4.1-3-g733a5 From 46f49833566381887ba74e3f756271a6e8723636 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Feb 2024 09:36:08 +1100 Subject: Adjust the `has_errors*` methods. Currently `has_errors` excludes lint errors. This commit changes it to include lint errors. The motivation for this is that for most places it doesn't matter whether lint errors are included or not. But there are multiple places where they must be includes, and only one place where they must not be included. So it makes sense for `has_errors` to do the thing that fits the most situations, and the new `has_errors_excluding_lint_errors` method in the one exceptional place. The same change is made for `err_count`. Annoyingly, this requires the introduction of `err_count_excluding_lint_errs` for one place, to preserve existing error printing behaviour. But I still think the change is worthwhile overall. --- compiler/rustc_errors/src/lib.rs | 46 ++++++++++++---------- compiler/rustc_incremental/src/persist/fs.rs | 2 +- compiler/rustc_incremental/src/persist/save.rs | 4 +- compiler/rustc_infer/src/infer/mod.rs | 9 +++-- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_metadata/src/creader.rs | 2 +- compiler/rustc_query_system/src/dep_graph/graph.rs | 2 +- compiler/rustc_session/src/session.rs | 3 +- src/librustdoc/core.rs | 3 +- src/librustdoc/doctest.rs | 3 +- 10 files changed, 41 insertions(+), 35 deletions(-) (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 64b88adedbb..a6b0a0e8f17 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -754,13 +754,20 @@ impl DiagCtxt { self.inner.borrow_mut().emit_stashed_diagnostics() } - /// This excludes lint errors, delayed bugs, and stashed errors. + /// This excludes lint errors, delayed bugs and stashed errors. #[inline] - pub fn err_count(&self) -> usize { + pub fn err_count_excluding_lint_errs(&self) -> usize { self.inner.borrow().err_guars.len() } - /// This excludes normal errors, lint errors and delayed bugs. Unless + /// This excludes delayed bugs and stashed errors. + #[inline] + pub fn err_count(&self) -> usize { + let inner = self.inner.borrow(); + inner.err_guars.len() + inner.lint_err_guars.len() + } + + /// This excludes normal errors, lint errors, and delayed bugs. Unless /// absolutely necessary, avoid using this. It's dubious because stashed /// errors can later be cancelled, so the presence of a stashed error at /// some point of time doesn't guarantee anything -- there are no @@ -769,21 +776,21 @@ impl DiagCtxt { self.inner.borrow().stashed_err_count } - /// This excludes lint errors, delayed bugs, and stashed errors. - pub fn has_errors(&self) -> Option { - self.inner.borrow().has_errors() + /// This excludes lint errors, delayed bugs, and stashed errors. Unless + /// absolutely necessary, prefer `has_errors` to this method. + pub fn has_errors_excluding_lint_errors(&self) -> Option { + self.inner.borrow().has_errors_excluding_lint_errors() } - /// This excludes delayed bugs and stashed errors. Unless absolutely - /// necessary, prefer `has_errors` to this method. - pub fn has_errors_or_lint_errors(&self) -> Option { - self.inner.borrow().has_errors_or_lint_errors() + /// This excludes delayed bugs and stashed errors. + pub fn has_errors(&self) -> Option { + self.inner.borrow().has_errors() } /// This excludes stashed errors. Unless absolutely necessary, prefer - /// `has_errors` or `has_errors_or_lint_errors` to this method. - pub fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option { - self.inner.borrow().has_errors_or_lint_errors_or_delayed_bugs() + /// `has_errors` to this method. + pub fn has_errors_or_delayed_bugs(&self) -> Option { + self.inner.borrow().has_errors_or_delayed_bugs() } pub fn print_error_count(&self, registry: &Registry) { @@ -1328,7 +1335,7 @@ impl DiagCtxtInner { DelayedBug => { // If we have already emitted at least one error, we don't need // to record the delayed bug, because it'll never be used. - return if let Some(guar) = self.has_errors_or_lint_errors() { + return if let Some(guar) = self.has_errors() { Some(guar) } else { let backtrace = std::backtrace::Backtrace::capture(); @@ -1444,17 +1451,16 @@ impl DiagCtxtInner { .is_some_and(|c| self.err_guars.len() + self.lint_err_guars.len() + 1 >= c.get()) } - fn has_errors(&self) -> Option { + fn has_errors_excluding_lint_errors(&self) -> Option { self.err_guars.get(0).copied() } - fn has_errors_or_lint_errors(&self) -> Option { - self.has_errors().or_else(|| self.lint_err_guars.get(0).copied()) + fn has_errors(&self) -> Option { + self.has_errors_excluding_lint_errors().or_else(|| self.lint_err_guars.get(0).copied()) } - fn has_errors_or_lint_errors_or_delayed_bugs(&self) -> Option { - self.has_errors_or_lint_errors() - .or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) + fn has_errors_or_delayed_bugs(&self) -> Option { + self.has_errors().or_else(|| self.delayed_bugs.get(0).map(|(_, guar)| guar).copied()) } /// Translate `message` eagerly with `args` to `SubdiagnosticMessage::Eager`. diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 23d29916922..dd9c16d006a 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -312,7 +312,7 @@ pub fn finalize_session_directory(sess: &Session, svh: Option) { let incr_comp_session_dir: PathBuf = sess.incr_comp_session_dir().clone(); - if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() { + if sess.dcx().has_errors_or_delayed_bugs().is_some() { // If there have been any errors during compilation, we don't want to // publish this session directory. Rather, we'll just delete it. diff --git a/compiler/rustc_incremental/src/persist/save.rs b/compiler/rustc_incremental/src/persist/save.rs index ff0c58d09de..32759f5284a 100644 --- a/compiler/rustc_incremental/src/persist/save.rs +++ b/compiler/rustc_incremental/src/persist/save.rs @@ -32,7 +32,7 @@ pub fn save_dep_graph(tcx: TyCtxt<'_>) { return; } // This is going to be deleted in finalize_session_directory, so let's not create it. - if sess.dcx().has_errors_or_lint_errors_or_delayed_bugs().is_some() { + if sess.dcx().has_errors_or_delayed_bugs().is_some() { return; } @@ -87,7 +87,7 @@ pub fn save_work_product_index( return; } // This is going to be deleted in finalize_session_directory, so let's not create it - if sess.dcx().has_errors_or_lint_errors().is_some() { + if sess.dcx().has_errors().is_some() { return; } diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 243558b11a8..f131d11c411 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -713,7 +713,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { reported_trait_errors: Default::default(), reported_signature_mismatch: Default::default(), tainted_by_errors: Cell::new(None), - err_count_on_creation: tcx.dcx().err_count(), + err_count_on_creation: tcx.dcx().err_count_excluding_lint_errs(), stashed_err_count_on_creation: tcx.dcx().stashed_err_count(), universe: Cell::new(ty::UniverseIndex::ROOT), intercrate, @@ -1268,8 +1268,11 @@ impl<'tcx> InferCtxt<'tcx> { pub fn tainted_by_errors(&self) -> Option { if let Some(guar) = self.tainted_by_errors.get() { Some(guar) - } else if self.dcx().err_count() > self.err_count_on_creation { - // Errors reported since this infcx was made. + } else if self.dcx().err_count_excluding_lint_errs() > self.err_count_on_creation { + // Errors reported since this infcx was made. Lint errors are + // excluded to avoid some being swallowed in the presence of + // non-lint errors. (It's arguable whether or not this exclusion is + // important.) let guar = self.dcx().has_errors().unwrap(); self.set_tainted_by_errors(guar); Some(guar) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 60d13f02ad7..858db594b47 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -772,7 +772,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // lot of annoying errors in the ui tests (basically, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis - if let Some(reported) = sess.dcx().has_errors() { + if let Some(reported) = sess.dcx().has_errors_excluding_lint_errors() { return Err(reported); } else if sess.dcx().stashed_err_count() > 0 { // Without this case we sometimes get delayed bug ICEs and I don't diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index f6d3dba2470..baa9af87a83 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -926,7 +926,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> { what: &str, needs_dep: &dyn Fn(&CrateMetadata) -> bool, ) { - // don't perform this validation if the session has errors, as one of + // Don't perform this validation if the session has errors, as one of // those errors may indicate a circular dependency which could cause // this to stack overflow. if self.dcx().has_errors().is_some() { diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index b6ac54a9ab5..7dc1a1f7917 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -817,7 +817,7 @@ impl DepGraphData { None => {} } - if let None = qcx.dep_context().sess().dcx().has_errors_or_lint_errors_or_delayed_bugs() { + if let None = qcx.dep_context().sess().dcx().has_errors_or_delayed_bugs() { panic!("try_mark_previous_green() - Forcing the DepNode should have set its color") } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 422bbadf6a8..f8a1d79659d 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -313,8 +313,7 @@ impl Session { } pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> { - // We must include lint errors here. - if let Some(reported) = self.dcx().has_errors_or_lint_errors() { + if let Some(reported) = self.dcx().has_errors() { self.dcx().emit_stashed_diagnostics(); Err(reported) } else { diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index efad5a8d808..3f7edd049a7 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -452,8 +452,7 @@ pub(crate) fn run_global_ctxt( tcx.sess.time("check_lint_expectations", || tcx.check_expectations(Some(sym::rustdoc))); - // We must include lint errors here. - if tcx.dcx().has_errors_or_lint_errors().is_some() { + if tcx.dcx().has_errors().is_some() { rustc_errors::FatalError.raise(); } diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index f9d4d1af114..09a90b62d97 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -153,8 +153,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { collector }); - // We must include lint errors here. - if compiler.sess.dcx().has_errors_or_lint_errors().is_some() { + if compiler.sess.dcx().has_errors().is_some() { FatalError.raise(); } -- cgit 1.4.1-3-g733a5 From 72b172bdf631483d0f802c54c8dc8246f6b4e00e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Feb 2024 10:00:19 +1100 Subject: Overhaul the handling of errors at the top-level. Currently `emit_stashed_diagnostic` is called from four(!) different places: `print_error_count`, `DiagCtxtInner::drop`, `abort_if_errors`, and `compile_status`. And `flush_delayed` is called from two different places: `DiagCtxtInner::drop` and `Queries`. This is pretty gross! Each one should really be called from a single place, but there's a bunch of entanglements. This commit cleans up this mess. Specifically, it: - Removes all the existing calls to `emit_stashed_diagnostic`, and adds a single new call in `finish_diagnostics`. - Removes the early `flush_delayed` call in `codegen_and_build_linker`, replacing it with a simple early return if delayed bugs are present. - Changes `DiagCtxtInner::drop` and `DiagCtxtInner::flush_delayed` so they both assert that the stashed diagnostics are empty (i.e. processed beforehand). - Changes `interface::run_compiler` so that any errors emitted during `finish_diagnostics` (i.e. late-emitted stashed diagnostics) are counted and cannot be overlooked. This requires adding `ErrorGuaranteed` return values to several functions. - Removes the `stashed_err_count` call in `analysis`. This is possible now that we don't have to worry about calling `flush_delayed` early from `codegen_and_build_linker` when stashed diagnostics are pending. - Changes the `span_bug` case in `handle_tuple_field_pattern_match` to a `delayed_span_bug`, because it now can be reached due to the removal of the `stashed_err_count` call in `analysis`. - Slightly changes the expected output of three tests. If no errors are emitted but there are delayed bugs, the error count is no longer printed. This is because delayed bugs are now always printed after the error count is printed (or not printed, if the error count is zero). There is a lot going on in this commit. It's hard to break into smaller pieces because the existing code is very tangled. It took me a long time and a lot of effort to understand how the different pieces interact, and I think the new code is a lot simpler and easier to understand. --- compiler/rustc_errors/src/lib.rs | 32 ++++++++++++------- compiler/rustc_interface/src/interface.rs | 37 ++++++++++++++++++---- compiler/rustc_interface/src/passes.rs | 11 +++---- compiler/rustc_interface/src/queries.rs | 12 +++---- compiler/rustc_passes/src/dead.rs | 5 ++- compiler/rustc_session/src/session.rs | 14 +++++--- .../equality-in-canonical-query.clone.stderr | 2 -- tests/ui/inference/issue-80409.no-compat.stderr | 2 -- ...tait_equality_in_canonical_query.current.stderr | 2 -- 9 files changed, 76 insertions(+), 41 deletions(-) (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index a6b0a0e8f17..fafd636bb70 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -471,9 +471,10 @@ struct DiagCtxtInner { emitted_diagnostics: FxHashSet, /// Stashed diagnostics emitted in one stage of the compiler that may be - /// stolen by other stages (e.g. to improve them and add more information). - /// The stashed diagnostics count towards the total error count. - /// When `.abort_if_errors()` is called, these are also emitted. + /// stolen and emitted/cancelled by other stages (e.g. to improve them and + /// add more information). All stashed diagnostics must be emitted with + /// `emit_stashed_diagnostics` by the time the `DiagCtxtInner` is dropped, + /// otherwise an assertion failure will occur. stashed_diagnostics: FxIndexMap<(Span, StashKey), Diagnostic>, future_breakage_diagnostics: Vec, @@ -558,7 +559,9 @@ pub struct DiagCtxtFlags { impl Drop for DiagCtxtInner { fn drop(&mut self) { - self.emit_stashed_diagnostics(); + // Any stashed diagnostics should have been handled by + // `emit_stashed_diagnostics` by now. + assert!(self.stashed_diagnostics.is_empty()); if self.err_guars.is_empty() { self.flush_delayed() @@ -750,7 +753,7 @@ impl DiagCtxt { } /// Emit all stashed diagnostics. - pub fn emit_stashed_diagnostics(&self) { + pub fn emit_stashed_diagnostics(&self) -> Option { self.inner.borrow_mut().emit_stashed_diagnostics() } @@ -796,7 +799,9 @@ impl DiagCtxt { pub fn print_error_count(&self, registry: &Registry) { let mut inner = self.inner.borrow_mut(); - inner.emit_stashed_diagnostics(); + // Any stashed diagnostics should have been handled by + // `emit_stashed_diagnostics` by now. + assert!(inner.stashed_diagnostics.is_empty()); if inner.treat_err_as_bug() { return; @@ -872,9 +877,7 @@ impl DiagCtxt { } pub fn abort_if_errors(&self) { - let mut inner = self.inner.borrow_mut(); - inner.emit_stashed_diagnostics(); - if !inner.err_guars.is_empty() { + if self.has_errors().is_some() { FatalError.raise(); } } @@ -1275,7 +1278,8 @@ impl DiagCtxt { // `DiagCtxtInner::foo`. impl DiagCtxtInner { /// Emit all stashed diagnostics. - fn emit_stashed_diagnostics(&mut self) { + fn emit_stashed_diagnostics(&mut self) -> Option { + let mut guar = None; let has_errors = !self.err_guars.is_empty(); for (_, diag) in std::mem::take(&mut self.stashed_diagnostics).into_iter() { if diag.is_error() { @@ -1290,8 +1294,9 @@ impl DiagCtxtInner { continue; } } - self.emit_diagnostic(diag); + guar = guar.or(self.emit_diagnostic(diag)); } + guar } // Return value is only `Some` if the level is `Error` or `DelayedBug`. @@ -1493,6 +1498,11 @@ impl DiagCtxtInner { } fn flush_delayed(&mut self) { + // Stashed diagnostics must be emitted before delayed bugs are flushed. + // Otherwise, we might ICE prematurely when errors would have + // eventually happened. + assert!(self.stashed_diagnostics.is_empty()); + if self.delayed_bugs.is_empty() { return; } diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 8a4705e0056..cd7957c3bce 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -423,18 +423,43 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se Compiler { sess, codegen_backend, override_queries: config.override_queries }; rustc_span::set_source_map(compiler.sess.parse_sess.clone_source_map(), move || { - let r = { - let _sess_abort_error = defer(|| { - compiler.sess.finish_diagnostics(&config.registry); + // There are two paths out of `f`. + // - Normal exit. + // - Panic, e.g. triggered by `abort_if_errors`. + // + // We must run `finish_diagnostics` in both cases. + let res = { + // If `f` panics, `finish_diagnostics` will run during + // unwinding because of the `defer`. + let mut guar = None; + let sess_abort_guard = defer(|| { + guar = compiler.sess.finish_diagnostics(&config.registry); }); - f(&compiler) + let res = f(&compiler); + + // If `f` doesn't panic, `finish_diagnostics` will run + // normally when `sess_abort_guard` is dropped. + drop(sess_abort_guard); + + // If `finish_diagnostics` emits errors (e.g. stashed + // errors) we can't return an error directly, because the + // return type of this function is `R`, not `Result`. + // But we need to communicate the errors' existence to the + // caller, otherwise the caller might mistakenly think that + // no errors occurred and return a zero exit code. So we + // abort (panic) instead, similar to if `f` had panicked. + if guar.is_some() { + compiler.sess.dcx().abort_if_errors(); + } + + res }; let prof = compiler.sess.prof.clone(); - prof.generic_activity("drop_compiler").run(move || drop(compiler)); - r + + res }) }, ) diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 858db594b47..d35c2be1fb4 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -772,12 +772,11 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { // lot of annoying errors in the ui tests (basically, // lint warnings and so on -- kindck used to do this abort, but // kindck is gone now). -nmatsakis - if let Some(reported) = sess.dcx().has_errors_excluding_lint_errors() { - return Err(reported); - } else if sess.dcx().stashed_err_count() > 0 { - // Without this case we sometimes get delayed bug ICEs and I don't - // understand why. -nnethercote - return Err(sess.dcx().delayed_bug("some stashed error is waiting for use")); + // + // But we exclude lint errors from this, because lint errors are typically + // less serious and we're more likely to want to continue (#87337). + if let Some(guar) = sess.dcx().has_errors_excluding_lint_errors() { + return Err(guar); } sess.time("misc_checking_3", || { diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 211bcb9da94..8170c0a5a1a 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -222,12 +222,12 @@ impl<'tcx> Queries<'tcx> { pub fn codegen_and_build_linker(&'tcx self) -> Result { self.global_ctxt()?.enter(|tcx| { - // Don't do code generation if there were any errors - self.compiler.sess.compile_status()?; - - // If we have any delayed bugs, for example because we created TyKind::Error earlier, - // it's likely that codegen will only cause more ICEs, obscuring the original problem - self.compiler.sess.dcx().flush_delayed(); + // Don't do code generation if there were any errors. Likewise if + // there were any delayed bugs, because codegen will likely cause + // more ICEs, obscuring the original problem. + if let Some(guar) = self.compiler.sess.dcx().has_errors_or_delayed_bugs() { + return Err(guar); + } // Hook for UI tests. Self::check_for_rustc_errors_attr(tcx); diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 486396b0677..a3106856a67 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -237,7 +237,10 @@ impl<'tcx> MarkSymbolVisitor<'tcx> { ) { let variant = match self.typeck_results().node_type(lhs.hir_id).kind() { ty::Adt(adt, _) => adt.variant_of_res(res), - _ => span_bug!(lhs.span, "non-ADT in tuple struct pattern"), + _ => { + self.tcx.dcx().span_delayed_bug(lhs.span, "non-ADT in tuple struct pattern"); + return; + } }; let dotdot = dotdot.as_opt_usize().unwrap_or(pats.len()); let first_n = pats.iter().enumerate().take(dotdot); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index f8a1d79659d..48a18fca27e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -258,7 +258,8 @@ impl Session { } } - fn check_miri_unleashed_features(&self) { + fn check_miri_unleashed_features(&self) -> Option { + let mut guar = None; let unleashed_features = self.miri_unleashed_features.lock(); if !unleashed_features.is_empty() { let mut must_err = false; @@ -279,18 +280,22 @@ impl Session { // If we should err, make sure we did. if must_err && self.dcx().has_errors().is_none() { // We have skipped a feature gate, and not run into other errors... reject. - self.dcx().emit_err(errors::NotCircumventFeature); + guar = Some(self.dcx().emit_err(errors::NotCircumventFeature)); } } + guar } /// Invoked all the way at the end to finish off diagnostics printing. - pub fn finish_diagnostics(&self, registry: &Registry) { - self.check_miri_unleashed_features(); + pub fn finish_diagnostics(&self, registry: &Registry) -> Option { + let mut guar = None; + guar = guar.or(self.check_miri_unleashed_features()); + guar = guar.or(self.dcx().emit_stashed_diagnostics()); self.dcx().print_error_count(registry); if self.opts.json_future_incompat { self.dcx().emit_future_breakage_report(); } + guar } /// Returns true if the crate is a testing one. @@ -314,7 +319,6 @@ impl Session { pub fn compile_status(&self) -> Result<(), ErrorGuaranteed> { if let Some(reported) = self.dcx().has_errors() { - self.dcx().emit_stashed_diagnostics(); Err(reported) } else { Ok(()) diff --git a/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr b/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr index 0e3cd2ff060..e4c8aec3973 100644 --- a/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr +++ b/tests/ui/impl-trait/equality-in-canonical-query.clone.stderr @@ -21,5 +21,3 @@ LL | same_output(foo, rpit); query stack during panic: end of query stack -error: aborting due to 2 previous errors - diff --git a/tests/ui/inference/issue-80409.no-compat.stderr b/tests/ui/inference/issue-80409.no-compat.stderr index f9772b2d5a6..523ca229b06 100644 --- a/tests/ui/inference/issue-80409.no-compat.stderr +++ b/tests/ui/inference/issue-80409.no-compat.stderr @@ -12,5 +12,3 @@ LL | builder.state().on_entry(|_| {}); query stack during panic: end of query stack -error: aborting due to 1 previous error - diff --git a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr index fd76526644b..069292239bc 100644 --- a/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr +++ b/tests/ui/type-alias-impl-trait/rpit_tait_equality_in_canonical_query.current.stderr @@ -21,5 +21,3 @@ LL | query(get_rpit); query stack during panic: end of query stack -error: aborting due to 2 previous errors - -- cgit 1.4.1-3-g733a5 From 4da67fff61ccc460370df6047563f8091c7e66bd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 19 Feb 2024 10:23:58 +1100 Subject: Replace unnecessary `abort_if_errors`. Replace `abort_if_errors` calls that are certain to abort -- because we emit an error immediately beforehand -- with `FatalErro.raise()`. --- compiler/rustc_codegen_ssa/src/back/link.rs | 9 +++------ compiler/rustc_codegen_ssa/src/base.rs | 5 +---- compiler/rustc_errors/src/lib.rs | 4 ++++ compiler/rustc_interface/src/passes.rs | 4 +--- compiler/rustc_session/src/output.rs | 3 ++- .../src/traits/error_reporting/type_err_ctxt_ext.rs | 11 ++--------- 6 files changed, 13 insertions(+), 23 deletions(-) (limited to 'compiler/rustc_errors/src/lib.rs') diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 7e3f324fe14..1ad0dec0640 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -3,7 +3,7 @@ use rustc_ast::CRATE_NODE_ID; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; -use rustc_errors::{DiagCtxt, ErrorGuaranteed}; +use rustc_errors::{DiagCtxt, ErrorGuaranteed, FatalError}; use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_metadata::find_native_static_library; @@ -722,10 +722,7 @@ fn link_dwarf_object<'a>( Ok(()) }) { Ok(()) => {} - Err(e) => { - sess.dcx().emit_err(errors::ThorinErrorWrapper(e)); - sess.dcx().abort_if_errors(); - } + Err(e) => sess.dcx().emit_fatal(errors::ThorinErrorWrapper(e)), } } @@ -1001,7 +998,7 @@ fn link_natively<'a>( sess.dcx().emit_note(errors::CheckInstalledVisualStudio); sess.dcx().emit_note(errors::InsufficientVSCodeProduct); } - sess.dcx().abort_if_errors(); + FatalError.raise(); } } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 760b3f30ee5..f7afd22a48c 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -449,10 +449,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let Some(llfn) = cx.declare_c_main(llfty) else { // FIXME: We should be smart and show a better diagnostic here. let span = cx.tcx().def_span(rust_main_def_id); - let dcx = cx.tcx().dcx(); - dcx.emit_err(errors::MultipleMainFunctions { span }); - dcx.abort_if_errors(); - bug!(); + cx.tcx().dcx().emit_fatal(errors::MultipleMainFunctions { span }); }; // `main` should respect same config for frame pointer elimination as rest of code diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index fafd636bb70..7e3d15ffc92 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -876,6 +876,10 @@ impl DiagCtxt { } } + /// This excludes delayed bugs and stashed errors. Used for early aborts + /// after errors occurred -- e.g. because continuing in the face of errors is + /// likely to lead to bad results, such as spurious/uninteresting + /// additional errors -- when returning an error `Result` is difficult. pub fn abort_if_errors(&self) { if self.has_errors().is_some() { FatalError.raise(); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index d35c2be1fb4..66140168759 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -936,9 +936,7 @@ pub fn start_codegen<'tcx>( if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) { if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) { - let dcx = tcx.dcx(); - dcx.emit_err(errors::CantEmitMIR { error }); - dcx.abort_if_errors(); + tcx.dcx().emit_fatal(errors::CantEmitMIR { error }); } } diff --git a/compiler/rustc_session/src/output.rs b/compiler/rustc_session/src/output.rs index db976b30404..74d26237f24 100644 --- a/compiler/rustc_session/src/output.rs +++ b/compiler/rustc_session/src/output.rs @@ -6,6 +6,7 @@ use crate::errors::{ }; use crate::Session; use rustc_ast::{self as ast, attr}; +use rustc_errors::FatalError; use rustc_span::symbol::sym; use rustc_span::{Span, Symbol}; use std::path::Path; @@ -115,7 +116,7 @@ pub fn validate_crate_name(sess: &Session, s: Symbol, sp: Option) { } if err_count > 0 { - sess.dcx().abort_if_errors(); + FatalError.raise(); } } diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs index aa8bd5fdc86..7186b96b40d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs @@ -22,7 +22,7 @@ use crate::traits::{ use rustc_data_structures::fx::{FxHashMap, FxIndexMap}; use rustc_errors::{ codes::*, pluralize, struct_span_code_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, StashKey, StringPart, + FatalError, MultiSpan, StashKey, StringPart, }; use rustc_hir as hir; use rustc_hir::def::{DefKind, Namespace, Res}; @@ -193,14 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit); mutate(&mut err); err.emit(); - - self.dcx().abort_if_errors(); - // FIXME: this should be something like `build_overflow_error_fatal`, which returns - // `DiagnosticBuilder<', !>`. Then we don't even need anything after that `emit()`. - unreachable!( - "did not expect compilation to continue after `abort_if_errors`, \ - since an error was definitely emitted!" - ); + FatalError.raise(); } fn build_overflow_error( -- cgit 1.4.1-3-g733a5