From af3343ae299c81a019d9d62d15c10cb99d7ceb89 Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Sun, 28 Aug 2022 19:45:19 +0300 Subject: [PATCH 1/8] Migrate E0623 --- .../locales/en-US/infer.ftl | 31 +++- compiler/rustc_infer/src/errors.rs | 173 +++++++++++++++++- .../nice_region_error/different_lifetimes.rs | 147 ++++----------- compiler/rustc_infer/src/lib.rs | 4 + 4 files changed, 234 insertions(+), 121 deletions(-) diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index 60086cd6e477f..478a4bdf8a966 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -104,9 +104,36 @@ infer_relate_object_bound = ...so that it can be closed over into an object infer_data_borrowed = ...so that the type `{$name}` is not borrowed for too long infer_reference_outlives_referent = ...so that the reference type `{$name}` does not outlive the data it points at infer_relate_param_bound = ...so that the type `{$name}` will meet its required lifetime bounds{$continues -> -[true] ... -*[false] {""} + [true] ... + *[false] {""} } infer_relate_param_bound_2 = ...that is required by this bound infer_relate_region_param_bound = ...so that the declared lifetime parameter bounds are satisfied infer_compare_impl_item_obligation = ...so that the definition in impl matches the definition from the trait + +infer_nothing = {""} + +infer_lifetime_mismatch = lifetime mismatch + +infer_declared_different = this parameter and the return type are declared with different lifetimes... +infer_data_returned = ...but data{$label_var1_exists -> + [true] {" "}from `{$label_var1}` + *[false] {""} +} is returned here + +infer_data_lifetime_flow = ...but data with one lifetime flows into the other here +infer_declared_multiple = this type is declared with multiple lifetimes... +infer_types_declared_different = these two types are declared with different lifetimes... +infer_data_flows = ...but data{$label_var1_exists -> + [true] -> {" "}from `{$label_var1}` + *[false] -> {""} +} flows{$label_var2_exists -> + [true] -> {" "}into `{$label_var2}` + *[false] -> {""} +} here + +infer_lifetime_param_suggestion = consider introducing a named lifetime parameter{$is_impl -> + [true] {" "}and update trait if needed + *[false] {""} +} +infer_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs index 938f8aa77a5b4..932ba1f35afcf 100644 --- a/compiler/rustc_infer/src/errors.rs +++ b/compiler/rustc_infer/src/errors.rs @@ -1,7 +1,13 @@ -use rustc_errors::{fluent, AddSubdiagnostic, DiagnosticMessage, DiagnosticStyledString}; -use rustc_hir::FnRetTy; +use hir::GenericParamKind; +use rustc_errors::{ + fluent, AddSubdiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, +}; +use rustc_hir as hir; +use rustc_hir::{FnRetTy, Ty}; use rustc_macros::SessionDiagnostic; -use rustc_span::{BytePos, Span}; +use rustc_middle::ty::{Region, TyCtxt}; +use rustc_span::symbol::kw; +use rustc_span::{symbol::Ident, BytePos, Span}; use crate::infer::error_reporting::{ need_type_info::{GeneratorKindAsDiagArg, UnderspecifiedArgKind}, @@ -252,3 +258,164 @@ impl AddSubdiagnostic for RegionOriginNote<'_> { }; } } + +pub enum LifetimeMismatchLabels { + InRet { + param_span: Span, + ret_span: Span, + span: Span, + label_var1: Option, + }, + Normal { + hir_equal: bool, + ty_sup: Span, + ty_sub: Span, + span: Span, + label_var1: Option, + label_var2: Option, + }, +} + +impl AddSubdiagnostic for LifetimeMismatchLabels { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + match self { + LifetimeMismatchLabels::InRet { param_span, ret_span, span, label_var1 } => { + diag.span_label(param_span, fluent::infer::declared_different); + diag.span_label(ret_span, fluent::infer::nothing); + diag.span_label(span, fluent::infer::data_returned); + diag.set_arg("label_var1_exists", label_var1.is_some()); + diag.set_arg("label_var1", label_var1.map(|x| x.to_string()).unwrap_or_default()); + } + LifetimeMismatchLabels::Normal { + hir_equal, + ty_sup, + ty_sub, + span, + label_var1, + label_var2, + } => { + if hir_equal { + diag.span_label(ty_sup, fluent::infer::declared_multiple); + diag.span_label(ty_sub, fluent::infer::nothing); + diag.span_label(span, fluent::infer::data_lifetime_flow); + } else { + diag.span_label(ty_sup, fluent::infer::types_declared_different); + diag.span_label(ty_sub, fluent::infer::nothing); + diag.span_label(span, fluent::infer::data_flows); + diag.set_arg("label_var1_exists", label_var1.is_some()); + diag.set_arg( + "label_var1", + label_var1.map(|x| x.to_string()).unwrap_or_default(), + ); + diag.set_arg("label_var2_exists", label_var2.is_some()); + diag.set_arg( + "label_var2", + label_var2.map(|x| x.to_string()).unwrap_or_default(), + ); + } + } + } + } +} + +pub struct AddLifetimeParamsSuggestion<'a> { + pub tcx: TyCtxt<'a>, + pub sub: Region<'a>, + pub ty_sup: &'a Ty<'a>, + pub ty_sub: &'a Ty<'a>, + pub add_note: bool, +} + +impl AddSubdiagnostic for AddLifetimeParamsSuggestion<'_> { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + let mut mk_suggestion = || { + let ( + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, + ) = (self.ty_sub, self.ty_sup) else { + return false; + }; + + if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { + return false; + }; + + let Some(anon_reg) = self.tcx.is_suitable_region(self.sub) else { + return false; + }; + + let hir_id = self.tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); + + let node = self.tcx.hir().get(hir_id); + let is_impl = matches!(&node, hir::Node::ImplItem(_)); + let generics = match node { + hir::Node::Item(&hir::Item { + kind: hir::ItemKind::Fn(_, ref generics, ..), + .. + }) + | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) + | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, + _ => return false, + }; + + let suggestion_param_name = generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .find(|i| *i != kw::UnderscoreLifetime); + let introduce_new = suggestion_param_name.is_none(); + let suggestion_param_name = + suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); + + debug!(?lifetime_sup.span); + debug!(?lifetime_sub.span); + let make_suggestion = |span: rustc_span::Span| { + if span.is_empty() { + (span, format!("{}, ", suggestion_param_name)) + } else if let Ok("&") = self.tcx.sess.source_map().span_to_snippet(span).as_deref() + { + (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) + } else { + (span, suggestion_param_name.clone()) + } + }; + let mut suggestions = + vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; + + if introduce_new { + let new_param_suggestion = if let Some(first) = + generics.params.iter().find(|p| !p.name.ident().span.is_empty()) + { + (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) + } else { + (generics.span, format!("<{}>", suggestion_param_name)) + }; + + suggestions.push(new_param_suggestion); + } + + diag.multipart_suggestion( + fluent::infer::lifetime_param_suggestion, + suggestions, + Applicability::MaybeIncorrect, + ); + diag.set_arg("is_impl", is_impl); + true + }; + if mk_suggestion() && self.add_note { + diag.note(fluent::infer::lifetime_param_suggestion_elided); + } + } +} + +#[derive(SessionDiagnostic)] +#[diag(infer::lifetime_mismatch, code = "E0623")] +pub struct LifetimeMismatch<'a> { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub labels: LifetimeMismatchLabels, + #[subdiagnostic] + pub suggestion: AddLifetimeParamsSuggestion<'a>, +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index 9a2ab3e32248b..ebd59a3fef795 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -1,6 +1,9 @@ //! Error Reporting for Anonymous Region Lifetime Errors //! where both the regions are anonymous. +use crate::errors::AddLifetimeParamsSuggestion; +use crate::errors::LifetimeMismatch; +use crate::errors::LifetimeMismatchLabels; use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; use crate::infer::error_reporting::nice_region_error::NiceRegionError; @@ -8,11 +11,10 @@ use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::SubregionOrigin; use crate::infer::TyCtxt; -use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; -use rustc_hir as hir; -use rustc_hir::{GenericParamKind, Ty}; +use rustc_errors::AddSubdiagnostic; +use rustc_errors::{Diagnostic, ErrorGuaranteed}; +use rustc_hir::Ty; use rustc_middle::ty::Region; -use rustc_span::symbol::kw; impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { /// Print the error message for lifetime errors when both the concerned regions are anonymous. @@ -98,137 +100,50 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { let sub_is_ret_type = self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); - let span_label_var1 = match anon_param_sup.pat.simple_ident() { - Some(simple_ident) => format!(" from `{}`", simple_ident), - None => String::new(), - }; - - let span_label_var2 = match anon_param_sub.pat.simple_ident() { - Some(simple_ident) => format!(" into `{}`", simple_ident), - None => String::new(), - }; - debug!( "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", sub_is_ret_type, sup_is_ret_type ); - let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); - - match (sup_is_ret_type, sub_is_ret_type) { + let labels = match (sup_is_ret_type, sub_is_ret_type) { (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { let param_span = if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; - - err.span_label( + LifetimeMismatchLabels::InRet { param_span, - "this parameter and the return type are declared with different lifetimes...", - ); - err.span_label(ret_span, ""); - err.span_label(span, format!("...but data{} is returned here", span_label_var1)); - } - - (None, None) => { - if ty_sup.hir_id == ty_sub.hir_id { - err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); - err.span_label(ty_sub.span, ""); - err.span_label(span, "...but data with one lifetime flows into the other here"); - } else { - err.span_label( - ty_sup.span, - "these two types are declared with different lifetimes...", - ); - err.span_label(ty_sub.span, ""); - err.span_label( - span, - format!("...but data{} flows{} here", span_label_var1, span_label_var2), - ); + ret_span, + span, + label_var1: anon_param_sup.pat.simple_ident(), } } - } - if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) { - err.note("each elided lifetime in input position becomes a distinct lifetime"); - } + (None, None) => LifetimeMismatchLabels::Normal { + hir_equal: ty_sup.hir_id == ty_sub.hir_id, + ty_sup: ty_sup.span, + ty_sub: ty_sub.span, + span, + label_var1: anon_param_sup.pat.simple_ident(), + label_var2: anon_param_sub.pat.simple_ident(), + }, + }; - let reported = err.emit(); + let suggestion = + AddLifetimeParamsSuggestion { tcx: self.tcx(), sub, ty_sup, ty_sub, add_note: true }; + let err = LifetimeMismatch { span, labels, suggestion }; + let reported = self.tcx().sess.emit_err(err); Some(reported) } } +/// Currently only used in rustc_borrowck, probably should be +/// removed in favour of public_errors::AddLifetimeParamsSuggestion pub fn suggest_adding_lifetime_params<'tcx>( tcx: TyCtxt<'tcx>, sub: Region<'tcx>, - ty_sup: &Ty<'_>, - ty_sub: &Ty<'_>, + ty_sup: &'tcx Ty<'_>, + ty_sub: &'tcx Ty<'_>, err: &mut Diagnostic, -) -> bool { - let ( - hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, - hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, - ) = (ty_sub, ty_sup) else { - return false; - }; - - if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { - return false; - }; - - let Some(anon_reg) = tcx.is_suitable_region(sub) else { - return false; - }; - - let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); - - let node = tcx.hir().get(hir_id); - let is_impl = matches!(&node, hir::Node::ImplItem(_)); - let generics = match node { - hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. }) - | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) - | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, - _ => return false, - }; - - let suggestion_param_name = generics - .params - .iter() - .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) - .map(|p| p.name.ident().name) - .find(|i| *i != kw::UnderscoreLifetime); - let introduce_new = suggestion_param_name.is_none(); - let suggestion_param_name = - suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); - - debug!(?lifetime_sup.span); - debug!(?lifetime_sub.span); - let make_suggestion = |span: rustc_span::Span| { - if span.is_empty() { - (span, format!("{}, ", suggestion_param_name)) - } else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() { - (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) - } else { - (span, suggestion_param_name.clone()) - } - }; - let mut suggestions = - vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; - - if introduce_new { - let new_param_suggestion = - if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) { - (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) - } else { - (generics.span, format!("<{}>", suggestion_param_name)) - }; - - suggestions.push(new_param_suggestion); - } - - let mut sugg = String::from("consider introducing a named lifetime parameter"); - if is_impl { - sugg.push_str(" and update trait if needed"); - } - err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect); - - true +) { + let suggestion = AddLifetimeParamsSuggestion { tcx, sub, ty_sup, ty_sub, add_note: false }; + suggestion.add_to_diagnostic(err); } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 931ebca7d0145..5810616fcdbf1 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -36,5 +36,9 @@ extern crate tracing; extern crate rustc_middle; mod errors; +pub mod public_errors { + // Probably would be useful in rustc_borrowck + pub use super::errors::AddLifetimeParamsSuggestion; +} pub mod infer; pub mod traits; From e0e9b21c78e4e6a7a5515944ace4cb0c1f2f9253 Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Tue, 30 Aug 2022 18:28:50 +0300 Subject: [PATCH 2/8] Mugrate mismatched_static_lifetime.rs --- .../locales/en-US/infer.ftl | 31 +++ .../src/{errors.rs => errors/mod.rs} | 68 +++++++ .../src/errors/note_and_explain.rs | 176 ++++++++++++++++++ .../mismatched_static_lifetime.rs | 48 +++-- 4 files changed, 303 insertions(+), 20 deletions(-) rename compiler/rustc_infer/src/{errors.rs => errors/mod.rs} (88%) create mode 100644 compiler/rustc_infer/src/errors/note_and_explain.rs diff --git a/compiler/rustc_error_messages/locales/en-US/infer.ftl b/compiler/rustc_error_messages/locales/en-US/infer.ftl index 478a4bdf8a966..2899b8304bc14 100644 --- a/compiler/rustc_error_messages/locales/en-US/infer.ftl +++ b/compiler/rustc_error_messages/locales/en-US/infer.ftl @@ -137,3 +137,34 @@ infer_lifetime_param_suggestion = consider introducing a named lifetime paramete *[false] {""} } infer_lifetime_param_suggestion_elided = each elided lifetime in input position becomes a distinct lifetime + +infer_region_explanation = {$pref_kind -> + *[should_not_happen] [{$pref_kind}] + [empty] {""} +}{$pref_kind -> + [empty] {""} + *[other] {" "} +}{$desc_kind -> + *[should_not_happen] [{$desc_kind}] + [restatic] the static lifetime + [reempty] the empty lifetime + [reemptyuni] the empty lifetime in universe {$desc_arg} + [revar] lifetime {$desc_arg} + + [as_defined] the lifetime `{$desc_arg}` as defined here + [as_defined_anon] the anonymous lifetime as defined here + [defined_here] the anonymous lifetime defined here + [anon_num_here] the anonymous lifetime #{$desc_num_arg} defined here + [defined_here_reg] the lifetime `{$desc_arg}` as defined here +}{$suff_kind -> + *[should_not_happen] [{$suff_kind}] + [empty]{""} + [continues] ... +} + +infer_mismatched_static_lifetime = incompatible lifetime on type +infer_msl_impl_note = ...does not necessarily outlive the static lifetime introduced by the compatible `impl` +infer_msl_introduces_static = introduces a `'static` lifetime requirement +infer_msl_unmet_req = because this has an unmet lifetime requirement +infer_msl_trait_note = this has an implicit `'static` lifetime requirement +infer_msl_trait_sugg = consider relaxing the implicit `'static` requirement diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors/mod.rs similarity index 88% rename from compiler/rustc_infer/src/errors.rs rename to compiler/rustc_infer/src/errors/mod.rs index 932ba1f35afcf..ad8eb2945fa99 100644 --- a/compiler/rustc_infer/src/errors.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -14,6 +14,8 @@ use crate::infer::error_reporting::{ ObligationCauseAsDiagArg, }; +pub mod note_and_explain; + #[derive(SessionDiagnostic)] #[diag(infer::opaque_hidden_type)] pub struct OpaqueHiddenTypeDiag { @@ -419,3 +421,69 @@ pub struct LifetimeMismatch<'a> { #[subdiagnostic] pub suggestion: AddLifetimeParamsSuggestion<'a>, } + +pub mod mismatched_static_lifetime { + use rustc_errors::{self, fluent, AddSubdiagnostic, MultiSpan}; + use rustc_span::Span; + + use super::note_and_explain; + + pub struct LabeledMultiSpan { + pub multi_span: MultiSpan, + pub binding_span: Span, + } + + impl AddSubdiagnostic for LabeledMultiSpan { + fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { + self.multi_span + .push_span_label(self.binding_span, fluent::infer::msl_introduces_static); + diag.span_note(self.multi_span, fluent::infer::msl_unmet_req); + } + } + + pub struct ImplNote { + pub impl_span: Option, + } + + impl AddSubdiagnostic for ImplNote { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + match self.impl_span { + Some(span) => diag.span_note(span, fluent::infer::msl_impl_note), + None => diag.note(fluent::infer::msl_impl_note), + }; + } + } + + #[derive(SessionSubdiagnostic)] + pub enum TraitSubdiag { + #[note(infer::msl_trait_note)] + Note { + #[primary_span] + span: Span, + }, + #[suggestion_verbose( + infer::msl_trait_sugg, + code = " + '_", + applicability = "maybe-incorrect" + )] + Sugg { + #[primary_span] + span: Span, + }, + } + + #[derive(SessionDiagnostic)] + #[diag(infer::mismatched_static_lifetime)] + pub struct MismatchedStaticLifetime<'a> { + #[primary_span] + pub cause_span: Span, + #[subdiagnostic] + pub multispan_subdiag: LabeledMultiSpan, + #[subdiagnostic] + pub expl: Option>, + #[subdiagnostic] + pub impl_note: ImplNote, + #[subdiagnostic] + pub trait_subdiags: Vec, + } +} diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs new file mode 100644 index 0000000000000..92bf3ecd131da --- /dev/null +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -0,0 +1,176 @@ +use crate::infer::error_reporting::nice_region_error::find_anon_type; +use rustc_errors::{self, fluent, AddSubdiagnostic}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::{symbol::kw, Span}; + +#[derive(Default)] +struct DescriptionCtx<'a> { + span: Option, + kind: &'a str, + arg: String, + num_arg: u32, +} + +impl<'a> DescriptionCtx<'a> { + fn new<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + alt_span: Option, + ) -> Option { + let mut me = DescriptionCtx::default(); + me.span = alt_span; + match *region { + ty::ReEarlyBound(_) | ty::ReFree(_) => { + return Self::from_early_bound_and_free_regions(tcx, region); + } + ty::ReStatic => { + me.kind = "restatic"; + } + + ty::ReEmpty(ty::UniverseIndex::ROOT) => me.kind = "reempty", + + // uh oh, hope no user ever sees THIS + ty::ReEmpty(ui) => { + me.kind = "reemptyuni"; + me.arg = format!("{:?}", ui); + } + + ty::RePlaceholder(_) => return None, + + // FIXME(#13998) RePlaceholder should probably print like + // ReFree rather than dumping Debug output on the user. + // + // We shouldn't really be having unification failures with ReVar + // and ReLateBound though. + ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => { + me.kind = "revar"; + me.arg = format!("{:?}", region); + } + }; + Some(me) + } + + fn from_early_bound_and_free_regions<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + ) -> Option { + let mut me = DescriptionCtx::default(); + let scope = region.free_region_binding_scope(tcx).expect_local(); + match *region { + ty::ReEarlyBound(ref br) => { + let mut sp = tcx.def_span(scope); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) + { + sp = param.span; + } + if br.has_name() { + me.kind = "as_defined"; + me.arg = br.name.to_string(); + } else { + me.kind = "as_defined_anon"; + }; + me.span = Some(sp) + } + ty::ReFree(ref fr) => { + if !fr.bound_region.is_named() + && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) + { + me.kind = "defined_here"; + me.span = Some(ty.span); + } else { + match fr.bound_region { + ty::BoundRegionKind::BrNamed(_, name) => { + let mut sp = tcx.def_span(scope); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) + { + sp = param.span; + } + if name == kw::UnderscoreLifetime { + me.kind = "as_defined_anon"; + } else { + me.kind = "as_defined"; + me.arg = name.to_string(); + }; + me.span = Some(sp); + } + ty::BrAnon(idx) => { + me.kind = "anon_num_here"; + me.num_arg = idx+1; + me.span = Some(tcx.def_span(scope)); + }, + _ => { + me.kind = "defined_here_reg"; + me.arg = region.to_string(); + me.span = Some(tcx.def_span(scope)); + }, + } + } + } + _ => bug!(), + } + Some(me) + } + + fn add_to(self, diag: &mut rustc_errors::Diagnostic) { + diag.set_arg("desc_kind", self.kind); + diag.set_arg("desc_arg", self.arg); + diag.set_arg("desc_num_arg", self.num_arg); + } +} + +pub enum PrefixKind { + Empty, +} + +pub enum SuffixKind { + Continues, +} + +impl PrefixKind { + fn add_to(self, diag: &mut rustc_errors::Diagnostic) { + match self { + Self::Empty => diag.set_arg("pref_kind", "empty"), + }; + } +} + +impl SuffixKind { + fn add_to(self, diag: &mut rustc_errors::Diagnostic) { + match self { + Self::Continues => diag.set_arg("suff_kind", "continues"), + }; + } +} + +pub struct RegionExplanation<'a> { + desc: DescriptionCtx<'a>, + prefix: PrefixKind, + suffix: SuffixKind, +} + +impl RegionExplanation<'_> { + pub fn new<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + alt_span: Option, + prefix: PrefixKind, + suffix: SuffixKind, + ) -> Option { + Some(Self { desc: DescriptionCtx::new(tcx, region, alt_span)?, prefix, suffix }) + } +} + +impl AddSubdiagnostic for RegionExplanation<'_> { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + if let Some(span) = self.desc.span { + diag.span_note(span, fluent::infer::region_explanation); + } else { + diag.note(fluent::infer::region_explanation); + } + self.desc.add_to(diag); + self.prefix.add_to(diag); + self.suffix.add_to(diag); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index c20b96cae2e4f..832a0eaba3b8e 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -1,13 +1,14 @@ //! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate //! to hold. +use crate::errors::mismatched_static_lifetime::{ImplNote, MismatchedStaticLifetime, TraitSubdiag}; +use crate::errors::{mismatched_static_lifetime::LabeledMultiSpan, note_and_explain}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; -use crate::infer::error_reporting::note_and_explain_region; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; use crate::traits::ObligationCauseCode; use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; use rustc_hir as hir; use rustc_hir::intravisit::Visitor; use rustc_middle::ty::TypeVisitor; @@ -39,12 +40,20 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { = *parent.code() else { return None; }; - let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); + // FIXME: we should point at the lifetime - let mut multi_span: MultiSpan = vec![binding_span].into(); - multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement"); - err.span_note(multi_span, "because this has an unmet lifetime requirement"); - note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); + let multi_span: MultiSpan = vec![binding_span].into(); + let multispan_subdiag = LabeledMultiSpan { multi_span, binding_span }; + + let expl = note_and_explain::RegionExplanation::new( + self.tcx(), + sup, + Some(binding_span), + note_and_explain::PrefixKind::Empty, + note_and_explain::SuffixKind::Continues, + ); + let mut impl_span = None; + let mut trait_subdiags = Vec::new(); if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { // If an impl is local, then maybe this isn't what they want. Try to // be as helpful as possible with implicit lifetimes. @@ -73,31 +82,30 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // there aren't trait objects or because none are implicit, then just // write a single note on the impl itself. - let impl_span = self.tcx().def_span(*impl_def_id); - err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + impl_span = Some(self.tcx().def_span(*impl_def_id)); } else { // Otherwise, point at all implicit static lifetimes - err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); for span in &traits { - err.span_note(*span, "this has an implicit `'static` lifetime requirement"); + trait_subdiags.push(TraitSubdiag::Note { span: *span }); // It would be nice to put this immediately under the above note, but they get // pushed to the end. - err.span_suggestion_verbose( - span.shrink_to_hi(), - "consider relaxing the implicit `'static` requirement", - " + '_", - Applicability::MaybeIncorrect, - ); + trait_subdiags.push(TraitSubdiag::Sugg { span: span.shrink_to_hi() }); } } } else { // Otherwise just point out the impl. - let impl_span = self.tcx().def_span(*impl_def_id); - err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + impl_span = Some(self.tcx().def_span(*impl_def_id)); } - let reported = err.emit(); + let err = MismatchedStaticLifetime { + cause_span: cause.span, + multispan_subdiag, + expl, + impl_note: ImplNote { impl_span }, + trait_subdiags, + }; + let reported = self.tcx().sess.emit_err(err); Some(reported) } } From 3190522294dac32db63b11b579f6b75a4f2f7d8e Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Wed, 31 Aug 2022 15:02:11 +0300 Subject: [PATCH 3/8] Address some comments --- compiler/rustc_infer/src/errors/mod.rs | 113 ++++++++---------- .../src/errors/note_and_explain.rs | 2 +- .../nice_region_error/different_lifetimes.rs | 4 +- .../mismatched_static_lifetime.rs | 11 +- compiler/rustc_infer/src/lib.rs | 4 - 5 files changed, 61 insertions(+), 73 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index ad8eb2945fa99..e191147cdfe4e 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1,6 +1,6 @@ use hir::GenericParamKind; use rustc_errors::{ - fluent, AddSubdiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, + fluent, AddSubdiagnostic, Applicability, DiagnosticMessage, DiagnosticStyledString, MultiSpan, }; use rustc_hir as hir; use rustc_hir::{FnRetTy, Ty}; @@ -273,8 +273,8 @@ pub enum LifetimeMismatchLabels { ty_sup: Span, ty_sub: Span, span: Span, - label_var1: Option, - label_var2: Option, + sup: Option, + sub: Option, }, } @@ -293,8 +293,8 @@ impl AddSubdiagnostic for LifetimeMismatchLabels { ty_sup, ty_sub, span, - label_var1, - label_var2, + sup: label_var1, + sub: label_var2, } => { if hir_equal { diag.span_label(ty_sup, fluent::infer::declared_multiple); @@ -422,68 +422,57 @@ pub struct LifetimeMismatch<'a> { pub suggestion: AddLifetimeParamsSuggestion<'a>, } -pub mod mismatched_static_lifetime { - use rustc_errors::{self, fluent, AddSubdiagnostic, MultiSpan}; - use rustc_span::Span; - - use super::note_and_explain; - - pub struct LabeledMultiSpan { - pub multi_span: MultiSpan, - pub binding_span: Span, - } - - impl AddSubdiagnostic for LabeledMultiSpan { - fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { - self.multi_span - .push_span_label(self.binding_span, fluent::infer::msl_introduces_static); - diag.span_note(self.multi_span, fluent::infer::msl_unmet_req); - } - } +pub struct IntroducesStaticBecauseUnmetLifetimeReq { + pub unmet_requirements: MultiSpan, + pub binding_span: Span, +} - pub struct ImplNote { - pub impl_span: Option, +impl AddSubdiagnostic for IntroducesStaticBecauseUnmetLifetimeReq { + fn add_to_diagnostic(mut self, diag: &mut rustc_errors::Diagnostic) { + self.unmet_requirements + .push_span_label(self.binding_span, fluent::infer::msl_introduces_static); + diag.span_note(self.unmet_requirements, fluent::infer::msl_unmet_req); } +} - impl AddSubdiagnostic for ImplNote { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { - match self.impl_span { - Some(span) => diag.span_note(span, fluent::infer::msl_impl_note), - None => diag.note(fluent::infer::msl_impl_note), - }; - } - } +pub struct ImplNote { + pub impl_span: Option, +} - #[derive(SessionSubdiagnostic)] - pub enum TraitSubdiag { - #[note(infer::msl_trait_note)] - Note { - #[primary_span] - span: Span, - }, - #[suggestion_verbose( - infer::msl_trait_sugg, - code = " + '_", - applicability = "maybe-incorrect" - )] - Sugg { - #[primary_span] - span: Span, - }, +impl AddSubdiagnostic for ImplNote { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + match self.impl_span { + Some(span) => diag.span_note(span, fluent::infer::msl_impl_note), + None => diag.note(fluent::infer::msl_impl_note), + }; } +} - #[derive(SessionDiagnostic)] - #[diag(infer::mismatched_static_lifetime)] - pub struct MismatchedStaticLifetime<'a> { +#[derive(SessionSubdiagnostic)] +pub enum TraitSubdiag { + #[note(infer::msl_trait_note)] + Note { #[primary_span] - pub cause_span: Span, - #[subdiagnostic] - pub multispan_subdiag: LabeledMultiSpan, - #[subdiagnostic] - pub expl: Option>, - #[subdiagnostic] - pub impl_note: ImplNote, - #[subdiagnostic] - pub trait_subdiags: Vec, - } + span: Span, + }, + #[suggestion_verbose(infer::msl_trait_sugg, code = " + '_", applicability = "maybe-incorrect")] + Sugg { + #[primary_span] + span: Span, + }, +} + +#[derive(SessionDiagnostic)] +#[diag(infer::mismatched_static_lifetime)] +pub struct MismatchedStaticLifetime<'a> { + #[primary_span] + pub cause_span: Span, + #[subdiagnostic] + pub unmet_lifetime_reqs: IntroducesStaticBecauseUnmetLifetimeReq, + #[subdiagnostic] + pub expl: Option>, + #[subdiagnostic] + pub impl_note: ImplNote, + #[subdiagnostic] + pub trait_subdiags: Vec, } diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index 92bf3ecd131da..c9df277c744c0 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -91,7 +91,7 @@ impl<'a> DescriptionCtx<'a> { me.kind = "as_defined_anon"; } else { me.kind = "as_defined"; - me.arg = name.to_string(); + me.arg = name.to_string(); }; me.span = Some(sp); } diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs index ebd59a3fef795..3a4320a9a8f1d 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -122,8 +122,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { ty_sup: ty_sup.span, ty_sub: ty_sub.span, span, - label_var1: anon_param_sup.pat.simple_ident(), - label_var2: anon_param_sub.pat.simple_ident(), + sup: anon_param_sup.pat.simple_ident(), + sub: anon_param_sub.pat.simple_ident(), }, }; diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs index 832a0eaba3b8e..1410e2b63b0b8 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -1,8 +1,8 @@ //! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate //! to hold. -use crate::errors::mismatched_static_lifetime::{ImplNote, MismatchedStaticLifetime, TraitSubdiag}; -use crate::errors::{mismatched_static_lifetime::LabeledMultiSpan, note_and_explain}; +use crate::errors::{note_and_explain, IntroducesStaticBecauseUnmetLifetimeReq}; +use crate::errors::{ImplNote, MismatchedStaticLifetime, TraitSubdiag}; use crate::infer::error_reporting::nice_region_error::NiceRegionError; use crate::infer::lexical_region_resolve::RegionResolutionError; use crate::infer::{SubregionOrigin, TypeTrace}; @@ -43,7 +43,10 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { // FIXME: we should point at the lifetime let multi_span: MultiSpan = vec![binding_span].into(); - let multispan_subdiag = LabeledMultiSpan { multi_span, binding_span }; + let multispan_subdiag = IntroducesStaticBecauseUnmetLifetimeReq { + unmet_requirements: multi_span, + binding_span, + }; let expl = note_and_explain::RegionExplanation::new( self.tcx(), @@ -100,7 +103,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { } let err = MismatchedStaticLifetime { cause_span: cause.span, - multispan_subdiag, + unmet_lifetime_reqs: multispan_subdiag, expl, impl_note: ImplNote { impl_span }, trait_subdiags, diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 5810616fcdbf1..931ebca7d0145 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -36,9 +36,5 @@ extern crate tracing; extern crate rustc_middle; mod errors; -pub mod public_errors { - // Probably would be useful in rustc_borrowck - pub use super::errors::AddLifetimeParamsSuggestion; -} pub mod infer; pub mod traits; From e750d7faa7e18fb3af51de96b35baebcce4123a2 Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Fri, 2 Sep 2022 16:05:00 +0300 Subject: [PATCH 4/8] Remove a comment and use IntoDiagnosticArg instead of add_to() where feasible --- .../src/errors/note_and_explain.rs | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_infer/src/errors/note_and_explain.rs b/compiler/rustc_infer/src/errors/note_and_explain.rs index c9df277c744c0..6f1f9522c869d 100644 --- a/compiler/rustc_infer/src/errors/note_and_explain.rs +++ b/compiler/rustc_infer/src/errors/note_and_explain.rs @@ -1,5 +1,5 @@ use crate::infer::error_reporting::nice_region_error::find_anon_type; -use rustc_errors::{self, fluent, AddSubdiagnostic}; +use rustc_errors::{self, fluent, AddSubdiagnostic, IntoDiagnosticArg}; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::{symbol::kw, Span}; @@ -29,7 +29,6 @@ impl<'a> DescriptionCtx<'a> { ty::ReEmpty(ty::UniverseIndex::ROOT) => me.kind = "reempty", - // uh oh, hope no user ever sees THIS ty::ReEmpty(ui) => { me.kind = "reemptyuni"; me.arg = format!("{:?}", ui); @@ -128,19 +127,23 @@ pub enum SuffixKind { Continues, } -impl PrefixKind { - fn add_to(self, diag: &mut rustc_errors::Diagnostic) { - match self { - Self::Empty => diag.set_arg("pref_kind", "empty"), - }; +impl IntoDiagnosticArg for PrefixKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self { + Self::Empty => "empty", + } + .into(); + rustc_errors::DiagnosticArgValue::Str(kind) } } -impl SuffixKind { - fn add_to(self, diag: &mut rustc_errors::Diagnostic) { - match self { - Self::Continues => diag.set_arg("suff_kind", "continues"), - }; +impl IntoDiagnosticArg for SuffixKind { + fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { + let kind = match self { + Self::Continues => "continues", + } + .into(); + rustc_errors::DiagnosticArgValue::Str(kind) } } @@ -170,7 +173,7 @@ impl AddSubdiagnostic for RegionExplanation<'_> { diag.note(fluent::infer::region_explanation); } self.desc.add_to(diag); - self.prefix.add_to(diag); - self.suffix.add_to(diag); + diag.set_arg("pref_kind", self.prefix); + diag.set_arg("suff_kind", self.suffix); } } From 59c567296a8dd2b6c36765dbb14caf68cc9cf9af Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Mon, 5 Sep 2022 19:04:35 +0300 Subject: [PATCH 5/8] Use untranslated messages for now --- compiler/rustc_infer/src/errors/mod.rs | 35 ++++++++++++++++++-------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index e191147cdfe4e..911381b367f46 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -448,18 +448,31 @@ impl AddSubdiagnostic for ImplNote { } } -#[derive(SessionSubdiagnostic)] pub enum TraitSubdiag { - #[note(infer::msl_trait_note)] - Note { - #[primary_span] - span: Span, - }, - #[suggestion_verbose(infer::msl_trait_sugg, code = " + '_", applicability = "maybe-incorrect")] - Sugg { - #[primary_span] - span: Span, - }, + Note { span: Span }, + Sugg { span: Span }, +} + +// FIXME We can't rely on Vec working well at the moment, +// as only the args from one of the subdiagnostics will actually be used. +// This results in an incorrect diagnostic if more than two subdiags with the same slug are added. +// Use untranslated messages for now. +impl AddSubdiagnostic for TraitSubdiag { + fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { + match self { + TraitSubdiag::Note { span } => { + diag.span_note(span, "this has an implicit `'static` lifetime requirement"); + } + TraitSubdiag::Sugg { span } => { + diag.span_suggestion_verbose( + span, + "consider relaxing the implicit `'static` requirement", + " + '_".to_owned(), + rustc_errors::Applicability::MaybeIncorrect, + ); + } + } + } } #[derive(SessionDiagnostic)] From cb7ad9e54853aa5bc217a147d74e3289f6c45489 Mon Sep 17 00:00:00 2001 From: IQuant Date: Tue, 6 Sep 2022 17:54:29 +0300 Subject: [PATCH 6/8] Slightly more concise comment Co-authored-by: David Wood --- compiler/rustc_infer/src/errors/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 911381b367f46..5eedca78489b9 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -453,10 +453,7 @@ pub enum TraitSubdiag { Sugg { span: Span }, } -// FIXME We can't rely on Vec working well at the moment, -// as only the args from one of the subdiagnostics will actually be used. -// This results in an incorrect diagnostic if more than two subdiags with the same slug are added. -// Use untranslated messages for now. +// FIXME(#100717) used in `Vec` so requires eager translation/list support impl AddSubdiagnostic for TraitSubdiag { fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { match self { From ee74f925f57c866669c69eecaa5579c327031674 Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Tue, 6 Sep 2022 22:18:56 +0300 Subject: [PATCH 7/8] Replace manual impl with a derive macro as multipart suggestions are now supported by them --- compiler/rustc_infer/src/errors/mod.rs | 89 +++++++++++-------- .../infer/error_reporting/need_type_info.rs | 10 +-- 2 files changed, 55 insertions(+), 44 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index 5eedca78489b9..f5519b989b64b 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -147,56 +147,67 @@ pub enum SourceKindSubdiag<'a> { }, } -// Has to be implemented manually because multipart suggestions are not supported by the derive macro. -// Would be a part of `SourceKindSubdiag` otherwise. +#[derive(SessionSubdiagnostic)] pub enum SourceKindMultiSuggestion<'a> { + #[multipart_suggestion_verbose( + infer::source_kind_fully_qualified, + applicability = "has-placeholders" + )] FullyQualified { - span: Span, + #[suggestion_part(code = "{def_path}({adjustment}")] + span_lo: Span, + #[suggestion_part(code = "{successor_pos}")] + span_hi: Span, def_path: String, adjustment: &'a str, - successor: (&'a str, BytePos), + successor_pos: &'a str, }, + #[multipart_suggestion_verbose( + infer::source_kind_closure_return, + applicability = "has-placeholders" + )] ClosureReturn { - ty_info: String, - data: &'a FnRetTy<'a>, - should_wrap_expr: Option, + #[suggestion_part(code = "{start_span_code}")] + start_span: Span, + start_span_code: String, + #[suggestion_part(code = "}}")] + end_span: Option, }, } -impl AddSubdiagnostic for SourceKindMultiSuggestion<'_> { - fn add_to_diagnostic(self, diag: &mut rustc_errors::Diagnostic) { - match self { - Self::FullyQualified { span, def_path, adjustment, successor } => { - let suggestion = vec![ - (span.shrink_to_lo(), format!("{def_path}({adjustment}")), - (span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()), - ]; - diag.multipart_suggestion_verbose( - fluent::infer::source_kind_fully_qualified, - suggestion, - rustc_errors::Applicability::HasPlaceholders, - ); - } - Self::ClosureReturn { ty_info, data, should_wrap_expr } => { - let (arrow, post) = match data { - FnRetTy::DefaultReturn(_) => ("-> ", " "), - _ => ("", ""), - }; - let suggestion = match should_wrap_expr { - Some(end_span) => vec![ - (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post)), - (end_span, " }".to_string()), - ], - None => vec![(data.span(), format!("{}{}{}", arrow, ty_info, post))], - }; - diag.multipart_suggestion_verbose( - fluent::infer::source_kind_closure_return, - suggestion, - rustc_errors::Applicability::HasPlaceholders, - ); - } +impl<'a> SourceKindMultiSuggestion<'a> { + pub fn new_fully_qualified( + span: Span, + def_path: String, + adjustment: &'a str, + successor: (&'a str, BytePos), + ) -> Self { + Self::FullyQualified { + span_lo: span.shrink_to_lo(), + span_hi: span.shrink_to_hi().with_hi(successor.1), + def_path, + adjustment, + successor_pos: successor.0, } } + + pub fn new_closure_return( + ty_info: String, + data: &'a FnRetTy<'a>, + should_wrap_expr: Option, + ) -> Self { + let (arrow, post) = match data { + FnRetTy::DefaultReturn(_) => ("-> ", " "), + _ => ("", ""), + }; + let (start_span, start_span_code, end_span) = match should_wrap_expr { + Some(end_span) => { + (data.span(), format!("{}{}{}{{ ", arrow, ty_info, post), Some(end_span)) + } + None => (data.span(), format!("{}{}{}", arrow, ty_info, post), None), + }; + Self::ClosureReturn { start_span, start_span_code, end_span } + } } pub enum RegionOriginNote<'a> { diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs index 232b9b11455db..b0f5897e34b03 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -511,20 +511,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { _ => "", }; - multi_suggestions.push(SourceKindMultiSuggestion::FullyQualified { - span: receiver.span, + multi_suggestions.push(SourceKindMultiSuggestion::new_fully_qualified( + receiver.span, def_path, adjustment, successor, - }); + )); } InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { let ty_info = ty_to_string(self, ty); - multi_suggestions.push(SourceKindMultiSuggestion::ClosureReturn { + multi_suggestions.push(SourceKindMultiSuggestion::new_closure_return( ty_info, data, should_wrap_expr, - }); + )); } } match error_code { From 3d70be7240b826c54411c76d18d3b203f657425a Mon Sep 17 00:00:00 2001 From: Nikita Tomashevich Date: Tue, 6 Sep 2022 22:55:49 +0300 Subject: [PATCH 8/8] Whoops forgot a space --- compiler/rustc_infer/src/errors/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index f5519b989b64b..d232a18646244 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -170,7 +170,7 @@ pub enum SourceKindMultiSuggestion<'a> { #[suggestion_part(code = "{start_span_code}")] start_span: Span, start_span_code: String, - #[suggestion_part(code = "}}")] + #[suggestion_part(code = " }}")] end_span: Option, }, }