summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/error_reporting/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_infer/src/infer/error_reporting/mod.rs')
-rw-r--r--compiler/rustc_infer/src/infer/error_reporting/mod.rs933
1 files changed, 185 insertions, 748 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,
- }
- }
-}