| // ignore-tidy-filelength |
| |
| use super::{ |
| DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode, |
| PredicateObligation, |
| }; |
| |
| use crate::errors; |
| use crate::infer::InferCtxt; |
| use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt}; |
| |
| use hir::def::CtorOf; |
| use rustc_data_structures::fx::FxHashSet; |
| use rustc_data_structures::stack::ensure_sufficient_stack; |
| use rustc_errors::{ |
| codes::*, pluralize, struct_span_code_err, Applicability, Diag, EmissionGuarantee, MultiSpan, |
| Style, SuggestionStyle, |
| }; |
| use rustc_hir as hir; |
| use rustc_hir::def::{DefKind, Res}; |
| use rustc_hir::def_id::DefId; |
| use rustc_hir::intravisit::Visitor; |
| use rustc_hir::is_range_literal; |
| use rustc_hir::lang_items::LangItem; |
| use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node}; |
| use rustc_infer::infer::error_reporting::TypeErrCtxt; |
| use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk}; |
| use rustc_macros::extension; |
| use rustc_middle::hir::map; |
| use rustc_middle::traits::IsConstable; |
| use rustc_middle::ty::error::TypeError::{self, Sorts}; |
| use rustc_middle::ty::{ |
| self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs, |
| InferTy, IsSuggestable, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, |
| TypeVisitableExt, TypeckResults, |
| }; |
| use rustc_span::def_id::LocalDefId; |
| use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
| use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span, DUMMY_SP}; |
| use rustc_target::spec::abi; |
| use std::assert_matches::debug_assert_matches; |
| use std::borrow::Cow; |
| use std::iter; |
| |
| use crate::infer::InferCtxtExt as _; |
| use crate::traits::error_reporting::type_err_ctxt_ext::InferCtxtPrivExt; |
| use crate::traits::query::evaluate_obligation::InferCtxtExt as _; |
| use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths}; |
| |
| use itertools::EitherOrBoth; |
| use itertools::Itertools; |
| |
| #[derive(Debug)] |
| pub enum CoroutineInteriorOrUpvar { |
| // span of interior type |
| Interior(Span, Option<(Span, Option<Span>)>), |
| // span of upvar |
| Upvar(Span), |
| } |
| |
| // This type provides a uniform interface to retrieve data on coroutines, whether it originated from |
| // the local crate being compiled or from a foreign crate. |
| #[derive(Debug)] |
| struct CoroutineData<'tcx, 'a>(&'a TypeckResults<'tcx>); |
| |
| impl<'tcx, 'a> CoroutineData<'tcx, 'a> { |
| /// Try to get information about variables captured by the coroutine that matches a type we are |
| /// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to |
| /// meet an obligation |
| fn try_get_upvar_span<F>( |
| &self, |
| infer_context: &InferCtxt<'tcx>, |
| coroutine_did: DefId, |
| ty_matches: F, |
| ) -> Option<CoroutineInteriorOrUpvar> |
| where |
| F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool, |
| { |
| infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| { |
| upvars.iter().find_map(|(upvar_id, upvar)| { |
| let upvar_ty = self.0.node_type(*upvar_id); |
| let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty); |
| ty_matches(ty::Binder::dummy(upvar_ty)) |
| .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span)) |
| }) |
| }) |
| } |
| |
| /// Try to get the span of a type being awaited on that matches the type we are looking with the |
| /// `ty_matches` function. We uses it to find awaited type which causes a failure to meet an |
| /// obligation |
| fn get_from_await_ty<F>( |
| &self, |
| visitor: AwaitsVisitor, |
| hir: map::Map<'tcx>, |
| ty_matches: F, |
| ) -> Option<Span> |
| where |
| F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool, |
| { |
| visitor |
| .awaits |
| .into_iter() |
| .map(|id| hir.expect_expr(id)) |
| .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr)))) |
| .map(|expr| expr.span) |
| } |
| } |
| |
| fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) { |
| ( |
| generics.tail_span_for_predicate_suggestion(), |
| format!("{} {}", generics.add_where_or_trailing_comma(), pred), |
| ) |
| } |
| |
| /// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but |
| /// it can also be an `impl Trait` param that needs to be decomposed to a type |
| /// param for cleaner code. |
| pub fn suggest_restriction<'tcx, G: EmissionGuarantee>( |
| tcx: TyCtxt<'tcx>, |
| item_id: LocalDefId, |
| hir_generics: &hir::Generics<'tcx>, |
| msg: &str, |
| err: &mut Diag<'_, G>, |
| fn_sig: Option<&hir::FnSig<'_>>, |
| 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 {}` |
| // - ^^^^^^^^^ GenericBounds |
| // | |
| // &Ident |
| super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>, |
| ) { |
| if hir_generics.where_clause_span.from_expansion() |
| || hir_generics.where_clause_span.desugaring_kind().is_some() |
| || projection.is_some_and(|projection| tcx.is_impl_trait_in_trait(projection.def_id)) |
| { |
| return; |
| } |
| let generics = tcx.generics_of(item_id); |
| // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`... |
| if let Some((param, bound_str, fn_sig)) = |
| fn_sig.zip(projection).and_then(|(sig, p)| match *p.self_ty().kind() { |
| // Shenanigans to get the `Trait` from the `impl Trait`. |
| ty::Param(param) => { |
| let param_def = generics.type_param(param, tcx); |
| if param_def.kind.is_synthetic() { |
| let bound_str = |
| param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string(); |
| return Some((param_def, bound_str, sig)); |
| } |
| None |
| } |
| _ => None, |
| }) |
| { |
| let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str)); |
| let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder { |
| tcx, |
| param, |
| replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name)) |
| .to_ty(tcx), |
| }); |
| if !trait_pred.is_suggestable(tcx, false) { |
| return; |
| } |
| // We know we have an `impl Trait` that doesn't satisfy a required projection. |
| |
| // Find all of the occurrences of `impl Trait` for `Trait` in the function arguments' |
| // types. There should be at least one, but there might be *more* than one. In that |
| // case we could just ignore it and try to identify which one needs the restriction, |
| // but instead we choose to suggest replacing all instances of `impl Trait` with `T` |
| // where `T: Trait`. |
| let mut ty_spans = vec![]; |
| for input in fn_sig.decl.inputs { |
| ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id } |
| .visit_ty(input); |
| } |
| // The type param `T: Trait` we will suggest to introduce. |
| let type_param = format!("{type_param_name}: {bound_str}"); |
| |
| let mut sugg = vec![ |
| if let Some(span) = hir_generics.span_for_param_suggestion() { |
| (span, format!(", {type_param}")) |
| } else { |
| (hir_generics.span, format!("<{type_param}>")) |
| }, |
| // `fn foo(t: impl Trait)` |
| // ^ suggest `where <T as Trait>::A: Bound` |
| predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)), |
| ]; |
| sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string()))); |
| |
| // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`. |
| // FIXME: we should suggest `fn foo(t: impl Trait<A: Bound>)` instead. |
| err.multipart_suggestion( |
| "introduce a type parameter with a trait bound instead of using `impl Trait`", |
| sugg, |
| Applicability::MaybeIncorrect, |
| ); |
| } else { |
| if !trait_pred.is_suggestable(tcx, false) { |
| return; |
| } |
| // Trivial case: `T` needs an extra bound: `T: Bound`. |
| let (sp, suggestion) = match ( |
| hir_generics |
| .params |
| .iter() |
| .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })), |
| super_traits, |
| ) { |
| (_, None) => predicate_constraint(hir_generics, trait_pred.to_predicate(tcx)), |
| (None, Some((ident, []))) => ( |
| ident.span.shrink_to_hi(), |
| format!(": {}", trait_pred.print_modifiers_and_trait_path()), |
| ), |
| (_, Some((_, [.., bounds]))) => ( |
| bounds.span().shrink_to_hi(), |
| format!(" + {}", trait_pred.print_modifiers_and_trait_path()), |
| ), |
| (Some(_), Some((_, []))) => ( |
| hir_generics.span.shrink_to_hi(), |
| format!(": {}", trait_pred.print_modifiers_and_trait_path()), |
| ), |
| }; |
| |
| err.span_suggestion_verbose( |
| sp, |
| format!("consider further restricting {msg}"), |
| suggestion, |
| Applicability::MachineApplicable, |
| ); |
| } |
| } |
| |
| #[extension(pub trait TypeErrCtxtExt<'tcx>)] |
| impl<'tcx> TypeErrCtxt<'_, 'tcx> { |
| fn suggest_restricting_param_bound( |
| &self, |
| err: &mut Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| associated_ty: Option<(&'static str, Ty<'tcx>)>, |
| mut body_id: LocalDefId, |
| ) { |
| if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive { |
| return; |
| } |
| |
| let trait_pred = self.resolve_numeric_literals_with_default(trait_pred); |
| |
| let self_ty = trait_pred.skip_binder().self_ty(); |
| let (param_ty, projection) = match *self_ty.kind() { |
| ty::Param(_) => (true, None), |
| ty::Alias(ty::Projection, projection) => (false, Some(projection)), |
| _ => (false, None), |
| }; |
| |
| // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we |
| // don't suggest `T: Sized + ?Sized`. |
| loop { |
| let node = self.tcx.hir_node_by_def_id(body_id); |
| match node { |
| hir::Node::Item(hir::Item { |
| ident, |
| kind: hir::ItemKind::Trait(_, _, generics, bounds, _), |
| .. |
| }) if self_ty == self.tcx.types.self_param => { |
| assert!(param_ty); |
| // Restricting `Self` for a single method. |
| suggest_restriction( |
| self.tcx, |
| body_id, |
| generics, |
| "`Self`", |
| err, |
| None, |
| projection, |
| trait_pred, |
| Some((ident, bounds)), |
| ); |
| return; |
| } |
| |
| hir::Node::TraitItem(hir::TraitItem { |
| generics, |
| kind: hir::TraitItemKind::Fn(..), |
| .. |
| }) if self_ty == self.tcx.types.self_param => { |
| assert!(param_ty); |
| // Restricting `Self` for a single method. |
| suggest_restriction( |
| self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred, |
| None, |
| ); |
| return; |
| } |
| |
| hir::Node::TraitItem(hir::TraitItem { |
| generics, |
| kind: hir::TraitItemKind::Fn(fn_sig, ..), |
| .. |
| }) |
| | hir::Node::ImplItem(hir::ImplItem { |
| generics, |
| kind: hir::ImplItemKind::Fn(fn_sig, ..), |
| .. |
| }) |
| | hir::Node::Item(hir::Item { |
| kind: hir::ItemKind::Fn(fn_sig, generics, _), .. |
| }) if projection.is_some() => { |
| // Missing restriction on associated type of type parameter (unmet projection). |
| suggest_restriction( |
| self.tcx, |
| body_id, |
| generics, |
| "the associated type", |
| err, |
| Some(fn_sig), |
| projection, |
| trait_pred, |
| None, |
| ); |
| return; |
| } |
| hir::Node::Item(hir::Item { |
| kind: |
| hir::ItemKind::Trait(_, _, generics, ..) |
| | hir::ItemKind::Impl(hir::Impl { generics, .. }), |
| .. |
| }) if projection.is_some() => { |
| // Missing restriction on associated type of type parameter (unmet projection). |
| suggest_restriction( |
| self.tcx, |
| body_id, |
| generics, |
| "the associated type", |
| err, |
| None, |
| projection, |
| trait_pred, |
| None, |
| ); |
| return; |
| } |
| |
| hir::Node::Item(hir::Item { |
| kind: |
| hir::ItemKind::Struct(_, generics) |
| | hir::ItemKind::Enum(_, generics) |
| | hir::ItemKind::Union(_, generics) |
| | hir::ItemKind::Trait(_, _, generics, ..) |
| | hir::ItemKind::Impl(hir::Impl { generics, .. }) |
| | hir::ItemKind::Fn(_, generics, _) |
| | hir::ItemKind::TyAlias(_, generics) |
| | hir::ItemKind::Const(_, generics, _) |
| | hir::ItemKind::TraitAlias(generics, _) |
| | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), |
| .. |
| }) |
| | hir::Node::TraitItem(hir::TraitItem { generics, .. }) |
| | hir::Node::ImplItem(hir::ImplItem { generics, .. }) |
| if param_ty => |
| { |
| // We skip the 0'th arg (self) because we do not want |
| // to consider the predicate as not suggestible if the |
| // self type is an arg position `impl Trait` -- instead, |
| // we handle that by adding ` + Bound` below. |
| // FIXME(compiler-errors): It would be nice to do the same |
| // this that we do in `suggest_restriction` and pull the |
| // `impl Trait` into a new generic if it shows up somewhere |
| // else in the predicate. |
| if !trait_pred.skip_binder().trait_ref.args[1..] |
| .iter() |
| .all(|g| g.is_suggestable(self.tcx, false)) |
| { |
| return; |
| } |
| // Missing generic type parameter bound. |
| let param_name = self_ty.to_string(); |
| let mut constraint = with_no_trimmed_paths!( |
| trait_pred.print_modifiers_and_trait_path().to_string() |
| ); |
| |
| if let Some((name, term)) = associated_ty { |
| // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err. |
| // That should be extracted into a helper function. |
| if constraint.ends_with('>') { |
| constraint = format!( |
| "{}, {} = {}>", |
| &constraint[..constraint.len() - 1], |
| name, |
| term |
| ); |
| } else { |
| constraint.push_str(&format!("<{name} = {term}>")); |
| } |
| } |
| |
| if suggest_constraining_type_param( |
| self.tcx, |
| generics, |
| err, |
| ¶m_name, |
| &constraint, |
| Some(trait_pred.def_id()), |
| None, |
| ) { |
| return; |
| } |
| } |
| |
| hir::Node::Item(hir::Item { |
| kind: |
| hir::ItemKind::Struct(_, generics) |
| | hir::ItemKind::Enum(_, generics) |
| | hir::ItemKind::Union(_, generics) |
| | hir::ItemKind::Trait(_, _, generics, ..) |
| | hir::ItemKind::Impl(hir::Impl { generics, .. }) |
| | hir::ItemKind::Fn(_, generics, _) |
| | hir::ItemKind::TyAlias(_, generics) |
| | hir::ItemKind::Const(_, generics, _) |
| | hir::ItemKind::TraitAlias(generics, _) |
| | hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. }), |
| .. |
| }) if !param_ty => { |
| // Missing generic type parameter bound. |
| if suggest_arbitrary_trait_bound( |
| self.tcx, |
| generics, |
| err, |
| trait_pred, |
| associated_ty, |
| ) { |
| return; |
| } |
| } |
| hir::Node::Crate(..) => return, |
| |
| _ => {} |
| } |
| body_id = self.tcx.local_parent(body_id); |
| } |
| } |
| |
| /// When after several dereferencing, the reference satisfies the trait |
| /// binding. This function provides dereference suggestion for this |
| /// specific situation. |
| fn suggest_dereferences( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) -> bool { |
| let mut code = obligation.cause.code(); |
| if let ObligationCauseCode::FunctionArg { arg_hir_id, call_hir_id, .. } = code |
| && let Some(typeck_results) = &self.typeck_results |
| && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id) |
| && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr) |
| { |
| // Suggest dereferencing the argument to a function/method call if possible |
| |
| let mut real_trait_pred = trait_pred; |
| while let Some((parent_code, parent_trait_pred)) = code.parent() { |
| code = parent_code; |
| if let Some(parent_trait_pred) = parent_trait_pred { |
| real_trait_pred = parent_trait_pred; |
| } |
| |
| // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle |
| // `ReBound`, and we don't particularly care about the regions. |
| let real_ty = |
| self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty()); |
| |
| if self.can_eq(obligation.param_env, real_ty, arg_ty) |
| && let ty::Ref(region, base_ty, mutbl) = *real_ty.kind() |
| { |
| 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 = Ty::new_ref(self.tcx, region, 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, |
| ); |
| let may_hold = obligations |
| .iter() |
| .chain([&obligation]) |
| .all(|obligation| self.predicate_may_hold(obligation)) |
| .then_some(steps); |
| |
| may_hold |
| }) |
| { |
| if steps > 0 { |
| // Don't care about `&mut` because `DerefMut` is used less |
| // often and user will not expect that an autoderef happens. |
| if let hir::Node::Expr(hir::Expr { |
| kind: |
| hir::ExprKind::AddrOf( |
| hir::BorrowKind::Ref, |
| hir::Mutability::Not, |
| expr, |
| ), |
| .. |
| }) = self.tcx.hir_node(*arg_hir_id) |
| { |
| let derefs = "*".repeat(steps); |
| err.span_suggestion_verbose( |
| expr.span.shrink_to_lo(), |
| "consider dereferencing here", |
| derefs, |
| Applicability::MachineApplicable, |
| ); |
| return true; |
| } |
| } |
| } else if real_trait_pred != trait_pred { |
| // This branch addresses #87437. |
| |
| let span = obligation.cause.span; |
| // Remapping bound vars here |
| let real_trait_pred_and_base_ty = real_trait_pred |
| .map_bound(|inner_trait_pred| (inner_trait_pred, base_ty)); |
| let obligation = self.mk_trait_obligation_with_new_self_ty( |
| obligation.param_env, |
| real_trait_pred_and_base_ty, |
| ); |
| let sized_obligation = Obligation::new( |
| self.tcx, |
| obligation.cause.clone(), |
| obligation.param_env, |
| ty::TraitRef::from_lang_item( |
| self.tcx, |
| hir::LangItem::Sized, |
| obligation.cause.span, |
| [base_ty], |
| ), |
| ); |
| if self.predicate_may_hold(&obligation) |
| && self.predicate_must_hold_modulo_regions(&sized_obligation) |
| { |
| let call_node = self.tcx.hir_node(*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; |
| } |
| } |
| } |
| } |
| } else if let ( |
| ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id: Some(rhs_hir_id), .. }, |
| predicate, |
| ) = code.peel_derives_with_predicate() |
| && let Some(typeck_results) = &self.typeck_results |
| && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id) |
| && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id) |
| && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs) |
| && let trait_pred = predicate.unwrap_or(trait_pred) |
| // Only run this code on binary operators |
| && hir::lang_items::BINARY_OPERATORS |
| .iter() |
| .filter_map(|&op| self.tcx.lang_items().get(op)) |
| .any(|op| { |
| op == trait_pred.skip_binder().trait_ref.def_id |
| }) |
| { |
| // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible |
| |
| let trait_pred = predicate.unwrap_or(trait_pred); |
| let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty()); |
| let lhs_autoderef = (self.autoderef_steps)(lhs_ty); |
| let rhs_autoderef = (self.autoderef_steps)(rhs_ty); |
| let first_lhs = lhs_autoderef.first().unwrap().clone(); |
| let first_rhs = rhs_autoderef.first().unwrap().clone(); |
| let mut autoderefs = lhs_autoderef |
| .into_iter() |
| .enumerate() |
| .rev() |
| .zip_longest(rhs_autoderef.into_iter().enumerate().rev()) |
| .map(|t| match t { |
| EitherOrBoth::Both(a, b) => (a, b), |
| EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())), |
| EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b), |
| }) |
| .rev(); |
| if let Some((lsteps, rsteps)) = |
| autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| { |
| // Create a new predicate with the dereferenced LHS and RHS |
| // We simultaneously dereference both sides rather than doing them |
| // one at a time to account for cases such as &Box<T> == &&T |
| let trait_pred_and_ty = trait_pred.map_bound(|inner| { |
| ( |
| ty::TraitPredicate { |
| trait_ref: ty::TraitRef::new( |
| self.tcx, |
| inner.trait_ref.def_id, |
| self.tcx.mk_args( |
| &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]] |
| .concat(), |
| ), |
| ), |
| ..inner |
| }, |
| l_ty, |
| ) |
| }); |
| let obligation = self.mk_trait_obligation_with_new_self_ty( |
| obligation.param_env, |
| trait_pred_and_ty, |
| ); |
| self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) { |
| (_, 0) => (Some(lsteps), None), |
| (0, _) => (None, Some(rsteps)), |
| _ => (Some(lsteps), Some(rsteps)), |
| }) |
| }) |
| { |
| let make_sugg = |mut expr: &Expr<'_>, mut steps| { |
| let mut prefix_span = expr.span.shrink_to_lo(); |
| let mut msg = "consider dereferencing here"; |
| if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind { |
| msg = "consider removing the borrow and dereferencing instead"; |
| if let hir::ExprKind::AddrOf(..) = inner.kind { |
| msg = "consider removing the borrows and dereferencing instead"; |
| } |
| } |
| while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind |
| && steps > 0 |
| { |
| prefix_span = prefix_span.with_hi(inner.span.lo()); |
| expr = inner; |
| steps -= 1; |
| } |
| // Empty suggestions with empty spans ICE with debug assertions |
| if steps == 0 { |
| return ( |
| msg.trim_end_matches(" and dereferencing instead"), |
| vec![(prefix_span, String::new())], |
| ); |
| } |
| let derefs = "*".repeat(steps); |
| let needs_parens = steps > 0 |
| && match expr.kind { |
| hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, |
| _ if is_range_literal(expr) => true, |
| _ => false, |
| }; |
| let mut suggestion = if needs_parens { |
| vec![ |
| ( |
| expr.span.with_lo(prefix_span.hi()).shrink_to_lo(), |
| format!("{derefs}("), |
| ), |
| (expr.span.shrink_to_hi(), ")".to_string()), |
| ] |
| } else { |
| vec![( |
| expr.span.with_lo(prefix_span.hi()).shrink_to_lo(), |
| format!("{derefs}"), |
| )] |
| }; |
| // Empty suggestions with empty spans ICE with debug assertions |
| if !prefix_span.is_empty() { |
| suggestion.push((prefix_span, String::new())); |
| } |
| (msg, suggestion) |
| }; |
| |
| if let Some(lsteps) = lsteps |
| && let Some(rsteps) = rsteps |
| && lsteps > 0 |
| && rsteps > 0 |
| { |
| let mut suggestion = make_sugg(lhs, lsteps).1; |
| suggestion.append(&mut make_sugg(rhs, rsteps).1); |
| err.multipart_suggestion_verbose( |
| "consider dereferencing both sides of the expression", |
| suggestion, |
| Applicability::MachineApplicable, |
| ); |
| return true; |
| } else if let Some(lsteps) = lsteps |
| && lsteps > 0 |
| { |
| let (msg, suggestion) = make_sugg(lhs, lsteps); |
| err.multipart_suggestion_verbose( |
| msg, |
| suggestion, |
| Applicability::MachineApplicable, |
| ); |
| return true; |
| } else if let Some(rsteps) = rsteps |
| && rsteps > 0 |
| { |
| let (msg, suggestion) = make_sugg(rhs, rsteps); |
| err.multipart_suggestion_verbose( |
| msg, |
| suggestion, |
| Applicability::MachineApplicable, |
| ); |
| return true; |
| } |
| } |
| } |
| false |
| } |
| |
| /// Given a closure's `DefId`, return the given name of the closure. |
| /// |
| /// This doesn't account for reassignments, but it's only used for suggestions. |
| fn get_closure_name( |
| &self, |
| def_id: DefId, |
| err: &mut Diag<'_>, |
| msg: Cow<'static, str>, |
| ) -> Option<Symbol> { |
| let get_name = |err: &mut Diag<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> { |
| // Get the local name of this closure. This can be inaccurate because |
| // of the possibility of reassignment, but this should be good enough. |
| match &kind { |
| hir::PatKind::Binding(hir::BindingMode::NONE, _, ident, None) => Some(ident.name), |
| _ => { |
| err.note(msg); |
| None |
| } |
| } |
| }; |
| |
| let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?); |
| match self.tcx.parent_hir_node(hir_id) { |
| hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(local), .. }) => { |
| get_name(err, &local.pat.kind) |
| } |
| // Different to previous arm because one is `&hir::Local` and the other |
| // is `P<hir::Local>`. |
| hir::Node::LetStmt(local) => get_name(err, &local.pat.kind), |
| _ => None, |
| } |
| } |
| |
| /// We tried to apply the bound to an `fn` or closure. Check whether calling it would |
| /// evaluate to a type that *would* satisfy the trait binding. If it would, suggest calling |
| /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`. |
| fn suggest_fn_call( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| 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::ClauseKind::Trait(trait_pred)) = |
| obligation.predicate.kind().skip_binder() |
| && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait() |
| { |
| // Don't suggest calling to turn an unsized type into a sized type |
| return false; |
| } |
| |
| let self_ty = self.instantiate_binder_with_fresh_vars( |
| DUMMY_SP, |
| BoundRegionConversionTime::FnCall, |
| trait_pred.self_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)); |
| |
| 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) { |
| return false; |
| } |
| |
| // Get the name of the callable and the arguments to be used in the suggestion. |
| let hir = self.tcx.hir(); |
| |
| let msg = match def_id_or_name { |
| DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { |
| DefKind::Ctor(CtorOf::Struct, _) => { |
| Cow::from("use parentheses to construct this tuple struct") |
| } |
| DefKind::Ctor(CtorOf::Variant, _) => { |
| Cow::from("use parentheses to construct this tuple variant") |
| } |
| kind => Cow::from(format!( |
| "use parentheses to call this {}", |
| self.tcx.def_kind_descr(kind, def_id) |
| )), |
| }, |
| DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")), |
| }; |
| |
| let args = inputs |
| .into_iter() |
| .map(|ty| { |
| if ty.is_suggestable(self.tcx, false) { |
| format!("/* {ty} */") |
| } else { |
| "/* value */".to_string() |
| } |
| }) |
| .collect::<Vec<_>>() |
| .join(", "); |
| |
| if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArg { .. }) |
| && obligation.cause.span.can_be_used_for_suggestions() |
| { |
| // When the obligation error has been ensured to have been caused by |
| // an argument, the `obligation.cause.span` points at the expression |
| // of the argument, so we can provide a suggestion. Otherwise, we give |
| // a more general note. |
| err.span_suggestion_verbose( |
| obligation.cause.span.shrink_to_hi(), |
| msg, |
| format!("({args})"), |
| Applicability::HasPlaceholders, |
| ); |
| } else if let DefIdOrName::DefId(def_id) = def_id_or_name { |
| let name = match hir.get_if_local(def_id) { |
| Some(hir::Node::Expr(hir::Expr { |
| kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), |
| .. |
| })) => { |
| err.span_label(*fn_decl_span, "consider calling this closure"); |
| let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else { |
| return false; |
| }; |
| name.to_string() |
| } |
| Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { |
| err.span_label(ident.span, "consider calling this function"); |
| ident.to_string() |
| } |
| Some(hir::Node::Ctor(..)) => { |
| let name = self.tcx.def_path_str(def_id); |
| err.span_label( |
| self.tcx.def_span(def_id), |
| format!("consider calling the constructor for `{name}`"), |
| ); |
| name |
| } |
| _ => return false, |
| }; |
| err.help(format!("{msg}: `{name}({args})`")); |
| } |
| true |
| } |
| |
| fn check_for_binding_assigned_block_without_tail_expression( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| 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, self.tcx); |
| let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { |
| return; |
| }; |
| let body = self.tcx.hir().body(body_id); |
| expr_finder.visit_expr(body.value); |
| 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 Res::Local(hir_id) = path.res else { |
| return; |
| }; |
| let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else { |
| return; |
| }; |
| let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) = |
| self.tcx.parent_hir_node(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 Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) -> bool { |
| let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); |
| self.enter_forall(self_ty, |ty: Ty<'_>| { |
| let Some(generics) = self.tcx.hir().get_generics(obligation.cause.body_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::FunctionArg { arg_hir_id, .. } = obligation.cause.code() |
| 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 existing_clone_call = match self.tcx.hir_node(*arg_hir_id) { |
| // It's just a variable. Propose cloning it. |
| Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) => None, |
| // It's already a call to `clone()`. We might be able to suggest |
| // adding a `+ Clone` bound, though. |
| Node::Expr(Expr { |
| kind: |
| hir::ExprKind::MethodCall( |
| hir::PathSegment { ident, .. }, |
| _receiver, |
| &[], |
| call_span, |
| ), |
| hir_id, |
| .. |
| }) if ident.name == sym::clone |
| && !call_span.from_expansion() |
| && !has_clone(*inner_ty) => |
| { |
| // We only care about method calls corresponding to the real `Clone` trait. |
| let Some(typeck_results) = self.typeck_results.as_ref() else { return false }; |
| let Some((DefKind::AssocFn, did)) = typeck_results.type_dependent_def(*hir_id) |
| else { |
| return false; |
| }; |
| if self.tcx.trait_of_item(did) != Some(clone_trait) { |
| return false; |
| } |
| Some(ident.span) |
| } |
| _ => return false, |
| }; |
| |
| 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), |
| None, |
| ); |
| } |
| if let Some(existing_clone_call) = existing_clone_call { |
| err.span_note( |
| existing_clone_call, |
| format!( |
| "this `clone()` copies the reference, \ |
| which does not do anything, \ |
| because `{inner_ty}` does not implement `Clone`" |
| ), |
| ); |
| } else { |
| 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, |
| body_id: LocalDefId, |
| 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, args) => { |
| let fn_sig = args.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, args, .. }) => { |
| self.tcx |
| .item_super_predicates(def_id) |
| .instantiate(self.tcx, args) |
| .iter() |
| .find_map(|pred| { |
| if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder() |
| && Some(proj.projection_ty.def_id) == self.tcx.lang_items().fn_once_output() |
| // args tuple will always be args[1] |
| && let ty::Tuple(args) = proj.projection_ty.args.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, args are shifted over by 1 |
| && let ty::Tuple(args) = proj.args.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(body_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::ClauseKind::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 args[1] |
| && let ty::Tuple(args) = proj.projection_ty.args.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.instantiate_binder_with_fresh_vars( |
| DUMMY_SP, |
| BoundRegionConversionTime::FnCall, |
| output, |
| ); |
| let inputs = inputs |
| .skip_binder() |
| .iter() |
| .map(|ty| { |
| self.instantiate_binder_with_fresh_vars( |
| DUMMY_SP, |
| BoundRegionConversionTime::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>, |
| err: &mut Diag<'_>, |
| poly_trait_pred: ty::PolyTraitPredicate<'tcx>, |
| has_custom_message: bool, |
| ) -> bool { |
| let span = obligation.cause.span; |
| |
| let code = match obligation.cause.code() { |
| ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code, |
| c @ ObligationCauseCode::WhereClause(_) |
| | c @ ObligationCauseCode::WhereClauseInExpr(..) => c, |
| c if matches!( |
| span.ctxt().outer_expn_data().kind, |
| ExpnKind::Desugaring(DesugaringKind::ForLoop) |
| ) => |
| { |
| c |
| } |
| _ => return false, |
| }; |
| |
| // List of traits for which it would be nonsensical to suggest borrowing. |
| // For instance, immutable references are always Copy, so suggesting to |
| // borrow would always succeed, but it's probably not what the user wanted. |
| let mut never_suggest_borrow: Vec<_> = |
| [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized] |
| .iter() |
| .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item)) |
| .collect(); |
| |
| if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) { |
| never_suggest_borrow.push(def_id); |
| } |
| |
| let param_env = obligation.param_env; |
| |
| // Try to apply the original trait binding obligation by borrowing. |
| let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>, |
| blacklist: &[DefId]| |
| -> bool { |
| if blacklist.contains(&old_pred.def_id()) { |
| return false; |
| } |
| // We map bounds to `&T` and `&mut T` |
| let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| { |
| ( |
| trait_pred, |
| Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()), |
| ) |
| }); |
| let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| { |
| ( |
| trait_pred, |
| Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()), |
| ) |
| }); |
| |
| let mk_result = |trait_pred_and_new_ty| { |
| let obligation = |
| self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty); |
| self.predicate_must_hold_modulo_regions(&obligation) |
| }; |
| let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref); |
| let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref); |
| |
| let (ref_inner_ty_satisfies_pred, ref_inner_ty_mut) = |
| if let ObligationCauseCode::WhereClause(_) |
| | ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code() |
| && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind() |
| { |
| ( |
| mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))), |
| mutability.is_mut(), |
| ) |
| } else { |
| (false, false) |
| }; |
| |
| if imm_ref_self_ty_satisfies_pred |
| || mut_ref_self_ty_satisfies_pred |
| || ref_inner_ty_satisfies_pred |
| { |
| if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { |
| // We don't want a borrowing suggestion on the fields in structs, |
| // ``` |
| // struct Foo { |
| // the_foos: Vec<Foo> |
| // } |
| // ``` |
| if !matches!( |
| span.ctxt().outer_expn_data().kind, |
| ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop) |
| ) { |
| return false; |
| } |
| if snippet.starts_with('&') { |
| // This is already a literal borrow and the obligation is failing |
| // somewhere else in the obligation chain. Do not suggest non-sense. |
| return false; |
| } |
| // We have a very specific type of error, where just borrowing this argument |
| // might solve the problem. In cases like this, the important part is the |
| // original type obligation, not the last one that failed, which is arbitrary. |
| // Because of this, we modify the error to refer to the original obligation and |
| // return early in the caller. |
| |
| let msg = format!("the trait bound `{old_pred}` is not satisfied"); |
| if has_custom_message { |
| err.note(msg); |
| } else { |
| err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)]; |
| } |
| let mut file = None; |
| err.span_label( |
| span, |
| format!( |
| "the trait `{}` is not implemented for `{}`", |
| old_pred.print_modifiers_and_trait_path(), |
| self.tcx.short_ty_string(old_pred.self_ty().skip_binder(), &mut file), |
| ), |
| ); |
| if let Some(file) = file { |
| err.note(format!( |
| "the full type name has been written to '{}'", |
| file.display() |
| )); |
| err.note( |
| "consider using `--verbose` to print full type name to the console", |
| ); |
| } |
| |
| if imm_ref_self_ty_satisfies_pred && mut_ref_self_ty_satisfies_pred { |
| err.span_suggestions( |
| span.shrink_to_lo(), |
| "consider borrowing here", |
| ["&".to_string(), "&mut ".to_string()], |
| Applicability::MaybeIncorrect, |
| ); |
| } else { |
| let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_mut; |
| let sugg_prefix = format!("&{}", if is_mut { "mut " } else { "" }); |
| let sugg_msg = format!( |
| "consider{} borrowing here", |
| if is_mut { " mutably" } else { "" } |
| ); |
| |
| // Issue #109436, we need to add parentheses properly for method calls |
| // for example, `foo.into()` should be `(&foo).into()` |
| if let Some(_) = |
| self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50)) |
| { |
| err.multipart_suggestion_verbose( |
| sugg_msg, |
| vec![ |
| (span.shrink_to_lo(), format!("({sugg_prefix}")), |
| (span.shrink_to_hi(), ")".to_string()), |
| ], |
| Applicability::MaybeIncorrect, |
| ); |
| return true; |
| } |
| |
| // Issue #104961, we need to add parentheses properly for compound expressions |
| // for example, `x.starts_with("hi".to_string() + "you")` |
| // should be `x.starts_with(&("hi".to_string() + "you"))` |
| let Some(body_id) = |
| self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) |
| else { |
| return false; |
| }; |
| let body = self.tcx.hir().body(body_id); |
| let mut expr_finder = FindExprBySpan::new(span, self.tcx); |
| expr_finder.visit_expr(body.value); |
| let Some(expr) = expr_finder.result else { |
| return false; |
| }; |
| let needs_parens = match expr.kind { |
| // parenthesize if needed (Issue #46756) |
| hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, |
| // parenthesize borrows of range literals (Issue #54505) |
| _ if is_range_literal(expr) => true, |
| _ => false, |
| }; |
| |
| let span = if needs_parens { span } else { span.shrink_to_lo() }; |
| let suggestions = if !needs_parens { |
| vec![(span.shrink_to_lo(), sugg_prefix)] |
| } else { |
| vec![ |
| (span.shrink_to_lo(), format!("{sugg_prefix}(")), |
| (span.shrink_to_hi(), ")".to_string()), |
| ] |
| }; |
| err.multipart_suggestion_verbose( |
| sugg_msg, |
| suggestions, |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| return true; |
| } |
| } |
| return false; |
| }; |
| |
| if let ObligationCauseCode::ImplDerived(cause) = &*code { |
| try_borrowing(cause.derived.parent_trait_pred, &[]) |
| } else if let ObligationCauseCode::SpannedWhereClause(_, _) |
| | ObligationCauseCode::WhereClause(_) |
| | ObligationCauseCode::WhereClauseInExpr(..) |
| | ObligationCauseCode::SpannedWhereClauseInExpr(..) = code |
| { |
| try_borrowing(poly_trait_pred, &never_suggest_borrow) |
| } else { |
| false |
| } |
| } |
| |
| // Suggest borrowing the type |
| fn suggest_borrowing_for_object_cast( |
| &self, |
| err: &mut Diag<'_>, |
| obligation: &PredicateObligation<'tcx>, |
| self_ty: Ty<'tcx>, |
| target_ty: Ty<'tcx>, |
| ) { |
| let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else { |
| return; |
| }; |
| let ty::Dynamic(predicates, _, ty::Dyn) = object_ty.kind() else { |
| return; |
| }; |
| let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty); |
| |
| for predicate in predicates.iter() { |
| if !self.predicate_must_hold_modulo_regions( |
| &obligation.with(self.tcx, predicate.with_self_ty(self.tcx, self_ref_ty)), |
| ) { |
| return; |
| } |
| } |
| |
| err.span_suggestion( |
| obligation.cause.span.shrink_to_lo(), |
| format!( |
| "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`" |
| ), |
| "&", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| |
| /// Whenever references are used by mistake, like `for (i, e) in &vec.iter().enumerate()`, |
| /// suggest removing these references until we reach a type that implements the trait. |
| fn suggest_remove_reference( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) -> bool { |
| 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, self.tcx); |
| let Some(body_id) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) else { |
| return false; |
| }; |
| let body = self.tcx.hir().body(body_id); |
| expr_finder.visit_expr(body.value); |
| 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, |
| ); |
| |
| 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 |
| } |
| }; |
| |
| // 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())); |
| |
| let ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { |
| break; |
| }; |
| suggested_ty = *inner_ty; |
| |
| hir_ty = mut_ty.ty; |
| |
| if maybe_suggest(suggested_ty, count, suggestions.clone()) { |
| return true; |
| } |
| } |
| } |
| |
| // 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 ty::Ref(_, inner_ty, _) = suggested_ty.kind() else { |
| break 'outer; |
| }; |
| suggested_ty = *inner_ty; |
| |
| expr = borrowed; |
| |
| if maybe_suggest(suggested_ty, count, suggestions.clone()) { |
| return true; |
| } |
| } |
| if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind |
| && let Res::Local(hir_id) = path.res |
| && let hir::Node::Pat(binding) = self.tcx.hir_node(hir_id) |
| && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.hir_id) |
| && let None = local.ty |
| && let Some(binding_expr) = local.init |
| { |
| expr = binding_expr; |
| } else { |
| break 'outer; |
| } |
| } |
| false |
| } |
| |
| fn suggest_remove_await(&self, obligation: &PredicateObligation<'tcx>, err: &mut Diag<'_>) { |
| let hir = self.tcx.hir(); |
| if let ObligationCauseCode::AwaitableExpr(hir_id) = obligation.cause.code().peel_derives() |
| && let hir::Node::Expr(expr) = self.tcx.hir_node(*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. |
| |
| // use nth(1) to skip one layer of desugaring from `IntoIter::into_iter` |
| if let Some((_, hir::Node::Expr(await_expr))) = hir.parent_iter(*hir_id).nth(1) |
| && let Some(expr_span) = expr.span.find_ancestor_inside_same_ctxt(await_expr.span) |
| { |
| let removal_span = self |
| .tcx |
| .sess |
| .source_map() |
| .span_extend_while_whitespace(expr_span) |
| .shrink_to_hi() |
| .to(await_expr.span.shrink_to_hi()); |
| err.span_suggestion( |
| removal_span, |
| "remove the `.await`", |
| "", |
| Applicability::MachineApplicable, |
| ); |
| } else { |
| err.span_label(obligation.cause.span, "remove the `.await`"); |
| } |
| // FIXME: account for associated `async fn`s. |
| if let hir::Expr { span, kind: hir::ExprKind::Call(base, _), .. } = expr { |
| if let ty::PredicateKind::Clause(ty::ClauseKind::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, _args) = ty.kind() |
| && let Some(hir::Node::Item(hir::Item { ident, span, vis_span, .. })) = |
| hir.get_if_local(*def_id) |
| { |
| let msg = format!("alternatively, consider making `fn {ident}` asynchronous"); |
| if vis_span.is_empty() { |
| err.span_suggestion_verbose( |
| span.shrink_to_lo(), |
| msg, |
| "async ", |
| Applicability::MaybeIncorrect, |
| ); |
| } else { |
| err.span_suggestion_verbose( |
| vis_span.shrink_to_hi(), |
| msg, |
| " async", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| /// Check if the trait bound is implemented for a different mutability and note it in the |
| /// final error. |
| fn suggest_change_mut( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) { |
| let points_at_arg = |
| matches!(obligation.cause.code(), ObligationCauseCode::FunctionArg { .. },); |
| |
| let span = obligation.cause.span; |
| 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; |
| } |
| let trait_pred = self.resolve_vars_if_possible(trait_pred); |
| if trait_pred.has_non_region_infer() { |
| // Do not ICE while trying to find if a reborrow would succeed on a trait with |
| // unresolved bindings. |
| return; |
| } |
| |
| // Skipping binder here, remapping below |
| if let ty::Ref(region, t_type, mutability) = *trait_pred.skip_binder().self_ty().kind() |
| { |
| let suggested_ty = match mutability { |
| hir::Mutability::Mut => Ty::new_imm_ref(self.tcx, region, t_type), |
| hir::Mutability::Not => Ty::new_mut_ref(self.tcx, region, t_type), |
| }; |
| |
| // 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 suggested_ty_would_satisfy_obligation = self |
| .evaluate_obligation_no_overflow(&new_obligation) |
| .must_apply_modulo_regions(); |
| if suggested_ty_would_satisfy_obligation { |
| let sp = self |
| .tcx |
| .sess |
| .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", |
| "&mut ", |
| Applicability::MachineApplicable, |
| ); |
| } else { |
| err.note(format!( |
| "`{}` is implemented for `{}`, but not for `{}`", |
| trait_pred.print_modifiers_and_trait_path(), |
| suggested_ty, |
| trait_pred.skip_binder().self_ty(), |
| )); |
| } |
| } |
| } |
| } |
| } |
| |
| fn suggest_semicolon_removal( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| span: Span, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) -> bool { |
| let hir = self.tcx.hir(); |
| let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); |
| if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. }) = node |
| && let hir::ExprKind::Block(blk, _) = &hir.body(*body_id).value.kind |
| && sig.decl.output.span().overlaps(span) |
| && blk.expr.is_none() |
| && trait_pred.self_ty().skip_binder().is_unit() |
| && let Some(stmt) = blk.stmts.last() |
| && let hir::StmtKind::Semi(expr) = stmt.kind |
| // Only suggest this if the expression behind the semicolon implements the predicate |
| && let Some(typeck_results) = &self.typeck_results |
| && let Some(ty) = typeck_results.expr_ty_opt(expr) |
| && self.predicate_may_hold(&self.mk_trait_obligation_with_new_self_ty( |
| obligation.param_env, trait_pred.map_bound(|trait_pred| (trait_pred, ty)) |
| )) |
| { |
| err.span_label( |
| expr.span, |
| format!( |
| "this expression has type `{}`, which implements `{}`", |
| ty, |
| trait_pred.print_modifiers_and_trait_path() |
| ), |
| ); |
| err.span_suggestion( |
| self.tcx.sess.source_map().end_point(stmt.span), |
| "remove this semicolon", |
| "", |
| Applicability::MachineApplicable, |
| ); |
| return true; |
| } |
| false |
| } |
| |
| fn return_type_span(&self, obligation: &PredicateObligation<'tcx>) -> Option<Span> { |
| let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, ..), .. }) = |
| self.tcx.hir_node_by_def_id(obligation.cause.body_id) |
| else { |
| return None; |
| }; |
| |
| if let hir::FnRetTy::Return(ret_ty) = sig.decl.output { Some(ret_ty.span) } else { None } |
| } |
| |
| /// If all conditions are met to identify a returned `dyn Trait`, suggest using `impl Trait` if |
| /// applicable and signal that the error has been expanded appropriately and needs to be |
| /// emitted. |
| fn suggest_impl_trait( |
| &self, |
| err: &mut Diag<'_>, |
| obligation: &PredicateObligation<'tcx>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) -> bool { |
| let ObligationCauseCode::SizedReturnType = obligation.cause.code() else { |
| return false; |
| }; |
| let ty::Dynamic(_, _, ty::Dyn) = trait_pred.self_ty().skip_binder().kind() else { |
| return false; |
| }; |
| |
| err.code(E0746); |
| err.primary_message("return type cannot have an unboxed trait object"); |
| err.children.clear(); |
| |
| let span = obligation.cause.span; |
| if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) |
| && snip.starts_with("dyn ") |
| { |
| err.span_suggestion( |
| span.with_hi(span.lo() + BytePos(4)), |
| "return an `impl Trait` instead of a `dyn Trait`, \ |
| if all returned values are the same type", |
| "impl ", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| |
| let body = self.tcx.hir().body(self.tcx.hir().body_owned_by(obligation.cause.body_id)); |
| |
| let mut visitor = ReturnsVisitor::default(); |
| visitor.visit_body(body); |
| |
| let mut sugg = |
| vec![(span.shrink_to_lo(), "Box<".to_string()), (span.shrink_to_hi(), ">".to_string())]; |
| sugg.extend(visitor.returns.into_iter().flat_map(|expr| { |
| let span = |
| expr.span.find_ancestor_in_same_ctxt(obligation.cause.span).unwrap_or(expr.span); |
| if !span.can_be_used_for_suggestions() { |
| vec![] |
| } else if let hir::ExprKind::Call(path, ..) = expr.kind |
| && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, method)) = path.kind |
| && method.ident.name == sym::new |
| && let hir::TyKind::Path(hir::QPath::Resolved(.., box_path)) = ty.kind |
| && box_path |
| .res |
| .opt_def_id() |
| .is_some_and(|def_id| Some(def_id) == self.tcx.lang_items().owned_box()) |
| { |
| // Don't box `Box::new` |
| vec![] |
| } else { |
| vec![ |
| (span.shrink_to_lo(), "Box::new(".to_string()), |
| (span.shrink_to_hi(), ")".to_string()), |
| ] |
| } |
| })); |
| |
| err.multipart_suggestion( |
| "box the return type, and wrap all of the returned values in `Box::new`", |
| sugg, |
| Applicability::MaybeIncorrect, |
| ); |
| |
| true |
| } |
| |
| fn point_at_returns_when_relevant( |
| &self, |
| err: &mut Diag<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| ) { |
| match obligation.cause.code().peel_derives() { |
| ObligationCauseCode::SizedReturnType => {} |
| _ => return, |
| } |
| |
| let hir = self.tcx.hir(); |
| let node = self.tcx.hir_node_by_def_id(obligation.cause.body_id); |
| if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) = node { |
| let body = hir.body(*body_id); |
| // Point at all the `return`s in the function as they have failed trait bounds. |
| let mut visitor = ReturnsVisitor::default(); |
| visitor.visit_body(body); |
| let typeck_results = self.typeck_results.as_ref().unwrap(); |
| 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); |
| if ty.references_error() { |
| // don't print out the [type error] here |
| err.downgrade_to_delayed_bug(); |
| } else { |
| err.span_label(expr.span, format!("this returned value is of type `{ty}`")); |
| } |
| } |
| } |
| } |
| } |
| |
| fn report_closure_arg_mismatch( |
| &self, |
| span: Span, |
| found_span: Option<Span>, |
| found: ty::TraitRef<'tcx>, |
| expected: ty::TraitRef<'tcx>, |
| cause: &ObligationCauseCode<'tcx>, |
| found_node: Option<Node<'_>>, |
| param_env: ty::ParamEnv<'tcx>, |
| ) -> Diag<'tcx> { |
| pub(crate) fn build_fn_sig_ty<'tcx>( |
| infcx: &InferCtxt<'tcx>, |
| trait_ref: ty::TraitRef<'tcx>, |
| ) -> Ty<'tcx> { |
| let inputs = trait_ref.args.type_at(1); |
| let sig = match inputs.kind() { |
| ty::Tuple(inputs) if infcx.tcx.is_fn_trait(trait_ref.def_id) => { |
| infcx.tcx.mk_fn_sig( |
| *inputs, |
| infcx.next_ty_var(DUMMY_SP), |
| false, |
| hir::Unsafety::Normal, |
| abi::Abi::Rust, |
| ) |
| } |
| _ => infcx.tcx.mk_fn_sig( |
| [inputs], |
| infcx.next_ty_var(DUMMY_SP), |
| false, |
| hir::Unsafety::Normal, |
| abi::Abi::Rust, |
| ), |
| }; |
| |
| Ty::new_fn_ptr(infcx.tcx, ty::Binder::dummy(sig)) |
| } |
| |
| let argument_kind = match expected.self_ty().kind() { |
| ty::Closure(..) => "closure", |
| ty::Coroutine(..) => "coroutine", |
| _ => "function", |
| }; |
| let mut err = struct_span_code_err!( |
| self.dcx(), |
| span, |
| E0631, |
| "type mismatch in {argument_kind} arguments", |
| ); |
| |
| err.span_label(span, "expected due to this"); |
| |
| let found_span = found_span.unwrap_or(span); |
| err.span_label(found_span, "found signature defined here"); |
| |
| let expected = build_fn_sig_ty(self, expected); |
| let found = build_fn_sig_ty(self, found); |
| |
| let (expected_str, found_str) = self.cmp(expected, found); |
| |
| let signature_kind = format!("{argument_kind} signature"); |
| err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str); |
| |
| self.note_conflicting_fn_args(&mut err, cause, expected, found, param_env); |
| 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 |
| } |
| |
| fn note_conflicting_fn_args( |
| &self, |
| err: &mut Diag<'_>, |
| cause: &ObligationCauseCode<'tcx>, |
| expected: Ty<'tcx>, |
| found: Ty<'tcx>, |
| param_env: ty::ParamEnv<'tcx>, |
| ) { |
| let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = cause else { |
| return; |
| }; |
| let ty::FnPtr(expected) = expected.kind() else { |
| return; |
| }; |
| let ty::FnPtr(found) = found.kind() else { |
| return; |
| }; |
| let Node::Expr(arg) = self.tcx.hir_node(*arg_hir_id) else { |
| return; |
| }; |
| let hir::ExprKind::Path(path) = arg.kind else { |
| return; |
| }; |
| let expected_inputs = self.tcx.instantiate_bound_regions_with_erased(*expected).inputs(); |
| let found_inputs = self.tcx.instantiate_bound_regions_with_erased(*found).inputs(); |
| let both_tys = expected_inputs.iter().copied().zip(found_inputs.iter().copied()); |
| |
| let arg_expr = |infcx: &InferCtxt<'tcx>, name, expected: Ty<'tcx>, found: Ty<'tcx>| { |
| let (expected_ty, expected_refs) = get_deref_type_and_refs(expected); |
| let (found_ty, found_refs) = get_deref_type_and_refs(found); |
| |
| if infcx.can_eq(param_env, found_ty, expected_ty) { |
| if found_refs.len() == expected_refs.len() |
| && found_refs.iter().eq(expected_refs.iter()) |
| { |
| name |
| } else if found_refs.len() > expected_refs.len() { |
| let refs = &found_refs[..found_refs.len() - expected_refs.len()]; |
| if found_refs[..expected_refs.len()].iter().eq(expected_refs.iter()) { |
| format!( |
| "{}{name}", |
| refs.iter() |
| .map(|mutbl| format!("&{}", mutbl.prefix_str())) |
| .collect::<Vec<_>>() |
| .join(""), |
| ) |
| } else { |
| // The refs have different mutability. |
| format!( |
| "{}*{name}", |
| refs.iter() |
| .map(|mutbl| format!("&{}", mutbl.prefix_str())) |
| .collect::<Vec<_>>() |
| .join(""), |
| ) |
| } |
| } else if expected_refs.len() > found_refs.len() { |
| format!( |
| "{}{name}", |
| (0..(expected_refs.len() - found_refs.len())) |
| .map(|_| "*") |
| .collect::<Vec<_>>() |
| .join(""), |
| ) |
| } else { |
| format!( |
| "{}{name}", |
| found_refs |
| .iter() |
| .map(|mutbl| format!("&{}", mutbl.prefix_str())) |
| .chain(found_refs.iter().map(|_| "*".to_string())) |
| .collect::<Vec<_>>() |
| .join(""), |
| ) |
| } |
| } else { |
| format!("/* {found} */") |
| } |
| }; |
| let args_have_same_underlying_type = both_tys.clone().all(|(expected, found)| { |
| let (expected_ty, _) = get_deref_type_and_refs(expected); |
| let (found_ty, _) = get_deref_type_and_refs(found); |
| self.can_eq(param_env, found_ty, expected_ty) |
| }); |
| let (closure_names, call_names): (Vec<_>, Vec<_>) = if args_have_same_underlying_type |
| && !expected_inputs.is_empty() |
| && expected_inputs.len() == found_inputs.len() |
| && let Some(typeck) = &self.typeck_results |
| && let Res::Def(res_kind, fn_def_id) = typeck.qpath_res(&path, *arg_hir_id) |
| && res_kind.is_fn_like() |
| { |
| let closure: Vec<_> = self |
| .tcx |
| .fn_arg_names(fn_def_id) |
| .iter() |
| .enumerate() |
| .map(|(i, ident)| { |
| if ident.name.is_empty() || ident.name == kw::SelfLower { |
| format!("arg{i}") |
| } else { |
| format!("{ident}") |
| } |
| }) |
| .collect(); |
| let args = closure |
| .iter() |
| .zip(both_tys) |
| .map(|(name, (expected, found))| { |
| arg_expr(self.infcx, name.to_owned(), expected, found) |
| }) |
| .collect(); |
| (closure, args) |
| } else { |
| let closure_args = expected_inputs |
| .iter() |
| .enumerate() |
| .map(|(i, _)| format!("arg{i}")) |
| .collect::<Vec<_>>(); |
| let call_args = both_tys |
| .enumerate() |
| .map(|(i, (expected, found))| { |
| arg_expr(self.infcx, format!("arg{i}"), expected, found) |
| }) |
| .collect::<Vec<_>>(); |
| (closure_args, call_args) |
| }; |
| let closure_names: Vec<_> = closure_names |
| .into_iter() |
| .zip(expected_inputs.iter()) |
| .map(|(name, ty)| { |
| format!( |
| "{name}{}", |
| if ty.has_infer_types() { |
| String::new() |
| } else if ty.references_error() { |
| ": /* type */".to_string() |
| } else { |
| format!(": {ty}") |
| } |
| ) |
| }) |
| .collect(); |
| err.multipart_suggestion( |
| "consider wrapping the function in a closure", |
| vec![ |
| (arg.span.shrink_to_lo(), format!("|{}| ", closure_names.join(", "))), |
| (arg.span.shrink_to_hi(), format!("({})", call_names.join(", "))), |
| ], |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| |
| // Add a note if there are two `Fn`-family bounds that have conflicting argument |
| // requirements, which will always cause a closure to have a type error. |
| fn note_conflicting_closure_bounds( |
| &self, |
| cause: &ObligationCauseCode<'tcx>, |
| err: &mut Diag<'tcx>, |
| ) { |
| // First, look for an `SpannedWhereClauseInExpr`, which means we can get |
| // the uninstantiated predicate list of the called function. And check |
| // that the predicate that we failed to satisfy is a `Fn`-like trait. |
| if let ObligationCauseCode::SpannedWhereClauseInExpr(def_id, _, _, idx) = cause |
| && let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx) |
| && let Some(pred) = predicates.predicates.get(*idx) |
| && let ty::ClauseKind::Trait(trait_pred) = pred.kind().skip_binder() |
| && self.tcx.is_fn_trait(trait_pred.def_id()) |
| { |
| let expected_self = |
| self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.self_ty())); |
| let expected_args = |
| self.tcx.anonymize_bound_vars(pred.kind().rebind(trait_pred.trait_ref.args)); |
| |
| // Find another predicate whose self-type is equal to the expected self type, |
| // but whose args don't match. |
| let other_pred = predicates.into_iter().enumerate().find(|(other_idx, (pred, _))| { |
| match pred.kind().skip_binder() { |
| ty::ClauseKind::Trait(trait_pred) |
| if self.tcx.is_fn_trait(trait_pred.def_id()) |
| && other_idx != idx |
| // Make sure that the self type matches |
| // (i.e. constraining this closure) |
| && expected_self |
| == self.tcx.anonymize_bound_vars( |
| pred.kind().rebind(trait_pred.self_ty()), |
| ) |
| // But the args don't match (i.e. incompatible args) |
| && expected_args |
| != self.tcx.anonymize_bound_vars( |
| pred.kind().rebind(trait_pred.trait_ref.args), |
| ) => |
| { |
| true |
| } |
| _ => false, |
| } |
| }); |
| // 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, |
| "closure inferred to have a different signature due to this bound", |
| ); |
| } |
| } |
| } |
| |
| fn suggest_fully_qualified_path( |
| &self, |
| err: &mut Diag<'_>, |
| item_def_id: DefId, |
| span: Span, |
| trait_ref: DefId, |
| ) { |
| if let Some(assoc_item) = self.tcx.opt_associated_item(item_def_id) { |
| if let ty::AssocKind::Const | ty::AssocKind::Type = assoc_item.kind { |
| err.note(format!( |
| "{}s cannot be accessed directly on a `trait`, they can only be \ |
| accessed through a specific `impl`", |
| self.tcx.def_kind_descr(assoc_item.kind.as_def_kind(), item_def_id) |
| )); |
| err.span_suggestion( |
| span, |
| "use the fully qualified path to an implementation", |
| format!("<Type as {}>::{}", self.tcx.def_path_str(trait_ref), assoc_item.name), |
| Applicability::HasPlaceholders, |
| ); |
| } |
| } |
| } |
| |
| /// Adds an async-await specific note to the diagnostic when the future does not implement |
| /// an auto trait because of a captured type. |
| /// |
| /// ```text |
| /// note: future does not implement `Qux` as this value is used across an await |
| /// --> $DIR/issue-64130-3-other.rs:17:5 |
| /// | |
| /// LL | let x = Foo; |
| /// | - has type `Foo` |
| /// LL | baz().await; |
| /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later |
| /// LL | } |
| /// | - `x` is later dropped here |
| /// ``` |
| /// |
| /// When the diagnostic does not implement `Send` or `Sync` specifically, then the diagnostic |
| /// is "replaced" with a different message and a more specific error. |
| /// |
| /// ```text |
| /// error: future cannot be sent between threads safely |
| /// --> $DIR/issue-64130-2-send.rs:21:5 |
| /// | |
| /// LL | fn is_send<T: Send>(t: T) { } |
| /// | ---- required by this bound in `is_send` |
| /// ... |
| /// LL | is_send(bar()); |
| /// | ^^^^^^^ future returned by `bar` is not send |
| /// | |
| /// = help: within `impl std::future::Future`, the trait `std::marker::Send` is not |
| /// implemented for `Foo` |
| /// note: future is not send as this value is used across an await |
| /// --> $DIR/issue-64130-2-send.rs:15:5 |
| /// | |
| /// LL | let x = Foo; |
| /// | - has type `Foo` |
| /// LL | baz().await; |
| /// | ^^^^^^^^^^^ await occurs here, with `x` maybe used later |
| /// LL | } |
| /// | - `x` is later dropped here |
| /// ``` |
| /// |
| /// Returns `true` if an async-await specific note was added to the diagnostic. |
| #[instrument(level = "debug", skip_all, fields(?obligation.predicate, ?obligation.cause.span))] |
| fn maybe_note_obligation_cause_for_async_await<G: EmissionGuarantee>( |
| &self, |
| err: &mut Diag<'_, G>, |
| obligation: &PredicateObligation<'tcx>, |
| ) -> bool { |
| let hir = self.tcx.hir(); |
| |
| // Attempt to detect an async-await error by looking at the obligation causes, looking |
| // for a coroutine to be present. |
| // |
| // When a future does not implement a trait because of a captured type in one of the |
| // coroutines somewhere in the call stack, then the result is a chain of obligations. |
| // |
| // Given an `async fn` A that calls an `async fn` B which captures a non-send type and that |
| // future is passed as an argument to a function C which requires a `Send` type, then the |
| // chain looks something like this: |
| // |
| // - `BuiltinDerivedObligation` with a coroutine witness (B) |
| // - `BuiltinDerivedObligation` with a coroutine (B) |
| // - `BuiltinDerivedObligation` with `impl std::future::Future` (B) |
| // - `BuiltinDerivedObligation` with a coroutine witness (A) |
| // - `BuiltinDerivedObligation` with a coroutine (A) |
| // - `BuiltinDerivedObligation` with `impl std::future::Future` (A) |
| // - `BindingObligation` with `impl_send` (Send requirement) |
| // |
| // The first obligation in the chain is the most useful and has the coroutine that captured |
| // the type. The last coroutine (`outer_coroutine` below) has information about where the |
| // bound was introduced. At least one coroutine should be present for this diagnostic to be |
| // modified. |
| let (mut trait_ref, mut target_ty) = match obligation.predicate.kind().skip_binder() { |
| ty::PredicateKind::Clause(ty::ClauseKind::Trait(p)) => (Some(p), Some(p.self_ty())), |
| _ => (None, None), |
| }; |
| let mut coroutine = None; |
| let mut outer_coroutine = None; |
| let mut next_code = Some(obligation.cause.code()); |
| |
| let mut seen_upvar_tys_infer_tuple = false; |
| |
| while let Some(code) = next_code { |
| debug!(?code); |
| match code { |
| ObligationCauseCode::FunctionArg { parent_code, .. } => { |
| next_code = Some(parent_code); |
| } |
| ObligationCauseCode::ImplDerived(cause) => { |
| let ty = cause.derived.parent_trait_pred.skip_binder().self_ty(); |
| debug!( |
| parent_trait_ref = ?cause.derived.parent_trait_pred, |
| self_ty.kind = ?ty.kind(), |
| "ImplDerived", |
| ); |
| |
| match *ty.kind() { |
| ty::Coroutine(did, ..) | ty::CoroutineWitness(did, _) => { |
| coroutine = coroutine.or(Some(did)); |
| outer_coroutine = Some(did); |
| } |
| ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { |
| // By introducing a tuple of upvar types into the chain of obligations |
| // of a coroutine, the first non-coroutine item is now the tuple itself, |
| // we shall ignore this. |
| |
| seen_upvar_tys_infer_tuple = true; |
| } |
| _ if coroutine.is_none() => { |
| trait_ref = Some(cause.derived.parent_trait_pred.skip_binder()); |
| target_ty = Some(ty); |
| } |
| _ => {} |
| } |
| |
| next_code = Some(&cause.derived.parent_code); |
| } |
| ObligationCauseCode::WellFormedDerived(derived_obligation) |
| | ObligationCauseCode::BuiltinDerived(derived_obligation) => { |
| let ty = derived_obligation.parent_trait_pred.skip_binder().self_ty(); |
| debug!( |
| parent_trait_ref = ?derived_obligation.parent_trait_pred, |
| self_ty.kind = ?ty.kind(), |
| ); |
| |
| match *ty.kind() { |
| ty::Coroutine(did, ..) | ty::CoroutineWitness(did, ..) => { |
| coroutine = coroutine.or(Some(did)); |
| outer_coroutine = Some(did); |
| } |
| ty::Tuple(_) if !seen_upvar_tys_infer_tuple => { |
| // By introducing a tuple of upvar types into the chain of obligations |
| // of a coroutine, the first non-coroutine item is now the tuple itself, |
| // we shall ignore this. |
| |
| seen_upvar_tys_infer_tuple = true; |
| } |
| _ if coroutine.is_none() => { |
| trait_ref = Some(derived_obligation.parent_trait_pred.skip_binder()); |
| target_ty = Some(ty); |
| } |
| _ => {} |
| } |
| |
| next_code = Some(&derived_obligation.parent_code); |
| } |
| _ => break, |
| } |
| } |
| |
| // Only continue if a coroutine was found. |
| debug!(?coroutine, ?trait_ref, ?target_ty); |
| let (Some(coroutine_did), Some(trait_ref), Some(target_ty)) = |
| (coroutine, trait_ref, target_ty) |
| else { |
| return false; |
| }; |
| |
| let span = self.tcx.def_span(coroutine_did); |
| |
| let coroutine_did_root = self.tcx.typeck_root_def_id(coroutine_did); |
| debug!( |
| ?coroutine_did, |
| ?coroutine_did_root, |
| typeck_results.hir_owner = ?self.typeck_results.as_ref().map(|t| t.hir_owner), |
| ?span, |
| ); |
| |
| let coroutine_body = coroutine_did |
| .as_local() |
| .and_then(|def_id| hir.maybe_body_owned_by(def_id)) |
| .map(|body_id| hir.body(body_id)); |
| let mut visitor = AwaitsVisitor::default(); |
| if let Some(body) = coroutine_body { |
| visitor.visit_body(body); |
| } |
| debug!(awaits = ?visitor.awaits); |
| |
| // Look for a type inside the coroutine interior that matches the target type to get |
| // a span. |
| let target_ty_erased = self.tcx.erase_regions(target_ty); |
| let ty_matches = |ty| -> bool { |
| // Careful: the regions for types that appear in the |
| // coroutine 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 |
| // also have to erase late-bound regions. This is |
| // because the types that appear in the coroutine |
| // interior generally contain "bound regions" to |
| // represent regions that are part of the suspended |
| // coroutine frame. Bound regions are preserved by |
| // `erase_regions` and so we must also call |
| // `instantiate_bound_regions_with_erased`. |
| let ty_erased = self.tcx.instantiate_bound_regions_with_erased(ty); |
| let ty_erased = self.tcx.erase_regions(ty_erased); |
| let eq = ty_erased == target_ty_erased; |
| debug!(?ty_erased, ?target_ty_erased, ?eq); |
| eq |
| }; |
| |
| // Get the typeck results from the infcx if the coroutine is the function we are currently |
| // type-checking; otherwise, get them by performing a query. This is needed to avoid |
| // cycles. If we can't use resolved types because the coroutine comes from another crate, |
| // we still provide a targeted error but without all the relevant spans. |
| let coroutine_data = match &self.typeck_results { |
| Some(t) if t.hir_owner.to_def_id() == coroutine_did_root => CoroutineData(t), |
| _ if coroutine_did.is_local() => { |
| CoroutineData(self.tcx.typeck(coroutine_did.expect_local())) |
| } |
| _ => return false, |
| }; |
| |
| let coroutine_within_in_progress_typeck = match &self.typeck_results { |
| Some(t) => t.hir_owner.to_def_id() == coroutine_did_root, |
| _ => false, |
| }; |
| |
| let mut interior_or_upvar_span = None; |
| |
| let from_awaited_ty = coroutine_data.get_from_await_ty(visitor, hir, ty_matches); |
| debug!(?from_awaited_ty); |
| |
| // Avoid disclosing internal information to downstream crates. |
| if coroutine_did.is_local() |
| // Try to avoid cycles. |
| && !coroutine_within_in_progress_typeck |
| && let Some(coroutine_info) = self.tcx.mir_coroutine_witnesses(coroutine_did) |
| { |
| debug!(?coroutine_info); |
| 'find_source: for (variant, source_info) in |
| coroutine_info.variant_fields.iter().zip(&coroutine_info.variant_source_info) |
| { |
| debug!(?variant); |
| for &local in variant { |
| let decl = &coroutine_info.field_tys[local]; |
| debug!(?decl); |
| if ty_matches(ty::Binder::dummy(decl.ty)) && !decl.ignore_for_traits { |
| interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior( |
| decl.source_info.span, |
| Some((source_info.span, from_awaited_ty)), |
| )); |
| break 'find_source; |
| } |
| } |
| } |
| } |
| |
| if interior_or_upvar_span.is_none() { |
| interior_or_upvar_span = |
| coroutine_data.try_get_upvar_span(self, coroutine_did, ty_matches); |
| } |
| |
| if interior_or_upvar_span.is_none() && !coroutine_did.is_local() { |
| interior_or_upvar_span = Some(CoroutineInteriorOrUpvar::Interior(span, None)); |
| } |
| |
| debug!(?interior_or_upvar_span); |
| if let Some(interior_or_upvar_span) = interior_or_upvar_span { |
| let is_async = self.tcx.coroutine_is_async(coroutine_did); |
| self.note_obligation_cause_for_async_await( |
| err, |
| interior_or_upvar_span, |
| is_async, |
| outer_coroutine, |
| trait_ref, |
| target_ty, |
| obligation, |
| next_code, |
| ); |
| true |
| } else { |
| false |
| } |
| } |
| |
| /// Unconditionally adds the diagnostic note described in |
| /// `maybe_note_obligation_cause_for_async_await`'s documentation comment. |
| #[instrument(level = "debug", skip_all)] |
| fn note_obligation_cause_for_async_await<G: EmissionGuarantee>( |
| &self, |
| err: &mut Diag<'_, G>, |
| interior_or_upvar_span: CoroutineInteriorOrUpvar, |
| is_async: bool, |
| outer_coroutine: Option<DefId>, |
| trait_pred: ty::TraitPredicate<'tcx>, |
| target_ty: Ty<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| next_code: Option<&ObligationCauseCode<'tcx>>, |
| ) { |
| let source_map = self.tcx.sess.source_map(); |
| |
| let (await_or_yield, an_await_or_yield) = |
| if is_async { ("await", "an await") } else { ("yield", "a yield") }; |
| let future_or_coroutine = if is_async { "future" } else { "coroutine" }; |
| |
| // Special case the primary error message when send or sync is the trait that was |
| // not implemented. |
| let hir = self.tcx.hir(); |
| let trait_explanation = if let Some(name @ (sym::Send | sym::Sync)) = |
| self.tcx.get_diagnostic_name(trait_pred.def_id()) |
| { |
| let (trait_name, trait_verb) = |
| if name == sym::Send { ("`Send`", "sent") } else { ("`Sync`", "shared") }; |
| |
| err.code = None; |
| err.primary_message(format!( |
| "{future_or_coroutine} cannot be {trait_verb} between threads safely" |
| )); |
| |
| let original_span = err.span.primary_span().unwrap(); |
| let mut span = MultiSpan::from_span(original_span); |
| |
| let message = outer_coroutine |
| .and_then(|coroutine_did| { |
| Some(match self.tcx.coroutine_kind(coroutine_did).unwrap() { |
| CoroutineKind::Coroutine(_) => format!("coroutine is not {trait_name}"), |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::Async, |
| CoroutineSource::Fn, |
| ) => self |
| .tcx |
| .parent(coroutine_did) |
| .as_local() |
| .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) |
| .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) |
| .map(|name| { |
| format!("future returned by `{name}` is not {trait_name}") |
| })?, |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::Async, |
| CoroutineSource::Block, |
| ) => { |
| format!("future created by async block is not {trait_name}") |
| } |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::Async, |
| CoroutineSource::Closure, |
| ) => { |
| format!("future created by async closure is not {trait_name}") |
| } |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::AsyncGen, |
| CoroutineSource::Fn, |
| ) => self |
| .tcx |
| .parent(coroutine_did) |
| .as_local() |
| .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) |
| .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) |
| .map(|name| { |
| format!("async iterator returned by `{name}` is not {trait_name}") |
| })?, |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::AsyncGen, |
| CoroutineSource::Block, |
| ) => { |
| format!("async iterator created by async gen block is not {trait_name}") |
| } |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::AsyncGen, |
| CoroutineSource::Closure, |
| ) => { |
| format!( |
| "async iterator created by async gen closure is not {trait_name}" |
| ) |
| } |
| CoroutineKind::Desugared(CoroutineDesugaring::Gen, CoroutineSource::Fn) => { |
| self.tcx |
| .parent(coroutine_did) |
| .as_local() |
| .map(|parent_did| self.tcx.local_def_id_to_hir_id(parent_did)) |
| .and_then(|parent_hir_id| hir.opt_name(parent_hir_id)) |
| .map(|name| { |
| format!("iterator returned by `{name}` is not {trait_name}") |
| })? |
| } |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::Gen, |
| CoroutineSource::Block, |
| ) => { |
| format!("iterator created by gen block is not {trait_name}") |
| } |
| CoroutineKind::Desugared( |
| CoroutineDesugaring::Gen, |
| CoroutineSource::Closure, |
| ) => { |
| format!("iterator created by gen closure is not {trait_name}") |
| } |
| }) |
| }) |
| .unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}")); |
| |
| span.push_span_label(original_span, message); |
| err.span(span); |
| |
| format!("is not {trait_name}") |
| } else { |
| format!("does not implement `{}`", trait_pred.print_modifiers_and_trait_path()) |
| }; |
| |
| let mut explain_yield = |interior_span: Span, yield_span: 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 |
| // | |
| // LL | baz(|| async { |
| // | ______________- |
| // | | |
| // | | |
| // LL | | foo(tx.clone()); |
| // LL | | }).await; |
| // | | - ^^^^^^ await occurs here, with value maybe used later |
| // | |__________| |
| // | has type `closure` which is not `Send` |
| // note: value is later dropped here |
| // LL | | }).await; |
| // | | ^ |
| // |
| span.push_span_label( |
| yield_span, |
| format!("{await_or_yield} occurs here, with {snippet} maybe used later"), |
| ); |
| span.push_span_label( |
| interior_span, |
| format!("has type `{target_ty}` which {trait_explanation}"), |
| ); |
| err.span_note( |
| span, |
| format!("{future_or_coroutine} {trait_explanation} as this value is used across {an_await_or_yield}"), |
| ); |
| }; |
| match interior_or_upvar_span { |
| CoroutineInteriorOrUpvar::Interior(interior_span, interior_extra_info) => { |
| if let Some((yield_span, from_awaited_ty)) = interior_extra_info { |
| if let Some(await_span) = from_awaited_ty { |
| // The type causing this obligation is one being awaited at await_span. |
| let mut span = MultiSpan::from_span(await_span); |
| span.push_span_label( |
| await_span, |
| format!( |
| "await occurs here on type `{target_ty}`, which {trait_explanation}" |
| ), |
| ); |
| err.span_note( |
| span, |
| format!( |
| "future {trait_explanation} as it awaits another future which {trait_explanation}" |
| ), |
| ); |
| } else { |
| // Look at the last interior type to get a span for the `.await`. |
| explain_yield(interior_span, yield_span); |
| } |
| } |
| } |
| CoroutineInteriorOrUpvar::Upvar(upvar_span) => { |
| // `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 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 `{target_ty}` which {trait_explanation}, because `{ref_ty}` is not `{ref_ty_trait}`" |
| ), |
| format!( |
| "captured value {trait_explanation} because `{ref_kind}` references cannot be sent unless their referent is `{ref_ty_trait}`" |
| ), |
| ) |
| } |
| None => ( |
| format!("has type `{target_ty}` which {trait_explanation}"), |
| format!("captured value {trait_explanation}"), |
| ), |
| }; |
| |
| let mut span = MultiSpan::from_span(upvar_span); |
| span.push_span_label(upvar_span, span_label); |
| err.span_note(span, span_note); |
| } |
| } |
| |
| // Add a note for the item obligation that remains - normally a note pointing to the |
| // bound that introduced the obligation (e.g. `T: Send`). |
| debug!(?next_code); |
| self.note_obligation_cause_code( |
| obligation.cause.body_id, |
| err, |
| obligation.predicate, |
| obligation.param_env, |
| next_code.unwrap(), |
| &mut Vec::new(), |
| &mut Default::default(), |
| ); |
| } |
| |
| fn note_obligation_cause_code<G: EmissionGuarantee, T>( |
| &self, |
| body_id: LocalDefId, |
| err: &mut Diag<'_, G>, |
| predicate: T, |
| param_env: ty::ParamEnv<'tcx>, |
| cause_code: &ObligationCauseCode<'tcx>, |
| obligated_types: &mut Vec<Ty<'tcx>>, |
| seen_requirements: &mut FxHashSet<DefId>, |
| ) where |
| T: ToPredicate<'tcx>, |
| { |
| let mut long_ty_file = None; |
| |
| let tcx = self.tcx; |
| let predicate = predicate.to_predicate(tcx); |
| match *cause_code { |
| ObligationCauseCode::ExprAssignable |
| | ObligationCauseCode::MatchExpressionArm { .. } |
| | ObligationCauseCode::Pattern { .. } |
| | ObligationCauseCode::IfExpression { .. } |
| | ObligationCauseCode::IfExpressionWithNoElse |
| | ObligationCauseCode::MainFunctionType |
| | ObligationCauseCode::StartFunctionType |
| | ObligationCauseCode::LangFunctionType(_) |
| | ObligationCauseCode::IntrinsicType |
| | ObligationCauseCode::MethodReceiver |
| | ObligationCauseCode::ReturnNoExpression |
| | ObligationCauseCode::UnifyReceiver(..) |
| | ObligationCauseCode::Misc |
| | ObligationCauseCode::WellFormed(..) |
| | ObligationCauseCode::MatchImpl(..) |
| | ObligationCauseCode::ReturnValue(_) |
| | ObligationCauseCode::BlockTailExpression(..) |
| | ObligationCauseCode::AwaitableExpr(_) |
| | ObligationCauseCode::ForLoopIterator |
| | ObligationCauseCode::QuestionMark |
| | ObligationCauseCode::CheckAssociatedTypeBounds { .. } |
| | ObligationCauseCode::LetElse |
| | ObligationCauseCode::BinOp { .. } |
| | ObligationCauseCode::AscribeUserTypeProvePredicate(..) |
| | ObligationCauseCode::DropImpl |
| | ObligationCauseCode::ConstParam(_) |
| | ObligationCauseCode::ReferenceOutlivesReferent(..) |
| | ObligationCauseCode::ObjectTypeBound(..) => {} |
| ObligationCauseCode::RustCall => { |
| if let Some(pred) = predicate.to_opt_poly_trait_pred() |
| && Some(pred.def_id()) == tcx.lang_items().sized_trait() |
| { |
| err.note("argument required to be sized due to `extern \"rust-call\"` ABI"); |
| } |
| } |
| ObligationCauseCode::SliceOrArrayElem => { |
| err.note("slice and array elements must have `Sized` type"); |
| } |
| ObligationCauseCode::TupleElem => { |
| err.note("only the last element of a tuple may have a dynamically sized type"); |
| } |
| ObligationCauseCode::WhereClause(_) | ObligationCauseCode::WhereClauseInExpr(..) => { |
| // We hold the `DefId` of the item introducing the obligation, but displaying it |
| // doesn't add user usable information. It always point at an associated item. |
| } |
| ObligationCauseCode::SpannedWhereClause(item_def_id, span) |
| | ObligationCauseCode::SpannedWhereClauseInExpr(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 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_visible(sm) && !ident.span.overlaps(span) && !same_line { |
| multispan.push_span_label( |
| ident.span, |
| format!( |
| "required by a bound in this {}", |
| tcx.def_kind(item_def_id).descr(item_def_id) |
| ), |
| ); |
| } |
| } |
| let mut a = "a"; |
| let mut this = "this bound"; |
| let mut note = None; |
| let mut help = None; |
| if let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() |
| && let ty::ClauseKind::Trait(trait_pred) = clause |
| { |
| let def_id = trait_pred.def_id(); |
| let visible_item = if let Some(local) = def_id.as_local() { |
| // Check for local traits being reachable. |
| let vis = &tcx.resolutions(()).effective_visibilities; |
| // Account for non-`pub` traits in the root of the local crate. |
| let is_locally_reachable = tcx.parent(def_id).is_crate_root(); |
| vis.is_reachable(local) || is_locally_reachable |
| } else { |
| // Check for foreign traits being reachable. |
| tcx.visible_parent_map(()).get(&def_id).is_some() |
| }; |
| if Some(def_id) == tcx.lang_items().sized_trait() { |
| // Check if this is an implicit bound, even in foreign crates. |
| if tcx |
| .generics_of(item_def_id) |
| .own_params |
| .iter() |
| .any(|param| tcx.def_span(param.def_id) == span) |
| { |
| a = "an implicit `Sized`"; |
| this = "the implicit `Sized` requirement on this type parameter"; |
| } |
| if let Some(hir::Node::TraitItem(hir::TraitItem { |
| generics, |
| kind: hir::TraitItemKind::Type(bounds, None), |
| .. |
| })) = tcx.hir().get_if_local(item_def_id) |
| // Do not suggest relaxing if there is an explicit `Sized` obligation. |
| && !bounds.iter() |
| .filter_map(|bound| bound.trait_ref()) |
| .any(|tr| tr.trait_def_id() == tcx.lang_items().sized_trait()) |
| { |
| let (span, separator) = if let [.., last] = bounds { |
| (last.span().shrink_to_hi(), " +") |
| } else { |
| (generics.span.shrink_to_hi(), ":") |
| }; |
| err.span_suggestion_verbose( |
| span, |
| "consider relaxing the implicit `Sized` restriction", |
| format!("{separator} ?Sized"), |
| Applicability::MachineApplicable, |
| ); |
| } |
| } |
| if let DefKind::Trait = tcx.def_kind(item_def_id) |
| && !visible_item |
| { |
| note = Some(format!( |
| "`{short_item_name}` is a \"sealed trait\", because to implement it \ |
| you also need to implement `{}`, which is not accessible; this is \ |
| usually done to force you to use one of the provided types that \ |
| already implement it", |
| with_no_trimmed_paths!(tcx.def_path_str(def_id)), |
| )); |
| let impls_of = tcx.trait_impls_of(def_id); |
| let impls = impls_of |
| .non_blanket_impls() |
| .values() |
| .flatten() |
| .chain(impls_of.blanket_impls().iter()) |
| .collect::<Vec<_>>(); |
| if !impls.is_empty() { |
| let len = impls.len(); |
| let mut types = impls |
| .iter() |
| .map(|t| { |
| with_no_trimmed_paths!(format!( |
| " {}", |
| tcx.type_of(*t).instantiate_identity(), |
| )) |
| }) |
| .collect::<Vec<_>>(); |
| let post = if types.len() > 9 { |
| types.truncate(8); |
| format!("\nand {} others", len - 8) |
| } else { |
| String::new() |
| }; |
| help = Some(format!( |
| "the following type{} implement{} the trait:\n{}{post}", |
| pluralize!(len), |
| if len == 1 { "s" } else { "" }, |
| types.join("\n"), |
| )); |
| } |
| } |
| }; |
| let descr = format!("required by {a} bound in `{item_name}`"); |
| if span.is_visible(sm) { |
| let msg = format!("required by {this} in `{short_item_name}`"); |
| multispan.push_span_label(span, msg); |
| err.span_note(multispan, descr); |
| } else { |
| err.span_note(tcx.def_span(item_def_id), descr); |
| } |
| if let Some(note) = note { |
| err.note(note); |
| } |
| if let Some(help) = help { |
| err.help(help); |
| } |
| } |
| ObligationCauseCode::Coercion { source, target } => { |
| let source = |
| tcx.short_ty_string(self.resolve_vars_if_possible(source), &mut long_ty_file); |
| let target = |
| tcx.short_ty_string(self.resolve_vars_if_possible(target), &mut long_ty_file); |
| err.note(with_forced_trimmed_paths!(format!( |
| "required for the cast from `{source}` to `{target}`", |
| ))); |
| } |
| ObligationCauseCode::RepeatElementCopy { |
| is_constable, |
| elt_type, |
| elt_span, |
| elt_stmt_span, |
| } => { |
| err.note( |
| "the `Copy` trait is required because this value will be copied for each element of the array", |
| ); |
| let value_kind = match is_constable { |
| IsConstable::Fn => Some("the result of the function call"), |
| IsConstable::Ctor => Some("the result of the constructor"), |
| _ => None, |
| }; |
| let sm = tcx.sess.source_map(); |
| if let Some(value_kind) = value_kind |
| && let Ok(snip) = sm.span_to_snippet(elt_span) |
| { |
| let help_msg = format!( |
| "consider creating a new `const` item and initializing it with {value_kind} \ |
| to be used in the repeat position" |
| ); |
| let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default(); |
| err.multipart_suggestion( |
| help_msg, |
| vec![ |
| ( |
| elt_stmt_span.shrink_to_lo(), |
| format!( |
| "const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}" |
| ), |
| ), |
| (elt_span, "ARRAY_REPEAT_VALUE".to_string()), |
| ], |
| Applicability::MachineApplicable, |
| ); |
| } else { |
| // FIXME: we may suggest array::repeat instead |
| err.help("consider using `core::array::from_fn` to initialize the array"); |
| err.help("see https://doc.rust-lang.org/stable/std/array/fn.from_fn.html for more information"); |
| } |
| |
| if tcx.sess.is_nightly_build() |
| && matches!(is_constable, IsConstable::Fn | IsConstable::Ctor) |
| { |
| err.help( |
| "create an inline `const` block, see RFC #2920 \ |
| <https://github.com/rust-lang/rfcs/pull/2920> for more information", |
| ); |
| } |
| } |
| ObligationCauseCode::VariableType(hir_id) => { |
| if let Some(typeck_results) = &self.typeck_results |
| && let Some(ty) = typeck_results.node_type_opt(hir_id) |
| && let ty::Error(_) = ty.kind() |
| { |
| err.note(format!( |
| "`{predicate}` isn't satisfied, but the type of this pattern is \ |
| `{{type error}}`", |
| )); |
| err.downgrade_to_delayed_bug(); |
| } |
| match tcx.parent_hir_node(hir_id) { |
| Node::LetStmt(hir::LetStmt { 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"); |
| } |
| Node::LetStmt(hir::LetStmt { |
| init: Some(hir::Expr { kind: hir::ExprKind::Index(..), span, .. }), |
| .. |
| }) => { |
| // When encountering an assignment of an unsized trait, like |
| // `let x = ""[..];`, provide a suggestion to borrow the initializer in |
| // order to use have a slice instead. |
| err.span_suggestion_verbose( |
| span.shrink_to_lo(), |
| "consider borrowing here", |
| "&", |
| Applicability::MachineApplicable, |
| ); |
| err.note("all local variables must have a statically known size"); |
| } |
| Node::Param(param) => { |
| err.span_suggestion_verbose( |
| param.ty_span.shrink_to_lo(), |
| "function arguments must have a statically known size, borrowed types \ |
| always have a known size", |
| "&", |
| Applicability::MachineApplicable, |
| ); |
| } |
| _ => { |
| err.note("all local variables must have a statically known size"); |
| } |
| } |
| if !tcx.features().unsized_locals { |
| err.help("unsized locals are gated as an unstable feature"); |
| } |
| } |
| ObligationCauseCode::SizedArgumentType(hir_id) => { |
| let mut ty = None; |
| let borrowed_msg = "function arguments must have a statically known size, borrowed \ |
| types always have a known size"; |
| if let Some(hir_id) = hir_id |
| && let hir::Node::Param(param) = self.tcx.hir_node(hir_id) |
| && let Some(decl) = self.tcx.parent_hir_node(hir_id).fn_decl() |
| && let Some(t) = decl.inputs.iter().find(|t| param.ty_span.contains(t.span)) |
| { |
| // We use `contains` because the type might be surrounded by parentheses, |
| // which makes `ty_span` and `t.span` disagree with each other, but one |
| // fully contains the other: `foo: (dyn Foo + Bar)` |
| // ^-------------^ |
| // || |
| // |t.span |
| // param._ty_span |
| ty = Some(t); |
| } else if let Some(hir_id) = hir_id |
| && let hir::Node::Ty(t) = self.tcx.hir_node(hir_id) |
| { |
| ty = Some(t); |
| } |
| if let Some(ty) = ty { |
| match ty.kind { |
| hir::TyKind::TraitObject(traits, _, _) => { |
| let (span, kw) = match traits { |
| [first, ..] if first.span.lo() == ty.span.lo() => { |
| // Missing `dyn` in front of trait object. |
| (ty.span.shrink_to_lo(), "dyn ") |
| } |
| [first, ..] => (ty.span.until(first.span), ""), |
| [] => span_bug!(ty.span, "trait object with no traits: {ty:?}"), |
| }; |
| let needs_parens = traits.len() != 1; |
| err.span_suggestion_verbose( |
| span, |
| "you can use `impl Trait` as the argument type", |
| "impl ", |
| Applicability::MaybeIncorrect, |
| ); |
| let sugg = if !needs_parens { |
| vec![(span.shrink_to_lo(), format!("&{kw}"))] |
| } else { |
| vec![ |
| (span.shrink_to_lo(), format!("&({kw}")), |
| (ty.span.shrink_to_hi(), ")".to_string()), |
| ] |
| }; |
| err.multipart_suggestion_verbose( |
| borrowed_msg, |
| sugg, |
| Applicability::MachineApplicable, |
| ); |
| } |
| hir::TyKind::Slice(_ty) => { |
| err.span_suggestion_verbose( |
| ty.span.shrink_to_lo(), |
| "function arguments must have a statically known size, borrowed \ |
| slices always have a known size", |
| "&", |
| Applicability::MachineApplicable, |
| ); |
| } |
| hir::TyKind::Path(_) => { |
| err.span_suggestion_verbose( |
| ty.span.shrink_to_lo(), |
| borrowed_msg, |
| "&", |
| Applicability::MachineApplicable, |
| ); |
| } |
| _ => {} |
| } |
| } else { |
| err.note("all function arguments must have a statically known size"); |
| } |
| if tcx.sess.opts.unstable_features.is_nightly_build() |
| && !tcx.features().unsized_fn_params |
| { |
| err.help("unsized fn params are gated as an unstable feature"); |
| } |
| } |
| ObligationCauseCode::SizedReturnType | ObligationCauseCode::SizedCallReturnType => { |
| err.note("the return type of a function must have a statically known size"); |
| } |
| ObligationCauseCode::SizedYieldType => { |
| err.note("the yield type of a coroutine must have a statically known size"); |
| } |
| ObligationCauseCode::AssignmentLhsSized => { |
| err.note("the left-hand-side of an assignment must have a statically known size"); |
| } |
| ObligationCauseCode::TupleInitializerSized => { |
| err.note("tuples must have a statically known size to be initialized"); |
| } |
| ObligationCauseCode::StructInitializerSized => { |
| err.note("structs must have a statically known size to be initialized"); |
| } |
| ObligationCauseCode::FieldSized { adt_kind: ref item, last, span } => { |
| match *item { |
| AdtKind::Struct => { |
| if last { |
| err.note( |
| "the last field of a packed struct may only have a \ |
| dynamically sized type if it does not need drop to be run", |
| ); |
| } else { |
| err.note( |
| "only the last field of a struct may have a dynamically sized type", |
| ); |
| } |
| } |
| AdtKind::Union => { |
| err.note("no field of a union may have a dynamically sized type"); |
| } |
| AdtKind::Enum => { |
| err.note("no field of an enum variant may have a dynamically sized type"); |
| } |
| } |
| err.help("change the field's type to have a statically known size"); |
| err.span_suggestion( |
| span.shrink_to_lo(), |
| "borrowed types always have a statically known size", |
| "&", |
| Applicability::MachineApplicable, |
| ); |
| err.multipart_suggestion( |
| "the `Box` type always has a statically known size and allocates its contents \ |
| in the heap", |
| vec![ |
| (span.shrink_to_lo(), "Box<".to_string()), |
| (span.shrink_to_hi(), ">".to_string()), |
| ], |
| Applicability::MachineApplicable, |
| ); |
| } |
| ObligationCauseCode::ConstSized => { |
| err.note("constant expressions must have a statically known size"); |
| } |
| ObligationCauseCode::InlineAsmSized => { |
| err.note("all inline asm arguments must have a statically known size"); |
| } |
| ObligationCauseCode::SizedClosureCapture(closure_def_id) => { |
| err.note( |
| "all values captured by value by a closure must have a statically known size", |
| ); |
| let hir::ExprKind::Closure(closure) = |
| tcx.hir_node_by_def_id(closure_def_id).expect_expr().kind |
| else { |
| bug!("expected closure in SizedClosureCapture obligation"); |
| }; |
| if let hir::CaptureBy::Value { .. } = closure.capture_clause |
| && let Some(span) = closure.fn_arg_span |
| { |
| err.span_label(span, "this closure captures all values by move"); |
| } |
| } |
| ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => { |
| let what = match tcx.coroutine_kind(coroutine_def_id) { |
| None |
| | Some(hir::CoroutineKind::Coroutine(_)) |
| | Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)) => { |
| "yield" |
| } |
| Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) => { |
| "await" |
| } |
| Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _)) => { |
| "yield`/`await" |
| } |
| }; |
| err.note(format!( |
| "all values live across `{what}` must have a statically known size" |
| )); |
| } |
| ObligationCauseCode::SharedStatic => { |
| err.note("shared static variables must have a type that implements `Sync`"); |
| } |
| ObligationCauseCode::BuiltinDerived(ref data) => { |
| let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); |
| let ty = parent_trait_ref.skip_binder().self_ty(); |
| if parent_trait_ref.references_error() { |
| // NOTE(eddyb) this was `.cancel()`, but `err` |
| // is borrowed, so we can't fully defuse it. |
| err.downgrade_to_delayed_bug(); |
| return; |
| } |
| |
| // If the obligation for a tuple is set directly by a Coroutine or Closure, |
| // then the tuple must be the one containing capture types. |
| let is_upvar_tys_infer_tuple = if !matches!(ty.kind(), ty::Tuple(..)) { |
| false |
| } else { |
| if let ObligationCauseCode::BuiltinDerived(data) = &*data.parent_code { |
| let parent_trait_ref = |
| self.resolve_vars_if_possible(data.parent_trait_pred); |
| let nested_ty = parent_trait_ref.skip_binder().self_ty(); |
| matches!(nested_ty.kind(), ty::Coroutine(..)) |
| || matches!(nested_ty.kind(), ty::Closure(..)) |
| } else { |
| false |
| } |
| }; |
| |
| if !is_upvar_tys_infer_tuple { |
| let ty_str = tcx.short_ty_string(ty, &mut long_ty_file); |
| let msg = format!("required because it appears within the type `{ty_str}`"); |
| match ty.kind() { |
| ty::Adt(def, _) => match tcx.opt_item_ident(def.did()) { |
| Some(ident) => { |
| err.span_note(ident.span, msg); |
| } |
| None => { |
| err.note(msg); |
| } |
| }, |
| ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => { |
| // If the previous type is async fn, this is the future generated by the body of an async function. |
| // Avoid printing it twice (it was already printed in the `ty::Coroutine` arm below). |
| let is_future = tcx.ty_is_opaque_future(ty); |
| debug!( |
| ?obligated_types, |
| ?is_future, |
| "note_obligation_cause_code: check for async fn" |
| ); |
| if is_future |
| && obligated_types.last().is_some_and(|ty| match ty.kind() { |
| ty::Coroutine(last_def_id, ..) => { |
| tcx.coroutine_is_async(*last_def_id) |
| } |
| _ => false, |
| }) |
| { |
| // See comment above; skip printing twice. |
| } else { |
| err.span_note(tcx.def_span(def_id), msg); |
| } |
| } |
| ty::Coroutine(def_id, _) => { |
| let sp = tcx.def_span(def_id); |
| |
| // Special-case this to say "async block" instead of `[static coroutine]`. |
| let kind = tcx.coroutine_kind(def_id).unwrap(); |
| err.span_note( |
| sp, |
| with_forced_trimmed_paths!(format!( |
| "required because it's used within this {kind:#}", |
| )), |
| ); |
| } |
| ty::CoroutineWitness(..) => { |
| // Skip printing coroutine-witnesses, since we'll drill into |
| // the bad field in another derived obligation cause. |
| } |
| ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => { |
| err.span_note( |
| tcx.def_span(def_id), |
| "required because it's used within this closure", |
| ); |
| } |
| ty::Str => { |
| err.note("`str` is considered to contain a `[u8]` slice for auto trait purposes"); |
| } |
| _ => { |
| err.note(msg); |
| } |
| }; |
| } |
| |
| obligated_types.push(ty); |
| |
| let parent_predicate = parent_trait_ref; |
| if !self.is_recursive_obligation(obligated_types, &data.parent_code) { |
| // #74711: avoid a stack overflow |
| ensure_sufficient_stack(|| { |
| self.note_obligation_cause_code( |
| body_id, |
| err, |
| parent_predicate, |
| param_env, |
| &data.parent_code, |
| obligated_types, |
| seen_requirements, |
| ) |
| }); |
| } else { |
| ensure_sufficient_stack(|| { |
| self.note_obligation_cause_code( |
| body_id, |
| err, |
| parent_predicate, |
| param_env, |
| cause_code.peel_derives(), |
| obligated_types, |
| seen_requirements, |
| ) |
| }); |
| } |
| } |
| ObligationCauseCode::ImplDerived(ref data) => { |
| let mut parent_trait_pred = |
| self.resolve_vars_if_possible(data.derived.parent_trait_pred); |
| let parent_def_id = parent_trait_pred.def_id(); |
| let self_ty_str = tcx |
| .short_ty_string(parent_trait_pred.skip_binder().self_ty(), &mut long_ty_file); |
| let trait_name = parent_trait_pred.print_modifiers_and_trait_path().to_string(); |
| let msg = format!("required for `{self_ty_str}` to implement `{trait_name}`"); |
| let mut is_auto_trait = false; |
| match tcx.hir().get_if_local(data.impl_or_alias_def_id) { |
| Some(Node::Item(hir::Item { |
| kind: hir::ItemKind::Trait(is_auto, ..), |
| ident, |
| .. |
| })) => { |
| // 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); |
| } |
| Some(Node::Item(hir::Item { |
| kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, generics, .. }), |
| .. |
| })) => { |
| let mut spans = Vec::with_capacity(2); |
| if let Some(trait_ref) = of_trait { |
| spans.push(trait_ref.path.span); |
| } |
| spans.push(self_ty.span); |
| 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); |
| point_at_assoc_type_restriction( |
| tcx, |
| err, |
| &self_ty_str, |
| &trait_name, |
| predicate, |
| &generics, |
| &data, |
| ); |
| } |
| _ => { |
| err.note(msg); |
| } |
| }; |
| |
| let mut parent_predicate = parent_trait_pred; |
| let mut data = &data.derived; |
| let mut count = 0; |
| seen_requirements.insert(parent_def_id); |
| if is_auto_trait { |
| // We don't want to point at the ADT saying "required because it appears within |
| // the type `X`", like we would otherwise do in test `supertrait-auto-trait.rs`. |
| while let ObligationCauseCode::BuiltinDerived(derived) = &*data.parent_code { |
| let child_trait_ref = |
| self.resolve_vars_if_possible(derived.parent_trait_pred); |
| let child_def_id = child_trait_ref.def_id(); |
| if seen_requirements.insert(child_def_id) { |
| break; |
| } |
| data = derived; |
| parent_predicate = child_trait_ref.to_predicate(tcx); |
| parent_trait_pred = child_trait_ref; |
| } |
| } |
| while let ObligationCauseCode::ImplDerived(child) = &*data.parent_code { |
| // Skip redundant recursive obligation notes. See `ui/issue-20413.rs`. |
| let child_trait_pred = |
| self.resolve_vars_if_possible(child.derived.parent_trait_pred); |
| let child_def_id = child_trait_pred.def_id(); |
| if seen_requirements.insert(child_def_id) { |
| break; |
| } |
| count += 1; |
| data = &child.derived; |
| parent_predicate = child_trait_pred.to_predicate(tcx); |
| parent_trait_pred = child_trait_pred; |
| } |
| if count > 0 { |
| err.note(format!( |
| "{} redundant requirement{} hidden", |
| count, |
| pluralize!(count) |
| )); |
| let self_ty = tcx.short_ty_string( |
| parent_trait_pred.skip_binder().self_ty(), |
| &mut long_ty_file, |
| ); |
| err.note(format!( |
| "required for `{self_ty}` to implement `{}`", |
| parent_trait_pred.print_modifiers_and_trait_path() |
| )); |
| } |
| // #74711: avoid a stack overflow |
| ensure_sufficient_stack(|| { |
| self.note_obligation_cause_code( |
| body_id, |
| err, |
| parent_predicate, |
| param_env, |
| &data.parent_code, |
| obligated_types, |
| seen_requirements, |
| ) |
| }); |
| } |
| ObligationCauseCode::WellFormedDerived(ref data) => { |
| let parent_trait_ref = self.resolve_vars_if_possible(data.parent_trait_pred); |
| let parent_predicate = parent_trait_ref; |
| // #74711: avoid a stack overflow |
| ensure_sufficient_stack(|| { |
| self.note_obligation_cause_code( |
| body_id, |
| err, |
| parent_predicate, |
| param_env, |
| &data.parent_code, |
| obligated_types, |
| seen_requirements, |
| ) |
| }); |
| } |
| ObligationCauseCode::TypeAlias(ref nested, span, def_id) => { |
| // #74711: avoid a stack overflow |
| ensure_sufficient_stack(|| { |
| self.note_obligation_cause_code( |
| body_id, |
| err, |
| predicate, |
| param_env, |
| nested, |
| obligated_types, |
| seen_requirements, |
| ) |
| }); |
| let mut multispan = MultiSpan::from(span); |
| multispan.push_span_label(span, "required by this bound"); |
| err.span_note( |
| multispan, |
| format!("required by a bound on the type alias `{}`", tcx.item_name(def_id)), |
| ); |
| } |
| ObligationCauseCode::FunctionArg { |
| arg_hir_id, call_hir_id, ref parent_code, .. |
| } => { |
| self.note_function_argument_obligation( |
| body_id, |
| err, |
| arg_hir_id, |
| parent_code, |
| param_env, |
| predicate, |
| call_hir_id, |
| ); |
| ensure_sufficient_stack(|| { |
| self.note_obligation_cause_code( |
| body_id, |
| err, |
| predicate, |
| param_env, |
| parent_code, |
| obligated_types, |
| seen_requirements, |
| ) |
| }); |
| } |
| ObligationCauseCode::CompareImplItem { trait_item_def_id, kind, .. } => { |
| let item_name = tcx.item_name(trait_item_def_id); |
| let msg = format!( |
| "the requirement `{predicate}` appears on the `impl`'s {kind} \ |
| `{item_name}` but not on the corresponding trait's {kind}", |
| ); |
| let sp = tcx |
| .opt_item_ident(trait_item_def_id) |
| .map(|i| i.span) |
| .unwrap_or_else(|| tcx.def_span(trait_item_def_id)); |
| let mut assoc_span: MultiSpan = sp.into(); |
| assoc_span.push_span_label( |
| sp, |
| format!("this trait's {kind} doesn't have the requirement `{predicate}`"), |
| ); |
| if let Some(ident) = tcx |
| .opt_associated_item(trait_item_def_id) |
| .and_then(|i| tcx.opt_item_ident(i.container_id(tcx))) |
| { |
| assoc_span.push_span_label(ident.span, "in this trait"); |
| } |
| err.span_note(assoc_span, msg); |
| } |
| ObligationCauseCode::TrivialBound => { |
| err.help("see issue #48214"); |
| tcx.disabled_nightly_features( |
| err, |
| Some(tcx.local_def_id_to_hir_id(body_id)), |
| [(String::new(), sym::trivial_bounds)], |
| ); |
| } |
| ObligationCauseCode::OpaqueReturnType(expr_info) => { |
| if let Some((expr_ty, expr_span)) = expr_info { |
| let expr_ty = self.tcx.short_ty_string(expr_ty, &mut long_ty_file); |
| err.span_label( |
| expr_span, |
| with_forced_trimmed_paths!(format!( |
| "return type was inferred to be `{expr_ty}` here", |
| )), |
| ); |
| } |
| } |
| } |
| |
| if let Some(file) = long_ty_file { |
| err.note(format!( |
| "the full name for the type has been written to '{}'", |
| file.display(), |
| )); |
| err.note("consider using `--verbose` to print the full type name to the console"); |
| } |
| } |
| |
| #[instrument( |
| level = "debug", skip(self, err), fields(trait_pred.self_ty = ?trait_pred.self_ty()) |
| )] |
| fn suggest_await_before_try( |
| &self, |
| err: &mut Diag<'_>, |
| obligation: &PredicateObligation<'tcx>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| span: Span, |
| ) { |
| if let Some(hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Async, _)) = |
| self.tcx.coroutine_kind(obligation.cause.body_id) |
| { |
| let future_trait = self.tcx.require_lang_item(LangItem::Future, None); |
| |
| let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty()); |
| let impls_future = self.type_implements_trait( |
| future_trait, |
| [self.tcx.instantiate_bound_regions_with_erased(self_ty)], |
| obligation.param_env, |
| ); |
| if !impls_future.must_apply_modulo_regions() { |
| return; |
| } |
| |
| let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; |
| // `<T as Future>::Output` |
| let projection_ty = trait_pred.map_bound(|trait_pred| { |
| Ty::new_projection( |
| self.tcx, |
| item_def_id, |
| // Future::Output has no args |
| [trait_pred.self_ty()], |
| ) |
| }); |
| let InferOk { value: projection_ty, .. } = |
| self.at(&obligation.cause, obligation.param_env).normalize(projection_ty); |
| |
| debug!( |
| normalized_projection_type = ?self.resolve_vars_if_possible(projection_ty) |
| ); |
| let try_obligation = self.mk_trait_obligation_with_new_self_ty( |
| obligation.param_env, |
| trait_pred.map_bound(|trait_pred| (trait_pred, projection_ty.skip_binder())), |
| ); |
| debug!(try_trait_obligation = ?try_obligation); |
| if self.predicate_may_hold(&try_obligation) |
| && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) |
| && snippet.ends_with('?') |
| { |
| err.span_suggestion_verbose( |
| span.with_hi(span.hi() - BytePos(1)).shrink_to_hi(), |
| "consider `await`ing on the `Future`", |
| ".await", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| } |
| |
| fn suggest_floating_point_literal( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| trait_ref: &ty::PolyTraitRef<'tcx>, |
| ) { |
| let rhs_span = match obligation.cause.code() { |
| ObligationCauseCode::BinOp { rhs_span: Some(span), rhs_is_lit, .. } if *rhs_is_lit => { |
| span |
| } |
| _ => return, |
| }; |
| if let ty::Float(_) = trait_ref.skip_binder().self_ty().kind() |
| && let ty::Infer(InferTy::IntVar(_)) = trait_ref.skip_binder().args.type_at(1).kind() |
| { |
| err.span_suggestion_verbose( |
| rhs_span.shrink_to_hi(), |
| "consider using a floating-point literal by writing it with `.0`", |
| ".0", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| |
| fn suggest_derive( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) { |
| let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) else { |
| return; |
| }; |
| let (adt, args) = match trait_pred.skip_binder().self_ty().kind() { |
| ty::Adt(adt, args) if adt.did().is_local() => (adt, args), |
| _ => return, |
| }; |
| let can_derive = { |
| let is_derivable_trait = match diagnostic_name { |
| sym::Default => !adt.is_enum(), |
| sym::PartialEq | sym::PartialOrd => { |
| let rhs_ty = trait_pred.skip_binder().trait_ref.args.type_at(1); |
| trait_pred.skip_binder().self_ty() == rhs_ty |
| } |
| sym::Eq | sym::Ord | sym::Clone | sym::Copy | sym::Hash | sym::Debug => true, |
| _ => false, |
| }; |
| is_derivable_trait && |
| // Ensure all fields impl the trait. |
| adt.all_fields().all(|field| { |
| let field_ty = ty::GenericArg::from(field.ty(self.tcx, args)); |
| let trait_args = match diagnostic_name { |
| sym::PartialEq | sym::PartialOrd => { |
| Some(field_ty) |
| } |
| _ => None, |
| }; |
| // Also add host param, if present |
| let host = self.tcx.generics_of(trait_pred.def_id()).host_effect_index.map(|idx| trait_pred.skip_binder().trait_ref.args[idx]); |
| let trait_pred = trait_pred.map_bound_ref(|tr| ty::TraitPredicate { |
| trait_ref: ty::TraitRef::new(self.tcx, |
| trait_pred.def_id(), |
| [field_ty].into_iter().chain(trait_args).chain(host), |
| ), |
| ..*tr |
| }); |
| let field_obl = Obligation::new( |
| self.tcx, |
| obligation.cause.clone(), |
| obligation.param_env, |
| trait_pred, |
| ); |
| self.predicate_must_hold_modulo_regions(&field_obl) |
| }) |
| }; |
| if can_derive { |
| err.span_suggestion_verbose( |
| self.tcx.def_span(adt.did()).shrink_to_lo(), |
| format!( |
| "consider annotating `{}` with `#[derive({})]`", |
| trait_pred.skip_binder().self_ty(), |
| diagnostic_name, |
| ), |
| // FIXME(effects, const_trait_impl) derive_const as suggestion? |
| format!("#[derive({diagnostic_name})]\n"), |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| |
| fn suggest_dereferencing_index( |
| &self, |
| obligation: &PredicateObligation<'tcx>, |
| err: &mut Diag<'_>, |
| trait_pred: ty::PolyTraitPredicate<'tcx>, |
| ) { |
| if let ObligationCauseCode::ImplDerived(_) = obligation.cause.code() |
| && self |
| .tcx |
| .is_diagnostic_item(sym::SliceIndex, trait_pred.skip_binder().trait_ref.def_id) |
| && let ty::Slice(_) = trait_pred.skip_binder().trait_ref.args.type_at(1).kind() |
| && let ty::Ref(_, inner_ty, _) = trait_pred.skip_binder().self_ty().kind() |
| && let ty::Uint(ty::UintTy::Usize) = inner_ty.kind() |
| { |
| err.span_suggestion_verbose( |
| obligation.cause.span.shrink_to_lo(), |
| "dereference this index", |
| '*', |
| Applicability::MachineApplicable, |
| ); |
| } |
| } |
| |
| fn note_function_argument_obligation<G: EmissionGuarantee>( |
| &self, |
| body_id: LocalDefId, |
| err: &mut Diag<'_, G>, |
| arg_hir_id: HirId, |
| parent_code: &ObligationCauseCode<'tcx>, |
| param_env: ty::ParamEnv<'tcx>, |
| failed_pred: ty::Predicate<'tcx>, |
| call_hir_id: HirId, |
| ) { |
| let tcx = self.tcx; |
| if let Node::Expr(expr) = tcx.hir_node(arg_hir_id) |
| && let Some(typeck_results) = &self.typeck_results |
| { |
| if let hir::Expr { kind: hir::ExprKind::MethodCall(_, rcvr, _, _), .. } = expr |
| && let Some(ty) = typeck_results.node_type_opt(rcvr.hir_id) |
| && let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred() |
| && let pred = failed_pred.map_bound(|pred| pred.with_self_ty(tcx, ty)) |
| && self.predicate_must_hold_modulo_regions(&Obligation::misc( |
| tcx, expr.span, body_id, param_env, pred, |
| )) |
| { |
| err.span_suggestion_verbose( |
| expr.span.with_lo(rcvr.span.hi()), |
| format!( |
| "consider removing this method call, as the receiver has type `{ty}` and \ |
| `{pred}` trivially holds", |
| ), |
| "", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| if let hir::Expr { kind: hir::ExprKind::Block(block, _), .. } = expr { |
| let inner_expr = expr.peel_blocks(); |
| let ty = typeck_results |
| .expr_ty_adjusted_opt(inner_expr) |
| .unwrap_or(Ty::new_misc_error(tcx)); |
| let span = inner_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}`") |
| }, |
| ); |
| if let ty::PredicateKind::Clause(clause) = failed_pred.kind().skip_binder() |
| && let ty::ClauseKind::Trait(pred) = clause |
| && [ |
| tcx.lang_items().fn_once_trait(), |
| tcx.lang_items().fn_mut_trait(), |
| tcx.lang_items().fn_trait(), |
| ] |
| .contains(&Some(pred.def_id())) |
| { |
| if let [stmt, ..] = block.stmts |
| && let hir::StmtKind::Semi(value) = stmt.kind |
| && let hir::ExprKind::Closure(hir::Closure { |
| body, fn_decl_span, .. |
| }) = value.kind |
| && let body = tcx.hir().body(*body) |
| && !matches!(body.value.kind, hir::ExprKind::Block(..)) |
| { |
| // Check if the failed predicate was an expectation of a closure type |
| // and if there might have been a `{ |args|` typo instead of `|args| {`. |
| err.multipart_suggestion( |
| "you might have meant to open the closure body instead of placing \ |
| a closure within a block", |
| vec![ |
| (expr.span.with_hi(value.span.lo()), String::new()), |
| (fn_decl_span.shrink_to_hi(), " {".to_string()), |
| ], |
| Applicability::MaybeIncorrect, |
| ); |
| } else { |
| // Maybe the bare block was meant to be a closure. |
| err.span_suggestion_verbose( |
| expr.span.shrink_to_lo(), |
| "you might have meant to create the closure instead of a block", |
| format!( |
| "|{}| ", |
| (0..pred.trait_ref.args.len() - 1) |
| .map(|_| "_") |
| .collect::<Vec<_>>() |
| .join(", ") |
| ), |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| } |
| } |
| } |
| |
| // 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::SpannedWhereClauseInExpr(def_id, _, _, idx) = parent_code |
| && let Some(node_args) = typeck_results.node_args_opt(call_hir_id) |
| && let where_clauses = |
| self.tcx.predicates_of(def_id).instantiate(self.tcx, node_args) |
| && let Some(where_pred) = where_clauses.predicates.get(*idx) |
| { |
| if let Some(where_pred) = where_pred.as_trait_clause() |
| && let Some(failed_pred) = failed_pred.to_opt_poly_trait_pred() |
| { |
| self.enter_forall(where_pred, |where_pred| { |
| let failed_pred = self.instantiate_binder_with_fresh_vars( |
| expr.span, |
| BoundRegionConversionTime::FnCall, |
| failed_pred, |
| ); |
| |
| let zipped = |
| iter::zip(where_pred.trait_ref.args, failed_pred.trait_ref.args); |
| for (expected, actual) in zipped { |
| self.probe(|_| { |
| match self |
| .at(&ObligationCause::misc(expr.span, body_id), param_env) |
| // Doesn't actually matter if we define opaque types here, this is just used for |
| // diagnostics, and the result is never kept around. |
| .eq(DefineOpaqueTypes::Yes, expected, actual) |
| { |
| Ok(_) => (), // We ignore nested obligations here for now. |
| Err(err) => type_diffs.push(err), |
| } |
| }) |
| } |
| }) |
| } else if let Some(where_pred) = where_pred.as_projection_clause() |
| && 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: Ty::new_alias( |
| self.tcx, |
| 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: Res::Local(hir_id), .. } = path |
| && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) |
| && let hir::Node::LetStmt(local) = self.tcx.parent_hir_node(binding.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 = tcx.hir_node(call_hir_id); |
| if let 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 Node::Expr(expr) = call_node { |
| if let hir::ExprKind::Call(hir::Expr { span, .. }, _) |
| | hir::ExprKind::MethodCall( |
| hir::PathSegment { ident: Ident { span, .. }, .. }, |
| .., |
| ) = expr.kind |
| { |
| if Some(*span) != err.span.primary_span() { |
| err.span_label(*span, "required by a bound introduced by this call"); |
| } |
| } |
| |
| if let hir::ExprKind::MethodCall(_, expr, ..) = expr.kind { |
| self.suggest_option_method_if_applicable(failed_pred, param_env, err, expr); |
| } |
| } |
| } |
| |
| fn suggest_option_method_if_applicable<G: EmissionGuarantee>( |
| &self, |
| failed_pred: ty::Predicate<'tcx>, |
| param_env: ty::ParamEnv<'tcx>, |
| err: &mut Diag<'_, G>, |
| expr: &hir::Expr<'_>, |
| ) { |
| let tcx = self.tcx; |
| let infcx = self.infcx; |
| let Some(typeck_results) = self.typeck_results.as_ref() else { return }; |
| |
| // Make sure we're dealing with the `Option` type. |
| let Some(option_ty_adt) = typeck_results.expr_ty_adjusted(expr).ty_adt_def() else { |
| return; |
| }; |
| if !tcx.is_diagnostic_item(sym::Option, option_ty_adt.did()) { |
| return; |
| } |
| |
| // Given the predicate `fn(&T): FnOnce<(U,)>`, extract `fn(&T)` and `(U,)`, |
| // then suggest `Option::as_deref(_mut)` if `U` can deref to `T` |
| if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(ty::TraitPredicate { trait_ref, .. })) |
| = failed_pred.kind().skip_binder() |
| && tcx.is_fn_trait(trait_ref.def_id) |
| && let [self_ty, found_ty] = trait_ref.args.as_slice() |
| && let Some(fn_ty) = self_ty.as_type().filter(|ty| ty.is_fn()) |
| && let fn_sig @ ty::FnSig { |
| abi: abi::Abi::Rust, |
| c_variadic: false, |
| unsafety: hir::Unsafety::Normal, |
| .. |
| } = fn_ty.fn_sig(tcx).skip_binder() |
| |
| // Extract first param of fn sig with peeled refs, e.g. `fn(&T)` -> `T` |
| && let Some(&ty::Ref(_, target_ty, needs_mut)) = fn_sig.inputs().first().map(|t| t.kind()) |
| && !target_ty.has_escaping_bound_vars() |
| |
| // Extract first tuple element out of fn trait, e.g. `FnOnce<(U,)>` -> `U` |
| && let Some(ty::Tuple(tys)) = found_ty.as_type().map(Ty::kind) |
| && let &[found_ty] = tys.as_slice() |
| && !found_ty.has_escaping_bound_vars() |
| |
| // Extract `<U as Deref>::Target` assoc type and check that it is `T` |
| && let Some(deref_target_did) = tcx.lang_items().deref_target() |
| && let projection = Ty::new_projection(tcx,deref_target_did, tcx.mk_args(&[ty::GenericArg::from(found_ty)])) |
| && let InferOk { value: deref_target, obligations } = infcx.at(&ObligationCause::dummy(), param_env).normalize(projection) |
| && obligations.iter().all(|obligation| infcx.predicate_must_hold_modulo_regions(obligation)) |
| && infcx.can_eq(param_env, deref_target, target_ty) |
| { |
| let help = if let hir::Mutability::Mut = needs_mut |
| && let Some(deref_mut_did) = tcx.lang_items().deref_mut_trait() |
| && infcx |
| .type_implements_trait(deref_mut_did, iter::once(found_ty), param_env) |
| .must_apply_modulo_regions() |
| { |
| Some(("call `Option::as_deref_mut()` first", ".as_deref_mut()")) |
| } else if let hir::Mutability::Not = needs_mut { |
| Some(("call `Option::as_deref()` first", ".as_deref()")) |
| } else { |
| None |
| }; |
| |
| if let Some((msg, sugg)) = help { |
| err.span_suggestion_with_style( |
| expr.span.shrink_to_hi(), |
| msg, |
| sugg, |
| Applicability::MaybeIncorrect, |
| SuggestionStyle::ShowAlways, |
| ); |
| } |
| } |
| } |
| |
| fn look_for_iterator_item_mistakes<G: EmissionGuarantee>( |
| &self, |
| assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>], |
| typeck_results: &TypeckResults<'tcx>, |
| type_diffs: &[TypeError<'tcx>], |
| param_env: ty::ParamEnv<'tcx>, |
| path_segment: &hir::PathSegment<'_>, |
| args: &[hir::Expr<'_>], |
| err: &mut Diag<'_, G>, |
| ) { |
| let tcx = self.tcx; |
| // Special case for iterator chains, we look at potential failures of `Iterator::Item` |
| // not being `: Clone` and `Iterator::map` calls with spurious trailing `;`. |
| for entry in assocs_in_this_method { |
| let Some((_span, (def_id, ty))) = entry else { |
| continue; |
| }; |
| for diff in type_diffs { |
| let Sorts(expected_found) = diff else { |
| continue; |
| }; |
| if tcx.is_diagnostic_item(sym::IteratorItem, *def_id) |
| && path_segment.ident.name == sym::map |
| && self.can_eq(param_env, expected_found.found, *ty) |
| && let [arg] = args |
| && let hir::ExprKind::Closure(closure) = arg.kind |
| { |
| let body = tcx.hir().body(closure.body); |
| if let hir::ExprKind::Block(block, None) = body.value.kind |
| && let None = block.expr |
| && let [.., stmt] = block.stmts |
| && let hir::StmtKind::Semi(expr) = stmt.kind |
| // FIXME: actually check the expected vs found types, but right now |
| // the expected is a projection that we need to resolve. |
| // && let Some(tail_ty) = typeck_results.expr_ty_opt(expr) |
| && expected_found.found.is_unit() |
| { |
| err.span_suggestion_verbose( |
| expr.span.shrink_to_hi().with_hi(stmt.span.hi()), |
| "consider removing this semicolon", |
| String::new(), |
| Applicability::MachineApplicable, |
| ); |
| } |
| let expr = if let hir::ExprKind::Block(block, None) = body.value.kind |
| && let Some(expr) = block.expr |
| { |
| expr |
| } else { |
| body.value |
| }; |
| if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind |
| && path_segment.ident.name == sym::clone |
| && let Some(expr_ty) = typeck_results.expr_ty_opt(expr) |
| && let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr) |
| && self.can_eq(param_env, expr_ty, rcvr_ty) |
| && let ty::Ref(_, ty, _) = expr_ty.kind() |
| { |
| err.span_label( |
| span, |
| format!( |
| "this method call is cloning the reference `{expr_ty}`, not \ |
| `{ty}` which doesn't implement `Clone`", |
| ), |
| ); |
| let ty::Param(..) = ty.kind() else { |
| continue; |
| }; |
| let hir = tcx.hir(); |
| let node = tcx.hir_node_by_def_id(hir.get_parent_item(expr.hir_id).def_id); |
| |
| let pred = ty::Binder::dummy(ty::TraitPredicate { |
| trait_ref: ty::TraitRef::from_lang_item( |
| tcx, |
| LangItem::Clone, |
| span, |
| [*ty], |
| ), |
| polarity: ty::PredicatePolarity::Positive, |
| }); |
| let Some(generics) = node.generics() else { |
| continue; |
| }; |
| let Some(body_id) = node.body_id() else { |
| continue; |
| }; |
| suggest_restriction( |
| tcx, |
| hir.body_owner_def_id(body_id), |
| generics, |
| &format!("type parameter `{ty}`"), |
| err, |
| node.fn_sig(), |
| None, |
| pred, |
| None, |
| ); |
| } |
| } |
| } |
| } |
| } |
| |
| fn point_at_chain<G: EmissionGuarantee>( |
| &self, |
| expr: &hir::Expr<'_>, |
| typeck_results: &TypeckResults<'tcx>, |
| type_diffs: Vec<TypeError<'tcx>>, |
| param_env: ty::ParamEnv<'tcx>, |
| err: &mut Diag<'_, G>, |
| ) { |
| 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(Ty::new_misc_error(tcx)), |
| ); |
| 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); |
| self.look_for_iterator_item_mistakes( |
| &assocs_in_this_method, |
| typeck_results, |
| &type_diffs, |
| param_env, |
| path_segment, |
| args, |
| err, |
| ); |
| assocs.push(assocs_in_this_method); |
| prev_ty = self.resolve_vars_if_possible( |
| typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)), |
| ); |
| |
| if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind |
| && let hir::Path { res: Res::Local(hir_id), .. } = path |
| && let hir::Node::Pat(binding) = self.tcx.hir_node(*hir_id) |
| { |
| let parent = self.tcx.parent_hir_node(binding.hir_id); |
| // We've reached the root of the method call chain... |
| if let hir::Node::LetStmt(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(Ty::new_misc_error(tcx)), |
| ); |
| 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) |
| }) |
| { |
| // 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) { |
| if type_diffs.iter().any(|diff| { |
| let Sorts(expected_found) = diff else { |
| return false; |
| }; |
| self.can_eq(param_env, expected_found.found, ty) |
| }) { |
| 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: HirId, |
| param_env: ty::ParamEnv<'tcx>, |
| ) -> Vec<Option<(Span, (DefId, Ty<'tcx>))>> { |
| let ocx = ObligationCtxt::new(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; |
| }; |
| |
| // 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 args = GenericArgs::for_item(self.tcx, proj.def_id, |param, _| { |
| if param.index == 0 { |
| debug_assert_matches!(param.kind, ty::GenericParamDefKind::Type { .. }); |
| return prev_ty.into(); |
| } |
| 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 = self.infcx.next_ty_var(span); |
| // This corresponds to `<ExprTy as Iterator>::Item = _`. |
| let projection = ty::Binder::dummy(ty::PredicateKind::Clause( |
| ty::ClauseKind::Projection(ty::ProjectionPredicate { |
| projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args), |
| term: ty.into(), |
| }), |
| )); |
| let body_def_id = self.tcx.hir().enclosing_body_owner(body_id); |
| // Add `<ExprTy as Iterator>::Item = _` obligation. |
| ocx.register_obligation(Obligation::misc( |
| self.tcx, |
| span, |
| body_def_id, |
| param_env, |
| projection, |
| )); |
| if ocx.select_where_possible().is_empty() |
| && let ty = self.resolve_vars_if_possible(ty) |
| && !ty.is_ty_var() |
| { |
| assocs_in_this_method.push(Some((span, (proj.def_id, ty)))); |
| } else { |
| // `<ExprTy as Iterator>` didn't select, so likely we've |
| // reached the end of the iterator chain, like the originating |
| // `Vec<_>` or the `ty` couldn't be determined. |
| // Keep the space consistent for later zipping. |
| assocs_in_this_method.push(None); |
| } |
| } |
| assocs_in_this_method |
| } |
| |
| /// If the type that failed selection is an array or a reference to an array, |
| /// but the trait is implemented for slices, suggest that the user converts |
| /// the array into a slice. |
| fn suggest_convert_to_slice( |
| &self, |
| err: &mut Diag<'_>, |
| obligation: &PredicateObligation<'tcx>, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| candidate_impls: &[ImplCandidate<'tcx>], |
| span: Span, |
| ) { |
| // We can only suggest the slice coersion for function and binary operation arguments, |
| // since the suggestion would make no sense in turbofish or call |
| let (ObligationCauseCode::BinOp { .. } | ObligationCauseCode::FunctionArg { .. }) = |
| obligation.cause.code() |
| else { |
| return; |
| }; |
| |
| // Three cases where we can make a suggestion: |
| // 1. `[T; _]` (array of T) |
| // 2. `&[T; _]` (reference to array of T) |
| // 3. `&mut [T; _]` (mutable reference to array of T) |
| let (element_ty, mut mutability) = match *trait_ref.skip_binder().self_ty().kind() { |
| ty::Array(element_ty, _) => (element_ty, None), |
| |
| ty::Ref(_, pointee_ty, mutability) => match *pointee_ty.kind() { |
| ty::Array(element_ty, _) => (element_ty, Some(mutability)), |
| _ => return, |
| }, |
| |
| _ => return, |
| }; |
| |
| // Go through all the candidate impls to see if any of them is for |
| // slices of `element_ty` with `mutability`. |
| let mut is_slice = |candidate: Ty<'tcx>| match *candidate.kind() { |
| ty::RawPtr(t, m) | ty::Ref(_, t, m) => { |
| if matches!(*t.kind(), ty::Slice(e) if e == element_ty) |
| && m == mutability.unwrap_or(m) |
| { |
| // Use the candidate's mutability going forward. |
| mutability = Some(m); |
| true |
| } else { |
| false |
| } |
| } |
| _ => false, |
| }; |
| |
| // Grab the first candidate that matches, if any, and make a suggestion. |
| if let Some(slice_ty) = candidate_impls |
| .iter() |
| .map(|trait_ref| trait_ref.trait_ref.self_ty()) |
| .find(|t| is_slice(*t)) |
| { |
| let msg = format!("convert the array to a `{slice_ty}` slice instead"); |
| |
| if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { |
| let mut suggestions = vec![]; |
| if snippet.starts_with('&') { |
| } else if let Some(hir::Mutability::Mut) = mutability { |
| suggestions.push((span.shrink_to_lo(), "&mut ".into())); |
| } else { |
| suggestions.push((span.shrink_to_lo(), "&".into())); |
| } |
| suggestions.push((span.shrink_to_hi(), "[..]".into())); |
| err.multipart_suggestion_verbose(msg, suggestions, Applicability::MaybeIncorrect); |
| } else { |
| err.span_help(span, msg); |
| } |
| } |
| } |
| |
| fn explain_hrtb_projection( |
| &self, |
| diag: &mut Diag<'_>, |
| pred: ty::PolyTraitPredicate<'tcx>, |
| param_env: ty::ParamEnv<'tcx>, |
| cause: &ObligationCause<'tcx>, |
| ) { |
| if pred.skip_binder().has_escaping_bound_vars() && pred.skip_binder().has_non_region_infer() |
| { |
| self.probe(|_| { |
| let ocx = ObligationCtxt::new(self); |
| self.enter_forall(pred, |pred| { |
| let pred = ocx.normalize(&ObligationCause::dummy(), param_env, pred); |
| ocx.register_obligation(Obligation::new( |
| self.tcx, |
| ObligationCause::dummy(), |
| param_env, |
| pred, |
| )); |
| }); |
| if !ocx.select_where_possible().is_empty() { |
| // encountered errors. |
| return; |
| } |
| |
| if let ObligationCauseCode::FunctionArg { |
| call_hir_id, |
| arg_hir_id, |
| parent_code: _, |
| } = cause.code() |
| { |
| let arg_span = self.tcx.hir().span(*arg_hir_id); |
| let mut sp: MultiSpan = arg_span.into(); |
| |
| sp.push_span_label( |
| arg_span, |
| "the trait solver is unable to infer the \ |
| generic types that should be inferred from this argument", |
| ); |
| sp.push_span_label( |
| self.tcx.hir().span(*call_hir_id), |
| "add turbofish arguments to this call to \ |
| specify the types manually, even if it's redundant", |
| ); |
| diag.span_note( |
| sp, |
| "this is a known limitation of the trait solver that \ |
| will be lifted in the future", |
| ); |
| } else { |
| let mut sp: MultiSpan = cause.span.into(); |
| sp.push_span_label( |
| cause.span, |
| "try adding turbofish arguments to this expression to \ |
| specify the types manually, even if it's redundant", |
| ); |
| diag.span_note( |
| sp, |
| "this is a known limitation of the trait solver that \ |
| will be lifted in the future", |
| ); |
| } |
| }); |
| } |
| } |
| |
| fn suggest_desugaring_async_fn_in_trait( |
| &self, |
| err: &mut Diag<'_>, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| ) { |
| // Don't suggest if RTN is active -- we should prefer a where-clause bound instead. |
| if self.tcx.features().return_type_notation { |
| return; |
| } |
| |
| let trait_def_id = trait_ref.def_id(); |
| |
| // Only suggest specifying auto traits |
| if !self.tcx.trait_is_auto(trait_def_id) { |
| return; |
| } |
| |
| // Look for an RPITIT |
| let ty::Alias(ty::Projection, alias_ty) = trait_ref.self_ty().skip_binder().kind() else { |
| return; |
| }; |
| let Some(ty::ImplTraitInTraitData::Trait { fn_def_id, opaque_def_id }) = |
| self.tcx.opt_rpitit_info(alias_ty.def_id) |
| else { |
| return; |
| }; |
| |
| let auto_trait = self.tcx.def_path_str(trait_def_id); |
| // ... which is a local function |
| let Some(fn_def_id) = fn_def_id.as_local() else { |
| // If it's not local, we can at least mention that the method is async, if it is. |
| if self.tcx.asyncness(fn_def_id).is_async() { |
| err.span_note( |
| self.tcx.def_span(fn_def_id), |
| format!( |
| "`{}::{}` is an `async fn` in trait, which does not \ |
| automatically imply that its future is `{auto_trait}`", |
| alias_ty.trait_ref(self.tcx), |
| self.tcx.item_name(fn_def_id) |
| ), |
| ); |
| } |
| return; |
| }; |
| let hir::Node::TraitItem(item) = self.tcx.hir_node_by_def_id(fn_def_id) else { |
| return; |
| }; |
| |
| // ... whose signature is `async` (i.e. this is an AFIT) |
| let (sig, body) = item.expect_fn(); |
| let hir::FnRetTy::Return(hir::Ty { kind: hir::TyKind::OpaqueDef(def, ..), .. }) = |
| sig.decl.output |
| else { |
| // This should never happen, but let's not ICE. |
| return; |
| }; |
| |
| // Check that this is *not* a nested `impl Future` RPIT in an async fn |
| // (i.e. `async fn foo() -> impl Future`) |
| if def.owner_id.to_def_id() != opaque_def_id { |
| return; |
| } |
| |
| let Some(sugg) = suggest_desugaring_async_fn_to_impl_future_in_trait( |
| self.tcx, |
| *sig, |
| *body, |
| opaque_def_id.expect_local(), |
| &format!(" + {auto_trait}"), |
| ) else { |
| return; |
| }; |
| |
| let function_name = self.tcx.def_path_str(fn_def_id); |
| err.multipart_suggestion( |
| format!( |
| "`{auto_trait}` can be made part of the associated future's \ |
| guarantees for all implementations of `{function_name}`" |
| ), |
| sugg, |
| Applicability::MachineApplicable, |
| ); |
| } |
| |
| fn ty_kind_suggestion(&self, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> Option<String> { |
| let tcx = self.infcx.tcx; |
| let implements_default = |ty| { |
| let Some(default_trait) = tcx.get_diagnostic_item(sym::Default) else { |
| return false; |
| }; |
| self.type_implements_trait(default_trait, [ty], param_env).must_apply_modulo_regions() |
| }; |
| |
| Some(match *ty.kind() { |
| ty::Never | ty::Error(_) => return None, |
| ty::Bool => "false".to_string(), |
| ty::Char => "\'x\'".to_string(), |
| ty::Int(_) | ty::Uint(_) => "42".into(), |
| ty::Float(_) => "3.14159".into(), |
| ty::Slice(_) => "[]".to_string(), |
| ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Vec) => { |
| "vec![]".to_string() |
| } |
| ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::String) => { |
| "String::new()".to_string() |
| } |
| ty::Adt(def, args) if def.is_box() => { |
| format!("Box::new({})", self.ty_kind_suggestion(param_env, args[0].expect_ty())?) |
| } |
| ty::Adt(def, _) if Some(def.did()) == tcx.get_diagnostic_item(sym::Option) => { |
| "None".to_string() |
| } |
| ty::Adt(def, args) if Some(def.did()) == tcx.get_diagnostic_item(sym::Result) => { |
| format!("Ok({})", self.ty_kind_suggestion(param_env, args[0].expect_ty())?) |
| } |
| ty::Adt(_, _) if implements_default(ty) => "Default::default()".to_string(), |
| ty::Ref(_, ty, mutability) => { |
| if let (ty::Str, hir::Mutability::Not) = (ty.kind(), mutability) { |
| "\"\"".to_string() |
| } else { |
| let ty = self.ty_kind_suggestion(param_env, ty)?; |
| format!("&{}{ty}", mutability.prefix_str()) |
| } |
| } |
| ty::Array(ty, len) if let Some(len) = len.try_eval_target_usize(tcx, param_env) => { |
| if len == 0 { |
| "[]".to_string() |
| } else if self.type_is_copy_modulo_regions(param_env, ty) || len == 1 { |
| // Can only suggest `[ty; 0]` if sz == 1 or copy |
| format!("[{}; {}]", self.ty_kind_suggestion(param_env, ty)?, len) |
| } else { |
| "/* value */".to_string() |
| } |
| } |
| ty::Tuple(tys) => format!( |
| "({}{})", |
| tys.iter() |
| .map(|ty| self.ty_kind_suggestion(param_env, ty)) |
| .collect::<Option<Vec<String>>>()? |
| .join(", "), |
| if tys.len() == 1 { "," } else { "" } |
| ), |
| _ => "/* value */".to_string(), |
| }) |
| } |
| } |
| |
| /// 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 Diag<'_>, |
| ) { |
| if matches!(found_node, Node::TraitItem(..)) { |
| return; |
| } |
| |
| let found_args = match found.kind() { |
| ty::FnPtr(f) => infcx.enter_forall(*f, |f| f.inputs().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) => infcx.enter_forall(*f, |f| f.inputs().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(); |
| |
| 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) { |
| // FIXME: This could handle more exotic cases like mutability mismatches too! |
| if found_refs.len() < expected_refs.len() |
| && found_refs[..] == expected_refs[expected_refs.len() - found_refs.len()..] |
| { |
| to_borrow.push(( |
| arg.span.shrink_to_lo(), |
| expected_refs[..expected_refs.len() - found_refs.len()] |
| .iter() |
| .map(|mutbl| format!("&{}", mutbl.prefix_str())) |
| .collect::<Vec<_>>() |
| .join(""), |
| )); |
| } else if found_refs.len() > expected_refs.len() { |
| let mut span = arg.span.shrink_to_lo(); |
| let mut left = found_refs.len() - expected_refs.len(); |
| 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.subdiagnostic(infcx.dcx(), errors::AdjustSignatureBorrow::Borrow { to_borrow }); |
| } |
| |
| if !remove_borrow.is_empty() { |
| err.subdiagnostic( |
| infcx.dcx(), |
| errors::AdjustSignatureBorrow::RemoveBorrow { remove_borrow }, |
| ); |
| } |
| } |
| |
| /// Collect all the paths that reference `Self`. |
| /// Used to suggest replacing associated types with an explicit type in `where` clauses. |
| #[derive(Debug)] |
| pub struct SelfVisitor<'v> { |
| pub paths: Vec<&'v hir::Ty<'v>>, |
| pub name: Option<Symbol>, |
| } |
| |
| impl<'v> Visitor<'v> for SelfVisitor<'v> { |
| fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) { |
| if let hir::TyKind::Path(path) = ty.kind |
| && let hir::QPath::TypeRelative(inner_ty, segment) = path |
| && (Some(segment.ident.name) == self.name || self.name.is_none()) |
| && let hir::TyKind::Path(inner_path) = inner_ty.kind |
| && let hir::QPath::Resolved(None, inner_path) = inner_path |
| && let Res::SelfTyAlias { .. } = inner_path.res |
| { |
| self.paths.push(ty); |
| } |
| hir::intravisit::walk_ty(self, ty); |
| } |
| } |
| |
| /// Collect all the returned expressions within the input expression. |
| /// Used to point at the return spans when we want to suggest some change to them. |
| #[derive(Default)] |
| pub struct ReturnsVisitor<'v> { |
| pub returns: Vec<&'v hir::Expr<'v>>, |
| in_block_tail: bool, |
| } |
| |
| impl<'v> Visitor<'v> for ReturnsVisitor<'v> { |
| fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { |
| // Visit every expression to detect `return` paths, either through the function's tail |
| // expression or `return` statements. We walk all nodes to find `return` statements, but |
| // we only care about tail expressions when `in_block_tail` is `true`, which means that |
| // they're in the return path of the function body. |
| match ex.kind { |
| hir::ExprKind::Ret(Some(ex)) => { |
| self.returns.push(ex); |
| } |
| hir::ExprKind::Block(block, _) if self.in_block_tail => { |
| self.in_block_tail = false; |
| for stmt in block.stmts { |
| hir::intravisit::walk_stmt(self, stmt); |
| } |
| self.in_block_tail = true; |
| if let Some(expr) = block.expr { |
| self.visit_expr(expr); |
| } |
| } |
| hir::ExprKind::If(_, then, else_opt) if self.in_block_tail => { |
| self.visit_expr(then); |
| if let Some(el) = else_opt { |
| self.visit_expr(el); |
| } |
| } |
| hir::ExprKind::Match(_, arms, _) if self.in_block_tail => { |
| for arm in arms { |
| self.visit_expr(arm.body); |
| } |
| } |
| // We need to walk to find `return`s in the entire body. |
| _ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex), |
| _ => self.returns.push(ex), |
| } |
| } |
| |
| fn visit_body(&mut self, body: &'v hir::Body<'v>) { |
| assert!(!self.in_block_tail); |
| self.in_block_tail = true; |
| hir::intravisit::walk_body(self, body); |
| } |
| } |
| |
| /// Collect all the awaited expressions within the input expression. |
| #[derive(Default)] |
| struct AwaitsVisitor { |
| awaits: Vec<HirId>, |
| } |
| |
| impl<'v> Visitor<'v> for AwaitsVisitor { |
| fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) { |
| if let hir::ExprKind::Yield(_, hir::YieldSource::Await { expr: Some(id) }) = ex.kind { |
| self.awaits.push(id) |
| } |
| hir::intravisit::walk_expr(self, ex) |
| } |
| } |
| |
| pub trait NextTypeParamName { |
| fn next_type_param_name(&self, name: Option<&str>) -> String; |
| } |
| |
| impl NextTypeParamName for &[hir::GenericParam<'_>] { |
| fn next_type_param_name(&self, name: Option<&str>) -> String { |
| // This is the list of possible parameter names that we might suggest. |
| let name = name.and_then(|n| n.chars().next()).map(|c| c.to_uppercase().to_string()); |
| let name = name.as_deref(); |
| let possible_names = [name.unwrap_or("T"), "T", "U", "V", "X", "Y", "Z", "A", "B", "C"]; |
| let used_names = self |
| .iter() |
| .filter_map(|p| match p.name { |
| hir::ParamName::Plain(ident) => Some(ident.name), |
| _ => None, |
| }) |
| .collect::<Vec<_>>(); |
| |
| possible_names |
| .iter() |
| .find(|n| !used_names.contains(&Symbol::intern(n))) |
| .unwrap_or(&"ParamName") |
| .to_string() |
| } |
| } |
| |
| /// Collect the spans that we see the generic param `param_did` |
| struct ReplaceImplTraitVisitor<'a> { |
| ty_spans: &'a mut Vec<Span>, |
| param_did: DefId, |
| } |
| |
| impl<'a, 'hir> hir::intravisit::Visitor<'hir> for ReplaceImplTraitVisitor<'a> { |
| fn visit_ty(&mut self, t: &'hir hir::Ty<'hir>) { |
| if let hir::TyKind::Path(hir::QPath::Resolved( |
| None, |
| hir::Path { res: Res::Def(_, segment_did), .. }, |
| )) = t.kind |
| { |
| if self.param_did == *segment_did { |
| // `fn foo(t: impl Trait)` |
| // ^^^^^^^^^^ get this to suggest `T` instead |
| |
| // There might be more than one `impl Trait`. |
| self.ty_spans.push(t.span); |
| return; |
| } |
| } |
| |
| hir::intravisit::walk_ty(self, t); |
| } |
| } |
| |
| pub(super) fn get_explanation_based_on_obligation<'tcx>( |
| tcx: TyCtxt<'tcx>, |
| obligation: &PredicateObligation<'tcx>, |
| trait_ref: ty::PolyTraitRef<'tcx>, |
| trait_predicate: &ty::PolyTraitPredicate<'tcx>, |
| pre_message: String, |
| ) -> String { |
| if let ObligationCauseCode::MainFunctionType = obligation.cause.code() { |
| "consider using `()`, or a `Result`".to_owned() |
| } else { |
| let ty_desc = match trait_ref.skip_binder().self_ty().kind() { |
| ty::FnDef(_, _) => Some("fn item"), |
| ty::Closure(_, _) => Some("closure"), |
| _ => None, |
| }; |
| |
| let pred = obligation.predicate; |
| let (_, base) = obligation.cause.code().peel_derives_with_predicate(); |
| let post = if let ty::PredicateKind::Clause(clause) = pred.kind().skip_binder() |
| && let ty::ClauseKind::Trait(pred) = clause |
| && let Some(base) = base |
| && base.skip_binder() != pred |
| { |
| format!(", which is required by `{base}`") |
| } else { |
| String::new() |
| }; |
| let desc = match ty_desc { |
| Some(desc) => format!(" {desc}"), |
| None => String::new(), |
| }; |
| if let ty::PredicatePolarity::Positive = trait_predicate.polarity() { |
| format!( |
| "{pre_message}the trait `{}` is not implemented for{desc} `{}`{post}", |
| trait_predicate.print_modifiers_and_trait_path(), |
| tcx.short_ty_string(trait_ref.skip_binder().self_ty(), &mut None), |
| ) |
| } else { |
| // "the trait bound `T: !Send` is not satisfied" reads better than "`!Send` is |
| // not implemented for `T`". |
| // FIXME: add note explaining explicit negative trait bounds. |
| format!("{pre_message}the trait bound `{trait_predicate}` is not satisfied{post}") |
| } |
| } |
| } |
| |
| // Replace `param` with `replace_ty` |
| struct ReplaceImplTraitFolder<'tcx> { |
| tcx: TyCtxt<'tcx>, |
| param: &'tcx ty::GenericParamDef, |
| replace_ty: Ty<'tcx>, |
| } |
| |
| impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceImplTraitFolder<'tcx> { |
| fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { |
| if let ty::Param(ty::ParamTy { index, .. }) = t.kind() { |
| if self.param.index == *index { |
| return self.replace_ty; |
| } |
| } |
| t.super_fold_with(self) |
| } |
| |
| fn interner(&self) -> TyCtxt<'tcx> { |
| self.tcx |
| } |
| } |
| |
| pub fn suggest_desugaring_async_fn_to_impl_future_in_trait<'tcx>( |
| tcx: TyCtxt<'tcx>, |
| sig: hir::FnSig<'tcx>, |
| body: hir::TraitFn<'tcx>, |
| opaque_def_id: LocalDefId, |
| add_bounds: &str, |
| ) -> Option<Vec<(Span, String)>> { |
| let hir::IsAsync::Async(async_span) = sig.header.asyncness else { |
| return None; |
| }; |
| let async_span = tcx.sess.source_map().span_extend_while_whitespace(async_span); |
| |
| let future = tcx.hir_node_by_def_id(opaque_def_id).expect_item().expect_opaque_ty(); |
| let [hir::GenericBound::Trait(trait_ref, _)] = future.bounds else { |
| // `async fn` should always lower to a single bound... but don't ICE. |
| return None; |
| }; |
| let Some(hir::PathSegment { args: Some(generics), .. }) = |
| trait_ref.trait_ref.path.segments.last() |
| else { |
| // desugaring to a single path segment for `Future<...>`. |
| return None; |
| }; |
| let Some(hir::TypeBindingKind::Equality { term: hir::Term::Ty(future_output_ty) }) = |
| generics.bindings.get(0).map(|binding| binding.kind) |
| else { |
| // Also should never happen. |
| return None; |
| }; |
| |
| let mut sugg = if future_output_ty.span.is_empty() { |
| vec![ |
| (async_span, String::new()), |
| ( |
| future_output_ty.span, |
| format!(" -> impl std::future::Future<Output = ()>{add_bounds}"), |
| ), |
| ] |
| } else { |
| vec![ |
| (future_output_ty.span.shrink_to_lo(), "impl std::future::Future<Output = ".to_owned()), |
| (future_output_ty.span.shrink_to_hi(), format!(">{add_bounds}")), |
| (async_span, String::new()), |
| ] |
| }; |
| |
| // If there's a body, we also need to wrap it in `async {}` |
| if let hir::TraitFn::Provided(body) = body { |
| let body = tcx.hir().body(body); |
| let body_span = body.value.span; |
| let body_span_without_braces = |
| body_span.with_lo(body_span.lo() + BytePos(1)).with_hi(body_span.hi() - BytePos(1)); |
| if body_span_without_braces.is_empty() { |
| sugg.push((body_span_without_braces, " async {} ".to_owned())); |
| } else { |
| sugg.extend([ |
| (body_span_without_braces.shrink_to_lo(), "async {".to_owned()), |
| (body_span_without_braces.shrink_to_hi(), "} ".to_owned()), |
| ]); |
| } |
| } |
| |
| Some(sugg) |
| } |
| |
| /// On `impl` evaluation cycles, look for `Self::AssocTy` restrictions in `where` clauses, explain |
| /// they are not allowed and if possible suggest alternatives. |
| fn point_at_assoc_type_restriction<G: EmissionGuarantee>( |
| tcx: TyCtxt<'_>, |
| err: &mut Diag<'_, G>, |
| self_ty_str: &str, |
| trait_name: &str, |
| predicate: ty::Predicate<'_>, |
| generics: &hir::Generics<'_>, |
| data: &ImplDerivedCause<'_>, |
| ) { |
| let ty::PredicateKind::Clause(clause) = predicate.kind().skip_binder() else { |
| return; |
| }; |
| let ty::ClauseKind::Projection(proj) = clause else { |
| return; |
| }; |
| let name = tcx.item_name(proj.projection_ty.def_id); |
| let mut predicates = generics.predicates.iter().peekable(); |
| let mut prev: Option<&hir::WhereBoundPredicate<'_>> = None; |
| while let Some(pred) = predicates.next() { |
| let hir::WherePredicate::BoundPredicate(pred) = pred else { |
| continue; |
| }; |
| let mut bounds = pred.bounds.iter(); |
| while let Some(bound) = bounds.next() { |
| let Some(trait_ref) = bound.trait_ref() else { |
| continue; |
| }; |
| if bound.span() != data.span { |
| continue; |
| } |
| if let hir::TyKind::Path(path) = pred.bounded_ty.kind |
| && let hir::QPath::TypeRelative(ty, segment) = path |
| && segment.ident.name == name |
| && let hir::TyKind::Path(inner_path) = ty.kind |
| && let hir::QPath::Resolved(None, inner_path) = inner_path |
| && let Res::SelfTyAlias { .. } = inner_path.res |
| { |
| // The following block is to determine the right span to delete for this bound |
| // that will leave valid code after the suggestion is applied. |
| let span = if pred.origin == hir::PredicateOrigin::WhereClause |
| && generics |
| .predicates |
| .iter() |
| .filter(|p| { |
| matches!( |
| p, |
| hir::WherePredicate::BoundPredicate(p) |
| if hir::PredicateOrigin::WhereClause == p.origin |
| ) |
| }) |
| .count() |
| == 1 |
| { |
| // There's only one `where` bound, that needs to be removed. Remove the whole |
| // `where` clause. |
| generics.where_clause_span |
| } else if let Some(hir::WherePredicate::BoundPredicate(next)) = predicates.peek() |
| && pred.origin == next.origin |
| { |
| // There's another bound, include the comma for the current one. |
| pred.span.until(next.span) |
| } else if let Some(prev) = prev |
| && pred.origin == prev.origin |
| { |
| // Last bound, try to remove the previous comma. |
| prev.span.shrink_to_hi().to(pred.span) |
| } else if pred.origin == hir::PredicateOrigin::WhereClause { |
| pred.span.with_hi(generics.where_clause_span.hi()) |
| } else { |
| pred.span |
| }; |
| |
| err.span_suggestion_verbose( |
| span, |
| "associated type for the current `impl` cannot be restricted in `where` \ |
| clauses, remove this bound", |
| "", |
| Applicability::MaybeIncorrect, |
| ); |
| } |
| if let Some(new) = |
| tcx.associated_items(data.impl_or_alias_def_id).find_by_name_and_kind( |
| tcx, |
| Ident::with_dummy_span(name), |
| ty::AssocKind::Type, |
| data.impl_or_alias_def_id, |
| ) |
| { |
| // The associated type is specified in the `impl` we're |
| // looking at. Point at it. |
| let span = tcx.def_span(new.def_id); |
| err.span_label( |
| span, |
| format!( |
| "associated type `<{self_ty_str} as {trait_name}>::{name}` is specified \ |
| here", |
| ), |
| ); |
| // Search for the associated type `Self::{name}`, get |
| // its type and suggest replacing the bound with it. |
| let mut visitor = SelfVisitor { paths: vec![], name: Some(name) }; |
| visitor.visit_trait_ref(trait_ref); |
| for path in visitor.paths { |
| err.span_suggestion_verbose( |
| path.span, |
| "replace the associated type with the type specified in this `impl`", |
| tcx.type_of(new.def_id).skip_binder(), |
| Applicability::MachineApplicable, |
| ); |
| } |
| } else { |
| let mut visitor = SelfVisitor { paths: vec![], name: None }; |
| visitor.visit_trait_ref(trait_ref); |
| let span: MultiSpan = |
| visitor.paths.iter().map(|p| p.span).collect::<Vec<Span>>().into(); |
| err.span_note( |
| span, |
| "associated types for the current `impl` cannot be restricted in `where` \ |
| clauses", |
| ); |
| } |
| } |
| prev = Some(pred); |
| } |
| } |
| |
| fn get_deref_type_and_refs(mut ty: Ty<'_>) -> (Ty<'_>, Vec<hir::Mutability>) { |
| let mut refs = vec![]; |
| |
| while let ty::Ref(_, new_ty, mutbl) = ty.kind() { |
| ty = *new_ty; |
| refs.push(*mutbl); |
| } |
| |
| (ty, refs) |
| } |