summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_trait_selection/src/traits/error_reporting
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting')
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs79
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs90
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs469
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs65
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs1365
5 files changed, 1520 insertions, 548 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
index 752b53fbc..0419bb3f7 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/ambiguity.rs
@@ -1,52 +1,99 @@
use rustc_hir::def_id::DefId;
-use rustc_infer::infer::InferCtxt;
+use rustc_infer::infer::{InferCtxt, LateBoundRegionConversionTime};
+use rustc_infer::traits::util::elaborate_predicates_with_span;
use rustc_infer::traits::{Obligation, ObligationCause, TraitObligation};
-use rustc_span::DUMMY_SP;
+use rustc_middle::ty;
+use rustc_span::{Span, DUMMY_SP};
use crate::traits::ObligationCtxt;
+pub enum Ambiguity {
+ DefId(DefId),
+ ParamEnv(Span),
+}
+
pub fn recompute_applicable_impls<'tcx>(
infcx: &InferCtxt<'tcx>,
obligation: &TraitObligation<'tcx>,
-) -> Vec<DefId> {
+) -> Vec<Ambiguity> {
let tcx = infcx.tcx;
let param_env = obligation.param_env;
- let dummy_cause = ObligationCause::dummy();
+
let impl_may_apply = |impl_def_id| {
let ocx = ObligationCtxt::new_in_snapshot(infcx);
let placeholder_obligation =
infcx.replace_bound_vars_with_placeholders(obligation.predicate);
let obligation_trait_ref =
- ocx.normalize(&dummy_cause, param_env, placeholder_obligation.trait_ref);
+ ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
let impl_substs = infcx.fresh_substs_for_item(DUMMY_SP, impl_def_id);
- let impl_trait_ref = tcx.bound_impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap().subst(tcx, impl_substs);
let impl_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, impl_trait_ref);
- if let Err(_) = ocx.eq(&dummy_cause, param_env, obligation_trait_ref, impl_trait_ref) {
+ if let Err(_) =
+ ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, impl_trait_ref)
+ {
return false;
}
let impl_predicates = tcx.predicates_of(impl_def_id).instantiate(tcx, impl_substs);
- ocx.register_obligations(
- impl_predicates
- .predicates
- .iter()
- .map(|&predicate| Obligation::new(tcx, dummy_cause.clone(), param_env, predicate)),
+ ocx.register_obligations(impl_predicates.predicates.iter().map(|&predicate| {
+ Obligation::new(tcx, ObligationCause::dummy(), param_env, predicate)
+ }));
+
+ ocx.select_where_possible().is_empty()
+ };
+
+ let param_env_candidate_may_apply = |poly_trait_predicate: ty::PolyTraitPredicate<'tcx>| {
+ let ocx = ObligationCtxt::new_in_snapshot(infcx);
+ let placeholder_obligation =
+ infcx.replace_bound_vars_with_placeholders(obligation.predicate);
+ let obligation_trait_ref =
+ ocx.normalize(&ObligationCause::dummy(), param_env, placeholder_obligation.trait_ref);
+
+ let param_env_predicate = infcx.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::HigherRankedType,
+ poly_trait_predicate,
);
+ let param_env_trait_ref =
+ ocx.normalize(&ObligationCause::dummy(), param_env, param_env_predicate.trait_ref);
+
+ if let Err(_) =
+ ocx.eq(&ObligationCause::dummy(), param_env, obligation_trait_ref, param_env_trait_ref)
+ {
+ return false;
+ }
ocx.select_where_possible().is_empty()
};
- let mut impls = Vec::new();
+ let mut ambiguities = Vec::new();
+
tcx.for_each_relevant_impl(
obligation.predicate.def_id(),
obligation.predicate.skip_binder().trait_ref.self_ty(),
|impl_def_id| {
- if infcx.probe(move |_snapshot| impl_may_apply(impl_def_id)) {
- impls.push(impl_def_id)
+ if infcx.probe(|_| impl_may_apply(impl_def_id)) {
+ ambiguities.push(Ambiguity::DefId(impl_def_id))
}
},
);
- impls
+
+ let predicates =
+ tcx.predicates_of(obligation.cause.body_id.owner.to_def_id()).instantiate_identity(tcx);
+ for obligation in elaborate_predicates_with_span(tcx, predicates.into_iter()) {
+ let kind = obligation.predicate.kind();
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = kind.skip_binder()
+ && param_env_candidate_may_apply(kind.rebind(trait_pred))
+ {
+ if kind.rebind(trait_pred.trait_ref) == ty::TraitRef::identity(tcx, trait_pred.def_id()) {
+ ambiguities.push(Ambiguity::ParamEnv(tcx.def_span(trait_pred.def_id())))
+ } else {
+ ambiguities.push(Ambiguity::ParamEnv(obligation.cause.span))
+ }
+ }
+ }
+
+ ambiguities
}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs
new file mode 100644
index 000000000..ba9ee57d4
--- /dev/null
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/method_chain.rs
@@ -0,0 +1,90 @@
+use crate::infer::InferCtxt;
+
+use rustc_middle::ty::error::TypeError;
+use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
+use rustc_middle::ty::{self, Ty, TyCtxt};
+
+pub struct CollectAllMismatches<'a, 'tcx> {
+ pub infcx: &'a InferCtxt<'tcx>,
+ pub param_env: ty::ParamEnv<'tcx>,
+ pub errors: Vec<TypeError<'tcx>>,
+}
+
+impl<'a, 'tcx> TypeRelation<'tcx> for CollectAllMismatches<'a, 'tcx> {
+ fn tag(&self) -> &'static str {
+ "CollectAllMismatches"
+ }
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.infcx.tcx
+ }
+
+ fn intercrate(&self) -> bool {
+ false
+ }
+
+ fn param_env(&self) -> ty::ParamEnv<'tcx> {
+ self.param_env
+ }
+
+ fn a_is_expected(&self) -> bool {
+ true
+ }
+
+ fn mark_ambiguous(&mut self) {
+ bug!()
+ }
+
+ fn relate_with_variance<T: Relate<'tcx>>(
+ &mut self,
+ _: ty::Variance,
+ _: ty::VarianceDiagInfo<'tcx>,
+ a: T,
+ b: T,
+ ) -> RelateResult<'tcx, T> {
+ self.relate(a, b)
+ }
+
+ fn regions(
+ &mut self,
+ a: ty::Region<'tcx>,
+ _b: ty::Region<'tcx>,
+ ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+ Ok(a)
+ }
+
+ fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+ self.infcx.probe(|_| {
+ if a.is_ty_var() || b.is_ty_var() {
+ Ok(a)
+ } else {
+ self.infcx.super_combine_tys(self, a, b).or_else(|e| {
+ self.errors.push(e);
+ Ok(a)
+ })
+ }
+ })
+ }
+
+ fn consts(
+ &mut self,
+ a: ty::Const<'tcx>,
+ b: ty::Const<'tcx>,
+ ) -> RelateResult<'tcx, ty::Const<'tcx>> {
+ self.infcx.probe(|_| {
+ if a.is_ct_infer() || b.is_ct_infer() {
+ Ok(a)
+ } else {
+ relate::super_relate_consts(self, a, b) // could do something similar here for constants!
+ }
+ })
+ }
+
+ fn binders<T: Relate<'tcx>>(
+ &mut self,
+ a: ty::Binder<'tcx, T>,
+ b: ty::Binder<'tcx, T>,
+ ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> {
+ Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?))
+ }
+}
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index dda7b2b2f..52971486c 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -1,4 +1,5 @@
mod ambiguity;
+pub mod method_chain;
pub mod on_unimplemented;
pub mod suggestions;
@@ -32,16 +33,17 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::{InferOk, TypeTrace};
use rustc_middle::traits::select::OverflowError;
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
-use rustc_middle::ty::error::ExpectedFound;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::fold::{TypeFolder, TypeSuperFoldable};
-use rustc_middle::ty::print::{FmtPrinter, Print};
+use rustc_middle::ty::print::{with_forced_trimmed_paths, FmtPrinter, Print};
use rustc_middle::ty::{
self, SubtypePredicate, ToPolyTraitRef, ToPredicate, TraitRef, Ty, TyCtxt, TypeFoldable,
TypeVisitable,
};
+use rustc_session::config::TraitSolver;
use rustc_session::Limit;
use rustc_span::def_id::LOCAL_CRATE;
-use rustc_span::symbol::{kw, sym};
+use rustc_span::symbol::sym;
use rustc_span::{ExpnKind, Span, DUMMY_SP};
use std::fmt;
use std::iter;
@@ -225,7 +227,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
let arg_length = arguments.len();
let distinct = matches!(other, &[ArgKind::Tuple(..)]);
match (arg_length, arguments.get(0)) {
- (1, Some(&ArgKind::Tuple(_, ref fields))) => {
+ (1, Some(ArgKind::Tuple(_, fields))) => {
format!("a single {}-tuple as argument", fields.len())
}
_ => format!(
@@ -372,6 +374,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
})
}
}
+
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn report_fulfillment_errors(
&self,
@@ -451,9 +454,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- for (error, suppressed) in iter::zip(errors, is_suppressed) {
- if !suppressed {
- self.report_fulfillment_error(error, body_id);
+ for from_expansion in [false, true] {
+ for (error, suppressed) in iter::zip(errors, &is_suppressed) {
+ if !suppressed && error.obligation.cause.span.from_expansion() == from_expansion {
+ self.report_fulfillment_error(error, body_id);
+ }
}
}
@@ -536,7 +541,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|err| {
self.note_obligation_cause_code(
err,
- &predicate,
+ predicate,
obligation.param_env,
obligation.cause.code(),
&mut vec![],
@@ -597,6 +602,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// can get a better error message by performing HIR-based well-formedness checking.
if let ObligationCauseCode::WellFormed(Some(wf_loc)) =
root_obligation.cause.code().peel_derives()
+ && !obligation.predicate.has_non_region_infer()
{
if let Some(cause) = self
.tcx
@@ -768,7 +774,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
),
}
};
-
+ self.check_for_binding_assigned_block_without_tail_expression(
+ &obligation,
+ &mut err,
+ trait_predicate,
+ );
if self.suggest_add_reference_to_arg(
&obligation,
&mut err,
@@ -845,6 +855,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut suggested =
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
+ let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
+ suggested = if let &[cand] = &impl_candidates[..] {
+ let cand = cand.trait_ref;
+ if let (ty::FnPtr(_), ty::FnDef(..)) =
+ (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
+ {
+ err.span_suggestion(
+ span.shrink_to_hi(),
+ format!(
+ "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`",
+ cand.print_only_trait_path(),
+ cand.self_ty(),
+ ),
+ format!(" as {}", cand.self_ty()),
+ Applicability::MaybeIncorrect,
+ );
+ true
+ } else {
+ false
+ }
+ } else {
+ false
+ } || suggested;
suggested |=
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
suggested |= self.suggest_semicolon_removal(
@@ -866,6 +899,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
}
+ if self.suggest_add_clone_to_arg(&obligation, &mut err, trait_predicate) {
+ err.emit();
+ return;
+ }
+
if self.suggest_impl_trait(&mut err, span, &obligation, trait_predicate) {
err.emit();
return;
@@ -978,6 +1016,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_ref,
obligation.cause.body_id,
&mut err,
+ true,
) {
// This is *almost* equivalent to
// `obligation.cause.code().peel_derives()`, but it gives us the
@@ -1013,6 +1052,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_ref,
obligation.cause.body_id,
&mut err,
+ true,
);
}
}
@@ -1032,7 +1072,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& self.fallback_has_occurred
{
let predicate = trait_predicate.map_bound(|trait_pred| {
- trait_pred.with_self_type(self.tcx, self.tcx.mk_unit())
+ trait_pred.with_self_ty(self.tcx, self.tcx.mk_unit())
});
let unit_obligation = obligation.with(tcx, predicate);
if self.predicate_may_hold(&unit_obligation) {
@@ -1088,15 +1128,19 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(..))
- | ty::PredicateKind::Clause(ty::Clause::Projection(..))
| ty::PredicateKind::Clause(ty::Clause::TypeOutlives(..)) => {
- let predicate = self.resolve_vars_if_possible(obligation.predicate);
- struct_span_err!(
- self.tcx.sess,
+ span_bug!(
span,
- E0280,
- "the requirement `{}` is not satisfied",
- predicate
+ "outlives clauses should not error outside borrowck. obligation: `{:?}`",
+ obligation
+ )
+ }
+
+ ty::PredicateKind::Clause(ty::Clause::Projection(..)) => {
+ span_bug!(
+ span,
+ "projection clauses should be implied from elsewhere. obligation: `{:?}`",
+ obligation
)
}
@@ -1163,7 +1207,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ty::PredicateKind::WellFormed(ty) => {
- if !self.tcx.sess.opts.unstable_opts.chalk {
+ if self.tcx.sess.opts.unstable_opts.trait_solver == TraitSolver::Classic {
// WF predicates cannot themselves make
// errors. They can only block due to
// ambiguity; otherwise, they always
@@ -1175,7 +1219,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// which bounds actually failed to hold.
self.tcx.sess.struct_span_err(
span,
- &format!("the type `{}` is not well-formed (chalk)", ty),
+ &format!("the type `{}` is not well-formed", ty),
)
}
}
@@ -1211,6 +1255,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
+ OutputTypeParameterMismatch(
+ found_trait_ref,
+ expected_trait_ref,
+ terr @ TypeError::CyclicTy(_),
+ ) => {
+ let self_ty = found_trait_ref.self_ty().skip_binder();
+ let (cause, terr) = if let ty::Closure(def_id, _) = self_ty.kind() {
+ (
+ ObligationCause::dummy_with_span(tcx.def_span(def_id)),
+ TypeError::CyclicTy(self_ty),
+ )
+ } else {
+ (obligation.cause.clone(), terr)
+ };
+ self.report_and_explain_type_error(
+ TypeTrace::poly_trait_refs(&cause, true, expected_trait_ref, found_trait_ref),
+ terr,
+ )
+ }
OutputTypeParameterMismatch(found_trait_ref, expected_trait_ref, _) => {
let found_trait_ref = self.resolve_vars_if_possible(found_trait_ref);
let expected_trait_ref = self.resolve_vars_if_possible(expected_trait_ref);
@@ -1232,6 +1295,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
_ => None,
};
+ let found_node = found_did.and_then(|did| self.tcx.hir().get_if_local(did));
let found_span = found_did.and_then(|did| self.tcx.hir().span_if_local(did));
if self.reported_closure_mismatch.borrow().contains(&(span, found_span)) {
@@ -1285,6 +1349,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
found_trait_ref,
expected_trait_ref,
obligation.cause.code(),
+ found_node,
+ obligation.param_env,
)
} else {
let (closure_span, closure_arg_span, found) = found_did
@@ -1381,7 +1447,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.note_obligation_cause(&mut err, &obligation);
self.point_at_returns_when_relevant(&mut err, &obligation);
-
err.emit();
}
}
@@ -1430,6 +1495,7 @@ trait InferCtxtPrivExt<'tcx> {
trait_ref: ty::PolyTraitRef<'tcx>,
body_id: hir::HirId,
err: &mut Diagnostic,
+ other: bool,
) -> bool;
/// Gets the parent trait chain start
@@ -1480,7 +1546,7 @@ trait InferCtxtPrivExt<'tcx> {
fn annotate_source_of_ambiguity(
&self,
err: &mut Diagnostic,
- impls: &[DefId],
+ impls: &[ambiguity::Ambiguity],
predicate: ty::Predicate<'tcx>,
);
@@ -1567,7 +1633,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&error.obligation.cause,
expected_found.expected,
expected_found.found,
- err.clone(),
+ *err,
)
.emit();
}
@@ -1576,7 +1642,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&error.obligation.cause,
expected_found.expected,
expected_found.found,
- err.clone(),
+ *err,
);
let code = error.obligation.cause.code().peel_derives().peel_match_impls();
if let ObligationCauseCode::BindingObligation(..)
@@ -1586,7 +1652,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
{
self.note_obligation_cause_code(
&mut diag,
- &error.obligation.predicate,
+ error.obligation.predicate,
error.obligation.param_env,
code,
&mut vec![],
@@ -1629,18 +1695,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
infer::LateBoundRegionConversionTime::HigherRankedType,
bound_predicate.rebind(data),
);
- let normalized_ty = ocx.normalize(
- &obligation.cause,
- obligation.param_env,
- self.tcx
- .mk_projection(data.projection_ty.item_def_id, data.projection_ty.substs),
- );
+ let unnormalized_term = match data.term.unpack() {
+ ty::TermKind::Ty(_) => self
+ .tcx
+ .mk_projection(data.projection_ty.def_id, data.projection_ty.substs)
+ .into(),
+ ty::TermKind::Const(ct) => self
+ .tcx
+ .mk_const(
+ ty::UnevaluatedConst {
+ def: ty::WithOptConstParam::unknown(data.projection_ty.def_id),
+ substs: data.projection_ty.substs,
+ },
+ ct.ty(),
+ )
+ .into(),
+ };
+ let normalized_term =
+ ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
debug!(?obligation.cause, ?obligation.param_env);
- debug!(?normalized_ty, data.ty = ?data.term);
+ debug!(?normalized_term, data.ty = ?data.term);
- let is_normalized_ty_expected = !matches!(
+ let is_normalized_term_expected = !matches!(
obligation.cause.code().peel_derives(),
ObligationCauseCode::ItemObligation(_)
| ObligationCauseCode::BindingObligation(_, _)
@@ -1649,7 +1727,6 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
| ObligationCauseCode::ObjectCastObligation(..)
| ObligationCauseCode::OpaqueType
);
- let expected_ty = data.term.ty().unwrap_or_else(|| self.tcx.ty_error());
// constrain inference variables a bit more to nested obligations from normalize so
// we can have more helpful errors.
@@ -1658,11 +1735,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let Err(new_err) = ocx.eq_exp(
&obligation.cause,
obligation.param_env,
- is_normalized_ty_expected,
- normalized_ty,
- expected_ty,
+ is_normalized_term_expected,
+ normalized_term,
+ data.term,
) {
- (Some((data, is_normalized_ty_expected, normalized_ty, expected_ty)), new_err)
+ (Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err)
} else {
(None, error.err)
}
@@ -1671,23 +1748,31 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
};
let msg = values
- .and_then(|(predicate, _, normalized_ty, expected_ty)| {
- self.maybe_detailed_projection_msg(
- predicate,
- normalized_ty.into(),
- expected_ty.into(),
- )
+ .and_then(|(predicate, _, normalized_term, expected_term)| {
+ self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
})
- .unwrap_or_else(|| format!("type mismatch resolving `{}`", predicate));
+ .unwrap_or_else(|| {
+ with_forced_trimmed_paths!(format!(
+ "type mismatch resolving `{}`",
+ self.resolve_vars_if_possible(predicate)
+ .print(FmtPrinter::new_with_limit(
+ self.tcx,
+ Namespace::TypeNS,
+ rustc_session::Limit(10),
+ ))
+ .unwrap()
+ .into_buffer()
+ ))
+ });
let mut diag = struct_span_err!(self.tcx.sess, obligation.cause.span, E0271, "{msg}");
let secondary_span = match predicate.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Projection(proj)) => self
.tcx
- .opt_associated_item(proj.projection_ty.item_def_id)
+ .opt_associated_item(proj.projection_ty.def_id)
.and_then(|trait_assoc_item| {
self.tcx
- .trait_of_item(proj.projection_ty.item_def_id)
+ .trait_of_item(proj.projection_ty.def_id)
.map(|id| (trait_assoc_item, id))
})
.and_then(|(trait_assoc_item, id)| {
@@ -1709,7 +1794,20 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
kind: hir::ImplItemKind::Type(ty),
..
}),
- ) => Some((ty.span, format!("type mismatch resolving `{}`", predicate))),
+ ) => Some((
+ ty.span,
+ with_forced_trimmed_paths!(format!(
+ "type mismatch resolving `{}`",
+ self.resolve_vars_if_possible(predicate)
+ .print(FmtPrinter::new_with_limit(
+ self.tcx,
+ Namespace::TypeNS,
+ rustc_session::Limit(5),
+ ))
+ .unwrap()
+ .into_buffer()
+ )),
+ )),
_ => None,
}),
_ => None,
@@ -1721,8 +1819,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
values.map(|(_, is_normalized_ty_expected, normalized_ty, expected_ty)| {
infer::ValuePairs::Terms(ExpectedFound::new(
is_normalized_ty_expected,
- normalized_ty.into(),
- expected_ty.into(),
+ normalized_ty,
+ expected_ty,
))
}),
err,
@@ -1743,21 +1841,26 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let trait_def_id = pred.projection_ty.trait_def_id(self.tcx);
let self_ty = pred.projection_ty.self_ty();
- if Some(pred.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() {
- Some(format!(
- "expected `{self_ty}` to be a {fn_kind} that returns `{expected_ty}`, but it returns `{normalized_ty}`",
- fn_kind = self_ty.prefix_string(self.tcx)
- ))
- } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() {
- Some(format!(
- "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it resolves to `{normalized_ty}`"
- ))
- } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) {
- Some(format!(
- "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it yields `{normalized_ty}`"
- ))
- } else {
- None
+ with_forced_trimmed_paths! {
+ if Some(pred.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() {
+ Some(format!(
+ "expected `{self_ty}` to be a {fn_kind} that returns `{expected_ty}`, but it \
+ returns `{normalized_ty}`",
+ fn_kind = self_ty.prefix_string(self.tcx)
+ ))
+ } else if Some(trait_def_id) == self.tcx.lang_items().future_trait() {
+ Some(format!(
+ "expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \
+ resolves to `{normalized_ty}`"
+ ))
+ } else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) {
+ Some(format!(
+ "expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \
+ yields `{normalized_ty}`"
+ ))
+ } else {
+ None
+ }
}
}
@@ -1786,8 +1889,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ty::Closure(..) => Some(9),
ty::Tuple(..) => Some(10),
ty::Param(..) => Some(11),
- ty::Projection(..) => Some(12),
- ty::Opaque(..) => Some(13),
+ ty::Alias(ty::Projection, ..) => Some(12),
+ ty::Alias(ty::Opaque, ..) => Some(13),
ty::Never => Some(14),
ty::Adt(..) => Some(15),
ty::Generator(..) => Some(16),
@@ -1864,7 +1967,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return None;
}
- let imp = self.tcx.impl_trait_ref(def_id).unwrap();
+ let imp = self.tcx.impl_trait_ref(def_id).unwrap().skip_binder();
self.fuzzy_match_tys(trait_pred.skip_binder().self_ty(), imp.self_ty(), false)
.map(|similarity| ImplCandidate { trait_ref: imp, similarity })
@@ -1885,32 +1988,32 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_ref: ty::PolyTraitRef<'tcx>,
body_id: hir::HirId,
err: &mut Diagnostic,
+ other: bool,
) -> bool {
+ let other = if other { "other " } else { "" };
let report = |mut candidates: Vec<TraitRef<'tcx>>, err: &mut Diagnostic| {
candidates.sort();
candidates.dedup();
let len = candidates.len();
- if candidates.len() == 0 {
+ if candidates.is_empty() {
return false;
}
- if candidates.len() == 1 {
- let ty_desc = match candidates[0].self_ty().kind() {
- ty::FnPtr(_) => Some("fn pointer"),
- _ => None,
- };
- let the_desc = match ty_desc {
- Some(desc) => format!(" implemented for {} `", desc),
- None => " implemented for `".to_string(),
- };
+ if let &[cand] = &candidates[..] {
+ let (desc, mention_castable) =
+ match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
+ (ty::FnPtr(_), ty::FnDef(..)) => {
+ (" implemented for fn pointer `", ", cast using `as`")
+ }
+ (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
+ _ => (" implemented for `", ""),
+ };
err.highlighted_help(vec![
- (
- format!("the trait `{}` ", candidates[0].print_only_trait_path()),
- Style::NoStyle,
- ),
+ (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle),
("is".to_string(), Style::Highlight),
- (the_desc, Style::NoStyle),
- (candidates[0].self_ty().to_string(), Style::Highlight),
+ (desc.to_string(), Style::NoStyle),
+ (cand.self_ty().to_string(), Style::Highlight),
("`".to_string(), Style::NoStyle),
+ (mention_castable.to_string(), Style::NoStyle),
]);
return true;
}
@@ -1936,7 +2039,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
candidates.dedup();
let end = if candidates.len() <= 9 { candidates.len() } else { 8 };
err.help(&format!(
- "the following other types implement trait `{}`:{}{}",
+ "the following {other}types implement trait `{}`:{}{}",
trait_ref.print_only_trait_path(),
candidates[..end].join(""),
if len > 9 { format!("\nand {} others", len - 8) } else { String::new() }
@@ -1962,6 +2065,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|| self.tcx.is_builtin_derive(def_id)
})
.filter_map(|def_id| self.tcx.impl_trait_ref(def_id))
+ .map(ty::EarlyBinder::subst_identity)
.filter(|trait_ref| {
let self_ty = trait_ref.self_ty();
// Avoid mentioning type parameters.
@@ -2077,8 +2181,8 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
param_env: ty::ParamEnv<'tcx>,
trait_ref_and_ty: ty::Binder<'tcx, (ty::TraitPredicate<'tcx>, Ty<'tcx>)>,
) -> PredicateObligation<'tcx> {
- let trait_pred = trait_ref_and_ty
- .map_bound(|(tr, new_self_ty)| tr.with_self_type(self.tcx, new_self_ty));
+ let trait_pred =
+ trait_ref_and_ty.map_bound(|(tr, new_self_ty)| tr.with_self_ty(self.tcx, new_self_ty));
Obligation::new(self.tcx, ObligationCause::dummy(), param_env, trait_pred)
}
@@ -2115,7 +2219,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// This is kind of a hack: it frequently happens that some earlier
// error prevents types from being fully inferred, and then we get
// a bunch of uninteresting errors saying something like "<generic
- // #0> doesn't implement Sized". It may even be true that we
+ // #0> doesn't implement Sized". It may even be true that we
// could just skip over all checks where the self-ty is an
// inference variable, but I was afraid that there might be an
// inference variable created, registered as an obligation, and
@@ -2172,19 +2276,43 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut selcx = SelectionContext::new(&self);
match selcx.select_from_obligation(&obligation) {
Ok(None) => {
- let impls = ambiguity::recompute_applicable_impls(self.infcx, &obligation);
- let has_non_region_infer =
- trait_ref.skip_binder().substs.types().any(|t| !t.is_ty_infer());
+ let ambiguities =
+ ambiguity::recompute_applicable_impls(self.infcx, &obligation);
+ let has_non_region_infer = trait_ref
+ .skip_binder()
+ .substs
+ .types()
+ .any(|t| !t.is_ty_or_numeric_infer());
// It doesn't make sense to talk about applicable impls if there are more
// than a handful of them.
- if impls.len() > 1 && impls.len() < 5 && has_non_region_infer {
- self.annotate_source_of_ambiguity(&mut err, &impls, predicate);
+ if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer {
+ if self.tainted_by_errors().is_some() && subst.is_none() {
+ // If `subst.is_none()`, then this is probably two param-env
+ // candidates or impl candidates that are equal modulo lifetimes.
+ // Therefore, if we've already emitted an error, just skip this
+ // one, since it's not particularly actionable.
+ err.cancel();
+ return;
+ }
+ self.annotate_source_of_ambiguity(&mut err, &ambiguities, predicate);
} else {
if self.tainted_by_errors().is_some() {
err.cancel();
return;
}
err.note(&format!("cannot satisfy `{}`", predicate));
+ let impl_candidates = self.find_similar_impl_candidates(
+ predicate.to_opt_poly_trait_pred().unwrap(),
+ );
+ if impl_candidates.len() < 10 {
+ self.report_similar_impl_candidates(
+ impl_candidates,
+ trait_ref,
+ body_id.map(|id| id.hir_id).unwrap_or(obligation.cause.body_id),
+ &mut err,
+ false,
+ );
+ }
}
}
_ => {
@@ -2196,82 +2324,16 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
- if let ObligationCauseCode::ItemObligation(def_id) | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code() {
- self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
- } else if let Ok(snippet) = &self.tcx.sess.source_map().span_to_snippet(span)
- && let ObligationCauseCode::BindingObligation(def_id, _) | ObligationCauseCode::ExprBindingObligation(def_id, ..)
- = *obligation.cause.code()
+ if let ObligationCauseCode::ItemObligation(def_id)
+ | ObligationCauseCode::ExprItemObligation(def_id, ..) = *obligation.cause.code()
{
- let generics = self.tcx.generics_of(def_id);
- if generics.params.iter().any(|p| p.name != kw::SelfUpper)
- && !snippet.ends_with('>')
- && !generics.has_impl_trait()
- && !self.tcx.is_fn_trait(def_id)
- {
- // FIXME: To avoid spurious suggestions in functions where type arguments
- // where already supplied, we check the snippet to make sure it doesn't
- // end with a turbofish. Ideally we would have access to a `PathSegment`
- // instead. Otherwise we would produce the following output:
- //
- // error[E0283]: type annotations needed
- // --> $DIR/issue-54954.rs:3:24
- // |
- // LL | const ARR_LEN: usize = Tt::const_val::<[i8; 123]>();
- // | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- // | |
- // | cannot infer type
- // | help: consider specifying the type argument
- // | in the function call:
- // | `Tt::const_val::<[i8; 123]>::<T>`
- // ...
- // LL | const fn const_val<T: Sized>() -> usize {
- // | - required by this bound in `Tt::const_val`
- // |
- // = note: cannot satisfy `_: Tt`
-
- // Clear any more general suggestions in favor of our specific one
- err.clear_suggestions();
-
- err.span_suggestion_verbose(
- span.shrink_to_hi(),
- &format!(
- "consider specifying the type argument{} in the function call",
- pluralize!(generics.params.len()),
- ),
- format!(
- "::<{}>",
- generics
- .params
- .iter()
- .map(|p| p.name.to_string())
- .collect::<Vec<String>>()
- .join(", ")
- ),
- Applicability::HasPlaceholders,
- );
- }
+ self.suggest_fully_qualified_path(&mut err, def_id, span, trait_ref.def_id());
}
if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
(body_id, subst.map(|subst| subst.unpack()))
{
- struct FindExprBySpan<'hir> {
- span: Span,
- result: Option<&'hir hir::Expr<'hir>>,
- }
-
- impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
- fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
- if self.span == ex.span {
- self.result = Some(ex);
- } else {
- hir::intravisit::walk_expr(self, ex);
- }
- }
- }
-
- let mut expr_finder = FindExprBySpan { span, result: None };
-
+ let mut expr_finder = FindExprBySpan::new(span);
expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
if let Some(hir::Expr {
@@ -2320,18 +2382,19 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let trait_impls = self.tcx.trait_impls_of(data.trait_ref.def_id);
if trait_impls.blanket_impls().is_empty()
- && let Some((impl_ty, _)) = trait_impls.non_blanket_impls().iter().next()
- && let Some(impl_def_id) = impl_ty.def() {
- let message = if trait_impls.non_blanket_impls().len() == 1 {
+ && let Some(impl_def_id) = trait_impls.non_blanket_impls().values().flatten().next()
+ {
+ let non_blanket_impl_count = trait_impls.non_blanket_impls().values().flatten().count();
+ let message = if non_blanket_impl_count == 1 {
"use the fully-qualified path to the only available implementation".to_string()
} else {
format!(
"use a fully-qualified path to a specific available implementation ({} found)",
- trait_impls.non_blanket_impls().len()
+ non_blanket_impl_count
)
};
let mut suggestions = vec![(
- trait_path_segment.ident.span.shrink_to_lo(),
+ path.span.shrink_to_lo(),
format!("<{} as ", self.tcx.type_of(impl_def_id))
)];
if let Some(generic_arg) = trait_path_segment.args {
@@ -2339,9 +2402,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// get rid of :: between Trait and <type>
// must be '::' between them, otherwise the parser won't accept the code
suggestions.push((between_span, "".to_string(),));
- suggestions.push((generic_arg.span_ext.shrink_to_hi(), format!(">")));
+ suggestions.push((generic_arg.span_ext.shrink_to_hi(), ">".to_string()));
} else {
- suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), format!(">")));
+ suggestions.push((trait_path_segment.ident.span.shrink_to_hi(), ">".to_string()));
}
err.multipart_suggestion(
message,
@@ -2464,21 +2527,30 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn annotate_source_of_ambiguity(
&self,
err: &mut Diagnostic,
- impls: &[DefId],
+ ambiguities: &[ambiguity::Ambiguity],
predicate: ty::Predicate<'tcx>,
) {
let mut spans = vec![];
let mut crates = vec![];
let mut post = vec![];
- for def_id in impls {
- match self.tcx.span_of_impl(*def_id) {
- Ok(span) => spans.push(span),
- Err(name) => {
- crates.push(name);
- if let Some(header) = to_pretty_impl_header(self.tcx, *def_id) {
- post.push(header);
+ let mut has_param_env = false;
+ for ambiguity in ambiguities {
+ match ambiguity {
+ ambiguity::Ambiguity::DefId(impl_def_id) => {
+ match self.tcx.span_of_impl(*impl_def_id) {
+ Ok(span) => spans.push(span),
+ Err(name) => {
+ crates.push(name);
+ if let Some(header) = to_pretty_impl_header(self.tcx, *impl_def_id) {
+ post.push(header);
+ }
+ }
}
}
+ ambiguity::Ambiguity::ParamEnv(span) => {
+ has_param_env = true;
+ spans.push(*span);
+ }
}
}
let mut crate_names: Vec<_> = crates.iter().map(|n| format!("`{}`", n)).collect();
@@ -2502,7 +2574,11 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return;
}
- let msg = format!("multiple `impl`s satisfying `{}` found", predicate);
+ let msg = format!(
+ "multiple `impl`s{} satisfying `{}` found",
+ if has_param_env { " or `where` clauses" } else { "" },
+ predicate
+ );
let post = if post.len() > 1 || (post.len() == 1 && post[0].contains('\n')) {
format!(":\n{}", post.iter().map(|p| format!("- {}", p)).collect::<Vec<_>>().join("\n"),)
} else if post.len() == 1 {
@@ -2601,7 +2677,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if !self.maybe_note_obligation_cause_for_async_await(err, obligation) {
self.note_obligation_cause_code(
err,
- &obligation.predicate,
+ obligation.predicate,
obligation.param_env,
obligation.cause.code(),
&mut vec![],
@@ -2744,6 +2820,36 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
+/// Crude way of getting back an `Expr` from a `Span`.
+pub struct FindExprBySpan<'hir> {
+ pub span: Span,
+ pub result: Option<&'hir hir::Expr<'hir>>,
+ pub ty_result: Option<&'hir hir::Ty<'hir>>,
+}
+
+impl<'hir> FindExprBySpan<'hir> {
+ fn new(span: Span) -> Self {
+ Self { span, result: None, ty_result: None }
+ }
+}
+
+impl<'v> Visitor<'v> for FindExprBySpan<'v> {
+ fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+ if self.span == ex.span {
+ self.result = Some(ex);
+ } else {
+ hir::intravisit::walk_expr(self, ex);
+ }
+ }
+ fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+ if self.span == ty.span {
+ self.ty_result = Some(ty);
+ } else {
+ hir::intravisit::walk_ty(self, ty);
+ }
+ }
+}
+
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
/// `param: ?Sized` would be a valid constraint.
struct FindTypeParam {
@@ -2765,7 +2871,7 @@ impl<'v> Visitor<'v> for FindTypeParam {
// and suggest `T: ?Sized` regardless of their obligations. This is fine because the errors
// in that case should make what happened clear enough.
match ty.kind {
- hir::TyKind::Ptr(_) | hir::TyKind::Rptr(..) | hir::TyKind::TraitObject(..) => {}
+ hir::TyKind::Ptr(_) | hir::TyKind::Ref(..) | hir::TyKind::TraitObject(..) => {}
hir::TyKind::Path(hir::QPath::Resolved(None, path))
if path.segments.len() == 1 && path.segments[0].ident.name == self.param =>
{
@@ -2827,11 +2933,12 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor {
if matches!(ty.kind(), ty::Infer(ty::FloatVar(_) | ty::IntVar(_))) {
ControlFlow::Break(())
} else {
- ControlFlow::CONTINUE
+ ControlFlow::Continue(())
}
}
}
+#[derive(Copy, Clone)]
pub enum DefIdOrName {
DefId(DefId),
Name(&'static str),
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
index 9bfe52764..18d308f71 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/on_unimplemented.rs
@@ -37,6 +37,21 @@ pub trait TypeErrCtxtExt<'tcx> {
) -> OnUnimplementedNote;
}
+/// The symbols which are always allowed in a format string
+static ALLOWED_FORMAT_SYMBOLS: &[Symbol] = &[
+ kw::SelfUpper,
+ sym::ItemContext,
+ sym::from_method,
+ sym::from_desugaring,
+ sym::direct,
+ sym::cause,
+ sym::integral,
+ sym::integer_,
+ sym::float,
+ sym::_Self,
+ sym::crate_local,
+];
+
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn impl_similar_to(
&self,
@@ -53,7 +68,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.tcx.for_each_relevant_impl(trait_ref.def_id, trait_self_ty, |def_id| {
let impl_substs = self.fresh_substs_for_item(obligation.cause.span, def_id);
- let impl_trait_ref = tcx.bound_impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
+ let impl_trait_ref = tcx.impl_trait_ref(def_id).unwrap().subst(tcx, impl_substs);
let impl_self_ty = impl_trait_ref.self_ty();
@@ -117,7 +132,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
Some(if movability.is_some() { "an async closure" } else { "a closure" })
}),
hir::Node::Expr(hir::Expr { .. }) => {
- let parent_hid = hir.get_parent_node(hir_id);
+ let parent_hid = hir.parent_id(hir_id);
if parent_hid != hir_id { self.describe_enclosure(parent_hid) } else { None }
}
_ => None,
@@ -543,38 +558,26 @@ impl<'tcx> OnUnimplementedFormatString {
Piece::NextArgument(a) => match a.position {
Position::ArgumentNamed(s) => {
match Symbol::intern(s) {
- // `{Self}` is allowed
- kw::SelfUpper => (),
// `{ThisTraitsName}` is allowed
s if s == trait_name => (),
- // `{from_method}` is allowed
- sym::from_method => (),
- // `{from_desugaring}` is allowed
- sym::from_desugaring => (),
- // `{ItemContext}` is allowed
- sym::ItemContext => (),
- // `{integral}` and `{integer}` and `{float}` are allowed
- sym::integral | sym::integer_ | sym::float => (),
+ s if ALLOWED_FORMAT_SYMBOLS.contains(&s) => (),
// So is `{A}` if A is a type parameter
- s => match generics.params.iter().find(|param| param.name == s) {
- Some(_) => (),
- None => {
- let reported = struct_span_err!(
- tcx.sess,
- span,
- E0230,
- "there is no parameter `{}` on {}",
- s,
- if trait_def_id == item_def_id {
- format!("trait `{}`", trait_name)
- } else {
- "impl".to_string()
- }
- )
- .emit();
- result = Err(reported);
- }
- },
+ s if generics.params.iter().any(|param| param.name == s) => (),
+ s => {
+ result = Err(struct_span_err!(
+ tcx.sess,
+ span,
+ E0230,
+ "there is no parameter `{}` on {}",
+ s,
+ if trait_def_id == item_def_id {
+ format!("trait `{}`", trait_name)
+ } else {
+ "impl".to_string()
+ }
+ )
+ .emit());
+ }
}
}
// `{:1}` and `{}` are not to be used
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
index 6ea54b625..39e50b2ac 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs
@@ -1,11 +1,15 @@
-use super::{DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
+// ignore-tidy-filelength
+
+use super::{
+ DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
+ PredicateObligation,
+};
-use crate::autoderef::Autoderef;
use crate::infer::InferCtxt;
-use crate::traits::NormalizeExt;
+use crate::traits::{NormalizeExt, ObligationCtxt};
use hir::def::CtorOf;
-use hir::HirId;
+use hir::{Expr, HirId};
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::{
@@ -22,21 +26,24 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{InferOk, LateBoundRegionConversionTime};
use rustc_middle::hir::map;
+use rustc_middle::ty::error::TypeError::{self, Sorts};
+use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::{
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree,
- GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, IsSuggestable,
- ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
+ GeneratorDiagnosticData, GeneratorInteriorTypeCause, Infer, InferTy, InternalSubsts,
+ IsSuggestable, ToPredicate, Ty, TyCtxt, TypeAndMut, TypeFoldable, TypeFolder,
+ TypeSuperFoldable, TypeVisitable, TypeckResults,
};
-use rustc_middle::ty::{TypeAndMut, TypeckResults};
use rustc_span::symbol::{sym, Ident, Symbol};
-use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP};
+use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP};
use rustc_target::spec::abi;
-use std::fmt;
+use std::ops::Deref;
+use super::method_chain::CollectAllMismatches;
use super::InferCtxtPrivExt;
use crate::infer::InferCtxtExt as _;
use crate::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
#[derive(Debug)]
pub enum GeneratorInteriorOrUpvar {
@@ -191,6 +198,27 @@ pub trait TypeErrCtxtExt<'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
+ fn check_for_binding_assigned_block_without_tail_expression(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut Diagnostic,
+ trait_pred: ty::PolyTraitPredicate<'tcx>,
+ );
+
+ fn suggest_add_clone_to_arg(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut Diagnostic,
+ trait_pred: ty::PolyTraitPredicate<'tcx>,
+ ) -> bool;
+
+ fn extract_callable_info(
+ &self,
+ hir_id: HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)>;
+
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -243,7 +271,7 @@ pub trait TypeErrCtxtExt<'tcx> {
fn point_at_returns_when_relevant(
&self,
- err: &mut Diagnostic,
+ err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
obligation: &PredicateObligation<'tcx>,
);
@@ -254,6 +282,8 @@ pub trait TypeErrCtxtExt<'tcx> {
found: ty::PolyTraitRef<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
+ found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
fn note_conflicting_closure_bounds(
@@ -292,13 +322,13 @@ pub trait TypeErrCtxtExt<'tcx> {
fn note_obligation_cause_code<T>(
&self,
err: &mut Diagnostic,
- predicate: &T,
+ predicate: T,
param_env: ty::ParamEnv<'tcx>,
cause_code: &ObligationCauseCode<'tcx>,
obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
- T: fmt::Display + ToPredicate<'tcx>;
+ T: ToPredicate<'tcx>;
/// Suggest to await before try: future? => future.await?
fn suggest_await_before_try(
@@ -329,6 +359,31 @@ pub trait TypeErrCtxtExt<'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
+ fn note_function_argument_obligation(
+ &self,
+ arg_hir_id: HirId,
+ err: &mut Diagnostic,
+ parent_code: &ObligationCauseCode<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ predicate: ty::Predicate<'tcx>,
+ call_hir_id: HirId,
+ );
+ fn point_at_chain(
+ &self,
+ expr: &hir::Expr<'_>,
+ typeck_results: &TypeckResults<'tcx>,
+ type_diffs: Vec<TypeError<'tcx>>,
+ param_env: ty::ParamEnv<'tcx>,
+ err: &mut Diagnostic,
+ );
+ fn probe_assoc_types_at_expr(
+ &self,
+ type_diffs: &[TypeError<'tcx>],
+ span: Span,
+ prev_ty: Ty<'tcx>,
+ body_id: hir::HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>>;
}
fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {
@@ -348,7 +403,7 @@ fn suggest_restriction<'tcx>(
msg: &str,
err: &mut Diagnostic,
fn_sig: Option<&hir::FnSig<'_>>,
- projection: Option<&ty::ProjectionTy<'_>>,
+ projection: Option<&ty::AliasTy<'_>>,
trait_pred: ty::PolyTraitPredicate<'tcx>,
// When we are dealing with a trait, `super_traits` will be `Some`:
// Given `trait T: A + B + C {}`
@@ -474,7 +529,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let self_ty = trait_pred.skip_binder().self_ty();
let (param_ty, projection) = match self_ty.kind() {
ty::Param(_) => (true, None),
- ty::Projection(projection) => (false, Some(projection)),
+ ty::Alias(ty::Projection, projection) => (false, Some(projection)),
_ => (false, None),
};
@@ -673,7 +728,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
// It only make sense when suggesting dereferences for arguments
- let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code()
+ let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, call_hir_id, .. } = obligation.cause.code()
else { return false; };
let Some(typeck_results) = &self.typeck_results
else { return false; };
@@ -702,26 +757,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
if let ty::Ref(region, base_ty, mutbl) = *real_ty.skip_binder().kind() {
- let mut autoderef = Autoderef::new(
- self,
- obligation.param_env,
- obligation.cause.body_id,
- span,
- base_ty,
- );
- if let Some(steps) = autoderef.find_map(|(ty, steps)| {
- // Re-add the `&`
- let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
-
- // Remapping bound vars here
- let real_trait_pred_and_ty =
- real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
- let obligation = self.mk_trait_obligation_with_new_self_ty(
- obligation.param_env,
- real_trait_pred_and_ty,
- );
- Some(steps).filter(|_| self.predicate_may_hold(&obligation))
- }) {
+ let autoderef = (self.autoderef_steps)(base_ty);
+ if let Some(steps) =
+ autoderef.into_iter().enumerate().find_map(|(steps, (ty, obligations))| {
+ // Re-add the `&`
+ let ty = self.tcx.mk_ref(region, TypeAndMut { ty, mutbl });
+
+ // Remapping bound vars here
+ let real_trait_pred_and_ty =
+ real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));
+ let obligation = self.mk_trait_obligation_with_new_self_ty(
+ obligation.param_env,
+ real_trait_pred_and_ty,
+ );
+ if obligations
+ .iter()
+ .chain([&obligation])
+ .all(|obligation| self.predicate_may_hold(obligation))
+ {
+ Some(steps)
+ } else {
+ None
+ }
+ })
+ {
if steps > 0 {
// Don't care about `&mut` because `DerefMut` is used less
// often and user will not expect autoderef happens.
@@ -752,12 +811,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
real_trait_pred_and_base_ty,
);
if self.predicate_may_hold(&obligation) {
- err.span_suggestion_verbose(
- span.shrink_to_lo(),
- "consider dereferencing here",
- "*",
- Applicability::MachineApplicable,
+ let call_node = self.tcx.hir().get(*call_hir_id);
+ let msg = "consider dereferencing here";
+ let is_receiver = matches!(
+ call_node,
+ Node::Expr(hir::Expr {
+ kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),
+ ..
+ })
+ if receiver_expr.hir_id == *arg_hir_id
);
+ if is_receiver {
+ err.multipart_suggestion_verbose(
+ msg,
+ vec![
+ (span.shrink_to_lo(), "(*".to_string()),
+ (span.shrink_to_hi(), ")".to_string()),
+ ],
+ Applicability::MachineApplicable,
+ )
+ } else {
+ err.span_suggestion_verbose(
+ span.shrink_to_lo(),
+ msg,
+ '*',
+ Applicability::MachineApplicable,
+ )
+ };
return true;
}
}
@@ -786,8 +866,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let hir = self.tcx.hir();
let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?);
- let parent_node = hir.get_parent_node(hir_id);
- match hir.find(parent_node) {
+ match hir.find_parent(hir_id) {
Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => {
get_name(err, &local.pat.kind)
}
@@ -807,6 +886,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
+ // It doesn't make sense to make this suggestion outside of typeck...
+ // (also autoderef will ICE...)
+ if self.typeck_results.is_none() {
+ return false;
+ }
+
if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder()
&& Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait()
{
@@ -814,92 +899,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
return false;
}
- // This is duplicated from `extract_callable_info` in typeck, which
- // relies on autoderef, so we can't use it here.
- let found = trait_pred.self_ty().skip_binder().peel_refs();
- let Some((def_id_or_name, output, inputs)) = (match *found.kind()
- {
- ty::FnPtr(fn_sig) => {
- Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs()))
- }
- ty::FnDef(def_id, _) => {
- let fn_sig = found.fn_sig(self.tcx);
- Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
- }
- ty::Closure(def_id, substs) => {
- let fn_sig = substs.as_closure().sig();
- Some((
- DefIdOrName::DefId(def_id),
- fn_sig.output(),
- fn_sig.inputs().map_bound(|inputs| &inputs[1..]),
- ))
- }
- ty::Opaque(def_id, substs) => {
- self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::DefId(def_id),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Dynamic(data, _, ty::Dyn) => {
- data.iter().find_map(|pred| {
- if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
- && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output()
- // for existential projection, substs are shifted over by 1
- && let ty::Tuple(args) = proj.substs.type_at(0).kind()
- {
- Some((
- DefIdOrName::Name("trait object"),
- pred.rebind(proj.term.ty().unwrap()),
- pred.rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- ty::Param(_) => {
- obligation.param_env.caller_bounds().iter().find_map(|pred| {
- if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
- && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output()
- && proj.projection_ty.self_ty() == found
- // args tuple will always be substs[1]
- && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
- {
- Some((
- DefIdOrName::Name("type parameter"),
- pred.kind().rebind(proj.term.ty().unwrap()),
- pred.kind().rebind(args.as_slice()),
- ))
- } else {
- None
- }
- })
- }
- _ => None,
- }) else { return false; };
- let output = self.replace_bound_vars_with_fresh_vars(
- obligation.cause.span,
+ let self_ty = self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
LateBoundRegionConversionTime::FnCall,
- output,
+ trait_pred.self_ty(),
);
- let inputs = inputs.skip_binder().iter().map(|ty| {
- self.replace_bound_vars_with_fresh_vars(
- obligation.cause.span,
- LateBoundRegionConversionTime::FnCall,
- inputs.rebind(*ty),
- )
- });
+
+ let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(
+ obligation.cause.body_id,
+ obligation.param_env,
+ self_ty,
+ ) else { return false; };
// Remapping bound vars here
let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));
@@ -927,6 +937,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
};
let args = inputs
+ .into_iter()
.map(|ty| {
if ty.is_suggestable(self.tcx, false) {
format!("/* {ty} */")
@@ -981,6 +992,229 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
true
}
+ fn check_for_binding_assigned_block_without_tail_expression(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut Diagnostic,
+ trait_pred: ty::PolyTraitPredicate<'tcx>,
+ ) {
+ let mut span = obligation.cause.span;
+ while span.from_expansion() {
+ // Remove all the desugaring and macro contexts.
+ span.remove_mark();
+ }
+ let mut expr_finder = FindExprBySpan::new(span);
+ let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return; };
+ expr_finder.visit_expr(&body);
+ let Some(expr) = expr_finder.result else { return; };
+ let Some(typeck) = &self.typeck_results else { return; };
+ let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; };
+ if !ty.is_unit() {
+ return;
+ };
+ let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; };
+ let hir::def::Res::Local(hir_id) = path.res else { return; };
+ let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
+ return;
+ };
+ let Some(hir::Node::Local(hir::Local {
+ ty: None,
+ init: Some(init),
+ ..
+ })) = self.tcx.hir().find_parent(pat.hir_id) else { return; };
+ let hir::ExprKind::Block(block, None) = init.kind else { return; };
+ if block.expr.is_some() {
+ return;
+ }
+ let [.., stmt] = block.stmts else {
+ err.span_label(block.span, "this empty block is missing a tail expression");
+ return;
+ };
+ let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; };
+ let Some(ty) = typeck.expr_ty_opt(tail_expr) else {
+ err.span_label(block.span, "this block is missing a tail expression");
+ return;
+ };
+ let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty));
+ let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));
+
+ let new_obligation =
+ self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
+ if self.predicate_must_hold_modulo_regions(&new_obligation) {
+ err.span_suggestion_short(
+ stmt.span.with_lo(tail_expr.span.hi()),
+ "remove this semicolon",
+ "",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_label(block.span, "this block is missing a tail expression");
+ }
+ }
+
+ fn suggest_add_clone_to_arg(
+ &self,
+ obligation: &PredicateObligation<'tcx>,
+ err: &mut Diagnostic,
+ trait_pred: ty::PolyTraitPredicate<'tcx>,
+ ) -> bool {
+ let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());
+ let ty = self.tcx.erase_late_bound_regions(self_ty);
+ let owner = self.tcx.hir().get_parent_item(obligation.cause.body_id);
+ let Some(generics) = self.tcx.hir().get_generics(owner.def_id) else { return false };
+ let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };
+ let ty::Param(param) = inner_ty.kind() else { return false };
+ let ObligationCauseCode::FunctionArgumentObligation { arg_hir_id, .. } = obligation.cause.code() else { return false };
+ let arg_node = self.tcx.hir().get(*arg_hir_id);
+ let Node::Expr(Expr { kind: hir::ExprKind::Path(_), ..}) = arg_node else { return false };
+
+ let clone_trait = self.tcx.require_lang_item(LangItem::Clone, None);
+ let has_clone = |ty| {
+ self.type_implements_trait(clone_trait, [ty], obligation.param_env)
+ .must_apply_modulo_regions()
+ };
+
+ let new_obligation = self.mk_trait_obligation_with_new_self_ty(
+ obligation.param_env,
+ trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),
+ );
+
+ if self.predicate_may_hold(&new_obligation) && has_clone(ty) {
+ if !has_clone(param.to_ty(self.tcx)) {
+ suggest_constraining_type_param(
+ self.tcx,
+ generics,
+ err,
+ param.name.as_str(),
+ "Clone",
+ Some(clone_trait),
+ );
+ }
+ err.span_suggestion_verbose(
+ obligation.cause.span.shrink_to_hi(),
+ "consider using clone here",
+ ".clone()".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ return true;
+ }
+ false
+ }
+
+ /// Extracts information about a callable type for diagnostics. This is a
+ /// heuristic -- it doesn't necessarily mean that a type is always callable,
+ /// because the callable type must also be well-formed to be called.
+ fn extract_callable_info(
+ &self,
+ hir_id: HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ found: Ty<'tcx>,
+ ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {
+ // Autoderef is useful here because sometimes we box callables, etc.
+ let Some((def_id_or_name, output, inputs)) = (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| {
+ match *found.kind() {
+ ty::FnPtr(fn_sig) =>
+ Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())),
+ ty::FnDef(def_id, _) => {
+ let fn_sig = found.fn_sig(self.tcx);
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))
+ }
+ ty::Closure(def_id, substs) => {
+ let fn_sig = substs.as_closure().sig();
+ Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..])))
+ }
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
+ self.tcx.item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ DefIdOrName::DefId(def_id),
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Dynamic(data, _, ty::Dyn) => {
+ data.iter().find_map(|pred| {
+ if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()
+ && Some(proj.def_id) == self.tcx.lang_items().fn_once_output()
+ // for existential projection, substs are shifted over by 1
+ && let ty::Tuple(args) = proj.substs.type_at(0).kind()
+ {
+ Some((
+ DefIdOrName::Name("trait object"),
+ pred.rebind(proj.term.ty().unwrap()),
+ pred.rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ ty::Param(param) => {
+ let generics = self.tcx.generics_of(hir_id.owner.to_def_id());
+ let name = if generics.count() > param.index as usize
+ && let def = generics.param_at(param.index as usize, self.tcx)
+ && matches!(def.kind, ty::GenericParamDefKind::Type { .. })
+ && def.name == param.name
+ {
+ DefIdOrName::DefId(def.def_id)
+ } else {
+ DefIdOrName::Name("type parameter")
+ };
+ param_env.caller_bounds().iter().find_map(|pred| {
+ if let ty::PredicateKind::Clause(ty::Clause::Projection(proj)) = pred.kind().skip_binder()
+ && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output()
+ && proj.projection_ty.self_ty() == found
+ // args tuple will always be substs[1]
+ && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind()
+ {
+ Some((
+ name,
+ pred.kind().rebind(proj.term.ty().unwrap()),
+ pred.kind().rebind(args.as_slice()),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+ _ => None,
+ }
+ }) else { return None; };
+
+ let output = self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::FnCall,
+ output,
+ );
+ let inputs = inputs
+ .skip_binder()
+ .iter()
+ .map(|ty| {
+ self.replace_bound_vars_with_fresh_vars(
+ DUMMY_SP,
+ LateBoundRegionConversionTime::FnCall,
+ inputs.rebind(*ty),
+ )
+ })
+ .collect();
+
+ // We don't want to register any extra obligations, which should be
+ // implied by wf, but also because that would possibly result in
+ // erroneous errors later on.
+ let InferOk { value: output, obligations: _ } =
+ self.at(&ObligationCause::dummy(), param_env).normalize(output);
+
+ if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }
+ }
+
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
@@ -1181,57 +1415,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
- let span = obligation.cause.span;
+ let mut span = obligation.cause.span;
+ let mut trait_pred = trait_pred;
+ let mut code = obligation.cause.code();
+ while let Some((c, Some(parent_trait_pred))) = code.parent() {
+ // We want the root obligation, in order to detect properly handle
+ // `for _ in &mut &mut vec![] {}`.
+ code = c;
+ trait_pred = parent_trait_pred;
+ }
+ while span.desugaring_kind().is_some() {
+ // Remove all the hir desugaring contexts while maintaining the macro contexts.
+ span.remove_mark();
+ }
+ let mut expr_finder = super::FindExprBySpan::new(span);
+ let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else {
+ return false;
+ };
+ expr_finder.visit_expr(&body);
+ let mut maybe_suggest = |suggested_ty, count, suggestions| {
+ // Remapping bound vars here
+ let trait_pred_and_suggested_ty =
+ trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
+
+ let new_obligation = self.mk_trait_obligation_with_new_self_ty(
+ obligation.param_env,
+ trait_pred_and_suggested_ty,
+ );
- let mut suggested = false;
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
- let refs_number =
- snippet.chars().filter(|c| !c.is_whitespace()).take_while(|c| *c == '&').count();
- if let Some('\'') = snippet.chars().filter(|c| !c.is_whitespace()).nth(refs_number) {
- // Do not suggest removal of borrow from type arguments.
- return false;
+ if self.predicate_may_hold(&new_obligation) {
+ let msg = if count == 1 {
+ "consider removing the leading `&`-reference".to_string()
+ } else {
+ format!("consider removing {count} leading `&`-references")
+ };
+
+ err.multipart_suggestion_verbose(
+ &msg,
+ suggestions,
+ Applicability::MachineApplicable,
+ );
+ true
+ } else {
+ false
}
+ };
- // Skipping binder here, remapping below
- let mut suggested_ty = trait_pred.self_ty().skip_binder();
+ // Maybe suggest removal of borrows from types in type parameters, like in
+ // `src/test/ui/not-panic/not-panic-safe.rs`.
+ let mut count = 0;
+ let mut suggestions = vec![];
+ // Skipping binder here, remapping below
+ let mut suggested_ty = trait_pred.self_ty().skip_binder();
+ if let Some(mut hir_ty) = expr_finder.ty_result {
+ while let hir::TyKind::Ref(_, mut_ty) = &hir_ty.kind {
+ count += 1;
+ let span = hir_ty.span.until(mut_ty.ty.span);
+ suggestions.push((span, String::new()));
- for refs_remaining in 0..refs_number {
let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
break;
};
suggested_ty = *inner_ty;
- // Remapping bound vars here
- let trait_pred_and_suggested_ty =
- trait_pred.map_bound(|trait_pred| (trait_pred, suggested_ty));
+ hir_ty = mut_ty.ty;
- let new_obligation = self.mk_trait_obligation_with_new_self_ty(
- obligation.param_env,
- trait_pred_and_suggested_ty,
- );
+ if maybe_suggest(suggested_ty, count, suggestions.clone()) {
+ return true;
+ }
+ }
+ }
- if self.predicate_may_hold(&new_obligation) {
- let sp = self
- .tcx
- .sess
- .source_map()
- .span_take_while(span, |c| c.is_whitespace() || *c == '&');
+ // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
+ let Some(mut expr) = expr_finder.result else { return false; };
+ let mut count = 0;
+ let mut suggestions = vec![];
+ // Skipping binder here, remapping below
+ let mut suggested_ty = trait_pred.self_ty().skip_binder();
+ 'outer: loop {
+ while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {
+ count += 1;
+ let span = if expr.span.eq_ctxt(borrowed.span) {
+ expr.span.until(borrowed.span)
+ } else {
+ expr.span.with_hi(expr.span.lo() + BytePos(1))
+ };
+ suggestions.push((span, String::new()));
- let remove_refs = refs_remaining + 1;
+ let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else {
+ break 'outer;
+ };
+ suggested_ty = *inner_ty;
- let msg = if remove_refs == 1 {
- "consider removing the leading `&`-reference".to_string()
- } else {
- format!("consider removing {} leading `&`-references", remove_refs)
- };
+ expr = borrowed;
- err.span_suggestion_short(sp, &msg, "", Applicability::MachineApplicable);
- suggested = true;
- break;
+ if maybe_suggest(suggested_ty, count, suggestions.clone()) {
+ return true;
}
}
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
+ && let hir::def::Res::Local(hir_id) = path.res
+ && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(hir_id)
+ && let Some(hir::Node::Local(local)) = self.tcx.hir().find_parent(binding.hir_id)
+ && let None = local.ty
+ && let Some(binding_expr) = local.init
+ {
+ expr = binding_expr;
+ } else {
+ break 'outer;
+ }
}
- suggested
+ false
}
fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diagnostic) {
@@ -1239,29 +1533,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() {
let hir = self.tcx.hir();
- if let Some(node) = hir_id.and_then(|hir_id| hir.find(hir_id)) {
- if let hir::Node::Expr(expr) = node {
- // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()`
- // and if not maybe suggest doing something else? If we kept the expression around we
- // could also check if it is an fn call (very likely) and suggest changing *that*, if
- // it is from the local crate.
- err.span_suggestion(
- span,
- "remove the `.await`",
- "",
- Applicability::MachineApplicable,
- );
- // FIXME: account for associated `async fn`s.
- if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) =
- obligation.predicate.kind().skip_binder()
- {
- err.span_label(
- *span,
- &format!("this call returns `{}`", pred.self_ty()),
- );
- }
- if let Some(typeck_results) = &self.typeck_results
+ if let Some(hir::Node::Expr(expr)) = hir_id.and_then(|hir_id| hir.find(hir_id)) {
+ // FIXME: use `obligation.predicate.kind()...trait_ref.self_ty()` to see if we have `()`
+ // and if not maybe suggest doing something else? If we kept the expression around we
+ // could also check if it is an fn call (very likely) and suggest changing *that*, if
+ // it is from the local crate.
+ err.span_suggestion(
+ span,
+ "remove the `.await`",
+ "",
+ Applicability::MachineApplicable,
+ );
+ // FIXME: account for associated `async fn`s.
+ if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr {
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(pred)) =
+ obligation.predicate.kind().skip_binder()
+ {
+ err.span_label(*span, &format!("this call returns `{}`", pred.self_ty()));
+ }
+ if let Some(typeck_results) = &self.typeck_results
&& let ty = typeck_results.expr_ty_adjusted(base)
&& let ty::FnDef(def_id, _substs) = ty.kind()
&& let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) =
@@ -1287,7 +1577,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
}
}
- }
}
}
}
@@ -1347,6 +1636,13 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
.source_map()
.span_take_while(span, |c| c.is_whitespace() || *c == '&');
if points_at_arg && mutability.is_not() && refs_number > 0 {
+ // If we have a call like foo(&mut buf), then don't suggest foo(&mut mut buf)
+ if snippet
+ .trim_start_matches(|c: char| c.is_whitespace() || c == '&')
+ .starts_with("mut")
+ {
+ return;
+ }
err.span_suggestion_verbose(
sp,
"consider changing this borrow's mutability",
@@ -1374,7 +1670,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool {
let hir = self.tcx.hir();
- let parent_node = hir.get_parent_node(obligation.cause.body_id);
+ let parent_node = hir.parent_id(obligation.cause.body_id);
let node = hir.find(parent_node);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. })) = node
&& let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind
@@ -1411,7 +1707,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> {
let hir = self.tcx.hir();
- let parent_node = hir.get_parent_node(obligation.cause.body_id);
+ let parent_node = hir.parent_id(obligation.cause.body_id);
let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. })) = hir.find(parent_node) else {
return None;
};
@@ -1436,7 +1732,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
let hir = self.tcx.hir();
- let fn_hir_id = hir.get_parent_node(obligation.cause.body_id);
+ let fn_hir_id = hir.parent_id(obligation.cause.body_id);
let node = hir.find(fn_hir_id);
let Some(hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn(sig, _, body_id),
@@ -1574,7 +1870,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let trait_obj = if has_dyn { &snippet[4..] } else { &snippet };
if only_never_return {
// No return paths, probably using `panic!()` or similar.
- // Suggest `-> T`, `-> impl Trait`, and if `Trait` is object safe, `-> Box<dyn Trait>`.
+ // Suggest `-> impl Trait`, and if `Trait` is object safe, `-> Box<dyn Trait>`.
suggest_trait_object_return_type_alternatives(
err,
ret_ty.span,
@@ -1639,7 +1935,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn point_at_returns_when_relevant(
&self,
- err: &mut Diagnostic,
+ err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
obligation: &PredicateObligation<'tcx>,
) {
match obligation.cause.code().peel_derives() {
@@ -1648,7 +1944,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
let hir = self.tcx.hir();
- let parent_node = hir.get_parent_node(obligation.cause.body_id);
+ let parent_node = hir.parent_id(obligation.cause.body_id);
let node = hir.find(parent_node);
if let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. })) =
node
@@ -1661,7 +1957,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
for expr in &visitor.returns {
if let Some(returned_ty) = typeck_results.node_type_opt(expr.hir_id) {
let ty = self.resolve_vars_if_possible(returned_ty);
- err.span_label(expr.span, &format!("this returned value is of type `{}`", ty));
+ if ty.references_error() {
+ // don't print out the [type error] here
+ err.delay_as_bug();
+ } else {
+ err.span_label(
+ expr.span,
+ &format!("this returned value is of type `{}`", ty),
+ );
+ }
}
}
}
@@ -1674,6 +1978,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
found: ty::PolyTraitRef<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
+ found_node: Option<Node<'_>>,
+ param_env: ty::ParamEnv<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
pub(crate) fn build_fn_sig_ty<'tcx>(
infcx: &InferCtxt<'tcx>,
@@ -1735,6 +2041,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.note_conflicting_closure_bounds(cause, &mut err);
+ if let Some(found_node) = found_node {
+ hint_missing_borrow(self, param_env, span, found, expected, found_node, &mut err);
+ }
+
err
}
@@ -1755,14 +2065,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
&& self.tcx.is_fn_trait(trait_pred.def_id())
{
let expected_self =
- self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty()));
+ self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty()));
let expected_substs = self
.tcx
- .anonymize_late_bound_regions(pred.kind().rebind(trait_pred.trait_ref.substs));
+ .anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.substs));
// Find another predicate whose self-type is equal to the expected self type,
// but whose substs don't match.
- let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans)
+ let other_pred = predicates.into_iter()
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
ty::PredicateKind::Clause(ty::Clause::Trait(trait_pred))
@@ -1771,12 +2081,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Make sure that the self type matches
// (i.e. constraining this closure)
&& expected_self
- == self.tcx.anonymize_late_bound_regions(
+ == self.tcx.anonymize_bound_vars(
pred.kind().rebind(trait_pred.self_ty()),
)
// But the substs don't match (i.e. incompatible args)
&& expected_substs
- != self.tcx.anonymize_late_bound_regions(
+ != self.tcx.anonymize_bound_vars(
pred.kind().rebind(trait_pred.trait_ref.substs),
) =>
{
@@ -1787,7 +2097,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// If we found one, then it's very likely the cause of the error.
if let Some((_, (_, other_pred_span))) = other_pred {
err.span_note(
- *other_pred_span,
+ other_pred_span,
"closure inferred to have a different signature due to this bound",
);
}
@@ -2004,7 +2314,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// generator interior are not generally known, so we
// want to erase them when comparing (and anyway,
// `Send` and other bounds are generally unaffected by
- // the choice of region). When erasing regions, we
+ // the choice of region). When erasing regions, we
// also have to erase late-bound regions. This is
// because the types that appear in the generator
// interior generally contain "bound regions" to
@@ -2020,7 +2330,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
};
// Get the typeck results from the infcx if the generator is the function we are currently
- // type-checking; otherwise, get them by performing a query. This is needed to avoid
+ // type-checking; otherwise, get them by performing a query. This is needed to avoid
// cycles. If we can't use resolved types because the generator comes from another crate,
// we still provide a targeted error but without all the relevant spans.
let generator_data = match &self.typeck_results {
@@ -2158,15 +2468,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path())
};
- let mut explain_yield = |interior_span: Span,
- yield_span: Span,
- scope_span: Option<Span>| {
- let mut span = MultiSpan::from_span(yield_span);
- if let Ok(snippet) = source_map.span_to_snippet(interior_span) {
- // #70935: If snippet contains newlines, display "the value" instead
- // so that we do not emit complex diagnostics.
- let snippet = &format!("`{}`", snippet);
- let snippet = if snippet.contains('\n') { "the value" } else { snippet };
+ let mut explain_yield =
+ |interior_span: Span, yield_span: Span, scope_span: Option<Span>| {
+ let mut span = MultiSpan::from_span(yield_span);
+ let snippet = match source_map.span_to_snippet(interior_span) {
+ // #70935: If snippet contains newlines, display "the value" instead
+ // so that we do not emit complex diagnostics.
+ Ok(snippet) if !snippet.contains('\n') => format!("`{}`", snippet),
+ _ => "the value".to_string(),
+ };
// note: future is not `Send` as this value is used across an await
// --> $DIR/issue-70935-complex-spans.rs:13:9
// |
@@ -2191,17 +2501,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
interior_span,
format!("has type `{}` which {}", target_ty, trait_explanation),
);
- // If available, use the scope span to annotate the drop location.
- let mut scope_note = None;
if let Some(scope_span) = scope_span {
let scope_span = source_map.end_point(scope_span);
let msg = format!("{} is later dropped here", snippet);
- if source_map.is_multiline(yield_span.between(scope_span)) {
- span.push_span_label(scope_span, msg);
- } else {
- scope_note = Some((scope_span, msg));
- }
+ span.push_span_label(scope_span, msg);
}
err.span_note(
span,
@@ -2210,11 +2514,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
future_or_generator, trait_explanation, an_await_or_yield
),
);
- if let Some((span, msg)) = scope_note {
- err.span_note(span, &msg);
- }
- }
- };
+ };
match interior_or_upvar_span {
GeneratorInteriorOrUpvar::Interior(interior_span, interior_extra_info) => {
if let Some((scope_span, yield_span, expr, from_awaited_ty)) = interior_extra_info {
@@ -2249,7 +2549,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let expr = hir.expect_expr(expr_id);
debug!("target_ty evaluated from {:?}", expr);
- let parent = hir.get_parent_node(expr_id);
+ let parent = hir.parent_id(expr_id);
if let Some(hir::Node::Expr(e)) = hir.find(parent) {
let parent_span = hir.span(parent);
let parent_did = parent.owner.to_def_id();
@@ -2297,28 +2597,33 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
GeneratorInteriorOrUpvar::Upvar(upvar_span) => {
- // `Some(ref_ty)` if `target_ty` is `&T` and `T` fails to impl `Sync`
- let refers_to_non_sync = match target_ty.kind() {
- ty::Ref(_, ref_ty, _) => match self.evaluate_obligation(&obligation) {
- Ok(eval) if !eval.may_apply() => Some(ref_ty),
+ // `Some((ref_ty, is_mut))` if `target_ty` is `&T` or `&mut T` and fails to impl `Send`
+ let non_send = match target_ty.kind() {
+ ty::Ref(_, ref_ty, mutability) => match self.evaluate_obligation(&obligation) {
+ Ok(eval) if !eval.may_apply() => Some((ref_ty, mutability.is_mut())),
_ => None,
},
_ => None,
};
- let (span_label, span_note) = match refers_to_non_sync {
- // if `target_ty` is `&T` and `T` fails to impl `Sync`,
- // include suggestions to make `T: Sync` so that `&T: Send`
- Some(ref_ty) => (
- format!(
- "has type `{}` which {}, because `{}` is not `Sync`",
- target_ty, trait_explanation, ref_ty
- ),
- format!(
- "captured value {} because `&` references cannot be sent unless their referent is `Sync`",
- trait_explanation
- ),
- ),
+ let (span_label, span_note) = match non_send {
+ // if `target_ty` is `&T` or `&mut T` and fails to impl `Send`,
+ // include suggestions to make `T: Sync` so that `&T: Send`,
+ // or to make `T: Send` so that `&mut T: Send`
+ Some((ref_ty, is_mut)) => {
+ let ref_ty_trait = if is_mut { "Send" } else { "Sync" };
+ let ref_kind = if is_mut { "&mut" } else { "&" };
+ (
+ format!(
+ "has type `{}` which {}, because `{}` is not `{}`",
+ target_ty, trait_explanation, ref_ty, ref_ty_trait
+ ),
+ format!(
+ "captured value {} because `{}` references cannot be sent unless their referent is `{}`",
+ trait_explanation, ref_kind, ref_ty_trait
+ ),
+ )
+ }
None => (
format!("has type `{}` which {}", target_ty, trait_explanation),
format!("captured value {}", trait_explanation),
@@ -2336,7 +2641,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
debug!(?next_code);
self.note_obligation_cause_code(
err,
- &obligation.predicate,
+ obligation.predicate,
obligation.param_env,
next_code.unwrap(),
&mut Vec::new(),
@@ -2347,15 +2652,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
fn note_obligation_cause_code<T>(
&self,
err: &mut Diagnostic,
- predicate: &T,
+ predicate: T,
param_env: ty::ParamEnv<'tcx>,
cause_code: &ObligationCauseCode<'tcx>,
obligated_types: &mut Vec<Ty<'tcx>>,
seen_requirements: &mut FxHashSet<DefId>,
) where
- T: fmt::Display + ToPredicate<'tcx>,
+ T: ToPredicate<'tcx>,
{
let tcx = self.tcx;
+ let predicate = predicate.to_predicate(tcx);
match *cause_code {
ObligationCauseCode::ExprAssignable
| ObligationCauseCode::MatchExpressionArm { .. }
@@ -2390,12 +2696,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
err.note("only the last element of a tuple may have a dynamically sized type");
}
ObligationCauseCode::ProjectionWf(data) => {
- err.note(&format!("required so that the projection `{}` is well-formed", data,));
+ err.note(&format!("required so that the projection `{data}` is well-formed"));
}
ObligationCauseCode::ReferenceOutlivesReferent(ref_ty) => {
err.note(&format!(
- "required so that reference `{}` does not outlive its referent",
- ref_ty,
+ "required so that reference `{ref_ty}` does not outlive its referent"
));
}
ObligationCauseCode::ObjectTypeBound(object_ty, region) => {
@@ -2412,21 +2717,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ObligationCauseCode::BindingObligation(item_def_id, span)
| ObligationCauseCode::ExprBindingObligation(item_def_id, span, ..) => {
let item_name = tcx.def_path_str(item_def_id);
+ let short_item_name = with_forced_trimmed_paths!(tcx.def_path_str(item_def_id));
let mut multispan = MultiSpan::from(span);
+ let sm = tcx.sess.source_map();
if let Some(ident) = tcx.opt_item_ident(item_def_id) {
- let sm = tcx.sess.source_map();
let same_line =
match (sm.lookup_line(ident.span.hi()), sm.lookup_line(span.lo())) {
(Ok(l), Ok(r)) => l.line == r.line,
_ => true,
};
- if !ident.span.is_dummy() && !ident.span.overlaps(span) && !same_line {
+ if ident.span.is_visible(sm) && !ident.span.overlaps(span) && !same_line {
multispan.push_span_label(ident.span, "required by a bound in this");
}
}
- let descr = format!("required by a bound in `{}`", item_name);
- if !span.is_dummy() {
- let msg = format!("required by this bound in `{}`", item_name);
+ let descr = format!("required by a bound in `{item_name}`");
+ if span.is_visible(sm) {
+ let msg = format!("required by this bound in `{short_item_name}`");
multispan.push_span_label(span, msg);
err.span_note(multispan, &descr);
} else {
@@ -2434,11 +2740,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
ObligationCauseCode::ObjectCastObligation(concrete_ty, object_ty) => {
- err.note(&format!(
- "required for the cast from `{}` to the object type `{}`",
- self.ty_to_string(concrete_ty),
- self.ty_to_string(object_ty)
- ));
+ let (concrete_ty, concrete_file) =
+ self.tcx.short_ty_string(self.resolve_vars_if_possible(concrete_ty));
+ let (object_ty, object_file) =
+ self.tcx.short_ty_string(self.resolve_vars_if_possible(object_ty));
+ err.note(&with_forced_trimmed_paths!(format!(
+ "required for the cast from `{concrete_ty}` to the object type `{object_ty}`",
+ )));
+ if let Some(file) = concrete_file {
+ err.note(&format!(
+ "the full name for the casted type has been written to '{}'",
+ file.display(),
+ ));
+ }
+ if let Some(file) = object_file {
+ err.note(&format!(
+ "the full name for the object type has been written to '{}'",
+ file.display(),
+ ));
+ }
}
ObligationCauseCode::Coercion { source: _, target } => {
err.note(&format!("required by cast to type `{}`", self.ty_to_string(target)));
@@ -2464,8 +2784,17 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
ObligationCauseCode::VariableType(hir_id) => {
- let parent_node = self.tcx.hir().get_parent_node(hir_id);
+ let parent_node = self.tcx.hir().parent_id(hir_id);
match self.tcx.hir().find(parent_node) {
+ Some(Node::Local(hir::Local { ty: Some(ty), .. })) => {
+ err.span_suggestion_verbose(
+ ty.span.shrink_to_lo(),
+ "consider borrowing here",
+ "&",
+ Applicability::MachineApplicable,
+ );
+ err.note("all local variables must have a statically known size");
+ }
Some(Node::Local(hir::Local {
init: Some(hir::Expr { kind: hir::ExprKind::Index(_, _), span, .. }),
..
@@ -2500,6 +2829,25 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ObligationCauseCode::SizedArgumentType(sp) => {
if let Some(span) = sp {
+ if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder()
+ && let ty::Clause::Trait(trait_pred) = clause
+ && let ty::Dynamic(..) = trait_pred.self_ty().kind()
+ {
+ let span = if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span)
+ && snippet.starts_with("dyn ")
+ {
+ let pos = snippet.len() - snippet[3..].trim_start().len();
+ span.with_hi(span.lo() + BytePos(pos as u32))
+ } else {
+ span.shrink_to_lo()
+ };
+ err.span_suggestion_verbose(
+ span,
+ "you can use `impl Trait` as the argument type",
+ "impl ".to_string(),
+ Applicability::MaybeIncorrect,
+ );
+ }
err.span_suggestion_verbose(
span.shrink_to_lo(),
"function arguments must have a statically known size, borrowed types \
@@ -2616,13 +2964,15 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// Don't print the tuple of capture types
'print: {
if !is_upvar_tys_infer_tuple {
- let msg = format!("required because it appears within the type `{}`", ty);
+ let msg = with_forced_trimmed_paths!(format!(
+ "required because it appears within the type `{ty}`",
+ ));
match ty.kind() {
ty::Adt(def, _) => match self.tcx.opt_item_ident(def.did()) {
Some(ident) => err.span_note(ident.span, &msg),
None => err.note(&msg),
},
- ty::Opaque(def_id, _) => {
+ ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
// Avoid printing the future from `core::future::identity_future`, it's not helpful
if tcx.parent(*def_id) == identity_future {
break 'print;
@@ -2657,7 +3007,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut msg =
"required because it captures the following types: ".to_owned();
for ty in bound_tys.skip_binder() {
- write!(msg, "`{}`, ", ty).unwrap();
+ with_forced_trimmed_paths!(write!(msg, "`{}`, ", ty).unwrap());
}
err.note(msg.trim_end_matches(", "))
}
@@ -2668,12 +3018,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let kind = tcx.generator_kind(def_id).unwrap().descr();
err.span_note(
sp,
- &format!("required because it's used within this {}", kind),
+ with_forced_trimmed_paths!(&format!(
+ "required because it's used within this {kind}",
+ )),
)
}
ty::Closure(def_id, _) => err.span_note(
self.tcx.def_span(def_id),
- &format!("required because it's used within this closure"),
+ "required because it's used within this closure",
),
_ => err.note(&msg),
};
@@ -2688,7 +3040,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
err,
- &parent_predicate,
+ parent_predicate,
param_env,
&data.parent_code,
obligated_types,
@@ -2699,7 +3051,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
err,
- &parent_predicate,
+ parent_predicate,
param_env,
cause_code.peel_derives(),
obligated_types,
@@ -2729,7 +3081,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
// FIXME: we should do something else so that it works even on crate foreign
// auto traits.
is_auto_trait = matches!(is_auto, hir::IsAuto::Yes);
- err.span_note(ident.span, &msg)
+ err.span_note(ident.span, &msg);
}
Some(Node::Item(hir::Item {
kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }),
@@ -2740,9 +3092,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
spans.push(trait_ref.path.span);
}
spans.push(self_ty.span);
- err.span_note(spans, &msg)
+ let mut spans: MultiSpan = spans.into();
+ if matches!(
+ self_ty.span.ctxt().outer_expn_data().kind,
+ ExpnKind::Macro(MacroKind::Derive, _)
+ ) || matches!(
+ of_trait.as_ref().map(|t| t.path.span.ctxt().outer_expn_data().kind),
+ Some(ExpnKind::Macro(MacroKind::Derive, _))
+ ) {
+ spans.push_span_label(
+ data.span,
+ "unsatisfied trait bound introduced in this `derive` macro",
+ );
+ } else if !data.span.is_dummy() && !data.span.overlaps(self_ty.span) {
+ spans.push_span_label(
+ data.span,
+ "unsatisfied trait bound introduced here",
+ );
+ }
+ err.span_note(spans, &msg);
+ }
+ _ => {
+ err.note(&msg);
}
- _ => err.note(&msg),
};
if let Some(file) = file {
@@ -2808,7 +3180,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
err,
- &parent_predicate,
+ parent_predicate,
param_env,
&data.parent_code,
obligated_types,
@@ -2823,7 +3195,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
err,
- &parent_predicate,
+ parent_predicate,
param_env,
&data.parent_code,
obligated_types,
@@ -2835,44 +3207,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
arg_hir_id,
call_hir_id,
ref parent_code,
+ ..
} => {
- let hir = self.tcx.hir();
- if let Some(Node::Expr(expr @ hir::Expr { kind: hir::ExprKind::Block(..), .. })) =
- hir.find(arg_hir_id)
- {
- let parent_id = hir.get_parent_item(arg_hir_id);
- let typeck_results: &TypeckResults<'tcx> = match &self.typeck_results {
- Some(t) if t.hir_owner == parent_id => t,
- _ => self.tcx.typeck(parent_id.def_id),
- };
- let expr = expr.peel_blocks();
- let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error());
- let span = expr.span;
- if Some(span) != err.span.primary_span() {
- err.span_label(
- span,
- if ty.references_error() {
- String::new()
- } else {
- format!("this tail expression is of type `{:?}`", ty)
- },
- );
- }
- }
- if let Some(Node::Expr(hir::Expr {
- kind:
- hir::ExprKind::Call(hir::Expr { span, .. }, _)
- | hir::ExprKind::MethodCall(
- hir::PathSegment { ident: Ident { span, .. }, .. },
- ..,
- ),
- ..
- })) = hir.find(call_hir_id)
- {
- if Some(*span) != err.span.primary_span() {
- err.span_label(*span, "required by a bound introduced by this call");
- }
- }
+ self.note_function_argument_obligation(
+ arg_hir_id,
+ err,
+ parent_code,
+ param_env,
+ predicate,
+ call_hir_id,
+ );
ensure_sufficient_stack(|| {
self.note_obligation_cause_code(
err,
@@ -2887,9 +3231,8 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
ObligationCauseCode::CompareImplItemObligation { trait_item_def_id, kind, .. } => {
let item_name = self.tcx.item_name(trait_item_def_id);
let msg = format!(
- "the requirement `{}` appears on the `impl`'s {kind} `{}` but not on the \
- corresponding trait's {kind}",
- predicate, item_name,
+ "the requirement `{predicate}` appears on the `impl`'s {kind} \
+ `{item_name}` but not on the corresponding trait's {kind}",
);
let sp = self
.tcx
@@ -2899,7 +3242,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let mut assoc_span: MultiSpan = sp.into();
assoc_span.push_span_label(
sp,
- format!("this trait's {kind} doesn't have the requirement `{}`", predicate),
+ format!("this trait's {kind} doesn't have the requirement `{predicate}`"),
);
if let Some(ident) = self
.tcx
@@ -2918,10 +3261,12 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
ObligationCauseCode::OpaqueReturnType(expr_info) => {
if let Some((expr_ty, expr_span)) = expr_info {
- let expr_ty = self.resolve_vars_if_possible(expr_ty);
+ let expr_ty = with_forced_trimmed_paths!(self.ty_to_string(expr_ty));
err.span_label(
expr_span,
- format!("return type was inferred to be `{expr_ty}` here"),
+ with_forced_trimmed_paths!(format!(
+ "return type was inferred to be `{expr_ty}` here",
+ )),
);
}
}
@@ -2939,7 +3284,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
span: Span,
) {
let body_hir_id = obligation.cause.body_id;
- let item_id = self.tcx.hir().get_parent_node(body_hir_id);
+ let item_id = self.tcx.hir().parent_id(body_hir_id);
if let Some(body_id) =
self.tcx.hir().maybe_body_owned_by(self.tcx.hir().local_def_id(item_id))
@@ -2964,7 +3309,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
self.tcx.mk_projection(
item_def_id,
// Future::Output has no substs
- self.tcx.mk_substs_trait(trait_pred.self_ty(), []),
+ [trait_pred.self_ty()],
)
});
let InferOk { value: projection_ty, .. } =
@@ -3098,6 +3443,393 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
);
}
}
+ fn note_function_argument_obligation(
+ &self,
+ arg_hir_id: HirId,
+ err: &mut Diagnostic,
+ parent_code: &ObligationCauseCode<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ failed_pred: ty::Predicate<'tcx>,
+ call_hir_id: HirId,
+ ) {
+ let tcx = self.tcx;
+ let hir = tcx.hir();
+ if let Some(Node::Expr(expr)) = hir.find(arg_hir_id)
+ && let Some(typeck_results) = &self.typeck_results
+ {
+ if let hir::Expr { kind: hir::ExprKind::Block(..), .. } = expr {
+ let expr = expr.peel_blocks();
+ let ty = typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error());
+ let span = expr.span;
+ if Some(span) != err.span.primary_span() {
+ err.span_label(
+ span,
+ if ty.references_error() {
+ String::new()
+ } else {
+ let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
+ format!("this tail expression is of type `{ty}`")
+ },
+ );
+ }
+ }
+
+ // FIXME: visit the ty to see if there's any closure involved, and if there is,
+ // check whether its evaluated return type is the same as the one corresponding
+ // to an associated type (as seen from `trait_pred`) in the predicate. Like in
+ // trait_pred `S: Sum<<Self as Iterator>::Item>` and predicate `i32: Sum<&()>`
+ let mut type_diffs = vec![];
+
+ if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = parent_code.deref()
+ && let Some(node_substs) = typeck_results.node_substs_opt(call_hir_id)
+ && let where_clauses = self.tcx.predicates_of(def_id).instantiate(self.tcx, node_substs)
+ && let Some(where_pred) = where_clauses.predicates.get(*idx)
+ {
+ if let Some(where_pred) = where_pred.to_opt_poly_trait_pred()
+ && let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred()
+ {
+ let mut c = CollectAllMismatches {
+ infcx: self.infcx,
+ param_env,
+ errors: vec![],
+ };
+ if let Ok(_) = c.relate(where_pred, failed_pred) {
+ type_diffs = c.errors;
+ }
+ } else if let Some(where_pred) = where_pred.to_opt_poly_projection_pred()
+ && let Some(failed_pred) = failed_pred.to_opt_poly_projection_pred()
+ && let Some(found) = failed_pred.skip_binder().term.ty()
+ {
+ type_diffs = vec![
+ Sorts(ty::error::ExpectedFound {
+ expected: self.tcx.mk_ty(ty::Alias(ty::Projection, where_pred.skip_binder().projection_ty)),
+ found,
+ }),
+ ];
+ }
+ }
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
+ && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path
+ && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id)
+ && let parent_hir_id = self.tcx.hir().parent_id(binding.hir_id)
+ && let Some(hir::Node::Local(local)) = self.tcx.hir().find(parent_hir_id)
+ && let Some(binding_expr) = local.init
+ {
+ // If the expression we're calling on is a binding, we want to point at the
+ // `let` when talking about the type. Otherwise we'll point at every part
+ // of the method chain with the type.
+ self.point_at_chain(binding_expr, &typeck_results, type_diffs, param_env, err);
+ } else {
+ self.point_at_chain(expr, &typeck_results, type_diffs, param_env, err);
+ }
+ }
+ let call_node = hir.find(call_hir_id);
+ if let Some(Node::Expr(hir::Expr {
+ kind: hir::ExprKind::MethodCall(path, rcvr, ..), ..
+ })) = call_node
+ {
+ if Some(rcvr.span) == err.span.primary_span() {
+ err.replace_span_with(path.ident.span, true);
+ }
+ }
+ if let Some(Node::Expr(hir::Expr {
+ kind:
+ hir::ExprKind::Call(hir::Expr { span, .. }, _)
+ | hir::ExprKind::MethodCall(hir::PathSegment { ident: Ident { span, .. }, .. }, ..),
+ ..
+ })) = hir.find(call_hir_id)
+ {
+ if Some(*span) != err.span.primary_span() {
+ err.span_label(*span, "required by a bound introduced by this call");
+ }
+ }
+ }
+
+ fn point_at_chain(
+ &self,
+ expr: &hir::Expr<'_>,
+ typeck_results: &TypeckResults<'tcx>,
+ type_diffs: Vec<TypeError<'tcx>>,
+ param_env: ty::ParamEnv<'tcx>,
+ err: &mut Diagnostic,
+ ) {
+ let mut primary_spans = vec![];
+ let mut span_labels = vec![];
+
+ let tcx = self.tcx;
+
+ let mut print_root_expr = true;
+ let mut assocs = vec![];
+ let mut expr = expr;
+ let mut prev_ty = self.resolve_vars_if_possible(
+ typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()),
+ );
+ while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
+ // Point at every method call in the chain with the resulting type.
+ // vec![1, 2, 3].iter().map(mapper).sum<i32>()
+ // ^^^^^^ ^^^^^^^^^^^
+ expr = rcvr_expr;
+ let assocs_in_this_method =
+ self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
+ assocs.push(assocs_in_this_method);
+ prev_ty = self.resolve_vars_if_possible(
+ typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(tcx.ty_error()),
+ );
+
+ if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind
+ && let hir::Path { res: hir::def::Res::Local(hir_id), .. } = path
+ && let Some(hir::Node::Pat(binding)) = self.tcx.hir().find(*hir_id)
+ && let Some(parent) = self.tcx.hir().find_parent(binding.hir_id)
+ {
+ // We've reached the root of the method call chain...
+ if let hir::Node::Local(local) = parent
+ && let Some(binding_expr) = local.init
+ {
+ // ...and it is a binding. Get the binding creation and continue the chain.
+ expr = binding_expr;
+ }
+ if let hir::Node::Param(param) = parent {
+ // ...and it is a an fn argument.
+ let prev_ty = self.resolve_vars_if_possible(
+ typeck_results.node_type_opt(param.hir_id).unwrap_or(tcx.ty_error()),
+ );
+ let assocs_in_this_method = self.probe_assoc_types_at_expr(&type_diffs, param.ty_span, prev_ty, param.hir_id, param_env);
+ if assocs_in_this_method.iter().any(|a| a.is_some()) {
+ assocs.push(assocs_in_this_method);
+ print_root_expr = false;
+ }
+ break;
+ }
+ }
+ }
+ // We want the type before deref coercions, otherwise we talk about `&[_]`
+ // instead of `Vec<_>`.
+ if let Some(ty) = typeck_results.expr_ty_opt(expr) && print_root_expr {
+ let ty = with_forced_trimmed_paths!(self.ty_to_string(ty));
+ // Point at the root expression
+ // vec![1, 2, 3].iter().map(mapper).sum<i32>()
+ // ^^^^^^^^^^^^^
+ span_labels.push((expr.span, format!("this expression has type `{ty}`")));
+ };
+ // Only show this if it is not a "trivial" expression (not a method
+ // chain) and there are associated types to talk about.
+ let mut assocs = assocs.into_iter().peekable();
+ while let Some(assocs_in_method) = assocs.next() {
+ let Some(prev_assoc_in_method) = assocs.peek() else {
+ for entry in assocs_in_method {
+ let Some((span, (assoc, ty))) = entry else { continue; };
+ if primary_spans.is_empty() || type_diffs.iter().any(|diff| {
+ let Sorts(expected_found) = diff else { return false; };
+ self.can_eq(param_env, expected_found.found, ty).is_ok()
+ }) {
+ // FIXME: this doesn't quite work for `Iterator::collect`
+ // because we have `Vec<i32>` and `()`, but we'd want `i32`
+ // to point at the `.into_iter()` call, but as long as we
+ // still point at the other method calls that might have
+ // introduced the issue, this is fine for now.
+ primary_spans.push(span);
+ }
+ span_labels.push((
+ span,
+ with_forced_trimmed_paths!(format!(
+ "`{}` is `{ty}` here",
+ self.tcx.def_path_str(assoc),
+ )),
+ ));
+ }
+ break;
+ };
+ for (entry, prev_entry) in
+ assocs_in_method.into_iter().zip(prev_assoc_in_method.into_iter())
+ {
+ match (entry, prev_entry) {
+ (Some((span, (assoc, ty))), Some((_, (_, prev_ty)))) => {
+ let ty_str = with_forced_trimmed_paths!(self.ty_to_string(ty));
+
+ let assoc = with_forced_trimmed_paths!(self.tcx.def_path_str(assoc));
+ if self.can_eq(param_env, ty, *prev_ty).is_err() {
+ if type_diffs.iter().any(|diff| {
+ let Sorts(expected_found) = diff else { return false; };
+ self.can_eq(param_env, expected_found.found, ty).is_ok()
+ }) {
+ primary_spans.push(span);
+ }
+ span_labels
+ .push((span, format!("`{assoc}` changed to `{ty_str}` here")));
+ } else {
+ span_labels.push((span, format!("`{assoc}` remains `{ty_str}` here")));
+ }
+ }
+ (Some((span, (assoc, ty))), None) => {
+ span_labels.push((
+ span,
+ with_forced_trimmed_paths!(format!(
+ "`{}` is `{}` here",
+ self.tcx.def_path_str(assoc),
+ self.ty_to_string(ty),
+ )),
+ ));
+ }
+ (None, Some(_)) | (None, None) => {}
+ }
+ }
+ }
+ if !primary_spans.is_empty() {
+ let mut multi_span: MultiSpan = primary_spans.into();
+ for (span, label) in span_labels {
+ multi_span.push_span_label(span, label);
+ }
+ err.span_note(
+ multi_span,
+ "the method call chain might not have had the expected associated types",
+ );
+ }
+ }
+
+ fn probe_assoc_types_at_expr(
+ &self,
+ type_diffs: &[TypeError<'tcx>],
+ span: Span,
+ prev_ty: Ty<'tcx>,
+ body_id: hir::HirId,
+ param_env: ty::ParamEnv<'tcx>,
+ ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> {
+ let ocx = ObligationCtxt::new_in_snapshot(self.infcx);
+ let mut assocs_in_this_method = Vec::with_capacity(type_diffs.len());
+ for diff in type_diffs {
+ let Sorts(expected_found) = diff else { continue; };
+ let ty::Alias(ty::Projection, proj) = expected_found.expected.kind() else { continue; };
+
+ let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
+ let trait_def_id = proj.trait_def_id(self.tcx);
+ // Make `Self` be equivalent to the type of the call chain
+ // expression we're looking at now, so that we can tell what
+ // for example `Iterator::Item` is at this point in the chain.
+ let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| {
+ match param.kind {
+ ty::GenericParamDefKind::Type { .. } => {
+ if param.index == 0 {
+ return prev_ty.into();
+ }
+ }
+ ty::GenericParamDefKind::Lifetime | ty::GenericParamDefKind::Const { .. } => {}
+ }
+ self.var_for_def(span, param)
+ });
+ // This will hold the resolved type of the associated type, if the
+ // current expression implements the trait that associated type is
+ // in. For example, this would be what `Iterator::Item` is here.
+ let ty_var = self.infcx.next_ty_var(origin);
+ // This corresponds to `<ExprTy as Iterator>::Item = _`.
+ let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
+ ty::ProjectionPredicate {
+ projection_ty: self.tcx.mk_alias_ty(proj.def_id, substs),
+ term: ty_var.into(),
+ },
+ )));
+ // Add `<ExprTy as Iterator>::Item = _` obligation.
+ ocx.register_obligation(Obligation::misc(
+ self.tcx, span, body_id, param_env, projection,
+ ));
+ if ocx.select_where_possible().is_empty() {
+ // `ty_var` now holds the type that `Item` is for `ExprTy`.
+ let ty_var = self.resolve_vars_if_possible(ty_var);
+ assocs_in_this_method.push(Some((span, (proj.def_id, ty_var))));
+ } else {
+ // `<ExprTy as Iterator>` didn't select, so likely we've
+ // reached the end of the iterator chain, like the originating
+ // `Vec<_>`.
+ // Keep the space consistent for later zipping.
+ assocs_in_this_method.push(None);
+ }
+ }
+ assocs_in_this_method
+ }
+}
+
+/// Add a hint to add a missing borrow or remove an unnecessary one.
+fn hint_missing_borrow<'tcx>(
+ infcx: &InferCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Span,
+ found: Ty<'tcx>,
+ expected: Ty<'tcx>,
+ found_node: Node<'_>,
+ err: &mut Diagnostic,
+) {
+ let found_args = match found.kind() {
+ ty::FnPtr(f) => f.inputs().skip_binder().iter(),
+ kind => {
+ span_bug!(span, "found was converted to a FnPtr above but is now {:?}", kind)
+ }
+ };
+ let expected_args = match expected.kind() {
+ ty::FnPtr(f) => f.inputs().skip_binder().iter(),
+ kind => {
+ span_bug!(span, "expected was converted to a FnPtr above but is now {:?}", kind)
+ }
+ };
+
+ // This could be a variant constructor, for example.
+ let Some(fn_decl) = found_node.fn_decl() else { return; };
+
+ let args = fn_decl.inputs.iter().map(|ty| ty);
+
+ fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
+ let mut refs = 0;
+
+ while let ty::Ref(_, new_ty, _) = ty.kind() {
+ ty = *new_ty;
+ refs += 1;
+ }
+
+ (ty, refs)
+ }
+
+ let mut to_borrow = Vec::new();
+ let mut remove_borrow = Vec::new();
+
+ for ((found_arg, expected_arg), arg) in found_args.zip(expected_args).zip(args) {
+ let (found_ty, found_refs) = get_deref_type_and_refs(*found_arg);
+ let (expected_ty, expected_refs) = get_deref_type_and_refs(*expected_arg);
+
+ if infcx.can_eq(param_env, found_ty, expected_ty).is_ok() {
+ if found_refs < expected_refs {
+ to_borrow.push((arg.span.shrink_to_lo(), "&".repeat(expected_refs - found_refs)));
+ } else if found_refs > expected_refs {
+ let mut span = arg.span.shrink_to_lo();
+ let mut left = found_refs - expected_refs;
+ let mut ty = arg;
+ while let hir::TyKind::Ref(_, mut_ty) = &ty.kind && left > 0 {
+ span = span.with_hi(mut_ty.ty.span.lo());
+ ty = mut_ty.ty;
+ left -= 1;
+ }
+ let sugg = if left == 0 {
+ (span, String::new())
+ } else {
+ (arg.span, expected_arg.to_string())
+ };
+ remove_borrow.push(sugg);
+ }
+ }
+ }
+
+ if !to_borrow.is_empty() {
+ err.multipart_suggestion_verbose(
+ "consider borrowing the argument",
+ to_borrow,
+ Applicability::MaybeIncorrect,
+ );
+ }
+
+ if !remove_borrow.is_empty() {
+ err.multipart_suggestion_verbose(
+ "do not borrow the argument",
+ remove_borrow,
+ Applicability::MaybeIncorrect,
+ );
+ }
}
/// Collect all the returned expressions within the input expression.
@@ -3207,13 +3939,6 @@ fn suggest_trait_object_return_type_alternatives(
) {
err.span_suggestion(
ret_ty,
- "use some type `T` that is `T: Sized` as the return type if all return paths have the \
- same type",
- "T",
- Applicability::MaybeIncorrect,
- );
- err.span_suggestion(
- ret_ty,
&format!(
"use `impl {}` as the return type if all return paths have the same type but you \
want to expose only the trait in the signature",