Skip to content

Commit

Permalink
macros: raw translation of subdiagnostics
Browse files Browse the repository at this point in the history
Signed-off-by: David Wood <[email protected]>
  • Loading branch information
davidtwco committed Feb 20, 2024
1 parent 2edb5e2 commit bc61695
Show file tree
Hide file tree
Showing 25 changed files with 296 additions and 246 deletions.
8 changes: 3 additions & 5 deletions compiler/rustc_ast_lowering/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
use rustc_errors::{
codes::*, AddToDiagnostic, Diagnostic, DiagnosticArgFromDisplay, SubdiagnosticMessageOp,
};
use rustc_errors::{codes::*, AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticArgFromDisplay};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};

Expand Down Expand Up @@ -40,8 +38,8 @@ pub struct InvalidAbi {

pub struct InvalidAbiReason(pub &'static str);

impl AddToDiagnostic for InvalidAbiReason {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
impl<'a> AddToDiagnostic<'a> for InvalidAbiReason {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) {
#[allow(rustc::untranslatable_diagnostic)]
diag.note(self.0);
}
Expand Down
10 changes: 5 additions & 5 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Errors emitted by ast_passes.
use rustc_ast::ParamKindOrd;
use rustc_errors::{codes::*, AddToDiagnostic, Applicability, Diagnostic, SubdiagnosticMessageOp};
use rustc_errors::{codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};

Expand Down Expand Up @@ -371,8 +371,8 @@ pub struct ArgsBeforeConstraint {
pub struct EmptyLabelManySpans(pub Vec<Span>);

// The derive for `Vec<Span>` does multiple calls to `span_label`, adding commas between each
impl AddToDiagnostic for EmptyLabelManySpans {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
impl<'a> AddToDiagnostic<'a> for EmptyLabelManySpans {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) {
diag.span_labels(self.0, "");
}
}
Expand Down Expand Up @@ -728,8 +728,8 @@ pub struct StableFeature {
pub since: Symbol,
}

impl AddToDiagnostic for StableFeature {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
impl<'a> AddToDiagnostic<'a> for StableFeature {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) {
diag.arg("name", self.name);
diag.arg("since", self.since);
diag.help(fluent::ast_passes_stable_since);
Expand Down
13 changes: 8 additions & 5 deletions compiler/rustc_builtin_macros/src/errors.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use rustc_errors::{
codes::*, AddToDiagnostic, DiagCtxt, Diagnostic, DiagnosticBuilder, EmissionGuarantee,
IntoDiagnostic, Level, MultiSpan, SingleLabelManySpans, SubdiagnosticMessageOp,
IntoDiagnostic, Level, MultiSpan, SingleLabelManySpans,
};
use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{symbol::Ident, Span, Symbol};
Expand Down Expand Up @@ -610,11 +610,14 @@ pub(crate) struct FormatUnusedArg {

// Allow the singular form to be a subdiagnostic of the multiple-unused
// form of diagnostic.
impl AddToDiagnostic for FormatUnusedArg {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
impl<'a> AddToDiagnostic<'a> for FormatUnusedArg {
fn add_to_diagnostic(self, dcx: &'a DiagCtxt, diag: &mut Diagnostic) {
diag.arg("named", self.named);
let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into());
diag.span_label(self.span, msg);
let message = dcx.eagerly_translate(
crate::fluent_generated::builtin_macros_format_unused_arg,
diag.args(),
);
diag.span_label(self.span, message);
}
}

Expand Down
28 changes: 9 additions & 19 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::snippet::Style;
use crate::{
CodeSuggestion, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode, Level,
MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
CodeSuggestion, DiagCtxt, DiagnosticBuilder, DiagnosticMessage, EmissionGuarantee, ErrCode,
Level, MultiSpan, SubdiagnosticMessage, Substitution, SubstitutionPart, SuggestionStyle,
};
use rustc_data_structures::fx::FxIndexMap;
use rustc_error_messages::fluent_value_from_str_list_sep_by_and;
Expand Down Expand Up @@ -66,18 +66,12 @@ impl Into<FluentValue<'static>> for DiagnosticArgValue {
/// Trait implemented by error types. This should not be implemented manually. Instead, use
/// `#[derive(Subdiagnostic)]` -- see [rustc_macros::Subdiagnostic].
#[rustc_diagnostic_item = "AddToDiagnostic"]
pub trait AddToDiagnostic
pub trait AddToDiagnostic<'a>
where
Self: Sized,
{
/// Add a subdiagnostic to an existing diagnostic.
fn add_to_diagnostic(self, diag: &mut Diagnostic) {
self.add_to_diagnostic_with(diag, |_, m| m);
}

/// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
/// (to optionally perform eager translation).
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F);
fn add_to_diagnostic(self, dcx: &'a DiagCtxt, diag: &mut Diagnostic);
}

pub trait SubdiagnosticMessageOp =
Expand Down Expand Up @@ -855,16 +849,12 @@ impl Diagnostic {
/// [rustc_macros::Subdiagnostic]). Performs eager translation of any translatable messages
/// used in the subdiagnostic, so suitable for use with repeated messages (i.e. re-use of
/// interpolated variables).
pub fn subdiagnostic(
pub fn subdiagnostic<'a>(
&mut self,
dcx: &crate::DiagCtxt,
subdiagnostic: impl AddToDiagnostic,
dcx: &'a crate::DiagCtxt,
subdiagnostic: impl AddToDiagnostic<'a>,
) -> &mut Self {
subdiagnostic.add_to_diagnostic_with(self, |diag, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
dcx.eagerly_translate(msg, args)
});
subdiagnostic.add_to_diagnostic(dcx, self);
self
}

Expand Down Expand Up @@ -911,7 +901,7 @@ impl Diagnostic {
/// Helper function that takes a `SubdiagnosticMessage` and returns a `DiagnosticMessage` by
/// combining it with the primary message of the diagnostic (if translatable, otherwise it just
/// passes the user's string along).
pub(crate) fn subdiagnostic_message_to_diagnostic_message(
pub fn subdiagnostic_message_to_diagnostic_message(
&self,
attr: impl Into<SubdiagnosticMessage>,
) -> DiagnosticMessage {
Expand Down
23 changes: 19 additions & 4 deletions compiler/rustc_errors/src/diagnostic_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,25 @@ impl<'a, G: EmissionGuarantee> DiagnosticBuilder<'a, G> {
forward!((arg, with_arg)(
name: impl Into<Cow<'static, str>>, arg: impl IntoDiagnosticArg,
));
forward!((subdiagnostic, with_subdiagnostic)(
dcx: &DiagCtxt,
subdiagnostic: impl crate::AddToDiagnostic,
));

/// See [`Diagnostic::subdiagnostic()`].
pub fn subdiagnostic(
&mut self,
dcx: &'a DiagCtxt,
subdiagnostic: impl crate::AddToDiagnostic<'a>,
) -> &mut Self {
self.diag.as_mut().unwrap().subdiagnostic(dcx, subdiagnostic);
self
}
/// See [`Diagnostic::subdiagnostic()`].
pub fn with_subdiagnostic(
mut self,
dcx: &'a DiagCtxt,
subdiagnostic: impl crate::AddToDiagnostic<'a>,
) -> Self {
self.diag.as_mut().unwrap().subdiagnostic(dcx, subdiagnostic);
self
}
}

impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_errors/src/diagnostic_impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::diagnostic::DiagnosticLocation;
use crate::{fluent_generated as fluent, AddToDiagnostic};
use crate::{
DiagCtxt, DiagnosticArgValue, DiagnosticBuilder, EmissionGuarantee, ErrCode, IntoDiagnostic,
IntoDiagnosticArg, Level, SubdiagnosticMessageOp,
IntoDiagnosticArg, Level,
};
use rustc_ast as ast;
use rustc_ast_pretty::pprust;
Expand Down Expand Up @@ -298,8 +298,8 @@ pub struct SingleLabelManySpans {
pub spans: Vec<Span>,
pub label: &'static str,
}
impl AddToDiagnostic for SingleLabelManySpans {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut crate::Diagnostic, _: F) {
impl<'a> AddToDiagnostic<'a> for SingleLabelManySpans {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut crate::Diagnostic) {
diag.span_labels(self.spans, self.label);
}
}
Expand Down
74 changes: 43 additions & 31 deletions compiler/rustc_errors/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1532,26 +1532,46 @@ impl DiagCtxtInner {
self.emit_diagnostic(Diagnostic::new(Note, note2));
}

let mut bug =
if backtrace || self.ice_file.is_none() { bug.decorate(self) } else { bug.inner };

// "Undelay" the delayed bugs (into plain `Bug`s).
if bug.level != DelayedBug {
// NOTE(eddyb) not panicking here because we're already producing
// an ICE, and the more information the merrier.
let subdiag = InvalidFlushedDelayedDiagnosticLevel {
span: bug.span.primary_span().unwrap(),
level: bug.level,
};
// FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
// just uses `DiagCtxtInner` functions.
subdiag.add_to_diagnostic_with(&mut bug, |diag, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
self.eagerly_translate(msg, args)
});
}
bug.level = Bug;
// This is a bit gnarly, but the `AddToDiagnostic` type uses a `DiagCtxt` to trigger
// translation. It could take an emitter or a `DiagCtxtInner` but it would make
// every other use of `AddToDiagnostic` more complex. This function is only invoked
// during the `Drop` of `DiagCtxtInner`, so we temporarily get a `T` from the `&mut T`
// here so we can put this back into a lock and back into a `DiagCtxt` to call the
// functions from `AddToDiagnostic`.
//
// FIXME(davidtwco): `AddToDiagnostic` should eventually be merged with
// `IntoDiagnostic`, and then the context determined by an associated type, depending
// on the needs of the specific diagnostic - once this is done, we can make these
// specific diagnostics take an emitter directly.
let bug = unsafe {
let old_dcx = std::ptr::read(self);
let (new_dcx, bug) = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
let dcx = DiagCtxt { inner: Lock::new(old_dcx) };

let mut bug = if backtrace || self.ice_file.is_none() {
bug.decorate(&dcx)
} else {
bug.inner
};

// "Undelay" the delayed bugs (into plain `Bug`s).
if bug.level != DelayedBug {
// NOTE(eddyb) not panicking here because we're already producing
// an ICE, and the more information the merrier.
let subdiag = InvalidFlushedDelayedDiagnosticLevel {
span: bug.span.primary_span().unwrap(),
level: bug.level,
};
subdiag.add_to_diagnostic(&dcx, &mut bug);
}
bug.level = Bug;

(dcx.inner.into_inner(), bug)
}))
.unwrap_or_else(|_| std::process::abort());
std::ptr::write(self, new_dcx);
bug
};

self.emit_diagnostic(bug);
}
Expand Down Expand Up @@ -1583,15 +1603,7 @@ impl DelayedDiagnostic {
DelayedDiagnostic { inner: diagnostic, note: backtrace }
}

fn decorate(mut self, dcx: &DiagCtxtInner) -> Diagnostic {
// FIXME: Cannot use `Diagnostic::subdiagnostic` which takes `DiagCtxt`, but it
// just uses `DiagCtxtInner` functions.
let subdiag_with = |diag: &mut Diagnostic, msg| {
let args = diag.args();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
dcx.eagerly_translate(msg, args)
};

fn decorate(mut self, dcx: &DiagCtxt) -> Diagnostic {
match self.note.status() {
BacktraceStatus::Captured => {
let inner = &self.inner;
Expand All @@ -1600,7 +1612,7 @@ impl DelayedDiagnostic {
emitted_at: inner.emitted_at.clone(),
note: self.note,
};
subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
subdiag.add_to_diagnostic(dcx, &mut self.inner);
}
// Avoid the needless newline when no backtrace has been captured,
// the display impl should just be a single line.
Expand All @@ -1611,7 +1623,7 @@ impl DelayedDiagnostic {
emitted_at: inner.emitted_at.clone(),
note: self.note,
};
subdiag.add_to_diagnostic_with(&mut self.inner, subdiag_with);
subdiag.add_to_diagnostic(dcx, &mut self.inner);
}
}

Expand Down
25 changes: 11 additions & 14 deletions compiler/rustc_hir_typeck/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::borrow::Cow;

use crate::fluent_generated as fluent;
use rustc_errors::{
codes::*, AddToDiagnostic, Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg,
MultiSpan, SubdiagnosticMessageOp,
codes::*, AddToDiagnostic, Applicability, DiagCtxt, Diagnostic, DiagnosticArgValue,
IntoDiagnosticArg, MultiSpan,
};
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
use rustc_middle::ty::Ty;
Expand Down Expand Up @@ -194,8 +194,8 @@ pub struct TypeMismatchFruTypo {
pub expr: Option<String>,
}

impl AddToDiagnostic for TypeMismatchFruTypo {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
impl<'a> AddToDiagnostic<'a> for TypeMismatchFruTypo {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) {
diag.arg("expr", self.expr.as_deref().unwrap_or("NONE"));

// Only explain that `a ..b` is a range if it's split up
Expand Down Expand Up @@ -369,8 +369,8 @@ pub struct RemoveSemiForCoerce {
pub semi: Span,
}

impl AddToDiagnostic for RemoveSemiForCoerce {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, _: F) {
impl<'a> AddToDiagnostic<'a> for RemoveSemiForCoerce {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) {
let mut multispan: MultiSpan = self.semi.into();
multispan.push_span_label(self.expr, fluent::hir_typeck_remove_semi_for_coerce_expr);
multispan.push_span_label(self.ret, fluent::hir_typeck_remove_semi_for_coerce_ret);
Expand Down Expand Up @@ -541,18 +541,15 @@ pub enum CastUnknownPointerSub {
From(Span),
}

impl AddToDiagnostic for CastUnknownPointerSub {
fn add_to_diagnostic_with<F: SubdiagnosticMessageOp>(self, diag: &mut Diagnostic, f: F) {
impl<'a> AddToDiagnostic<'a> for CastUnknownPointerSub {
fn add_to_diagnostic(self, _: &'a DiagCtxt, diag: &mut Diagnostic) {
match self {
CastUnknownPointerSub::To(span) => {
let msg = f(diag, crate::fluent_generated::hir_typeck_label_to);
diag.span_label(span, msg);
let msg = f(diag, crate::fluent_generated::hir_typeck_note);
diag.note(msg);
diag.span_label(span, crate::fluent_generated::hir_typeck_label_to);
diag.note(crate::fluent_generated::hir_typeck_note);
}
CastUnknownPointerSub::From(span) => {
let msg = f(diag, crate::fluent_generated::hir_typeck_label_from);
diag.span_label(span, msg);
diag.span_label(span, crate::fluent_generated::hir_typeck_label_from);
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_data_structures::unord::UnordMap;
use rustc_errors::{
codes::*, pluralize, struct_span_code_err, AddToDiagnostic, Applicability, Diagnostic,
DiagnosticBuilder, ErrorGuaranteed, StashKey,
codes::*, pluralize, struct_span_code_err, Applicability, Diagnostic, DiagnosticBuilder,
ErrorGuaranteed, StashKey,
};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, DefKind, Res};
Expand Down Expand Up @@ -2611,7 +2611,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
// We know by construction that `<expr>.await` is either on Rust 2015
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
err.note("to `.await` a `Future`, switch to Rust 2018 or later");
HelpUseLatestEdition::new().add_to_diagnostic(&mut err);
err.subdiagnostic(self.dcx(), HelpUseLatestEdition::new());
}

err.emit()
Expand Down
Loading

0 comments on commit bc61695

Please sign in to comment.