Rollup merge of #124218 - Xiretza:subsubdiagnostics, r=davidtwco

Allow nesting subdiagnostics in #[derive(Subdiagnostic)]
This commit is contained in:
León Orell Valerian Liehr 2024-04-23 17:25:17 +02:00 committed by GitHub
commit 6e423e1651
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 145 additions and 114 deletions

View File

@ -44,7 +44,7 @@ impl Subdiagnostic for InvalidAbiReason {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
#[allow(rustc::untranslatable_diagnostic)] #[allow(rustc::untranslatable_diagnostic)]
diag.note(self.0); diag.note(self.0);

View File

@ -382,7 +382,7 @@ impl Subdiagnostic for EmptyLabelManySpans {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
diag.span_labels(self.0, ""); diag.span_labels(self.0, "");
} }
@ -751,7 +751,7 @@ impl Subdiagnostic for StableFeature {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
diag.arg("name", self.name); diag.arg("name", self.name);
diag.arg("since", self.since); diag.arg("since", self.since);

View File

@ -601,7 +601,7 @@ impl Subdiagnostic for FormatUnusedArg {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
diag.arg("named", self.named); diag.arg("named", self.named);
let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into()); let msg = f(diag, crate::fluent_generated::builtin_macros_format_unused_arg.into());

View File

@ -177,7 +177,7 @@ pub trait Subdiagnostic
{ {
/// Add a subdiagnostic to an existing diagnostic. /// Add a subdiagnostic to an existing diagnostic.
fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) { fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
self.add_to_diag_with(diag, |_, m| m); self.add_to_diag_with(diag, &|_, m| m);
} }
/// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used /// Add a subdiagnostic to an existing diagnostic where `f` is invoked on every message used
@ -185,7 +185,7 @@ fn add_to_diag<G: EmissionGuarantee>(self, diag: &mut Diag<'_, G>) {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
); );
} }
@ -1197,7 +1197,7 @@ pub fn subdiagnostic(
dcx: &crate::DiagCtxt, dcx: &crate::DiagCtxt,
subdiagnostic: impl Subdiagnostic, subdiagnostic: impl Subdiagnostic,
) -> &mut Self { ) -> &mut Self {
subdiagnostic.add_to_diag_with(self, |diag, msg| { subdiagnostic.add_to_diag_with(self, &|diag, msg| {
let args = diag.args.iter(); let args = diag.args.iter();
let msg = diag.subdiagnostic_message_to_diagnostic_message(msg); let msg = diag.subdiagnostic_message_to_diagnostic_message(msg);
dcx.eagerly_translate(msg, args) dcx.eagerly_translate(msg, args)

View File

@ -227,6 +227,36 @@ fn into_diag_arg(self) -> DiagArgValue {
} }
} }
impl<Id> IntoDiagArg for hir::def::Res<Id> {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::Borrowed(self.descr()))
}
}
impl IntoDiagArg for DiagLocation {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
impl IntoDiagArg for Backtrace {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
impl IntoDiagArg for Level {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
impl IntoDiagArg for type_ir::ClosureKind {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(self.as_str().into())
}
}
#[derive(Clone)] #[derive(Clone)]
pub struct DiagSymbolList(Vec<Symbol>); pub struct DiagSymbolList(Vec<Symbol>);
@ -244,12 +274,6 @@ fn into_diag_arg(self) -> DiagArgValue {
} }
} }
impl<Id> IntoDiagArg for hir::def::Res<Id> {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::Borrowed(self.descr()))
}
}
impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetDataLayoutErrors<'_> { impl<G: EmissionGuarantee> Diagnostic<'_, G> for TargetDataLayoutErrors<'_> {
fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> { fn into_diag(self, dcx: &DiagCtxt, level: Level) -> Diag<'_, G> {
match self { match self {
@ -302,7 +326,7 @@ impl Subdiagnostic for SingleLabelManySpans {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
diag.span_labels(self.spans, self.label); diag.span_labels(self.spans, self.label);
} }
@ -316,24 +340,6 @@ pub struct ExpectedLifetimeParameter {
pub count: usize, pub count: usize,
} }
impl IntoDiagArg for DiagLocation {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
impl IntoDiagArg for Backtrace {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
impl IntoDiagArg for Level {
fn into_diag_arg(self) -> DiagArgValue {
DiagArgValue::Str(Cow::from(self.to_string()))
}
}
#[derive(Subdiagnostic)] #[derive(Subdiagnostic)]
#[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")] #[suggestion(errors_indicate_anonymous_lifetime, code = "{suggestion}", style = "verbose")]
pub struct IndicateAnonymousLifetime { pub struct IndicateAnonymousLifetime {
@ -343,8 +349,10 @@ pub struct IndicateAnonymousLifetime {
pub suggestion: String, pub suggestion: String,
} }
impl IntoDiagArg for type_ir::ClosureKind { #[derive(Subdiagnostic)]
fn into_diag_arg(self) -> DiagArgValue { pub struct ElidedLifetimeInPathSubdiag {
DiagArgValue::Str(self.as_str().into()) #[subdiagnostic]
} pub expected: ExpectedLifetimeParameter,
#[subdiagnostic]
pub indicate: Option<IndicateAnonymousLifetime>,
} }

View File

@ -40,8 +40,8 @@
SubdiagMessageOp, Subdiagnostic, SubdiagMessageOp, Subdiagnostic,
}; };
pub use diagnostic_impls::{ pub use diagnostic_impls::{
DiagArgFromDisplay, DiagSymbolList, ExpectedLifetimeParameter, IndicateAnonymousLifetime, DiagArgFromDisplay, DiagSymbolList, ElidedLifetimeInPathSubdiag, ExpectedLifetimeParameter,
SingleLabelManySpans, IndicateAnonymousLifetime, SingleLabelManySpans,
}; };
pub use emitter::ColorConfig; pub use emitter::ColorConfig;
pub use rustc_error_messages::{ pub use rustc_error_messages::{
@ -1911,27 +1911,24 @@ fn can_be_subdiag(&self) -> bool {
} }
// FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite.
pub fn add_elided_lifetime_in_path_suggestion<G: EmissionGuarantee>( pub fn elided_lifetime_in_path_suggestion(
source_map: &SourceMap, source_map: &SourceMap,
diag: &mut Diag<'_, G>,
n: usize, n: usize,
path_span: Span, path_span: Span,
incl_angl_brckt: bool, incl_angl_brckt: bool,
insertion_span: Span, insertion_span: Span,
) { ) -> ElidedLifetimeInPathSubdiag {
diag.subdiagnostic(diag.dcx, ExpectedLifetimeParameter { span: path_span, count: n }); let expected = ExpectedLifetimeParameter { span: path_span, count: n };
if !source_map.is_span_accessible(insertion_span) {
// Do not try to suggest anything if generated by a proc-macro. // Do not try to suggest anything if generated by a proc-macro.
return; let indicate = source_map.is_span_accessible(insertion_span).then(|| {
}
let anon_lts = vec!["'_"; n].join(", "); let anon_lts = vec!["'_"; n].join(", ");
let suggestion = let suggestion =
if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") }; if incl_angl_brckt { format!("<{anon_lts}>") } else { format!("{anon_lts}, ") };
diag.subdiagnostic( IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion }
diag.dcx, });
IndicateAnonymousLifetime { span: insertion_span.shrink_to_hi(), count: n, suggestion },
); ElidedLifetimeInPathSubdiag { expected, indicate }
} }
pub fn report_ambiguity_error<'a, G: EmissionGuarantee>( pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(

View File

@ -191,7 +191,7 @@ impl Subdiagnostic for TypeMismatchFruTypo {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
diag.arg("expr", self.expr.as_deref().unwrap_or("NONE")); diag.arg("expr", self.expr.as_deref().unwrap_or("NONE"));
@ -370,7 +370,7 @@ impl Subdiagnostic for RemoveSemiForCoerce {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
let mut multispan: MultiSpan = self.semi.into(); 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.expr, fluent::hir_typeck_remove_semi_for_coerce_expr);
@ -546,7 +546,7 @@ impl rustc_errors::Subdiagnostic for CastUnknownPointerSub {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
match self { match self {
CastUnknownPointerSub::To(span) => { CastUnknownPointerSub::To(span) => {

View File

@ -239,7 +239,7 @@ impl Subdiagnostic for RegionOriginNote<'_> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
let mut label_or_note = |span, msg: DiagMessage| { let mut label_or_note = |span, msg: DiagMessage| {
let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count(); let sub_count = diag.children.iter().filter(|d| d.span.is_dummy()).count();
@ -304,7 +304,7 @@ impl Subdiagnostic for LifetimeMismatchLabels {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
match self { match self {
LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => {
@ -352,7 +352,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
let mut mk_suggestion = || { let mut mk_suggestion = || {
let ( let (
@ -454,7 +454,7 @@ impl Subdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
mut self, mut self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
self.unmet_requirements self.unmet_requirements
.push_span_label(self.binding_span, fluent::infer_msl_introduces_static); .push_span_label(self.binding_span, fluent::infer_msl_introduces_static);
@ -773,7 +773,7 @@ impl Subdiagnostic for ConsiderBorrowingParamHelp {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
let mut type_param_span: MultiSpan = self.spans.clone().into(); let mut type_param_span: MultiSpan = self.spans.clone().into();
for &span in &self.spans { for &span in &self.spans {
@ -818,7 +818,7 @@ impl Subdiagnostic for DynTraitConstraintSuggestion {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
let mut multi_span: MultiSpan = vec![self.span].into(); 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.span, fluent::infer_dtcs_has_lifetime_req_label);
@ -865,7 +865,7 @@ impl Subdiagnostic for ReqIntroducedLocations {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
mut self, mut self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
for sp in self.spans { for sp in self.spans {
self.span.push_span_label(sp, fluent::infer_ril_introduced_here); self.span.push_span_label(sp, fluent::infer_ril_introduced_here);
@ -888,7 +888,7 @@ impl Subdiagnostic for MoreTargeted {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
diag.code(E0772); diag.code(E0772);
diag.primary_message(fluent::infer_more_targeted); diag.primary_message(fluent::infer_more_targeted);
@ -1293,7 +1293,7 @@ impl Subdiagnostic for SuggestTuplePatternMany {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
diag.arg("path", self.path); diag.arg("path", self.path);
let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into()); let message = f(diag, crate::fluent_generated::infer_stp_wrap_many.into());

View File

@ -163,7 +163,7 @@ impl Subdiagnostic for RegionExplanation<'_> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
diag.arg("pref_kind", self.prefix); diag.arg("pref_kind", self.prefix);
diag.arg("suff_kind", self.suffix); diag.arg("suff_kind", self.suffix);

View File

@ -2,7 +2,7 @@
#![allow(rustc::untranslatable_diagnostic)] #![allow(rustc::untranslatable_diagnostic)]
use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS; use rustc_ast::util::unicode::TEXT_FLOW_CONTROL_CHARS;
use rustc_errors::{add_elided_lifetime_in_path_suggestion, Diag}; use rustc_errors::{elided_lifetime_in_path_suggestion, Diag};
use rustc_errors::{Applicability, SuggestionStyle}; use rustc_errors::{Applicability, SuggestionStyle};
use rustc_middle::middle::stability; use rustc_middle::middle::stability;
use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::BuiltinLintDiag;
@ -74,13 +74,15 @@ pub(super) fn builtin(sess: &Session, diagnostic: BuiltinLintDiag, diag: &mut Di
diag.span_note(span_def, "the macro is defined here"); diag.span_note(span_def, "the macro is defined here");
} }
BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => { BuiltinLintDiag::ElidedLifetimesInPaths(n, path_span, incl_angl_brckt, insertion_span) => {
add_elided_lifetime_in_path_suggestion( diag.subdiagnostic(
sess.dcx(),
elided_lifetime_in_path_suggestion(
sess.source_map(), sess.source_map(),
diag,
n, n,
path_span, path_span,
incl_angl_brckt, incl_angl_brckt,
insertion_span, insertion_span,
),
); );
} }
BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => { BuiltinLintDiag::UnknownCrateTypes(span, note, sugg) => {

View File

@ -27,7 +27,7 @@ impl Subdiagnostic for OverruledAttributeSub {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
match self { match self {
OverruledAttributeSub::DefaultSource { id } => { OverruledAttributeSub::DefaultSource { id } => {

View File

@ -274,7 +274,7 @@ impl<'a, 'b> Subdiagnostic for SuggestChangingAssocTypes<'a, 'b> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
// Access to associates types should use `<T as Bound>::Assoc`, which does not need a // Access to associates types should use `<T as Bound>::Assoc`, which does not need a
// bound. Let's see if this type does that. // bound. Let's see if this type does that.
@ -330,7 +330,7 @@ impl Subdiagnostic for BuiltinTypeAliasGenericBoundsSuggestion {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
diag.multipart_suggestion( diag.multipart_suggestion(
fluent::lint_suggestion, fluent::lint_suggestion,
@ -451,7 +451,7 @@ impl Subdiagnostic for BuiltinUnpermittedTypeInitSub {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
let mut err = self.err; let mut err = self.err;
loop { loop {
@ -506,7 +506,7 @@ impl Subdiagnostic for BuiltinClashingExternSub<'_> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
let mut expected_str = DiagStyledString::new(); let mut expected_str = DiagStyledString::new();
expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false); expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
@ -788,7 +788,7 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagLabels {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
for (c, span) in self.spans { for (c, span) in self.spans {
diag.span_label(span, format!("{c:?}")); diag.span_label(span, format!("{c:?}"));
@ -806,7 +806,7 @@ impl Subdiagnostic for HiddenUnicodeCodepointsDiagSub {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
match self { match self {
HiddenUnicodeCodepointsDiagSub::Escape { spans } => { HiddenUnicodeCodepointsDiagSub::Escape { spans } => {
@ -954,7 +954,7 @@ impl Subdiagnostic for NonBindingLetSub {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar; let can_suggest_binding = self.drop_fn_start_end.is_some() || !self.is_assign_desugar;
@ -1240,7 +1240,7 @@ impl Subdiagnostic for NonSnakeCaseDiagSub {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
match self { match self {
NonSnakeCaseDiagSub::Label { span } => { NonSnakeCaseDiagSub::Label { span } => {
@ -1482,7 +1482,7 @@ impl Subdiagnostic for OverflowingBinHexSign {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
match self { match self {
OverflowingBinHexSign::Positive => { OverflowingBinHexSign::Positive => {

View File

@ -71,6 +71,7 @@ pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream {
span_field: None, span_field: None,
applicability: None, applicability: None,
has_suggestion_parts: false, has_suggestion_parts: false,
has_subdiagnostic: false,
is_enum, is_enum,
}; };
builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
@ -90,7 +91,7 @@ pub(crate) fn into_tokens(self, mut structure: Structure<'_>) -> TokenStream {
fn add_to_diag_with<__G, __F>( fn add_to_diag_with<__G, __F>(
self, self,
#diag: &mut rustc_errors::Diag<'_, __G>, #diag: &mut rustc_errors::Diag<'_, __G>,
#f: __F #f: &__F
) where ) where
__G: rustc_errors::EmissionGuarantee, __G: rustc_errors::EmissionGuarantee,
__F: rustc_errors::SubdiagMessageOp<__G>, __F: rustc_errors::SubdiagMessageOp<__G>,
@ -133,6 +134,10 @@ struct SubdiagnosticDeriveVariantBuilder<'parent, 'a> {
/// during finalization if still `false`. /// during finalization if still `false`.
has_suggestion_parts: bool, has_suggestion_parts: bool,
/// Set to true when a `#[subdiagnostic]` field is encountered, used to suppress the error
/// emitted when no subdiagnostic kinds are specified on the variant itself.
has_subdiagnostic: bool,
/// Set to true when this variant is an enum variant rather than just the body of a struct. /// Set to true when this variant is an enum variant rather than just the body of a struct.
is_enum: bool, is_enum: bool,
} }
@ -373,6 +378,13 @@ fn generate_field_code_inner_path(
Ok(quote! {}) Ok(quote! {})
} }
"subdiagnostic" => {
let f = &self.parent.f;
let diag = &self.parent.diag;
let binding = &info.binding;
self.has_subdiagnostic = true;
Ok(quote! { #binding.add_to_diag_with(#diag, #f); })
}
_ => { _ => {
let mut span_attrs = vec![]; let mut span_attrs = vec![];
if kind_stats.has_multipart_suggestion { if kind_stats.has_multipart_suggestion {
@ -480,18 +492,6 @@ fn generate_field_code_inner_list(
pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> { pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveError> {
let kind_slugs = self.identify_kind()?; let kind_slugs = self.identify_kind()?;
if kind_slugs.is_empty() {
if self.is_enum {
// It's okay for a variant to not be a subdiagnostic at all..
return Ok(quote! {});
} else {
// ..but structs should always be _something_.
throw_span_err!(
self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified"
);
}
};
let kind_stats: KindsStatistics = let kind_stats: KindsStatistics =
kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect(); kind_slugs.iter().map(|(kind, _slug, _no_span)| kind).collect();
@ -510,6 +510,19 @@ pub(crate) fn into_tokens(&mut self) -> Result<TokenStream, DiagnosticDeriveErro
.map(|binding| self.generate_field_attr_code(binding, kind_stats)) .map(|binding| self.generate_field_attr_code(binding, kind_stats))
.collect(); .collect();
if kind_slugs.is_empty() {
if self.is_enum {
// It's okay for a variant to not be a subdiagnostic at all..
return Ok(quote! {});
} else if !self.has_subdiagnostic {
// ..but structs should always be _something_.
throw_span_err!(
self.variant.ast().ident.span().unwrap(),
"subdiagnostic kind not specified"
);
}
};
let span_field = self.span_field.value_ref(); let span_field = self.span_field.value_ref();
let diag = &self.parent.diag; let diag = &self.parent.diag;

View File

@ -144,6 +144,7 @@ pub fn extension(attr: TokenStream, input: TokenStream) -> TokenStream {
help, help,
note, note,
warning, warning,
subdiagnostic,
suggestion, suggestion,
suggestion_short, suggestion_short,
suggestion_hidden, suggestion_hidden,

View File

@ -423,7 +423,7 @@ impl Subdiagnostic for UnsafeNotInheritedLintNote {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body); diag.span_note(self.signature_span, fluent::mir_build_unsafe_fn_safe_body);
let body_start = self.body_span.shrink_to_lo(); let body_start = self.body_span.shrink_to_lo();
@ -871,7 +871,7 @@ impl<'tcx> Subdiagnostic for AdtDefinedHere<'tcx> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
diag.arg("ty", self.ty); diag.arg("ty", self.ty);
let mut spans = MultiSpan::from(self.adt_def_span); let mut spans = MultiSpan::from(self.adt_def_span);

View File

@ -1472,7 +1472,7 @@ impl Subdiagnostic for FnTraitMissingParen {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren);
let applicability = if self.machine_applicable { let applicability = if self.machine_applicable {

View File

@ -1765,7 +1765,7 @@ impl Subdiagnostic for UnusedVariableStringInterp {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation); diag.span_label(self.lit, crate::fluent_generated::passes_maybe_string_interpolation);
diag.multipart_suggestion( diag.multipart_suggestion(

View File

@ -65,7 +65,7 @@ impl<'tcx> Subdiagnostic for Overlap<'tcx> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
let Overlap { span, range } = self; let Overlap { span, range } = self;
@ -113,7 +113,7 @@ impl<'tcx> Subdiagnostic for GappedRange<'tcx> {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_: F, _: &F,
) { ) {
let GappedRange { span, gap, first_range } = self; let GappedRange { span, gap, first_range } = self;

View File

@ -1,4 +1,4 @@
use rustc_errors::{codes::*, Applicability, MultiSpan}; use rustc_errors::{codes::*, Applicability, ElidedLifetimeInPathSubdiag, MultiSpan};
use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_macros::{Diagnostic, Subdiagnostic};
use rustc_span::{ use rustc_span::{
symbol::{Ident, Symbol}, symbol::{Ident, Symbol},
@ -907,6 +907,8 @@ pub(crate) struct ExplicitAnonymousLivetimeReportError {
pub(crate) struct ImplicitElidedLifetimeNotAllowedHere { pub(crate) struct ImplicitElidedLifetimeNotAllowedHere {
#[primary_span] #[primary_span]
pub(crate) span: Span, pub(crate) span: Span,
#[subdiagnostic]
pub(crate) subdiag: ElidedLifetimeInPathSubdiag,
} }
#[derive(Diagnostic)] #[derive(Diagnostic)]

View File

@ -1883,20 +1883,18 @@ fn resolve_elided_lifetimes_in_path(
// async fn foo(_: std::cell::Ref<u32>) { ... } // async fn foo(_: std::cell::Ref<u32>) { ... }
LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. } LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. }
| LifetimeRibKind::AnonymousWarn(_) => { | LifetimeRibKind::AnonymousWarn(_) => {
let mut err =
self.r.dcx().create_err(errors::ImplicitElidedLifetimeNotAllowedHere {
span: path_span,
});
let sess = self.r.tcx.sess; let sess = self.r.tcx.sess;
rustc_errors::add_elided_lifetime_in_path_suggestion( let subdiag = rustc_errors::elided_lifetime_in_path_suggestion(
sess.source_map(), sess.source_map(),
&mut err,
expected_lifetimes, expected_lifetimes,
path_span, path_span,
!segment.has_generic_args, !segment.has_generic_args,
elided_lifetime_span, elided_lifetime_span,
); );
err.emit(); self.r.dcx().emit_err(errors::ImplicitElidedLifetimeNotAllowedHere {
span: path_span,
subdiag,
});
should_lint = false; should_lint = false;
for id in node_ids { for id in node_ids {

View File

@ -104,7 +104,7 @@ impl Subdiagnostic for AdjustSignatureBorrow {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
_f: F, _f: &F,
) { ) {
match self { match self {
AdjustSignatureBorrow::Borrow { to_borrow } => { AdjustSignatureBorrow::Borrow { to_borrow } => {

View File

@ -59,7 +59,7 @@ impl Subdiagnostic for UntranslatableInAddtoDiag {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
diag.note("untranslatable diagnostic"); diag.note("untranslatable diagnostic");
//~^ ERROR diagnostics should be created using translatable messages //~^ ERROR diagnostics should be created using translatable messages
@ -72,7 +72,7 @@ impl Subdiagnostic for TranslatableInAddtoDiag {
fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>( fn add_to_diag_with<G: EmissionGuarantee, F: SubdiagMessageOp<G>>(
self, self,
diag: &mut Diag<'_, G>, diag: &mut Diag<'_, G>,
f: F, f: &F,
) { ) {
diag.note(crate::fluent_generated::no_crate_note); diag.note(crate::fluent_generated::no_crate_note);
} }

View File

@ -827,3 +827,13 @@ struct PrimarySpanOnVec {
//~| NOTE there must be exactly one primary span //~| NOTE there must be exactly one primary span
sub: Vec<Span>, sub: Vec<Span>,
} }
#[derive(Subdiagnostic)]
struct NestedParent {
#[subdiagnostic]
single_sub: A,
#[subdiagnostic]
option_sub: Option<A>,
#[subdiagnostic]
vec_sub: Vec<A>,
}