Rollup merge of #107034 - IntQuant:issue-100717-infer-5, r=oli-obk

Migrating rustc_infer to session diagnostics (part 4)

`@rustbot` label +A-translation
r? rust-lang/diagnostics
cc https://github.com/rust-lang/rust/issues/100717
This commit is contained in:
Matthias Krüger 2023-02-15 21:30:55 +01:00 committed by GitHub
commit 1fdf0e1334
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 488 additions and 233 deletions

View File

@ -140,6 +140,18 @@ infer_lifetime_param_suggestion_elided = each elided lifetime in input position
infer_region_explanation = {$pref_kind -> infer_region_explanation = {$pref_kind ->
*[should_not_happen] [{$pref_kind}] *[should_not_happen] [{$pref_kind}]
[ref_valid_for] ...the reference is valid for
[content_valid_for] ...but the borrowed content is only valid for
[type_obj_valid_for] object type is valid for
[source_pointer_valid_for] source pointer is only valid for
[type_satisfy] type must satisfy
[type_outlive] type must outlive
[lf_param_instantiated_with] lifetime parameter instantiated with
[lf_param_must_outlive] but lifetime parameter must outlive
[lf_instantiated_with] lifetime instantiated with
[lf_must_outlive] but lifetime must outlive
[pointer_valid_for] the pointer is valid for
[data_valid_for] but the referenced data is only valid for
[empty] {""} [empty] {""}
}{$pref_kind -> }{$pref_kind ->
[empty] {""} [empty] {""}
@ -148,7 +160,6 @@ infer_region_explanation = {$pref_kind ->
*[should_not_happen] [{$desc_kind}] *[should_not_happen] [{$desc_kind}]
[restatic] the static lifetime [restatic] the static lifetime
[revar] lifetime {$desc_arg} [revar] lifetime {$desc_arg}
[as_defined] the lifetime `{$desc_arg}` as defined here [as_defined] the lifetime `{$desc_arg}` as defined here
[as_defined_anon] the anonymous lifetime as defined here [as_defined_anon] the anonymous lifetime as defined here
[defined_here] the anonymous lifetime defined here [defined_here] the anonymous lifetime defined here
@ -158,8 +169,16 @@ infer_region_explanation = {$pref_kind ->
*[should_not_happen] [{$suff_kind}] *[should_not_happen] [{$suff_kind}]
[empty]{""} [empty]{""}
[continues] ... [continues] ...
[req_by_binding] {" "}as required by this binding
} }
infer_outlives_content = lifetime of reference outlives lifetime of borrowed content...
infer_outlives_bound = lifetime of the source pointer does not outlive lifetime bound of the object type
infer_fullfill_req_lifetime = the type `{$ty}` does not fulfill the required lifetime
infer_lf_bound_not_satisfied = lifetime bound not satisfied
infer_borrowed_too_long = a value of type `{$ty}` is borrowed for too long
infer_ref_longer_than_data = in type `{$ty}`, reference has a longer lifetime than the data it references
infer_mismatched_static_lifetime = incompatible lifetime on type infer_mismatched_static_lifetime = incompatible lifetime on type
infer_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl` infer_does_not_outlive_static_from_impl = ...does not necessarily outlive the static lifetime introduced by the compatible `impl`
infer_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement infer_implicit_static_lifetime_note = this has an implicit `'static` lifetime requirement
@ -308,3 +327,21 @@ infer_ril_introduced_here = `'static` requirement introduced here
infer_ril_introduced_by = requirement introduced by this return type infer_ril_introduced_by = requirement introduced by this return type
infer_ril_because_of = because of this returned expression infer_ril_because_of = because of this returned expression
infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by the return type
infer_where_remove = remove the `where` clause
infer_where_copy_predicates = copy the `where` clause predicates from the trait
infer_srs_remove_and_box = consider removing this semicolon and boxing the expressions
infer_srs_remove = consider removing this semicolon
infer_srs_add = consider returning the local binding `{$ident}`
infer_srs_add_one = consider returning one of these bindings
infer_await_both_futures = consider `await`ing on both `Future`s
infer_await_future = consider `await`ing on the `Future`
infer_await_note = calling an async function returns a future
infer_prlf_defined_with_sub = the lifetime `{$sub_symbol}` defined here...
infer_prlf_defined_without_sub = the lifetime defined here...
infer_prlf_must_oultive_with_sup = ...must outlive the lifetime `{$sup_symbol}` defined here
infer_prlf_must_oultive_without_sup = ...must outlive the lifetime defined here
infer_prlf_known_limitation = this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)

View File

@ -933,3 +933,216 @@ pub struct ButNeedsToSatisfy {
pub has_lifetime: bool, pub has_lifetime: bool,
pub lifetime: String, pub lifetime: String,
} }
#[derive(Diagnostic)]
#[diag(infer_outlives_content, code = "E0312")]
pub struct OutlivesContent<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag(infer_outlives_bound, code = "E0476")]
pub struct OutlivesBound<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag(infer_fullfill_req_lifetime, code = "E0477")]
pub struct FullfillReqLifetime<'a> {
#[primary_span]
pub span: Span,
pub ty: Ty<'a>,
#[subdiagnostic]
pub note: Option<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag(infer_lf_bound_not_satisfied, code = "E0478")]
pub struct LfBoundNotSatisfied<'a> {
#[primary_span]
pub span: Span,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Diagnostic)]
#[diag(infer_ref_longer_than_data, code = "E0491")]
pub struct RefLongerThanData<'a> {
#[primary_span]
pub span: Span,
pub ty: Ty<'a>,
#[subdiagnostic]
pub notes: Vec<note_and_explain::RegionExplanation<'a>>,
}
#[derive(Subdiagnostic)]
pub enum WhereClauseSuggestions {
#[suggestion(
infer_where_remove,
code = "",
applicability = "machine-applicable",
style = "verbose"
)]
Remove {
#[primary_span]
span: Span,
},
#[suggestion(
infer_where_copy_predicates,
code = "{space}where {trait_predicates}",
applicability = "machine-applicable",
style = "verbose"
)]
CopyPredicates {
#[primary_span]
span: Span,
space: &'static str,
trait_predicates: String,
},
}
#[derive(Subdiagnostic)]
pub enum SuggestRemoveSemiOrReturnBinding {
#[multipart_suggestion(infer_srs_remove_and_box, applicability = "machine-applicable")]
RemoveAndBox {
#[suggestion_part(code = "Box::new(")]
first_lo: Span,
#[suggestion_part(code = ")")]
first_hi: Span,
#[suggestion_part(code = "Box::new(")]
second_lo: Span,
#[suggestion_part(code = ")")]
second_hi: Span,
#[suggestion_part(code = "")]
sp: Span,
},
#[suggestion(
infer_srs_remove,
style = "short",
code = "",
applicability = "machine-applicable"
)]
Remove {
#[primary_span]
sp: Span,
},
#[suggestion(
infer_srs_add,
style = "verbose",
code = "{code}",
applicability = "maybe-incorrect"
)]
Add {
#[primary_span]
sp: Span,
code: String,
ident: Ident,
},
#[note(infer_srs_add_one)]
AddOne {
#[primary_span]
spans: MultiSpan,
},
}
#[derive(Subdiagnostic)]
pub enum ConsiderAddingAwait {
#[help(infer_await_both_futures)]
BothFuturesHelp,
#[multipart_suggestion(infer_await_both_futures, applicability = "maybe-incorrect")]
BothFuturesSugg {
#[suggestion_part(code = ".await")]
first: Span,
#[suggestion_part(code = ".await")]
second: Span,
},
#[suggestion(
infer_await_future,
code = ".await",
style = "verbose",
applicability = "maybe-incorrect"
)]
FutureSugg {
#[primary_span]
span: Span,
},
#[note(infer_await_note)]
FutureSuggNote {
#[primary_span]
span: Span,
},
#[multipart_suggestion(
infer_await_future,
style = "verbose",
applicability = "maybe-incorrect"
)]
FutureSuggMultiple {
#[suggestion_part(code = ".await")]
spans: Vec<Span>,
},
}
#[derive(Diagnostic)]
pub enum PlaceholderRelationLfNotSatisfied {
#[diag(infer_lf_bound_not_satisfied)]
HasBoth {
#[primary_span]
span: Span,
#[note(infer_prlf_defined_with_sub)]
sub_span: Span,
#[note(infer_prlf_must_oultive_with_sup)]
sup_span: Span,
sub_symbol: Symbol,
sup_symbol: Symbol,
#[note(infer_prlf_known_limitation)]
note: (),
},
#[diag(infer_lf_bound_not_satisfied)]
HasSub {
#[primary_span]
span: Span,
#[note(infer_prlf_defined_with_sub)]
sub_span: Span,
#[note(infer_prlf_must_oultive_without_sup)]
sup_span: Span,
sub_symbol: Symbol,
#[note(infer_prlf_known_limitation)]
note: (),
},
#[diag(infer_lf_bound_not_satisfied)]
HasSup {
#[primary_span]
span: Span,
#[note(infer_prlf_defined_without_sub)]
sub_span: Span,
#[note(infer_prlf_must_oultive_with_sup)]
sup_span: Span,
sup_symbol: Symbol,
#[note(infer_prlf_known_limitation)]
note: (),
},
#[diag(infer_lf_bound_not_satisfied)]
HasNone {
#[primary_span]
span: Span,
#[note(infer_prlf_defined_without_sub)]
sub_span: Span,
#[note(infer_prlf_must_oultive_without_sup)]
sup_span: Span,
#[note(infer_prlf_known_limitation)]
note: (),
},
#[diag(infer_lf_bound_not_satisfied)]
OnlyPrimarySpan {
#[primary_span]
span: Span,
#[note(infer_prlf_known_limitation)]
note: (),
},
}

View File

@ -121,16 +121,42 @@ impl<'a> DescriptionCtx<'a> {
pub enum PrefixKind { pub enum PrefixKind {
Empty, Empty,
RefValidFor,
ContentValidFor,
TypeObjValidFor,
SourcePointerValidFor,
TypeSatisfy,
TypeOutlive,
LfParamInstantiatedWith,
LfParamMustOutlive,
LfInstantiatedWith,
LfMustOutlive,
PointerValidFor,
DataValidFor,
} }
pub enum SuffixKind { pub enum SuffixKind {
Empty,
Continues, Continues,
ReqByBinding,
} }
impl IntoDiagnosticArg for PrefixKind { impl IntoDiagnosticArg for PrefixKind {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
let kind = match self { let kind = match self {
Self::Empty => "empty", Self::Empty => "empty",
Self::RefValidFor => "ref_valid_for",
Self::ContentValidFor => "content_valid_for",
Self::TypeObjValidFor => "type_obj_valid_for",
Self::SourcePointerValidFor => "source_pointer_valid_for",
Self::TypeSatisfy => "type_satisfy",
Self::TypeOutlive => "type_outlive",
Self::LfParamInstantiatedWith => "lf_param_instantiated_with",
Self::LfParamMustOutlive => "lf_param_must_outlive",
Self::LfInstantiatedWith => "lf_instantiated_with",
Self::LfMustOutlive => "lf_must_outlive",
Self::PointerValidFor => "pointer_valid_for",
Self::DataValidFor => "data_valid_for",
} }
.into(); .into();
rustc_errors::DiagnosticArgValue::Str(kind) rustc_errors::DiagnosticArgValue::Str(kind)
@ -140,7 +166,9 @@ impl IntoDiagnosticArg for PrefixKind {
impl IntoDiagnosticArg for SuffixKind { impl IntoDiagnosticArg for SuffixKind {
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> { fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
let kind = match self { let kind = match self {
Self::Empty => "empty",
Self::Continues => "continues", Self::Continues => "continues",
Self::ReqByBinding => "req_by_binding",
} }
.into(); .into();
rustc_errors::DiagnosticArgValue::Str(kind) rustc_errors::DiagnosticArgValue::Str(kind)
@ -166,17 +194,19 @@ impl RegionExplanation<'_> {
} }
impl AddToDiagnostic for RegionExplanation<'_> { impl AddToDiagnostic for RegionExplanation<'_> {
fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, _: F) fn add_to_diagnostic_with<F>(self, diag: &mut Diagnostic, f: F)
where where
F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage, F: Fn(&mut Diagnostic, SubdiagnosticMessage) -> SubdiagnosticMessage,
{ {
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);
diag.set_arg("pref_kind", self.prefix); diag.set_arg("pref_kind", self.prefix);
diag.set_arg("suff_kind", self.suffix); diag.set_arg("suff_kind", self.suffix);
let desc_span = self.desc.span;
self.desc.add_to(diag);
let msg = f(diag, fluent::infer_region_explanation.into());
if let Some(span) = desc_span {
diag.span_note(span, msg);
} else {
diag.note(msg);
}
} }
} }

View File

@ -751,15 +751,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}; };
let msg = "`match` arms have incompatible types"; let msg = "`match` arms have incompatible types";
err.span_label(outer, msg); err.span_label(outer, msg);
self.suggest_remove_semi_or_return_binding( if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(
err,
prior_arm_block_id, prior_arm_block_id,
prior_arm_ty, prior_arm_ty,
prior_arm_span, prior_arm_span,
arm_block_id, arm_block_id,
arm_ty, arm_ty,
arm_span, arm_span,
); ) {
err.subdiagnostic(subdiag);
}
if let Some(ret_sp) = opt_suggest_box_span { if let Some(ret_sp) = opt_suggest_box_span {
// Get return type span and point to it. // Get return type span and point to it.
self.suggest_boxing_for_return_impl_trait( self.suggest_boxing_for_return_impl_trait(
@ -784,15 +785,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if let Some(sp) = outer_span { if let Some(sp) = outer_span {
err.span_label(sp, "`if` and `else` have incompatible types"); err.span_label(sp, "`if` and `else` have incompatible types");
} }
self.suggest_remove_semi_or_return_binding( if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(
err,
Some(then_id), Some(then_id),
then_ty, then_ty,
then_span, then_span,
Some(else_id), Some(else_id),
else_ty, else_ty,
else_span, else_span,
); ) {
err.subdiagnostic(subdiag);
}
if let Some(ret_sp) = opt_suggest_box_span { if let Some(ret_sp) = opt_suggest_box_span {
self.suggest_boxing_for_return_impl_trait( self.suggest_boxing_for_return_impl_trait(
err, err,

View File

@ -1,5 +1,8 @@
use crate::infer::{ use crate::{
error_reporting::nice_region_error::NiceRegionError, RegionResolutionError, SubregionOrigin, errors::PlaceholderRelationLfNotSatisfied,
infer::{
error_reporting::nice_region_error::NiceRegionError, RegionResolutionError, SubregionOrigin,
},
}; };
use rustc_data_structures::intern::Interned; use rustc_data_structures::intern::Interned;
use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
@ -16,8 +19,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
Region(Interned(RePlaceholder(ty::Placeholder { name: sub_name, .. }), _)), Region(Interned(RePlaceholder(ty::Placeholder { name: sub_name, .. }), _)),
Region(Interned(RePlaceholder(ty::Placeholder { name: sup_name, .. }), _)), Region(Interned(RePlaceholder(ty::Placeholder { name: sup_name, .. }), _)),
)) => { )) => {
let msg = "lifetime bound not satisfied"; let span = *span;
let mut err = self.tcx().sess.struct_span_err(*span, msg);
let (sub_span, sub_symbol) = match sub_name { let (sub_span, sub_symbol) = match sub_name {
ty::BrNamed(def_id, symbol) => { ty::BrNamed(def_id, symbol) => {
(Some(self.tcx().def_span(def_id)), Some(symbol)) (Some(self.tcx().def_span(def_id)), Some(symbol))
@ -32,41 +34,47 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
ty::BrAnon(_, span) => (*span, None), ty::BrAnon(_, span) => (*span, None),
ty::BrEnv => (None, None), ty::BrEnv => (None, None),
}; };
match (sub_span, sup_span, sub_symbol, sup_symbol) { let diag = match (sub_span, sup_span, sub_symbol, sup_symbol) {
(Some(sub_span), Some(sup_span), Some(sub_symbol), Some(sup_symbol)) => { (Some(sub_span), Some(sup_span), Some(&sub_symbol), Some(&sup_symbol)) => {
err.span_note( PlaceholderRelationLfNotSatisfied::HasBoth {
span,
sub_span, sub_span,
format!("the lifetime `{sub_symbol}` defined here..."),
);
err.span_note(
sup_span, sup_span,
format!("...must outlive the lifetime `{sup_symbol}` defined here"), sub_symbol,
); sup_symbol,
note: (),
}
} }
(Some(sub_span), Some(sup_span), _, Some(sup_symbol)) => { (Some(sub_span), Some(sup_span), _, Some(&sup_symbol)) => {
err.span_note(sub_span, "the lifetime defined here..."); PlaceholderRelationLfNotSatisfied::HasSup {
err.span_note( span,
sup_span,
format!("...must outlive the lifetime `{sup_symbol}` defined here"),
);
}
(Some(sub_span), Some(sup_span), Some(sub_symbol), _) => {
err.span_note(
sub_span, sub_span,
format!("the lifetime `{sub_symbol}` defined here..."), sup_span,
); sup_symbol,
err.span_note(sup_span, "...must outlive the lifetime defined here"); note: (),
}
}
(Some(sub_span), Some(sup_span), Some(&sub_symbol), _) => {
PlaceholderRelationLfNotSatisfied::HasSub {
span,
sub_span,
sup_span,
sub_symbol,
note: (),
}
} }
(Some(sub_span), Some(sup_span), _, _) => { (Some(sub_span), Some(sup_span), _, _) => {
err.span_note(sub_span, "the lifetime defined here..."); PlaceholderRelationLfNotSatisfied::HasNone {
err.span_note(sup_span, "...must outlive the lifetime defined here"); span,
sub_span,
sup_span,
note: (),
}
} }
_ => {} _ => PlaceholderRelationLfNotSatisfied::OnlyPrimarySpan { span, note: () },
} };
err.note("this is a known limitation that will be removed in the future (see issue #100013 <https://github.com/rust-lang/rust/issues/100013> for more information)"); Some(self.tcx().sess.create_err(diag))
Some(err)
} }
_ => None, _ => None,
} }
} }

View File

@ -1,9 +1,11 @@
use crate::errors::RegionOriginNote; use crate::errors::{
note_and_explain, FullfillReqLifetime, LfBoundNotSatisfied, OutlivesBound, OutlivesContent,
RefLongerThanData, RegionOriginNote, WhereClauseSuggestions,
};
use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt}; use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
use crate::infer::{self, SubregionOrigin}; use crate::infer::{self, SubregionOrigin};
use rustc_errors::{ use rustc_errors::{
fluent, struct_span_err, AddToDiagnostic, Applicability, Diagnostic, DiagnosticBuilder, fluent, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic,
ErrorGuaranteed,
}; };
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_middle::traits::ObligationCauseCode; use rustc_middle::traits::ObligationCauseCode;
@ -119,130 +121,105 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
err err
} }
infer::Reborrow(span) => { infer::Reborrow(span) => {
let mut err = struct_span_err!( let reference_valid = note_and_explain::RegionExplanation::new(
self.tcx.sess,
span,
E0312,
"lifetime of reference outlives lifetime of borrowed content..."
);
note_and_explain_region(
self.tcx, self.tcx,
&mut err,
"...the reference is valid for ",
sub, sub,
"...",
None, None,
note_and_explain::PrefixKind::RefValidFor,
note_and_explain::SuffixKind::Continues,
); );
note_and_explain_region( let content_valid = note_and_explain::RegionExplanation::new(
self.tcx, self.tcx,
&mut err,
"...but the borrowed content is only valid for ",
sup, sup,
"",
None, None,
note_and_explain::PrefixKind::ContentValidFor,
note_and_explain::SuffixKind::Empty,
); );
err OutlivesContent {
span,
notes: reference_valid.into_iter().chain(content_valid).collect(),
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
} }
infer::RelateObjectBound(span) => { infer::RelateObjectBound(span) => {
let mut err = struct_span_err!( let object_valid = note_and_explain::RegionExplanation::new(
self.tcx.sess,
span,
E0476,
"lifetime of the source pointer does not outlive lifetime bound of the \
object type"
);
note_and_explain_region(
self.tcx, self.tcx,
&mut err,
"object type is valid for ",
sub, sub,
"",
None, None,
note_and_explain::PrefixKind::TypeObjValidFor,
note_and_explain::SuffixKind::Empty,
); );
note_and_explain_region( let pointer_valid = note_and_explain::RegionExplanation::new(
self.tcx, self.tcx,
&mut err,
"source pointer is only valid for ",
sup, sup,
"",
None, None,
note_and_explain::PrefixKind::SourcePointerValidFor,
note_and_explain::SuffixKind::Empty,
); );
err OutlivesBound {
span,
notes: object_valid.into_iter().chain(pointer_valid).collect(),
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
} }
infer::RelateParamBound(span, ty, opt_span) => { infer::RelateParamBound(span, ty, opt_span) => {
let mut err = struct_span_err!( let prefix = match *sub {
self.tcx.sess, ty::ReStatic => note_and_explain::PrefixKind::TypeSatisfy,
span, _ => note_and_explain::PrefixKind::TypeOutlive,
E0477, };
"the type `{}` does not fulfill the required lifetime", let suffix = if opt_span.is_some() {
self.ty_to_string(ty) note_and_explain::SuffixKind::ReqByBinding
} else {
note_and_explain::SuffixKind::Empty
};
let note = note_and_explain::RegionExplanation::new(
self.tcx, sub, opt_span, prefix, suffix,
); );
match *sub { FullfillReqLifetime { span, ty: self.resolve_vars_if_possible(ty), note }
ty::ReStatic => note_and_explain_region( .into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
self.tcx,
&mut err,
"type must satisfy ",
sub,
if opt_span.is_some() { " as required by this binding" } else { "" },
opt_span,
),
_ => note_and_explain_region(
self.tcx,
&mut err,
"type must outlive ",
sub,
if opt_span.is_some() { " as required by this binding" } else { "" },
opt_span,
),
}
err
} }
infer::RelateRegionParamBound(span) => { infer::RelateRegionParamBound(span) => {
let mut err = let param_instantiated = note_and_explain::RegionExplanation::new(
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
note_and_explain_region(
self.tcx, self.tcx,
&mut err,
"lifetime parameter instantiated with ",
sup, sup,
"",
None, None,
note_and_explain::PrefixKind::LfParamInstantiatedWith,
note_and_explain::SuffixKind::Empty,
); );
note_and_explain_region( let param_must_outlive = note_and_explain::RegionExplanation::new(
self.tcx, self.tcx,
&mut err,
"but lifetime parameter must outlive ",
sub, sub,
"",
None, None,
note_and_explain::PrefixKind::LfParamMustOutlive,
note_and_explain::SuffixKind::Empty,
); );
err LfBoundNotSatisfied {
span,
notes: param_instantiated.into_iter().chain(param_must_outlive).collect(),
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
} }
infer::ReferenceOutlivesReferent(ty, span) => { infer::ReferenceOutlivesReferent(ty, span) => {
let mut err = struct_span_err!( let pointer_valid = note_and_explain::RegionExplanation::new(
self.tcx.sess,
span,
E0491,
"in type `{}`, reference has a longer lifetime than the data it references",
self.ty_to_string(ty)
);
note_and_explain_region(
self.tcx, self.tcx,
&mut err,
"the pointer is valid for ",
sub, sub,
"",
None, None,
note_and_explain::PrefixKind::PointerValidFor,
note_and_explain::SuffixKind::Empty,
); );
note_and_explain_region( let data_valid = note_and_explain::RegionExplanation::new(
self.tcx, self.tcx,
&mut err,
"but the referenced data is only valid for ",
sup, sup,
"",
None, None,
note_and_explain::PrefixKind::DataValidFor,
note_and_explain::SuffixKind::Empty,
); );
err RefLongerThanData {
span,
ty: self.resolve_vars_if_possible(ty),
notes: pointer_valid.into_iter().chain(data_valid).collect(),
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
} }
infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => { infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => {
let mut err = self.report_extra_impl_obligation( let mut err = self.report_extra_impl_obligation(
@ -279,25 +256,25 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
err err
} }
infer::AscribeUserTypeProvePredicate(span) => { infer::AscribeUserTypeProvePredicate(span) => {
let mut err = let instantiated = note_and_explain::RegionExplanation::new(
struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
note_and_explain_region(
self.tcx, self.tcx,
&mut err,
"lifetime instantiated with ",
sup, sup,
"",
None, None,
note_and_explain::PrefixKind::LfInstantiatedWith,
note_and_explain::SuffixKind::Empty,
); );
note_and_explain_region( let must_outlive = note_and_explain::RegionExplanation::new(
self.tcx, self.tcx,
&mut err,
"but lifetime must outlive ",
sub, sub,
"",
None, None,
note_and_explain::PrefixKind::LfMustOutlive,
note_and_explain::SuffixKind::Empty,
); );
err LfBoundNotSatisfied {
span,
notes: instantiated.into_iter().chain(must_outlive).collect(),
}
.into_diagnostic(&self.tcx.sess.parse_sess.span_diagnostic)
} }
}; };
if sub.is_error() || sup.is_error() { if sub.is_error() || sup.is_error() {
@ -347,22 +324,17 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { return; }; let Some(generics) = self.tcx.hir().get_generics(impl_item_def_id) else { return; };
if trait_predicates.is_empty() { let suggestion = if trait_predicates.is_empty() {
err.span_suggestion_verbose( WhereClauseSuggestions::Remove { span: generics.where_clause_span }
generics.where_clause_span,
"remove the `where` clause",
String::new(),
Applicability::MachineApplicable,
);
} else { } else {
let space = if generics.where_clause_span.is_empty() { " " } else { "" }; let space = if generics.where_clause_span.is_empty() { " " } else { "" };
err.span_suggestion_verbose( WhereClauseSuggestions::CopyPredicates {
generics.where_clause_span, span: generics.where_clause_span,
"copy the `where` clause predicates from the trait", space,
format!("{space}where {}", trait_predicates.join(", ")), trait_predicates: trait_predicates.join(", "),
Applicability::MachineApplicable, }
); };
} err.subdiagnostic(suggestion);
} }
pub(super) fn report_placeholder_failure( pub(super) fn report_placeholder_failure(

View File

@ -11,21 +11,22 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable}; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
use rustc_span::{sym, BytePos, Span}; use rustc_span::{sym, BytePos, Span};
use crate::errors::SuggAddLetForLetChains; use crate::errors::{
ConsiderAddingAwait, SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding,
};
use super::TypeErrCtxt; use super::TypeErrCtxt;
impl<'tcx> TypeErrCtxt<'_, 'tcx> { impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub(super) fn suggest_remove_semi_or_return_binding( pub(super) fn suggest_remove_semi_or_return_binding(
&self, &self,
err: &mut Diagnostic,
first_id: Option<hir::HirId>, first_id: Option<hir::HirId>,
first_ty: Ty<'tcx>, first_ty: Ty<'tcx>,
first_span: Span, first_span: Span,
second_id: Option<hir::HirId>, second_id: Option<hir::HirId>,
second_ty: Ty<'tcx>, second_ty: Ty<'tcx>,
second_span: Span, second_span: Span,
) { ) -> Option<SuggestRemoveSemiOrReturnBinding> {
let remove_semicolon = [ let remove_semicolon = [
(first_id, self.resolve_vars_if_possible(second_ty)), (first_id, self.resolve_vars_if_possible(second_ty)),
(second_id, self.resolve_vars_if_possible(first_ty)), (second_id, self.resolve_vars_if_possible(first_ty)),
@ -37,35 +38,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}); });
match remove_semicolon { match remove_semicolon {
Some((sp, StatementAsExpression::NeedsBoxing)) => { Some((sp, StatementAsExpression::NeedsBoxing)) => {
err.multipart_suggestion( Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
"consider removing this semicolon and boxing the expressions", first_lo: first_span.shrink_to_lo(),
vec![ first_hi: first_span.shrink_to_hi(),
(first_span.shrink_to_lo(), "Box::new(".to_string()), second_lo: second_span.shrink_to_lo(),
(first_span.shrink_to_hi(), ")".to_string()), second_hi: second_span.shrink_to_hi(),
(second_span.shrink_to_lo(), "Box::new(".to_string()), sp,
(second_span.shrink_to_hi(), ")".to_string()), })
(sp, String::new()),
],
Applicability::MachineApplicable,
);
} }
Some((sp, StatementAsExpression::CorrectType)) => { Some((sp, StatementAsExpression::CorrectType)) => {
err.span_suggestion_short( Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
sp,
"consider removing this semicolon",
"",
Applicability::MachineApplicable,
);
} }
None => { None => {
let mut ret = None;
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
if let Some(id) = id if let Some(id) = id
&& let hir::Node::Block(blk) = self.tcx.hir().get(id) && let hir::Node::Block(blk) = self.tcx.hir().get(id)
&& self.consider_returning_binding(blk, ty, err) && let Some(diag) = self.consider_returning_binding_diag(blk, ty)
{ {
ret = Some(diag);
break; break;
} }
} }
ret
} }
} }
} }
@ -198,7 +193,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
return; return;
} }
match ( let subdiag = match (
self.get_impl_future_output_ty(exp_found.expected), self.get_impl_future_output_ty(exp_found.expected),
self.get_impl_future_output_ty(exp_found.found), self.get_impl_future_output_ty(exp_found.found),
) { ) {
@ -207,65 +202,56 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
{ {
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
let then_span = self.find_block_span_from_hir_id(*then_id); let then_span = self.find_block_span_from_hir_id(*then_id);
diag.multipart_suggestion( Some(ConsiderAddingAwait::BothFuturesSugg {
"consider `await`ing on both `Future`s", first: then_span.shrink_to_hi(),
vec![ second: exp_span.shrink_to_hi(),
(then_span.shrink_to_hi(), ".await".to_string()), })
(exp_span.shrink_to_hi(), ".await".to_string()),
],
Applicability::MaybeIncorrect,
);
} }
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
prior_arms, prior_arms,
.. ..
}) => { }) => {
if let [.., arm_span] = &prior_arms[..] { if let [.., arm_span] = &prior_arms[..] {
diag.multipart_suggestion( Some(ConsiderAddingAwait::BothFuturesSugg {
"consider `await`ing on both `Future`s", first: arm_span.shrink_to_hi(),
vec![ second: exp_span.shrink_to_hi(),
(arm_span.shrink_to_hi(), ".await".to_string()), })
(exp_span.shrink_to_hi(), ".await".to_string()),
],
Applicability::MaybeIncorrect,
);
} else { } else {
diag.help("consider `await`ing on both `Future`s"); Some(ConsiderAddingAwait::BothFuturesHelp)
} }
} }
_ => { _ => Some(ConsiderAddingAwait::BothFuturesHelp),
diag.help("consider `await`ing on both `Future`s");
}
}, },
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
self.suggest_await_on_future(diag, exp_span); // FIXME: Seems like we can't have a suggestion and a note with different spans in a single subdiagnostic
diag.span_note(exp_span, "calling an async function returns a future"); diag.subdiagnostic(ConsiderAddingAwait::FutureSugg {
span: exp_span.shrink_to_hi(),
});
Some(ConsiderAddingAwait::FutureSuggNote { span: exp_span })
} }
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
{ {
ObligationCauseCode::Pattern { span: Some(then_span), .. } => { ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
self.suggest_await_on_future(diag, then_span.shrink_to_hi()); Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
} }
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
let then_span = self.find_block_span_from_hir_id(*then_id); let then_span = self.find_block_span_from_hir_id(*then_id);
self.suggest_await_on_future(diag, then_span.shrink_to_hi()); Some(ConsiderAddingAwait::FutureSugg { span: then_span.shrink_to_hi() })
} }
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
ref prior_arms, ref prior_arms,
.. ..
}) => { }) => Some({
diag.multipart_suggestion_verbose( ConsiderAddingAwait::FutureSuggMultiple {
"consider `await`ing on the `Future`", spans: prior_arms.iter().map(|arm| arm.shrink_to_hi()).collect(),
prior_arms }
.iter() }),
.map(|arm| (arm.shrink_to_hi(), ".await".to_string())) _ => None,
.collect(),
Applicability::MaybeIncorrect,
);
}
_ => {}
}, },
_ => {} _ => None,
};
if let Some(subdiag) = subdiag {
diag.subdiagnostic(subdiag);
} }
} }
@ -655,16 +641,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
/// Suggest returning a local binding with a compatible type if the block /// Suggest returning a local binding with a compatible type if the block
/// has no return expression. /// has no return expression.
pub fn consider_returning_binding( pub fn consider_returning_binding_diag(
&self, &self,
blk: &'tcx hir::Block<'tcx>, blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>, expected_ty: Ty<'tcx>,
err: &mut Diagnostic, ) -> Option<SuggestRemoveSemiOrReturnBinding> {
) -> bool {
let blk = blk.innermost_block(); let blk = blk.innermost_block();
// Do not suggest if we have a tail expr. // Do not suggest if we have a tail expr.
if blk.expr.is_some() { if blk.expr.is_some() {
return false; return None;
} }
let mut shadowed = FxIndexSet::default(); let mut shadowed = FxIndexSet::default();
let mut candidate_idents = vec![]; let mut candidate_idents = vec![];
@ -733,7 +718,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
match &candidate_idents[..] { match &candidate_idents[..] {
[(ident, _ty)] => { [(ident, _ty)] => {
let sm = self.tcx.sess.source_map(); let sm = self.tcx.sess.source_map();
if let Some(stmt) = blk.stmts.last() { let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
let stmt_span = sm.stmt_span(stmt.span, blk.span); let stmt_span = sm.stmt_span(stmt.span, blk.span);
let sugg = if sm.is_multiline(blk.span) let sugg = if sm.is_multiline(blk.span)
&& let Some(spacing) = sm.indentation_before(stmt_span) && let Some(spacing) = sm.indentation_before(stmt_span)
@ -742,12 +727,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
} else { } else {
format!(" {ident}") format!(" {ident}")
}; };
err.span_suggestion_verbose( (stmt_span.shrink_to_hi(), sugg)
stmt_span.shrink_to_hi(),
format!("consider returning the local binding `{ident}`"),
sugg,
Applicability::MaybeIncorrect,
);
} else { } else {
let sugg = if sm.is_multiline(blk.span) let sugg = if sm.is_multiline(blk.span)
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
@ -757,21 +737,34 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!(" {ident} ") format!(" {ident} ")
}; };
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
err.span_suggestion_verbose( (
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
format!("consider returning the local binding `{ident}`"),
sugg, sugg,
Applicability::MaybeIncorrect, )
); };
} Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
true
} }
values if (1..3).contains(&values.len()) => { values if (1..3).contains(&values.len()) => {
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
err.span_note(spans, "consider returning one of these bindings"); Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
}
_ => None,
}
}
pub fn consider_returning_binding(
&self,
blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>,
err: &mut Diagnostic,
) -> bool {
let diag = self.consider_returning_binding_diag(blk, expected_ty);
match diag {
Some(diag) => {
err.subdiagnostic(diag);
true true
} }
_ => false, None => false,
} }
} }
} }