summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/error_reporting
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:18:32 +0000
commit4547b622d8d29df964fa2914213088b148c498fc (patch)
tree9fc6b25f3c3add6b745be9a2400a6e96140046e9 /compiler/rustc_infer/src/infer/error_reporting
parentReleasing progress-linux version 1.66.0+dfsg1-1~progress7.99u1. (diff)
downloadrustc-4547b622d8d29df964fa2914213088b148c498fc.tar.xz
rustc-4547b622d8d29df964fa2914213088b148c498fc.zip
Merging upstream version 1.67.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_infer/src/infer/error_reporting')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs933
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs9
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs4
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs5
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs5
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs79
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs38
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note.rs2
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/note_region.rs427
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/suggest.rs672
12 files changed, 1395 insertions, 783 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 9ff703e52..987559d7e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -1,3 +1,4 @@
+// ignore-tidy-filelength
//! Error Reporting Code for the inference engine
//!
//! Because of the way inference, and in particular region inference,
@@ -55,10 +56,9 @@ use crate::infer::ExpectedFound;
use crate::traits::error_reporting::report_object_safety_error;
use crate::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
- StatementAsExpression,
};
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
use rustc_hir as hir;
@@ -67,18 +67,19 @@ use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::Node;
use rustc_middle::dep_graph::DepContext;
-use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
use rustc_middle::ty::{
- self, error::TypeError, Binder, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
+ self, error::TypeError, List, Region, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable,
TypeVisitable,
};
use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span};
use rustc_target::spec::abi;
use std::ops::{ControlFlow, Deref};
+use std::path::PathBuf;
use std::{cmp, fmt, iter};
mod note;
+mod suggest;
pub(crate) mod need_type_info;
pub use need_type_info::TypeAnnotationNeeded;
@@ -91,6 +92,8 @@ pub mod nice_region_error;
pub struct TypeErrCtxt<'a, 'tcx> {
pub infcx: &'a InferCtxt<'tcx>,
pub typeck_results: Option<std::cell::Ref<'a, ty::TypeckResults<'tcx>>>,
+ pub normalize_fn_sig: Box<dyn Fn(ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> + 'a>,
+ pub fallback_has_occurred: bool,
}
impl TypeErrCtxt<'_, '_> {
@@ -206,9 +209,12 @@ fn msg_span_from_early_bound_and_free_regions<'tcx>(
};
(text, sp)
}
- ty::BrAnon(idx) => (
+ ty::BrAnon(idx, span) => (
format!("the anonymous lifetime #{} defined here", idx + 1),
- tcx.def_span(scope)
+ match span {
+ Some(span) => span,
+ None => tcx.def_span(scope)
+ }
),
_ => (
format!("the lifetime `{}` as defined here", region),
@@ -253,6 +259,7 @@ fn label_msg_span(
}
}
+#[instrument(level = "trace", skip(tcx))]
pub fn unexpected_hidden_region_diagnostic<'tcx>(
tcx: TyCtxt<'tcx>,
span: Span,
@@ -330,33 +337,36 @@ pub fn unexpected_hidden_region_diagnostic<'tcx>(
}
impl<'tcx> InferCtxt<'tcx> {
- pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> {
- if let ty::Opaque(def_id, substs) = ty.kind() {
- let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
- // Future::Output
- let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
+ pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
+ let (def_id, substs) = match *ty.kind() {
+ ty::Opaque(def_id, substs) => (def_id, substs),
+ ty::Projection(data)
+ if self.tcx.def_kind(data.item_def_id) == DefKind::ImplTraitPlaceholder =>
+ {
+ (data.item_def_id, data.substs)
+ }
+ _ => return None,
+ };
- let bounds = self.tcx.bound_explicit_item_bounds(*def_id);
+ let future_trait = self.tcx.require_lang_item(LangItem::Future, None);
+ let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0];
- for (predicate, _) in bounds.subst_iter_copied(self.tcx, substs) {
- let output = predicate
+ self.tcx.bound_explicit_item_bounds(def_id).subst_iter_copied(self.tcx, substs).find_map(
+ |(predicate, _)| {
+ predicate
.kind()
.map_bound(|kind| match kind {
- ty::PredicateKind::Projection(projection_predicate)
+ ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate))
if projection_predicate.projection_ty.item_def_id == item_def_id =>
{
projection_predicate.term.ty()
}
_ => None,
})
- .transpose();
- if output.is_some() {
- // We don't account for multiple `Future::Output = Ty` constraints.
- return output;
- }
- }
- }
- None
+ .no_bound_vars()
+ .flatten()
+ },
+ )
}
}
@@ -534,7 +544,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn print_dyn_existential(
self,
- _predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
+ _predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
) -> Result<Self::DynExistential, Self::Error> {
Err(NonTrivialPath)
}
@@ -793,87 +803,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
- fn suggest_remove_semi_or_return_binding(
- &self,
- err: &mut Diagnostic,
- first_id: Option<hir::HirId>,
- first_ty: Ty<'tcx>,
- first_span: Span,
- second_id: Option<hir::HirId>,
- second_ty: Ty<'tcx>,
- second_span: Span,
- ) {
- let remove_semicolon = [
- (first_id, self.resolve_vars_if_possible(second_ty)),
- (second_id, self.resolve_vars_if_possible(first_ty)),
- ]
- .into_iter()
- .find_map(|(id, ty)| {
- let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
- self.could_remove_semicolon(blk, ty)
- });
- match remove_semicolon {
- Some((sp, StatementAsExpression::NeedsBoxing)) => {
- err.multipart_suggestion(
- "consider removing this semicolon and boxing the expressions",
- vec![
- (first_span.shrink_to_lo(), "Box::new(".to_string()),
- (first_span.shrink_to_hi(), ")".to_string()),
- (second_span.shrink_to_lo(), "Box::new(".to_string()),
- (second_span.shrink_to_hi(), ")".to_string()),
- (sp, String::new()),
- ],
- Applicability::MachineApplicable,
- );
- }
- Some((sp, StatementAsExpression::CorrectType)) => {
- err.span_suggestion_short(
- sp,
- "consider removing this semicolon",
- "",
- Applicability::MachineApplicable,
- );
- }
- None => {
- for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
- if let Some(id) = id
- && let hir::Node::Block(blk) = self.tcx.hir().get(id)
- && self.consider_returning_binding(blk, ty, err)
- {
- break;
- }
- }
- }
- }
- }
-
- fn suggest_boxing_for_return_impl_trait(
- &self,
- err: &mut Diagnostic,
- return_sp: Span,
- arm_spans: impl Iterator<Item = Span>,
- ) {
- err.multipart_suggestion(
- "you could change the return type to be a boxed trait object",
- vec![
- (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
- (return_sp.shrink_to_hi(), ">".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- let sugg = arm_spans
- .flat_map(|sp| {
- [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
- .into_iter()
- })
- .collect::<Vec<_>>();
- err.multipart_suggestion(
- "if you change the return type to expect trait objects, box the returned expressions",
- sugg,
- Applicability::MaybeIncorrect,
- );
- }
-
/// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
/// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
/// populate `other_value` with `other_ty`.
@@ -1003,22 +932,14 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
- fn normalize_fn_sig_for_diagnostic(&self, sig: ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx> {
- if let Some(normalize) = &self.normalize_fn_sig_for_diagnostic {
- normalize(self, sig)
- } else {
- sig
- }
- }
-
/// Given two `fn` signatures highlight only sub-parts that are different.
fn cmp_fn_sig(
&self,
sig1: &ty::PolyFnSig<'tcx>,
sig2: &ty::PolyFnSig<'tcx>,
) -> (DiagnosticStyledString, DiagnosticStyledString) {
- let sig1 = &self.normalize_fn_sig_for_diagnostic(*sig1);
- let sig2 = &self.normalize_fn_sig_for_diagnostic(*sig2);
+ let sig1 = &(self.normalize_fn_sig)(*sig1);
+ let sig2 = &(self.normalize_fn_sig)(*sig2);
let get_lifetimes = |sig| {
use rustc_hir::def::Namespace;
@@ -1258,7 +1179,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let num_display_types = consts_offset - regions_len;
for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() {
let i = i + regions_len;
- if ta1 == ta2 {
+ if ta1 == ta2 && !self.tcx.sess.verbose() {
values.0.push_normal("_");
values.1.push_normal("_");
} else {
@@ -1274,7 +1195,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
let const_arguments = sub1.consts().zip(sub2.consts());
for (i, (ca1, ca2)) in const_arguments.enumerate() {
let i = i + consts_offset;
- if ca1 == ca2 {
+ if ca1 == ca2 && !self.tcx.sess.verbose() {
values.0.push_normal("_");
values.1.push_normal("_");
} else {
@@ -1347,10 +1268,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
.map(|(mod_str, _)| mod_str.len() + separator_len)
.sum();
- debug!(
- "cmp: separator_len={}, split_idx={}, min_len={}",
- separator_len, split_idx, min_len
- );
+ debug!(?separator_len, ?split_idx, ?min_len, "cmp");
if split_idx >= min_len {
// paths are identical, highlight everything
@@ -1361,7 +1279,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
} else {
let (common, uniq1) = t1_str.split_at(split_idx);
let (_, uniq2) = t2_str.split_at(split_idx);
- debug!("cmp: common={}, uniq1={}, uniq2={}", common, uniq1, uniq2);
+ debug!(?common, ?uniq1, ?uniq2, "cmp");
values.0.push_normal(common);
values.0.push_highlighted(uniq1);
@@ -1453,7 +1371,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
(ty::FnPtr(sig1), ty::FnPtr(sig2)) => self.cmp_fn_sig(sig1, sig2),
_ => {
- if t1 == t2 {
+ if t1 == t2 && !self.tcx.sess.verbose() {
// The two types are the same, elide and don't highlight.
(DiagnosticStyledString::normal("_"), DiagnosticStyledString::normal("_"))
} else {
@@ -1498,9 +1416,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
values = None;
}
struct OpaqueTypesVisitor<'tcx> {
- types: FxHashMap<TyCategory, FxHashSet<Span>>,
- expected: FxHashMap<TyCategory, FxHashSet<Span>>,
- found: FxHashMap<TyCategory, FxHashSet<Span>>,
+ types: FxIndexMap<TyCategory, FxIndexSet<Span>>,
+ expected: FxIndexMap<TyCategory, FxIndexSet<Span>>,
+ found: FxIndexMap<TyCategory, FxIndexSet<Span>>,
ignore_span: Span,
tcx: TyCtxt<'tcx>,
}
@@ -1538,7 +1456,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
&self,
err: &mut Diagnostic,
target: &str,
- types: &FxHashMap<TyCategory, FxHashSet<Span>>,
+ types: &FxIndexMap<TyCategory, FxIndexSet<Span>>,
) {
for (key, values) in types.iter() {
let count = values.len();
@@ -1654,55 +1572,46 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
ValuePairs::Regions(_) => (false, Mismatch::Fixed("lifetime")),
};
- let vals = match self.values_str(values) {
- Some((expected, found)) => Some((expected, found)),
- None => {
- // Derived error. Cancel the emitter.
- // NOTE(eddyb) this was `.cancel()`, but `diag`
- // is borrowed, so we can't fully defuse it.
- diag.downgrade_to_delayed_bug();
- return;
- }
+ let Some(vals) = self.values_str(values) else {
+ // Derived error. Cancel the emitter.
+ // NOTE(eddyb) this was `.cancel()`, but `diag`
+ // is borrowed, so we can't fully defuse it.
+ diag.downgrade_to_delayed_bug();
+ return;
};
- (vals, exp_found, is_simple_error, Some(values))
+ (Some(vals), exp_found, is_simple_error, Some(values))
}
};
- match terr {
- // Ignore msg for object safe coercion
- // since E0038 message will be printed
- TypeError::ObjectUnsafeCoercion(_) => {}
- _ => {
- let mut label_or_note = |span: Span, msg: &str| {
- if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {
- diag.span_label(span, msg);
- } else {
- diag.span_note(span, msg);
- }
- };
- if let Some((sp, msg)) = secondary_span {
- if swap_secondary_and_primary {
- let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound {
- expected,
- ..
- })) = values
- {
- format!("expected this to be `{}`", expected)
- } else {
- terr.to_string()
- };
- label_or_note(sp, &terr);
- label_or_note(span, &msg);
- } else {
- label_or_note(span, &terr.to_string());
- label_or_note(sp, &msg);
- }
- } else {
- label_or_note(span, &terr.to_string());
- }
+ let mut label_or_note = |span: Span, msg: &str| {
+ if (prefer_label && is_simple_error) || &[span] == diag.span.primary_spans() {
+ diag.span_label(span, msg);
+ } else {
+ diag.span_note(span, msg);
}
};
- if let Some((expected, found)) = expected_found {
+ if let Some((sp, msg)) = secondary_span {
+ if swap_secondary_and_primary {
+ let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound {
+ expected,
+ ..
+ })) = values
+ {
+ format!("expected this to be `{}`", expected)
+ } else {
+ terr.to_string()
+ };
+ label_or_note(sp, &terr);
+ label_or_note(span, &msg);
+ } else {
+ label_or_note(span, &terr.to_string());
+ label_or_note(sp, &msg);
+ }
+ } else {
+ label_or_note(span, &terr.to_string());
+ }
+
+ if let Some((expected, found, exp_p, found_p)) = expected_found {
let (expected_label, found_label, exp_found) = match exp_found {
Mismatch::Variable(ef) => (
ef.expected.prefix_string(self.tcx),
@@ -1819,32 +1728,41 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
TypeError::Sorts(values) => {
let extra = expected == found;
- let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) {
- (true, ty::Opaque(def_id, _)) => {
- let sm = self.tcx.sess.source_map();
- let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo());
- format!(
- " (opaque type at <{}:{}:{}>)",
- sm.filename_for_diagnostics(&pos.file.name),
- pos.line,
- pos.col.to_usize() + 1,
- )
- }
- (true, ty::Projection(proj))
- if self.tcx.def_kind(proj.item_def_id)
- == DefKind::ImplTraitPlaceholder =>
- {
- let sm = self.tcx.sess.source_map();
- let pos = sm.lookup_char_pos(self.tcx.def_span(proj.item_def_id).lo());
- format!(
- " (trait associated opaque type at <{}:{}:{}>)",
- sm.filename_for_diagnostics(&pos.file.name),
- pos.line,
- pos.col.to_usize() + 1,
- )
+ let sort_string = |ty: Ty<'tcx>, path: Option<PathBuf>| {
+ let mut s = match (extra, ty.kind()) {
+ (true, ty::Opaque(def_id, _)) => {
+ let sm = self.tcx.sess.source_map();
+ let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo());
+ format!(
+ " (opaque type at <{}:{}:{}>)",
+ sm.filename_for_diagnostics(&pos.file.name),
+ pos.line,
+ pos.col.to_usize() + 1,
+ )
+ }
+ (true, ty::Projection(proj))
+ if self.tcx.def_kind(proj.item_def_id)
+ == DefKind::ImplTraitPlaceholder =>
+ {
+ let sm = self.tcx.sess.source_map();
+ let pos = sm.lookup_char_pos(self.tcx.def_span(proj.item_def_id).lo());
+ format!(
+ " (trait associated opaque type at <{}:{}:{}>)",
+ sm.filename_for_diagnostics(&pos.file.name),
+ pos.line,
+ pos.col.to_usize() + 1,
+ )
+ }
+ (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
+ (false, _) => "".to_string(),
+ };
+ if let Some(path) = path {
+ s.push_str(&format!(
+ "\nthe full type name has been written to '{}'",
+ path.display(),
+ ));
}
- (true, _) => format!(" ({})", ty.sort_string(self.tcx)),
- (false, _) => "".to_string(),
+ s
};
if !(values.expected.is_simple_text() && values.found.is_simple_text())
|| (exp_found.map_or(false, |ef| {
@@ -1866,14 +1784,11 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
expected,
&found_label,
found,
- &sort_string(values.expected),
- &sort_string(values.found),
+ &sort_string(values.expected, exp_p),
+ &sort_string(values.found, found_p),
);
}
}
- TypeError::ObjectUnsafeCoercion(_) => {
- diag.note_unsuccessful_coercion(found, expected);
- }
_ => {
debug!(
"note_type_err: exp_found={:?}, expected={:?} found={:?}",
@@ -1944,310 +1859,6 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
debug!(?diag);
}
- fn suggest_tuple_pattern(
- &self,
- cause: &ObligationCause<'tcx>,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
- // some modifications due to that being in typeck and this being in infer.
- if let ObligationCauseCode::Pattern { .. } = cause.code() {
- if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
- let compatible_variants: Vec<_> = expected_adt
- .variants()
- .iter()
- .filter(|variant| {
- variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn
- })
- .filter_map(|variant| {
- let sole_field = &variant.fields[0];
- let sole_field_ty = sole_field.ty(self.tcx, substs);
- if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
- let variant_path =
- with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
- // FIXME #56861: DRYer prelude filtering
- if let Some(path) = variant_path.strip_prefix("std::prelude::") {
- if let Some((_, path)) = path.split_once("::") {
- return Some(path.to_string());
- }
- }
- Some(variant_path)
- } else {
- None
- }
- })
- .collect();
- match &compatible_variants[..] {
- [] => {}
- [variant] => {
- diag.multipart_suggestion_verbose(
- &format!("try wrapping the pattern in `{}`", variant),
- vec![
- (cause.span.shrink_to_lo(), format!("{}(", variant)),
- (cause.span.shrink_to_hi(), ")".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- _ => {
- // More than one matching variant.
- diag.multipart_suggestions(
- &format!(
- "try wrapping the pattern in a variant of `{}`",
- self.tcx.def_path_str(expected_adt.did())
- ),
- compatible_variants.into_iter().map(|variant| {
- vec![
- (cause.span.shrink_to_lo(), format!("{}(", variant)),
- (cause.span.shrink_to_hi(), ")".to_string()),
- ]
- }),
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
- }
-
- /// A possible error is to forget to add `.await` when using futures:
- ///
- /// ```compile_fail,E0308
- /// async fn make_u32() -> u32 {
- /// 22
- /// }
- ///
- /// fn take_u32(x: u32) {}
- ///
- /// async fn foo() {
- /// let x = make_u32();
- /// take_u32(x);
- /// }
- /// ```
- ///
- /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
- /// expected type. If this is the case, and we are inside of an async body, it suggests adding
- /// `.await` to the tail of the expression.
- fn suggest_await_on_expect_found(
- &self,
- cause: &ObligationCause<'tcx>,
- exp_span: Span,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- debug!(
- "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
- exp_span, exp_found.expected, exp_found.found,
- );
-
- if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
- return;
- }
-
- match (
- self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder),
- self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder),
- ) {
- (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
- .code()
- {
- ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
- let then_span = self.find_block_span_from_hir_id(*then_id);
- diag.multipart_suggestion(
- "consider `await`ing on both `Future`s",
- vec![
- (then_span.shrink_to_hi(), ".await".to_string()),
- (exp_span.shrink_to_hi(), ".await".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- }
- ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
- prior_arms,
- ..
- }) => {
- if let [.., arm_span] = &prior_arms[..] {
- diag.multipart_suggestion(
- "consider `await`ing on both `Future`s",
- vec![
- (arm_span.shrink_to_hi(), ".await".to_string()),
- (exp_span.shrink_to_hi(), ".await".to_string()),
- ],
- Applicability::MaybeIncorrect,
- );
- } else {
- diag.help("consider `await`ing on both `Future`s");
- }
- }
- _ => {
- diag.help("consider `await`ing on both `Future`s");
- }
- },
- (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
- diag.span_suggestion_verbose(
- exp_span.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
- {
- ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
- diag.span_suggestion_verbose(
- then_span.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
- let then_span = self.find_block_span_from_hir_id(*then_id);
- diag.span_suggestion_verbose(
- then_span.shrink_to_hi(),
- "consider `await`ing on the `Future`",
- ".await",
- Applicability::MaybeIncorrect,
- );
- }
- ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
- ref prior_arms,
- ..
- }) => {
- diag.multipart_suggestion_verbose(
- "consider `await`ing on the `Future`",
- prior_arms
- .iter()
- .map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
- .collect(),
- Applicability::MaybeIncorrect,
- );
- }
- _ => {}
- },
- _ => {}
- }
- }
-
- fn suggest_accessing_field_where_appropriate(
- &self,
- cause: &ObligationCause<'tcx>,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- debug!(
- "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
- cause, exp_found
- );
- if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
- if expected_def.is_enum() {
- return;
- }
-
- if let Some((name, ty)) = expected_def
- .non_enum_variant()
- .fields
- .iter()
- .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
- .map(|field| (field.name, field.ty(self.tcx, expected_substs)))
- .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
- {
- if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- let suggestion = if expected_def.is_struct() {
- format!("{}.{}", snippet, name)
- } else if expected_def.is_union() {
- format!("unsafe {{ {}.{} }}", snippet, name)
- } else {
- return;
- };
- diag.span_suggestion(
- span,
- &format!(
- "you might have meant to use field `{}` whose type is `{}`",
- name, ty
- ),
- suggestion,
- Applicability::MaybeIncorrect,
- );
- }
- }
- }
- }
- }
-
- /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
- /// suggests it.
- fn suggest_as_ref_where_appropriate(
- &self,
- span: Span,
- exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
- diag: &mut Diagnostic,
- ) {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
- && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
- {
- diag.span_suggestion(
- span,
- msg,
- // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
- format!("{}.as_ref()", snippet.trim_start_matches('&')),
- Applicability::MachineApplicable,
- );
- }
- }
-
- pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
- if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
- (expected.kind(), found.kind())
- {
- if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
- if exp_def == &found_def {
- let have_as_ref = &[
- (
- sym::Option,
- "you can convert from `&Option<T>` to `Option<&T>` using \
- `.as_ref()`",
- ),
- (
- sym::Result,
- "you can convert from `&Result<T, E>` to \
- `Result<&T, &E>` using `.as_ref()`",
- ),
- ];
- if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
- self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
- }) {
- let mut show_suggestion = true;
- for (exp_ty, found_ty) in
- iter::zip(exp_substs.types(), found_substs.types())
- {
- match *exp_ty.kind() {
- ty::Ref(_, exp_ty, _) => {
- match (exp_ty.kind(), found_ty.kind()) {
- (_, ty::Param(_))
- | (_, ty::Infer(_))
- | (ty::Param(_), _)
- | (ty::Infer(_), _) => {}
- _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
- _ => show_suggestion = false,
- };
- }
- ty::Param(_) | ty::Infer(_) => {}
- _ => show_suggestion = false,
- }
- }
- if show_suggestion {
- return Some(*msg);
- }
- }
- }
- }
- }
- None
- }
-
pub fn report_and_explain_type_error(
&self,
trace: TypeTrace<'tcx>,
@@ -2332,13 +1943,18 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}
}
+ // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`,
+ // we try to suggest to add the missing `let` for `if let Some(..) = expr`
+ (ty::Bool, ty::Tuple(list)) => if list.len() == 0 {
+ self.suggest_let_for_letchains(&mut err, &trace.cause, span);
+ }
_ => {}
}
}
let code = trace.cause.code();
if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code
&& let hir::MatchSource::TryDesugar = source
- && let Some((expected_ty, found_ty)) = self.values_str(trace.values)
+ && let Some((expected_ty, found_ty, _, _)) = self.values_str(trace.values)
{
err.note(&format!(
"`?` operator cannot convert from `{}` to `{}`",
@@ -2393,7 +2009,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn values_str(
&self,
values: ValuePairs<'tcx>,
- ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+ ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)>
+ {
match values {
infer::Regions(exp_found) => self.expected_found_str(exp_found),
infer::Terms(exp_found) => self.expected_found_str_term(exp_found),
@@ -2403,7 +2020,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
found: exp_found.found.print_only_trait_path(),
};
match self.expected_found_str(pretty_exp_found) {
- Some((expected, found)) if expected == found => {
+ Some((expected, found, _, _)) if expected == found => {
self.expected_found_str(exp_found)
}
ret => ret,
@@ -2415,7 +2032,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
found: exp_found.found.print_only_trait_path(),
};
match self.expected_found_str(pretty_exp_found) {
- Some((expected, found)) if expected == found => {
+ Some((expected, found, _, _)) if expected == found => {
self.expected_found_str(exp_found)
}
ret => ret,
@@ -2427,17 +2044,41 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn expected_found_str_term(
&self,
exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>,
- ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+ ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)>
+ {
let exp_found = self.resolve_vars_if_possible(exp_found);
if exp_found.references_error() {
return None;
}
Some(match (exp_found.expected.unpack(), exp_found.found.unpack()) {
- (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => self.cmp(expected, found),
+ (ty::TermKind::Ty(expected), ty::TermKind::Ty(found)) => {
+ let (mut exp, mut fnd) = self.cmp(expected, found);
+ // Use the terminal width as the basis to determine when to compress the printed
+ // out type, but give ourselves some leeway to avoid ending up creating a file for
+ // a type that is somewhat shorter than the path we'd write to.
+ let len = self.tcx.sess().diagnostic_width() + 40;
+ let exp_s = exp.content();
+ let fnd_s = fnd.content();
+ let mut exp_p = None;
+ let mut fnd_p = None;
+ if exp_s.len() > len {
+ let (exp_s, exp_path) = self.tcx.short_ty_string(expected);
+ exp = DiagnosticStyledString::highlighted(exp_s);
+ exp_p = exp_path;
+ }
+ if fnd_s.len() > len {
+ let (fnd_s, fnd_path) = self.tcx.short_ty_string(found);
+ fnd = DiagnosticStyledString::highlighted(fnd_s);
+ fnd_p = fnd_path;
+ }
+ (exp, fnd, exp_p, fnd_p)
+ }
_ => (
DiagnosticStyledString::highlighted(exp_found.expected.to_string()),
DiagnosticStyledString::highlighted(exp_found.found.to_string()),
+ None,
+ None,
),
})
}
@@ -2446,7 +2087,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>(
&self,
exp_found: ty::error::ExpectedFound<T>,
- ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+ ) -> Option<(DiagnosticStyledString, DiagnosticStyledString, Option<PathBuf>, Option<PathBuf>)>
+ {
let exp_found = self.resolve_vars_if_possible(exp_found);
if exp_found.references_error() {
return None;
@@ -2455,6 +2097,8 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
Some((
DiagnosticStyledString::highlighted(exp_found.expected.to_string()),
DiagnosticStyledString::highlighted(exp_found.found.to_string()),
+ None,
+ None,
))
}
@@ -2788,36 +2432,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
debug!("report_sub_sup_conflict: sup_region={:?}", sup_region);
debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin);
- if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) =
- (&sup_origin, &sub_origin)
+ if let infer::Subtype(ref sup_trace) = sup_origin
+ && let infer::Subtype(ref sub_trace) = sub_origin
+ && let Some((sup_expected, sup_found, _, _)) = self.values_str(sup_trace.values)
+ && let Some((sub_expected, sub_found, _, _)) = self.values_str(sub_trace.values)
+ && sub_expected == sup_expected
+ && sub_found == sup_found
{
- debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace);
- debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace);
- debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values);
- debug!("report_sub_sup_conflict: sub_trace.values={:?}", sub_trace.values);
-
- if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) =
- (self.values_str(sup_trace.values), self.values_str(sub_trace.values))
- {
- if sub_expected == sup_expected && sub_found == sup_found {
- note_and_explain_region(
- self.tcx,
- &mut err,
- "...but the lifetime must also be valid for ",
- sub_region,
- "...",
- None,
- );
- err.span_note(
- sup_trace.cause.span,
- &format!("...so that the {}", sup_trace.cause.as_requirement_str()),
- );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...but the lifetime must also be valid for ",
+ sub_region,
+ "...",
+ None,
+ );
+ err.span_note(
+ sup_trace.cause.span,
+ &format!("...so that the {}", sup_trace.cause.as_requirement_str()),
+ );
- err.note_expected_found(&"", sup_expected, &"", sup_found);
- err.emit();
- return;
- }
- }
+ err.note_expected_found(&"", sup_expected, &"", sup_found);
+ err.emit();
+ return;
}
self.note_region_origin(&mut err, &sup_origin);
@@ -2862,6 +2499,11 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
self.0.tcx
}
+ fn intercrate(&self) -> bool {
+ assert!(!self.0.intercrate);
+ false
+ }
+
fn param_env(&self) -> ty::ParamEnv<'tcx> {
// Unused, only for consts which we treat as always equal
ty::ParamEnv::empty()
@@ -2875,6 +2517,10 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
true
}
+ fn mark_ambiguous(&mut self) {
+ bug!()
+ }
+
fn relate_with_variance<T: relate::Relate<'tcx>>(
&mut self,
_variance: ty::Variance,
@@ -3043,7 +2689,6 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
TypeError::IntrinsicCast => {
Error0308("cannot coerce intrinsics to function pointers")
}
- TypeError::ObjectUnsafeCoercion(did) => Error0038(did),
_ => Error0308("mismatched types"),
},
}
@@ -3158,211 +2803,3 @@ impl<'tcx> InferCtxt<'tcx> {
}
}
}
-
-impl<'tcx> TypeErrCtxt<'_, 'tcx> {
- /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
- /// is enough to fix the error.
- pub fn could_remove_semicolon(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- ) -> Option<(Span, StatementAsExpression)> {
- let blk = blk.innermost_block();
- // Do not suggest if we have a tail expr.
- if blk.expr.is_some() {
- return None;
- }
- let last_stmt = blk.stmts.last()?;
- let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
- return None;
- };
- let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
- let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
- _ if last_expr_ty.references_error() => return None,
- _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
- StatementAsExpression::CorrectType
- }
- (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
- if last_def_id == exp_def_id =>
- {
- StatementAsExpression::CorrectType
- }
- (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
- debug!(
- "both opaque, likely future {:?} {:?} {:?} {:?}",
- last_def_id, last_bounds, exp_def_id, exp_bounds
- );
-
- let last_local_id = last_def_id.as_local()?;
- let exp_local_id = exp_def_id.as_local()?;
-
- match (
- &self.tcx.hir().expect_item(last_local_id).kind,
- &self.tcx.hir().expect_item(exp_local_id).kind,
- ) {
- (
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
- hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
- ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
- match (left, right) {
- (
- hir::GenericBound::Trait(tl, ml),
- hir::GenericBound::Trait(tr, mr),
- ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
- && ml == mr =>
- {
- true
- }
- (
- hir::GenericBound::LangItemTrait(langl, _, _, argsl),
- hir::GenericBound::LangItemTrait(langr, _, _, argsr),
- ) if langl == langr => {
- // FIXME: consider the bounds!
- debug!("{:?} {:?}", argsl, argsr);
- true
- }
- _ => false,
- }
- }) =>
- {
- StatementAsExpression::NeedsBoxing
- }
- _ => StatementAsExpression::CorrectType,
- }
- }
- _ => return None,
- };
- let span = if last_stmt.span.from_expansion() {
- let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
- self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
- } else {
- last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
- };
- Some((span, needs_box))
- }
-
- /// Suggest returning a local binding with a compatible type if the block
- /// has no return expression.
- pub fn consider_returning_binding(
- &self,
- blk: &'tcx hir::Block<'tcx>,
- expected_ty: Ty<'tcx>,
- err: &mut Diagnostic,
- ) -> bool {
- let blk = blk.innermost_block();
- // Do not suggest if we have a tail expr.
- if blk.expr.is_some() {
- return false;
- }
- let mut shadowed = FxHashSet::default();
- let mut candidate_idents = vec![];
- let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
- if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
- && let Some(pat_ty) = self
- .typeck_results
- .as_ref()
- .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
- {
- let pat_ty = self.resolve_vars_if_possible(pat_ty);
- if self.same_type_modulo_infer(pat_ty, expected_ty)
- && !(pat_ty, expected_ty).references_error()
- && shadowed.insert(ident.name)
- {
- candidate_idents.push((*ident, pat_ty));
- }
- }
- true
- };
-
- let hir = self.tcx.hir();
- for stmt in blk.stmts.iter().rev() {
- let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
- local.pat.walk(&mut find_compatible_candidates);
- }
- match hir.find(hir.get_parent_node(blk.hir_id)) {
- Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
- match hir.find(hir.get_parent_node(*hir_id)) {
- Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
- pat.walk(&mut find_compatible_candidates);
- }
- Some(
- hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
- | hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Fn(_, body),
- ..
- })
- | hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
- ..
- })
- | hir::Node::Expr(hir::Expr {
- kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
- ..
- }),
- ) => {
- for param in hir.body(*body).params {
- param.pat.walk(&mut find_compatible_candidates);
- }
- }
- Some(hir::Node::Expr(hir::Expr {
- kind:
- hir::ExprKind::If(
- hir::Expr { kind: hir::ExprKind::Let(let_), .. },
- then_block,
- _,
- ),
- ..
- })) if then_block.hir_id == *hir_id => {
- let_.pat.walk(&mut find_compatible_candidates);
- }
- _ => {}
- }
- }
- _ => {}
- }
-
- match &candidate_idents[..] {
- [(ident, _ty)] => {
- let sm = self.tcx.sess.source_map();
- if let Some(stmt) = blk.stmts.last() {
- let stmt_span = sm.stmt_span(stmt.span, blk.span);
- let sugg = if sm.is_multiline(blk.span)
- && let Some(spacing) = sm.indentation_before(stmt_span)
- {
- format!("\n{spacing}{ident}")
- } else {
- format!(" {ident}")
- };
- err.span_suggestion_verbose(
- stmt_span.shrink_to_hi(),
- format!("consider returning the local binding `{ident}`"),
- sugg,
- Applicability::MaybeIncorrect,
- );
- } else {
- let sugg = if sm.is_multiline(blk.span)
- && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
- {
- format!("\n{spacing} {ident}\n{spacing}")
- } else {
- format!(" {ident} ")
- };
- 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),
- format!("consider returning the local binding `{ident}`"),
- sugg,
- Applicability::MaybeIncorrect,
- );
- }
- true
- }
- values if (1..3).contains(&values.len()) => {
- let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
- err.span_note(spans, "consider returning one of these bindings");
- true
- }
- _ => false,
- }
- }
-}
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 7b3178e61..8ff1639a3 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
@@ -15,7 +15,7 @@ use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
use rustc_middle::hir::nested_filter;
use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind};
-use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer};
use rustc_middle::ty::{self, DefIdTree, InferConst};
use rustc_middle::ty::{GenericArg, GenericArgKind, SubstsRef};
@@ -508,10 +508,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
[
..,
Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ },
- ] => match mut_ {
- AutoBorrowMutability::Mut { .. } => "&mut ",
- AutoBorrowMutability::Not => "&",
- },
+ ] => hir::Mutability::from(*mut_).ref_prefix_str(),
_ => "",
};
@@ -571,7 +568,7 @@ impl<'tcx> InferCtxt<'tcx> {
&self,
kind: hir::GeneratorKind,
span: Span,
- ty: Ty<'tcx>,
+ ty: ty::Term<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let ty = self.resolve_vars_if_possible(ty);
let data = self.extract_inference_diagnostics_data(ty.into(), None);
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 c5f2a1a3f..1067ccda2 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
@@ -9,7 +9,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::ObligationCauseCode;
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{ErrorGuaranteed, MultiSpan};
use rustc_hir as hir;
use rustc_hir::intravisit::Visitor;
@@ -73,7 +73,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
// Next, let's figure out the set of trait objects with implicit static bounds
let ty = self.tcx().type_of(*impl_def_id);
- let mut v = super::static_impl_trait::TraitObjectVisitor(FxHashSet::default());
+ let mut v = super::static_impl_trait::TraitObjectVisitor(FxIndexSet::default());
v.visit_ty(ty);
let mut traits = vec![];
for matching_def_id in v.0 {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
index aaf5a7af0..8a0e332f9 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs
@@ -10,6 +10,7 @@ pub mod find_anon_type;
mod mismatched_static_lifetime;
mod named_anon_conflict;
mod placeholder_error;
+mod placeholder_relation;
mod static_impl_trait;
mod trait_impl_difference;
mod util;
@@ -52,7 +53,9 @@ impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> {
pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
// Due to the improved diagnostics returned by the MIR borrow checker, only a subset of
// the nice region errors are required when running under the MIR borrow checker.
- self.try_report_named_anon_conflict().or_else(|| self.try_report_placeholder_conflict())
+ self.try_report_named_anon_conflict()
+ .or_else(|| self.try_report_placeholder_conflict())
+ .or_else(|| self.try_report_placeholder_relation())
}
pub fn try_report(&self) -> Option<ErrorGuaranteed> {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
index 76cb76d9f..3fe7c1598 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs
@@ -68,7 +68,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
let is_impl_item = region_info.is_impl_item;
match br {
- ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) => {}
+ ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(..) => {}
_ => {
/* not an anonymous region */
debug!("try_report_named_anon_conflict: not an anonymous region");
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
index a58516829..1f554c81e 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs
@@ -399,10 +399,7 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid);
if self_ty.value.is_closure()
- && self
- .tcx()
- .fn_trait_kind_from_lang_item(expected_trait_ref.value.def_id)
- .is_some()
+ && self.tcx().is_fn_trait(expected_trait_ref.value.def_id)
{
let closure_sig = self_ty.map(|closure| {
if let ty::Closure(_, substs) = closure.kind() {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
new file mode 100644
index 000000000..c42240f21
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_relation.rs
@@ -0,0 +1,79 @@
+use crate::infer::{
+ error_reporting::nice_region_error::NiceRegionError, RegionResolutionError, SubregionOrigin,
+};
+use rustc_data_structures::intern::Interned;
+use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
+use rustc_middle::ty::{self, RePlaceholder, Region};
+
+impl<'tcx> NiceRegionError<'_, 'tcx> {
+ /// Emitted wwhen given a `ConcreteFailure` when relating two placeholders.
+ pub(super) fn try_report_placeholder_relation(
+ &self,
+ ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> {
+ match &self.error {
+ Some(RegionResolutionError::ConcreteFailure(
+ SubregionOrigin::RelateRegionParamBound(span),
+ Region(Interned(RePlaceholder(ty::Placeholder { name: sub_name, .. }), _)),
+ Region(Interned(RePlaceholder(ty::Placeholder { name: sup_name, .. }), _)),
+ )) => {
+ let msg = "lifetime bound not satisfied";
+ let mut err = self.tcx().sess.struct_span_err(*span, msg);
+ let (sub_span, sub_symbol) = match sub_name {
+ ty::BrNamed(def_id, symbol) => {
+ (Some(self.tcx().def_span(def_id)), Some(symbol))
+ }
+ ty::BrAnon(_, span) => (*span, None),
+ ty::BrEnv => (None, None),
+ };
+ let (sup_span, sup_symbol) = match sup_name {
+ ty::BrNamed(def_id, symbol) => {
+ (Some(self.tcx().def_span(def_id)), Some(symbol))
+ }
+ ty::BrAnon(_, span) => (*span, None),
+ ty::BrEnv => (None, None),
+ };
+ match (sub_span, sup_span, sub_symbol, sup_symbol) {
+ (Some(sub_span), Some(sup_span), Some(sub_symbol), Some(sup_symbol)) => {
+ err.span_note(
+ sub_span,
+ format!("the lifetime `{sub_symbol}` defined here..."),
+ );
+ err.span_note(
+ sup_span,
+ format!("...must outlive the lifetime `{sup_symbol}` defined here"),
+ );
+ }
+ (Some(sub_span), Some(sup_span), _, Some(sup_symbol)) => {
+ err.span_note(sub_span, format!("the lifetime defined here..."));
+ err.span_note(
+ 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,
+ format!("the lifetime `{sub_symbol}` defined here..."),
+ );
+ err.span_note(
+ sup_span,
+ format!("...must outlive the lifetime defined here"),
+ );
+ }
+ (Some(sub_span), Some(sup_span), _, _) => {
+ err.span_note(sub_span, format!("the lifetime defined here..."));
+ err.span_note(
+ sup_span,
+ format!("...must outlive the lifetime defined here"),
+ );
+ }
+ _ => {}
+ }
+ 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(err)
+ }
+
+ _ => None,
+ }
+ }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
index 9bf755d7f..09f9aa3c8 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs
@@ -4,7 +4,7 @@ use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::{SubregionOrigin, TypeTrace};
use crate::traits::{ObligationCauseCode, UnifyReceiverContext};
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_ty, Visitor};
@@ -236,7 +236,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
// Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static`
// lifetime as above, but called using a fully-qualified path to the method:
// `Foo::qux(bar)`.
- let mut v = TraitObjectVisitor(FxHashSet::default());
+ let mut v = TraitObjectVisitor(FxIndexSet::default());
v.visit_ty(param.param_ty);
if let Some((ident, self_ty)) =
self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0)
@@ -314,10 +314,10 @@ pub fn suggest_new_region_bound(
.iter()
.filter_map(|arg| match arg {
GenericBound::Outlives(Lifetime {
- name: LifetimeName::Static,
- span,
+ res: LifetimeName::Static,
+ ident,
..
- }) => Some(*span),
+ }) => Some(ident.span),
_ => None,
})
.next()
@@ -342,10 +342,10 @@ pub fn suggest_new_region_bound(
.bounds
.iter()
.filter_map(|arg| match arg {
- GenericBound::Outlives(Lifetime { name, span, .. })
- if name.ident().to_string() == lifetime_name =>
+ GenericBound::Outlives(Lifetime { ident, .. })
+ if ident.name.to_string() == lifetime_name =>
{
- Some(*span)
+ Some(ident.span)
}
_ => None,
})
@@ -361,8 +361,8 @@ pub fn suggest_new_region_bound(
);
}
}
- TyKind::TraitObject(_, lt, _) => match lt.name {
- LifetimeName::ImplicitObjectLifetimeDefault => {
+ TyKind::TraitObject(_, lt, _) => {
+ if let LifetimeName::ImplicitObjectLifetimeDefault = lt.res {
err.span_suggestion_verbose(
fn_return.span.shrink_to_hi(),
&format!(
@@ -374,15 +374,14 @@ pub fn suggest_new_region_bound(
&plus_lt,
Applicability::MaybeIncorrect,
);
- }
- name if name.ident().to_string() != lifetime_name => {
+ } else if lt.ident.name.to_string() != lifetime_name {
// With this check we avoid suggesting redundant bounds. This
// would happen if there are nested impl/dyn traits and only
// one of them has the bound we'd suggest already there, like
// in `impl Foo<X = dyn Bar> + '_`.
if let Some(explicit_static) = &explicit_static {
err.span_suggestion_verbose(
- lt.span,
+ lt.ident.span,
&format!("{} the trait object's {}", consider, explicit_static),
&lifetime_name,
Applicability::MaybeIncorrect,
@@ -397,8 +396,7 @@ pub fn suggest_new_region_bound(
);
}
}
- _ => {}
- },
+ }
_ => {}
}
}
@@ -408,7 +406,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
fn get_impl_ident_and_self_ty_from_trait(
&self,
def_id: DefId,
- trait_objects: &FxHashSet<DefId>,
+ trait_objects: &FxIndexSet<DefId>,
) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> {
let tcx = self.tcx();
match tcx.hir().get_if_local(def_id) {
@@ -490,7 +488,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
return false;
};
- let mut v = TraitObjectVisitor(FxHashSet::default());
+ let mut v = TraitObjectVisitor(FxIndexSet::default());
v.visit_ty(ty);
// Get the `Ident` of the method being called and the corresponding `impl` (to point at
@@ -506,7 +504,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
fn suggest_constrain_dyn_trait_in_impl(
&self,
err: &mut Diagnostic,
- found_dids: &FxHashSet<DefId>,
+ found_dids: &FxIndexSet<DefId>,
ident: Ident,
self_ty: &hir::Ty<'_>,
) -> bool {
@@ -538,7 +536,7 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
}
/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime.
-pub struct TraitObjectVisitor(pub FxHashSet<DefId>);
+pub struct TraitObjectVisitor(pub FxIndexSet<DefId>);
impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor {
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
@@ -561,7 +559,7 @@ impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> {
fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) {
if let TyKind::TraitObject(
poly_trait_refs,
- Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. },
+ Lifetime { res: LifetimeName::ImplicitObjectLifetimeDefault, .. },
_,
) = t.kind
{
diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
index f1461d701..fd26d7d29 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs
@@ -149,6 +149,8 @@ impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
region: ty::BoundRegionKind,
) -> bool {
let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty);
+ // We are only checking is any region meets the condition so order doesn't matter
+ #[allow(rustc::potential_query_instability)]
late_bound_regions.iter().any(|r| *r == region)
}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs
index 41b115f33..d2dffa4a0 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/note.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs
@@ -16,7 +16,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
span: trace.cause.span,
requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
- expected_found: self.values_str(trace.values),
+ expected_found: self.values_str(trace.values).map(|(e, f, _, _)| (e, f)),
}
.add_to_diagnostic(err),
infer::Reborrow(span) => {
diff --git a/compiler/rustc_infer/src/infer/error_reporting/note_region.rs b/compiler/rustc_infer/src/infer/error_reporting/note_region.rs
new file mode 100644
index 000000000..41b115f33
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/note_region.rs
@@ -0,0 +1,427 @@
+use crate::errors::RegionOriginNote;
+use crate::infer::error_reporting::{note_and_explain_region, TypeErrCtxt};
+use crate::infer::{self, SubregionOrigin};
+use rustc_errors::{
+ fluent, struct_span_err, AddToDiagnostic, Diagnostic, DiagnosticBuilder, ErrorGuaranteed,
+};
+use rustc_middle::traits::ObligationCauseCode;
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::{self, Region};
+
+use super::ObligationCauseAsDiagArg;
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) {
+ match *origin {
+ infer::Subtype(ref trace) => RegionOriginNote::WithRequirement {
+ span: trace.cause.span,
+ requirement: ObligationCauseAsDiagArg(trace.cause.clone()),
+ expected_found: self.values_str(trace.values),
+ }
+ .add_to_diagnostic(err),
+ infer::Reborrow(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_reborrow }.add_to_diagnostic(err)
+ }
+ infer::ReborrowUpvar(span, ref upvar_id) => {
+ let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_reborrow,
+ name: &var_name.to_string(),
+ continues: false,
+ }
+ .add_to_diagnostic(err);
+ }
+ infer::RelateObjectBound(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_object_bound }
+ .add_to_diagnostic(err);
+ }
+ infer::DataBorrowed(ty, span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_data_borrowed,
+ name: &self.ty_to_string(ty),
+ continues: false,
+ }
+ .add_to_diagnostic(err);
+ }
+ infer::ReferenceOutlivesReferent(ty, span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_reference_outlives_referent,
+ name: &self.ty_to_string(ty),
+ continues: false,
+ }
+ .add_to_diagnostic(err);
+ }
+ infer::RelateParamBound(span, ty, opt_span) => {
+ RegionOriginNote::WithName {
+ span,
+ msg: fluent::infer_relate_param_bound,
+ name: &self.ty_to_string(ty),
+ continues: opt_span.is_some(),
+ }
+ .add_to_diagnostic(err);
+ if let Some(span) = opt_span {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_param_bound_2 }
+ .add_to_diagnostic(err);
+ }
+ }
+ infer::RelateRegionParamBound(span) => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_relate_region_param_bound }
+ .add_to_diagnostic(err);
+ }
+ infer::CompareImplItemObligation { span, .. } => {
+ RegionOriginNote::Plain { span, msg: fluent::infer_compare_impl_item_obligation }
+ .add_to_diagnostic(err);
+ }
+ infer::CheckAssociatedTypeBounds { ref parent, .. } => {
+ self.note_region_origin(err, &parent);
+ }
+ infer::AscribeUserTypeProvePredicate(span) => {
+ RegionOriginNote::Plain {
+ span,
+ msg: fluent::infer_ascribe_user_type_prove_predicate,
+ }
+ .add_to_diagnostic(err);
+ }
+ }
+ }
+
+ pub(super) fn report_concrete_failure(
+ &self,
+ origin: SubregionOrigin<'tcx>,
+ sub: Region<'tcx>,
+ sup: Region<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ match origin {
+ infer::Subtype(box trace) => {
+ let terr = TypeError::RegionsDoesNotOutlive(sup, sub);
+ let mut err = self.report_and_explain_type_error(trace, terr);
+ match (*sub, *sup) {
+ (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {}
+ (ty::RePlaceholder(_), _) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "",
+ sup,
+ " doesn't meet the lifetime requirements",
+ None,
+ );
+ }
+ (_, ty::RePlaceholder(_)) => {
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the required lifetime does not necessarily outlive ",
+ sub,
+ "",
+ None,
+ );
+ }
+ _ => {
+ note_and_explain_region(self.tcx, &mut err, "", sup, "...", None);
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...does not necessarily outlive ",
+ sub,
+ "",
+ None,
+ );
+ }
+ }
+ err
+ }
+ infer::Reborrow(span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0312,
+ "lifetime of reference outlives lifetime of borrowed content..."
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...the reference is valid for ",
+ sub,
+ "...",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...but the borrowed content is only valid for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::ReborrowUpvar(span, ref upvar_id) => {
+ let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id);
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0313,
+ "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...",
+ var_name
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "...the borrowed pointer is valid for ",
+ sub,
+ "...",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ &format!("...but `{}` is only valid for ", var_name),
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::RelateObjectBound(span) => {
+ let mut err = struct_span_err!(
+ 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,
+ &mut err,
+ "object type is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "source pointer is only valid for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::RelateParamBound(span, ty, opt_span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0477,
+ "the type `{}` does not fulfill the required lifetime",
+ self.ty_to_string(ty)
+ );
+ match *sub {
+ ty::ReStatic => note_and_explain_region(
+ 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) => {
+ let mut err =
+ struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "lifetime parameter instantiated with ",
+ sup,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but lifetime parameter must outlive ",
+ sub,
+ "",
+ None,
+ );
+ err
+ }
+ infer::DataBorrowed(ty, span) => {
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0490,
+ "a value of type `{}` is borrowed for too long",
+ self.ty_to_string(ty)
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "the type is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but the borrow lasts for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::ReferenceOutlivesReferent(ty, span) => {
+ let mut err = struct_span_err!(
+ 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,
+ &mut err,
+ "the pointer is valid for ",
+ sub,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but the referenced data is only valid for ",
+ sup,
+ "",
+ None,
+ );
+ err
+ }
+ infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self
+ .report_extra_impl_obligation(
+ span,
+ impl_item_def_id,
+ trait_item_def_id,
+ &format!("`{}: {}`", sup, sub),
+ ),
+ infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => {
+ let mut err = self.report_concrete_failure(*parent, sub, sup);
+
+ let trait_item_span = self.tcx.def_span(trait_item_def_id);
+ let item_name = self.tcx.item_name(impl_item_def_id.to_def_id());
+ err.span_label(
+ trait_item_span,
+ format!("definition of `{}` from trait", item_name),
+ );
+
+ let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id);
+ let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id);
+
+ let impl_predicates: rustc_data_structures::fx::FxHashSet<_> =
+ impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect();
+ let clauses: Vec<_> = trait_predicates
+ .predicates
+ .into_iter()
+ .filter(|&(pred, _)| !impl_predicates.contains(pred))
+ .map(|(pred, _)| format!("{}", pred))
+ .collect();
+
+ if !clauses.is_empty() {
+ let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap();
+ let where_clause_span = generics.tail_span_for_predicate_suggestion();
+
+ let suggestion = format!(
+ "{} {}",
+ generics.add_where_or_trailing_comma(),
+ clauses.join(", "),
+ );
+ err.span_suggestion(
+ where_clause_span,
+ &format!(
+ "try copying {} from the trait",
+ if clauses.len() > 1 { "these clauses" } else { "this clause" }
+ ),
+ suggestion,
+ rustc_errors::Applicability::MaybeIncorrect,
+ );
+ }
+
+ err
+ }
+ infer::AscribeUserTypeProvePredicate(span) => {
+ let mut err =
+ struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied");
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "lifetime instantiated with ",
+ sup,
+ "",
+ None,
+ );
+ note_and_explain_region(
+ self.tcx,
+ &mut err,
+ "but lifetime must outlive ",
+ sub,
+ "",
+ None,
+ );
+ err
+ }
+ }
+ }
+
+ pub(super) fn report_placeholder_failure(
+ &self,
+ placeholder_origin: SubregionOrigin<'tcx>,
+ sub: Region<'tcx>,
+ sup: Region<'tcx>,
+ ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ // I can't think how to do better than this right now. -nikomatsakis
+ debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure");
+ match placeholder_origin {
+ infer::Subtype(box ref trace)
+ if matches!(
+ &trace.cause.code().peel_derives(),
+ ObligationCauseCode::BindingObligation(..)
+ | ObligationCauseCode::ExprBindingObligation(..)
+ ) =>
+ {
+ // Hack to get around the borrow checker because trace.cause has an `Rc`.
+ if let ObligationCauseCode::BindingObligation(_, span)
+ | ObligationCauseCode::ExprBindingObligation(_, span, ..) =
+ &trace.cause.code().peel_derives()
+ {
+ let span = *span;
+ let mut err = self.report_concrete_failure(placeholder_origin, sub, sup);
+ err.span_note(span, "the lifetime requirement is introduced here");
+ err
+ } else {
+ unreachable!()
+ }
+ }
+ infer::Subtype(box trace) => {
+ let terr = TypeError::RegionsPlaceholderMismatch;
+ return self.report_and_explain_type_error(trace, terr);
+ }
+ _ => return self.report_concrete_failure(placeholder_origin, sub, sup),
+ }
+ }
+}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
new file mode 100644
index 000000000..73b5a2cc4
--- /dev/null
+++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs
@@ -0,0 +1,672 @@
+use hir::def::CtorKind;
+use hir::intravisit::{walk_expr, walk_stmt, Visitor};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_errors::{Applicability, Diagnostic};
+use rustc_hir as hir;
+use rustc_middle::traits::{
+ IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
+ StatementAsExpression,
+};
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
+use rustc_span::{sym, BytePos, Span};
+
+use crate::errors::SuggAddLetForLetChains;
+
+use super::TypeErrCtxt;
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ pub(super) fn suggest_remove_semi_or_return_binding(
+ &self,
+ err: &mut Diagnostic,
+ first_id: Option<hir::HirId>,
+ first_ty: Ty<'tcx>,
+ first_span: Span,
+ second_id: Option<hir::HirId>,
+ second_ty: Ty<'tcx>,
+ second_span: Span,
+ ) {
+ let remove_semicolon = [
+ (first_id, self.resolve_vars_if_possible(second_ty)),
+ (second_id, self.resolve_vars_if_possible(first_ty)),
+ ]
+ .into_iter()
+ .find_map(|(id, ty)| {
+ let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None };
+ self.could_remove_semicolon(blk, ty)
+ });
+ match remove_semicolon {
+ Some((sp, StatementAsExpression::NeedsBoxing)) => {
+ err.multipart_suggestion(
+ "consider removing this semicolon and boxing the expressions",
+ vec![
+ (first_span.shrink_to_lo(), "Box::new(".to_string()),
+ (first_span.shrink_to_hi(), ")".to_string()),
+ (second_span.shrink_to_lo(), "Box::new(".to_string()),
+ (second_span.shrink_to_hi(), ")".to_string()),
+ (sp, String::new()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ }
+ Some((sp, StatementAsExpression::CorrectType)) => {
+ err.span_suggestion_short(
+ sp,
+ "consider removing this semicolon",
+ "",
+ Applicability::MachineApplicable,
+ );
+ }
+ None => {
+ for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
+ if let Some(id) = id
+ && let hir::Node::Block(blk) = self.tcx.hir().get(id)
+ && self.consider_returning_binding(blk, ty, err)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ pub(super) fn suggest_boxing_for_return_impl_trait(
+ &self,
+ err: &mut Diagnostic,
+ return_sp: Span,
+ arm_spans: impl Iterator<Item = Span>,
+ ) {
+ err.multipart_suggestion(
+ "you could change the return type to be a boxed trait object",
+ vec![
+ (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()),
+ (return_sp.shrink_to_hi(), ">".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ let sugg = arm_spans
+ .flat_map(|sp| {
+ [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())]
+ .into_iter()
+ })
+ .collect::<Vec<_>>();
+ err.multipart_suggestion(
+ "if you change the return type to expect trait objects, box the returned expressions",
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ pub(super) fn suggest_tuple_pattern(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with
+ // some modifications due to that being in typeck and this being in infer.
+ if let ObligationCauseCode::Pattern { .. } = cause.code() {
+ if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() {
+ let compatible_variants: Vec<_> = expected_adt
+ .variants()
+ .iter()
+ .filter(|variant| {
+ variant.fields.len() == 1 && variant.ctor_kind() == Some(CtorKind::Fn)
+ })
+ .filter_map(|variant| {
+ let sole_field = &variant.fields[0];
+ let sole_field_ty = sole_field.ty(self.tcx, substs);
+ if self.same_type_modulo_infer(sole_field_ty, exp_found.found) {
+ let variant_path =
+ with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id));
+ // FIXME #56861: DRYer prelude filtering
+ if let Some(path) = variant_path.strip_prefix("std::prelude::") {
+ if let Some((_, path)) = path.split_once("::") {
+ return Some(path.to_string());
+ }
+ }
+ Some(variant_path)
+ } else {
+ None
+ }
+ })
+ .collect();
+ match &compatible_variants[..] {
+ [] => {}
+ [variant] => {
+ diag.multipart_suggestion_verbose(
+ &format!("try wrapping the pattern in `{}`", variant),
+ vec![
+ (cause.span.shrink_to_lo(), format!("{}(", variant)),
+ (cause.span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ // More than one matching variant.
+ diag.multipart_suggestions(
+ &format!(
+ "try wrapping the pattern in a variant of `{}`",
+ self.tcx.def_path_str(expected_adt.did())
+ ),
+ compatible_variants.into_iter().map(|variant| {
+ vec![
+ (cause.span.shrink_to_lo(), format!("{}(", variant)),
+ (cause.span.shrink_to_hi(), ")".to_string()),
+ ]
+ }),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /// A possible error is to forget to add `.await` when using futures:
+ ///
+ /// ```compile_fail,E0308
+ /// async fn make_u32() -> u32 {
+ /// 22
+ /// }
+ ///
+ /// fn take_u32(x: u32) {}
+ ///
+ /// async fn foo() {
+ /// let x = make_u32();
+ /// take_u32(x);
+ /// }
+ /// ```
+ ///
+ /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the
+ /// expected type. If this is the case, and we are inside of an async body, it suggests adding
+ /// `.await` to the tail of the expression.
+ pub(super) fn suggest_await_on_expect_found(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ exp_span: Span,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ debug!(
+ "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}",
+ exp_span, exp_found.expected, exp_found.found,
+ );
+
+ if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() {
+ return;
+ }
+
+ match (
+ self.get_impl_future_output_ty(exp_found.expected),
+ self.get_impl_future_output_ty(exp_found.found),
+ ) {
+ (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
+ .code()
+ {
+ ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+ let then_span = self.find_block_span_from_hir_id(*then_id);
+ diag.multipart_suggestion(
+ "consider `await`ing on both `Future`s",
+ vec![
+ (then_span.shrink_to_hi(), ".await".to_string()),
+ (exp_span.shrink_to_hi(), ".await".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+ prior_arms,
+ ..
+ }) => {
+ if let [.., arm_span] = &prior_arms[..] {
+ diag.multipart_suggestion(
+ "consider `await`ing on both `Future`s",
+ vec![
+ (arm_span.shrink_to_hi(), ".await".to_string()),
+ (exp_span.shrink_to_hi(), ".await".to_string()),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ diag.help("consider `await`ing on both `Future`s");
+ }
+ }
+ _ => {
+ diag.help("consider `await`ing on both `Future`s");
+ }
+ },
+ (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
+ diag.span_suggestion_verbose(
+ exp_span.shrink_to_hi(),
+ "consider `await`ing on the `Future`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
+ {
+ ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
+ diag.span_suggestion_verbose(
+ then_span.shrink_to_hi(),
+ "consider `await`ing on the `Future`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
+ let then_span = self.find_block_span_from_hir_id(*then_id);
+ diag.span_suggestion_verbose(
+ then_span.shrink_to_hi(),
+ "consider `await`ing on the `Future`",
+ ".await",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
+ ref prior_arms,
+ ..
+ }) => {
+ diag.multipart_suggestion_verbose(
+ "consider `await`ing on the `Future`",
+ prior_arms
+ .iter()
+ .map(|arm| (arm.shrink_to_hi(), ".await".to_string()))
+ .collect(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ }
+
+ pub(super) fn suggest_accessing_field_where_appropriate(
+ &self,
+ cause: &ObligationCause<'tcx>,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ debug!(
+ "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})",
+ cause, exp_found
+ );
+ if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() {
+ if expected_def.is_enum() {
+ return;
+ }
+
+ if let Some((name, ty)) = expected_def
+ .non_enum_variant()
+ .fields
+ .iter()
+ .filter(|field| field.vis.is_accessible_from(field.did, self.tcx))
+ .map(|field| (field.name, field.ty(self.tcx, expected_substs)))
+ .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found))
+ {
+ if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() {
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ let suggestion = if expected_def.is_struct() {
+ format!("{}.{}", snippet, name)
+ } else if expected_def.is_union() {
+ format!("unsafe {{ {}.{} }}", snippet, name)
+ } else {
+ return;
+ };
+ diag.span_suggestion(
+ span,
+ &format!(
+ "you might have meant to use field `{}` whose type is `{}`",
+ name, ty
+ ),
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ }
+ }
+
+ /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate,
+ /// suggests it.
+ pub(super) fn suggest_as_ref_where_appropriate(
+ &self,
+ span: Span,
+ exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+ diag: &mut Diagnostic,
+ ) {
+ if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+ && let Some(msg) = self.should_suggest_as_ref(exp_found.expected, exp_found.found)
+ {
+ diag.span_suggestion(
+ span,
+ msg,
+ // HACK: fix issue# 100605, suggesting convert from &Option<T> to Option<&T>, remove the extra `&`
+ format!("{}.as_ref()", snippet.trim_start_matches('&')),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+
+ pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
+ if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
+ (expected.kind(), found.kind())
+ {
+ if let ty::Adt(found_def, found_substs) = *found_ty.kind() {
+ if exp_def == &found_def {
+ let have_as_ref = &[
+ (
+ sym::Option,
+ "you can convert from `&Option<T>` to `Option<&T>` using \
+ `.as_ref()`",
+ ),
+ (
+ sym::Result,
+ "you can convert from `&Result<T, E>` to \
+ `Result<&T, &E>` using `.as_ref()`",
+ ),
+ ];
+ if let Some(msg) = have_as_ref.iter().find_map(|(name, msg)| {
+ self.tcx.is_diagnostic_item(*name, exp_def.did()).then_some(msg)
+ }) {
+ let mut show_suggestion = true;
+ for (exp_ty, found_ty) in
+ std::iter::zip(exp_substs.types(), found_substs.types())
+ {
+ match *exp_ty.kind() {
+ ty::Ref(_, exp_ty, _) => {
+ match (exp_ty.kind(), found_ty.kind()) {
+ (_, ty::Param(_))
+ | (_, ty::Infer(_))
+ | (ty::Param(_), _)
+ | (ty::Infer(_), _) => {}
+ _ if self.same_type_modulo_infer(exp_ty, found_ty) => {}
+ _ => show_suggestion = false,
+ };
+ }
+ ty::Param(_) | ty::Infer(_) => {}
+ _ => show_suggestion = false,
+ }
+ }
+ if show_suggestion {
+ return Some(*msg);
+ }
+ }
+ }
+ }
+ }
+ None
+ }
+
+ /// Try to find code with pattern `if Some(..) = expr`
+ /// use a `visitor` to mark the `if` which its span contains given error span,
+ /// and then try to find a assignment in the `cond` part, which span is equal with error span
+ pub(super) fn suggest_let_for_letchains(
+ &self,
+ err: &mut Diagnostic,
+ cause: &ObligationCause<'_>,
+ span: Span,
+ ) {
+ let hir = self.tcx.hir();
+ let fn_hir_id = hir.get_parent_node(cause.body_id);
+ if let Some(node) = self.tcx.hir().find(fn_hir_id) &&
+ let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Fn(_sig, _, body_id), ..
+ }) = node {
+ let body = hir.body(*body_id);
+
+ /// Find the if expression with given span
+ struct IfVisitor {
+ pub result: bool,
+ pub found_if: bool,
+ pub err_span: Span,
+ }
+
+ impl<'v> Visitor<'v> for IfVisitor {
+ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+ if self.result { return; }
+ match ex.kind {
+ hir::ExprKind::If(cond, _, _) => {
+ self.found_if = true;
+ walk_expr(self, cond);
+ self.found_if = false;
+ }
+ _ => walk_expr(self, ex),
+ }
+ }
+
+ fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {
+ if let hir::StmtKind::Local(hir::Local {
+ span, pat: hir::Pat{..}, ty: None, init: Some(_), ..
+ }) = &ex.kind
+ && self.found_if
+ && span.eq(&self.err_span) {
+ self.result = true;
+ }
+ walk_stmt(self, ex);
+ }
+
+ fn visit_body(&mut self, body: &'v hir::Body<'v>) {
+ hir::intravisit::walk_body(self, body);
+ }
+ }
+
+ let mut visitor = IfVisitor { err_span: span, found_if: false, result: false };
+ visitor.visit_body(&body);
+ if visitor.result {
+ err.subdiagnostic(SuggAddLetForLetChains{span: span.shrink_to_lo()});
+ }
+ }
+ }
+}
+
+impl<'tcx> TypeErrCtxt<'_, 'tcx> {
+ /// Be helpful when the user wrote `{... expr; }` and taking the `;` off
+ /// is enough to fix the error.
+ pub fn could_remove_semicolon(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected_ty: Ty<'tcx>,
+ ) -> Option<(Span, StatementAsExpression)> {
+ let blk = blk.innermost_block();
+ // Do not suggest if we have a tail expr.
+ if blk.expr.is_some() {
+ return None;
+ }
+ let last_stmt = blk.stmts.last()?;
+ let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
+ return None;
+ };
+ let last_expr_ty = self.typeck_results.as_ref()?.expr_ty_opt(*last_expr)?;
+ let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
+ _ if last_expr_ty.references_error() => return None,
+ _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
+ StatementAsExpression::CorrectType
+ }
+ (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
+ if last_def_id == exp_def_id =>
+ {
+ StatementAsExpression::CorrectType
+ }
+ (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
+ debug!(
+ "both opaque, likely future {:?} {:?} {:?} {:?}",
+ last_def_id, last_bounds, exp_def_id, exp_bounds
+ );
+
+ let last_local_id = last_def_id.as_local()?;
+ let exp_local_id = exp_def_id.as_local()?;
+
+ match (
+ &self.tcx.hir().expect_item(last_local_id).kind,
+ &self.tcx.hir().expect_item(exp_local_id).kind,
+ ) {
+ (
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
+ ) if std::iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
+ match (left, right) {
+ (
+ hir::GenericBound::Trait(tl, ml),
+ hir::GenericBound::Trait(tr, mr),
+ ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
+ && ml == mr =>
+ {
+ true
+ }
+ (
+ hir::GenericBound::LangItemTrait(langl, _, _, argsl),
+ hir::GenericBound::LangItemTrait(langr, _, _, argsr),
+ ) if langl == langr => {
+ // FIXME: consider the bounds!
+ debug!("{:?} {:?}", argsl, argsr);
+ true
+ }
+ _ => false,
+ }
+ }) =>
+ {
+ StatementAsExpression::NeedsBoxing
+ }
+ _ => StatementAsExpression::CorrectType,
+ }
+ }
+ _ => return None,
+ };
+ let span = if last_stmt.span.from_expansion() {
+ let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
+ self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
+ } else {
+ last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
+ };
+ Some((span, needs_box))
+ }
+
+ /// Suggest returning a local binding with a compatible type if the block
+ /// has no return expression.
+ pub fn consider_returning_binding(
+ &self,
+ blk: &'tcx hir::Block<'tcx>,
+ expected_ty: Ty<'tcx>,
+ err: &mut Diagnostic,
+ ) -> bool {
+ let blk = blk.innermost_block();
+ // Do not suggest if we have a tail expr.
+ if blk.expr.is_some() {
+ return false;
+ }
+ let mut shadowed = FxIndexSet::default();
+ let mut candidate_idents = vec![];
+ let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
+ if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
+ && let Some(pat_ty) = self
+ .typeck_results
+ .as_ref()
+ .and_then(|typeck_results| typeck_results.node_type_opt(*hir_id))
+ {
+ let pat_ty = self.resolve_vars_if_possible(pat_ty);
+ if self.same_type_modulo_infer(pat_ty, expected_ty)
+ && !(pat_ty, expected_ty).references_error()
+ && shadowed.insert(ident.name)
+ {
+ candidate_idents.push((*ident, pat_ty));
+ }
+ }
+ true
+ };
+
+ let hir = self.tcx.hir();
+ for stmt in blk.stmts.iter().rev() {
+ let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
+ local.pat.walk(&mut find_compatible_candidates);
+ }
+ match hir.find(hir.get_parent_node(blk.hir_id)) {
+ Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
+ match hir.find(hir.get_parent_node(*hir_id)) {
+ Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
+ pat.walk(&mut find_compatible_candidates);
+ }
+ Some(
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Fn(_, body),
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
+ ..
+ })
+ | hir::Node::Expr(hir::Expr {
+ kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
+ ..
+ }),
+ ) => {
+ for param in hir.body(*body).params {
+ param.pat.walk(&mut find_compatible_candidates);
+ }
+ }
+ Some(hir::Node::Expr(hir::Expr {
+ kind:
+ hir::ExprKind::If(
+ hir::Expr { kind: hir::ExprKind::Let(let_), .. },
+ then_block,
+ _,
+ ),
+ ..
+ })) if then_block.hir_id == *hir_id => {
+ let_.pat.walk(&mut find_compatible_candidates);
+ }
+ _ => {}
+ }
+ }
+ _ => {}
+ }
+
+ match &candidate_idents[..] {
+ [(ident, _ty)] => {
+ let sm = self.tcx.sess.source_map();
+ if let Some(stmt) = blk.stmts.last() {
+ let stmt_span = sm.stmt_span(stmt.span, blk.span);
+ let sugg = if sm.is_multiline(blk.span)
+ && let Some(spacing) = sm.indentation_before(stmt_span)
+ {
+ format!("\n{spacing}{ident}")
+ } else {
+ format!(" {ident}")
+ };
+ err.span_suggestion_verbose(
+ stmt_span.shrink_to_hi(),
+ format!("consider returning the local binding `{ident}`"),
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ let sugg = if sm.is_multiline(blk.span)
+ && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
+ {
+ format!("\n{spacing} {ident}\n{spacing}")
+ } else {
+ format!(" {ident} ")
+ };
+ 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),
+ format!("consider returning the local binding `{ident}`"),
+ sugg,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ true
+ }
+ values if (1..3).contains(&values.len()) => {
+ let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
+ err.span_note(spans, "consider returning one of these bindings");
+ true
+ }
+ _ => false,
+ }
+ }
+}