diff options
Diffstat (limited to 'compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs')
-rw-r--r-- | compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs | 1365 |
1 files changed, 1045 insertions, 320 deletions
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", |