1// ignore-tidy-filelength23use std::borrow::Cow;4use std::path::PathBuf;5use std::{debug_assert_matches, iter};67use itertools::{EitherOrBoth, Itertools};8use rustc_abi::ExternAbi;9use rustc_data_structures::fx::FxHashSet;10use rustc_data_structures::stack::ensure_sufficient_stack;11use rustc_errors::codes::*;12use rustc_errors::{13 Applicability, Diag, EmissionGuarantee, MultiSpan, Style, SuggestionStyle, pluralize,14 struct_span_code_err,15};16use rustc_hir::def::{CtorOf, DefKind, Res};17use rustc_hir::def_id::DefId;18use rustc_hir::intravisit::{Visitor, VisitorExt};19use rustc_hir::lang_items::LangItem;20use rustc_hir::{21 self as hir, AmbigArg, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, HirId, Node,22 expr_needs_parens,23};24use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferCtxt, InferOk};25use rustc_infer::traits::ImplSource;26use rustc_middle::middle::privacy::Level;27use rustc_middle::traits::IsConstable;28use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind};29use rustc_middle::ty::error::TypeError;30use rustc_middle::ty::print::{31 PrintPolyTraitPredicateExt as _, PrintPolyTraitRefExt, PrintTraitPredicateExt as _,32 PrintTraitRefExt as _, with_forced_trimmed_paths, with_no_trimmed_paths,33 with_types_for_suggestion,34};35use rustc_middle::ty::{36 self, AdtKind, GenericArgs, InferTy, IsSuggestable, Ty, TyCtxt, TypeFoldable, TypeFolder,37 TypeSuperFoldable, TypeSuperVisitable, TypeVisitableExt, TypeVisitor, TypeckResults,38 Unnormalized, Upcast, suggest_arbitrary_trait_bound, suggest_constraining_type_param,39};40use rustc_middle::{bug, span_bug};41use rustc_span::def_id::LocalDefId;42use rustc_span::{43 BytePos, DUMMY_SP, DesugaringKind, ExpnKind, Ident, MacroKind, Span, Symbol, kw, sym,44};45use tracing::{debug, instrument};4647use super::{48 DefIdOrName, FindExprBySpan, ImplCandidate, Obligation, ObligationCause, ObligationCauseCode,49 PredicateObligation,50};51use crate::error_reporting::TypeErrCtxt;52use crate::errors;53use crate::infer::InferCtxtExt as _;54use crate::traits::query::evaluate_obligation::InferCtxtExt as _;55use crate::traits::{ImplDerivedCause, NormalizeExt, ObligationCtxt, SelectionContext};5657#[derive(Debug)]58pub enum CoroutineInteriorOrUpvar {59 // span of interior type60 Interior(Span, Option<(Span, Option<Span>)>),61 // span of upvar62 Upvar(Span),63}6465// This type provides a uniform interface to retrieve data on coroutines, whether it originated from66// the local crate being compiled or from a foreign crate.67#[derive(Debug)]68struct CoroutineData<'a, 'tcx>(&'a TypeckResults<'tcx>);6970impl<'a, 'tcx> CoroutineData<'a, 'tcx> {71 /// Try to get information about variables captured by the coroutine that matches a type we are72 /// looking for with `ty_matches` function. We uses it to find upvar which causes a failure to73 /// meet an obligation74 fn try_get_upvar_span<F>(75 &self,76 infer_context: &InferCtxt<'tcx>,77 coroutine_did: DefId,78 ty_matches: F,79 ) -> Option<CoroutineInteriorOrUpvar>80 where81 F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,82 {83 infer_context.tcx.upvars_mentioned(coroutine_did).and_then(|upvars| {84 upvars.iter().find_map(|(upvar_id, upvar)| {85 let upvar_ty = self.0.node_type(*upvar_id);86 let upvar_ty = infer_context.resolve_vars_if_possible(upvar_ty);87 ty_matches(ty::Binder::dummy(upvar_ty))88 .then(|| CoroutineInteriorOrUpvar::Upvar(upvar.span))89 })90 })91 }9293 /// Try to get the span of a type being awaited on that matches the type we are looking with the94 /// `ty_matches` function. We uses it to find awaited type which causes a failure to meet an95 /// obligation96 fn get_from_await_ty<F>(97 &self,98 visitor: AwaitsVisitor,99 tcx: TyCtxt<'tcx>,100 ty_matches: F,101 ) -> Option<Span>102 where103 F: Fn(ty::Binder<'tcx, Ty<'tcx>>) -> bool,104 {105 visitor106 .awaits107 .into_iter()108 .map(|id| tcx.hir_expect_expr(id))109 .find(|await_expr| ty_matches(ty::Binder::dummy(self.0.expr_ty_adjusted(await_expr))))110 .map(|expr| expr.span)111 }112}113114fn predicate_constraint(generics: &hir::Generics<'_>, pred: ty::Predicate<'_>) -> (Span, String) {115 (116 generics.tail_span_for_predicate_suggestion(),117 with_types_for_suggestion!(format!("{} {}", generics.add_where_or_trailing_comma(), pred)),118 )119}120121/// Type parameter needs more bounds. The trivial case is `T` `where T: Bound`, but122/// it can also be an `impl Trait` param that needs to be decomposed to a type123/// param for cleaner code.124pub fn suggest_restriction<'tcx, G: EmissionGuarantee>(125 tcx: TyCtxt<'tcx>,126 item_id: LocalDefId,127 hir_generics: &hir::Generics<'tcx>,128 msg: &str,129 err: &mut Diag<'_, G>,130 fn_sig: Option<&hir::FnSig<'_>>,131 projection: Option<ty::AliasTy<'_>>,132 trait_pred: ty::PolyTraitPredicate<'tcx>,133 // When we are dealing with a trait, `super_traits` will be `Some`:134 // Given `trait T: A + B + C {}`135 // - ^^^^^^^^^ GenericBounds136 // |137 // &Ident138 super_traits: Option<(&Ident, &hir::GenericBounds<'_>)>,139) {140 if hir_generics.where_clause_span.from_expansion()141 || hir_generics.where_clause_span.desugaring_kind().is_some()142 || projection.is_some_and(|projection| {143 (tcx.is_impl_trait_in_trait(projection.kind.def_id())144 && !tcx.features().return_type_notation())145 || tcx146 .lookup_stability(projection.kind.def_id())147 .is_some_and(|stab| stab.is_unstable())148 })149 {150 return;151 }152 let generics = tcx.generics_of(item_id);153 // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...154 if let Some((param, bound_str, fn_sig)) =155 fn_sig.zip(projection).and_then(|(sig, p)| match *p.self_ty().kind() {156 // Shenanigans to get the `Trait` from the `impl Trait`.157 ty::Param(param) => {158 let param_def = generics.type_param(param, tcx);159 if param_def.kind.is_synthetic() {160 let bound_str =161 param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string();162 return Some((param_def, bound_str, sig));163 }164 None165 }166 _ => None,167 })168 {169 let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str));170 let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder {171 tcx,172 param,173 replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name))174 .to_ty(tcx),175 });176 if !trait_pred.is_suggestable(tcx, false) {177 return;178 }179 // We know we have an `impl Trait` that doesn't satisfy a required projection.180181 // Find all of the occurrences of `impl Trait` for `Trait` in the function arguments'182 // types. There should be at least one, but there might be *more* than one. In that183 // case we could just ignore it and try to identify which one needs the restriction,184 // but instead we choose to suggest replacing all instances of `impl Trait` with `T`185 // where `T: Trait`.186 let mut ty_spans = vec![];187 for input in fn_sig.decl.inputs {188 ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id }189 .visit_ty_unambig(input);190 }191 // The type param `T: Trait` we will suggest to introduce.192 let type_param = format!("{type_param_name}: {bound_str}");193194 let mut sugg = vec![195 if let Some(span) = hir_generics.span_for_param_suggestion() {196 (span, format!(", {type_param}"))197 } else {198 (hir_generics.span, format!("<{type_param}>"))199 },200 // `fn foo(t: impl Trait)`201 // ^ suggest `where <T as Trait>::A: Bound`202 predicate_constraint(hir_generics, trait_pred.upcast(tcx)),203 ];204 sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string())));205206 // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.207 // FIXME: we should suggest `fn foo(t: impl Trait<A: Bound>)` instead.208 err.multipart_suggestion(209 "introduce a type parameter with a trait bound instead of using `impl Trait`",210 sugg,211 Applicability::MaybeIncorrect,212 );213 } else {214 if !trait_pred.is_suggestable(tcx, false) {215 return;216 }217 // Trivial case: `T` needs an extra bound: `T: Bound`.218 let (sp, suggestion) = match (219 hir_generics220 .params221 .iter()222 .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })),223 super_traits,224 ) {225 (_, None) => predicate_constraint(hir_generics, trait_pred.upcast(tcx)),226 (None, Some((ident, []))) => (227 ident.span.shrink_to_hi(),228 format!(": {}", trait_pred.print_modifiers_and_trait_path()),229 ),230 (_, Some((_, [.., bounds]))) => (231 bounds.span().shrink_to_hi(),232 format!(" + {}", trait_pred.print_modifiers_and_trait_path()),233 ),234 (Some(_), Some((_, []))) => (235 hir_generics.span.shrink_to_hi(),236 format!(": {}", trait_pred.print_modifiers_and_trait_path()),237 ),238 };239240 err.span_suggestion_verbose(241 sp,242 format!("consider further restricting {msg}"),243 suggestion,244 Applicability::MachineApplicable,245 );246 }247}248249/// A single layer of `&` peeled from an expression, used by250/// [`TypeErrCtxt::peel_expr_refs`].251struct PeeledRef<'tcx> {252 /// The span covering the `&` (and any whitespace/mutability keyword) to remove.253 span: Span,254 /// The type after peeling this layer (and all prior layers).255 peeled_ty: Ty<'tcx>,256}257258impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {259 pub fn note_field_shadowed_by_private_candidate_in_cause(260 &self,261 err: &mut Diag<'_>,262 cause: &ObligationCause<'tcx>,263 param_env: ty::ParamEnv<'tcx>,264 ) {265 let mut hir_ids = FxHashSet::default();266 // Walk the parent chain so we can recover267 // the source expression from whichever layer carries them.268 let mut next_code = Some(cause.code());269 while let Some(cause_code) = next_code {270 match cause_code {271 ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => {272 hir_ids.insert(*lhs_hir_id);273 hir_ids.insert(*rhs_hir_id);274 }275 ObligationCauseCode::FunctionArg { arg_hir_id, .. }276 | ObligationCauseCode::ReturnValue(arg_hir_id)277 | ObligationCauseCode::AwaitableExpr(arg_hir_id)278 | ObligationCauseCode::BlockTailExpression(arg_hir_id, _)279 | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => {280 hir_ids.insert(*arg_hir_id);281 }282 ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => {283 hir_ids.insert(*hir_id);284 }285 _ => {}286 }287 next_code = cause_code.parent();288 }289290 if !cause.span.is_dummy()291 && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id)292 {293 let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx);294 expr_finder.visit_body(body);295 if let Some(expr) = expr_finder.result {296 hir_ids.insert(expr.hir_id);297 }298 }299300 // we will sort immediately by source order before emitting any diagnostics301 #[allow(rustc::potential_query_instability)]302 let mut hir_ids: Vec<_> = hir_ids.into_iter().collect();303 let source_map = self.tcx.sess.source_map();304 hir_ids.sort_by_cached_key(|hir_id| {305 let span = self.tcx.hir_span(*hir_id);306 let lo = source_map.lookup_byte_offset(span.lo());307 let hi = source_map.lookup_byte_offset(span.hi());308 (lo.sf.name.prefer_remapped_unconditionally().to_string(), lo.pos.0, hi.pos.0)309 });310311 for hir_id in hir_ids {312 self.note_field_shadowed_by_private_candidate(err, hir_id, param_env);313 }314 }315316 pub fn note_field_shadowed_by_private_candidate(317 &self,318 err: &mut Diag<'_>,319 hir_id: hir::HirId,320 param_env: ty::ParamEnv<'tcx>,321 ) {322 let Some(typeck_results) = &self.typeck_results else {323 return;324 };325 let Node::Expr(expr) = self.tcx.hir_node(hir_id) else {326 return;327 };328 let hir::ExprKind::Field(base_expr, field_ident) = expr.kind else {329 return;330 };331332 let Some(base_ty) = typeck_results.expr_ty_opt(base_expr) else {333 return;334 };335 let base_ty = self.resolve_vars_if_possible(base_ty);336 if base_ty.references_error() {337 return;338 }339340 let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id);341 let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None;342343 for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) {344 let ty::Adt(base_def, args) = deref_base_ty.kind() else {345 continue;346 };347348 if base_def.is_enum() {349 continue;350 }351352 let (adjusted_ident, def_scope) =353 self.tcx.adjust_ident_and_get_scope(field_ident, base_def.did(), fn_body_hir_id);354355 let Some((_, field_def)) =356 base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| {357 field.ident(self.tcx).normalize_to_macros_2_0() == adjusted_ident358 })359 else {360 continue;361 };362 let field_span = self363 .tcx364 .def_ident_span(field_def.did)365 .unwrap_or_else(|| self.tcx.def_span(field_def.did));366367 if field_def.vis.is_accessible_from(def_scope, self.tcx) {368 let accessible_field_ty = field_def.ty(self.tcx, args);369 if let Some((private_base_ty, private_field_ty, private_field_span)) =370 private_candidate371 && !self.can_eq(param_env, private_field_ty, accessible_field_ty)372 {373 let private_struct_span = match private_base_ty.kind() {374 ty::Adt(private_base_def, _) => self375 .tcx376 .def_ident_span(private_base_def.did())377 .unwrap_or_else(|| self.tcx.def_span(private_base_def.did())),378 _ => DUMMY_SP,379 };380 let accessible_struct_span = self381 .tcx382 .def_ident_span(base_def.did())383 .unwrap_or_else(|| self.tcx.def_span(base_def.did()));384 let deref_impl_span = (typeck_results385 .expr_adjustments(base_expr)386 .iter()387 .filter(|adj| {388 matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))389 })390 .count()391 == 1)392 .then(|| {393 self.probe(|_| {394 let deref_trait_did =395 self.tcx.require_lang_item(LangItem::Deref, DUMMY_SP);396 let trait_ref =397 ty::TraitRef::new(self.tcx, deref_trait_did, [private_base_ty]);398 let obligation: Obligation<'tcx, ty::Predicate<'tcx>> =399 Obligation::new(400 self.tcx,401 ObligationCause::dummy(),402 param_env,403 trait_ref,404 );405 let Ok(Some(ImplSource::UserDefined(impl_data))) =406 SelectionContext::new(self)407 .select(&obligation.with(self.tcx, trait_ref))408 else {409 return None;410 };411 Some(self.tcx.def_span(impl_data.impl_def_id))412 })413 })414 .flatten();415416 let mut note_spans: MultiSpan = private_struct_span.into();417 if private_struct_span != DUMMY_SP {418 note_spans.push_span_label(private_struct_span, "in this struct");419 }420 if private_field_span != DUMMY_SP {421 note_spans.push_span_label(422 private_field_span,423 "if this field wasn't private, it would be accessible",424 );425 }426 if accessible_struct_span != DUMMY_SP {427 note_spans.push_span_label(428 accessible_struct_span,429 "this struct is accessible through auto-deref",430 );431 }432 if field_span != DUMMY_SP {433 note_spans434 .push_span_label(field_span, "this is the field that was accessed");435 }436 if let Some(deref_impl_span) = deref_impl_span437 && deref_impl_span != DUMMY_SP438 {439 note_spans.push_span_label(440 deref_impl_span,441 "the field was accessed through this `Deref`",442 );443 }444445 err.span_note(446 note_spans,447 format!(448 "there is a field `{field_ident}` on `{private_base_ty}` with type `{private_field_ty}` but it is private; `{field_ident}` from `{deref_base_ty}` was accessed through auto-deref instead"449 ),450 );451 }452453 // we finally get to the accessible field,454 // so we can return early without checking the rest of the autoderef candidates455 return;456 }457458 private_candidate.get_or_insert((459 deref_base_ty,460 field_def.ty(self.tcx, args),461 field_span,462 ));463 }464 }465466 pub fn suggest_restricting_param_bound(467 &self,468 err: &mut Diag<'_>,469 trait_pred: ty::PolyTraitPredicate<'tcx>,470 associated_ty: Option<(&'static str, Ty<'tcx>)>,471 mut body_id: LocalDefId,472 ) {473 if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive {474 return;475 }476477 let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);478479 let self_ty = trait_pred.skip_binder().self_ty();480 let (param_ty, projection) = match *self_ty.kind() {481 ty::Param(_) => (true, None),482 ty::Alias(projection @ ty::AliasTy { kind: ty::Projection { .. }, .. }) => {483 (false, Some(projection))484 }485 _ => (false, None),486 };487488 let mut finder = ParamFinder { .. };489 finder.visit_binder(&trait_pred);490491 // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we492 // don't suggest `T: Sized + ?Sized`.493 loop {494 let node = self.tcx.hir_node_by_def_id(body_id);495 match node {496 hir::Node::Item(hir::Item {497 kind: hir::ItemKind::Trait { ident, generics, bounds, .. },498 ..499 }) if self_ty == self.tcx.types.self_param => {500 assert!(param_ty);501 // Restricting `Self` for a single method.502 suggest_restriction(503 self.tcx,504 body_id,505 generics,506 "`Self`",507 err,508 None,509 projection,510 trait_pred,511 Some((&ident, bounds)),512 );513 return;514 }515516 hir::Node::TraitItem(hir::TraitItem {517 generics,518 kind: hir::TraitItemKind::Fn(..),519 ..520 }) if self_ty == self.tcx.types.self_param => {521 assert!(param_ty);522 // Restricting `Self` for a single method.523 suggest_restriction(524 self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred,525 None,526 );527 return;528 }529530 hir::Node::TraitItem(hir::TraitItem {531 generics,532 kind: hir::TraitItemKind::Fn(fn_sig, ..),533 ..534 })535 | hir::Node::ImplItem(hir::ImplItem {536 generics,537 kind: hir::ImplItemKind::Fn(fn_sig, ..),538 ..539 })540 | hir::Node::Item(hir::Item {541 kind: hir::ItemKind::Fn { sig: fn_sig, generics, .. },542 ..543 }) if projection.is_some() => {544 // Missing restriction on associated type of type parameter (unmet projection).545 suggest_restriction(546 self.tcx,547 body_id,548 generics,549 "the associated type",550 err,551 Some(fn_sig),552 projection,553 trait_pred,554 None,555 );556 return;557 }558 hir::Node::Item(hir::Item {559 kind:560 hir::ItemKind::Trait { generics, .. }561 | hir::ItemKind::Impl(hir::Impl { generics, .. }),562 ..563 }) if projection.is_some() => {564 // Missing restriction on associated type of type parameter (unmet projection).565 suggest_restriction(566 self.tcx,567 body_id,568 generics,569 "the associated type",570 err,571 None,572 projection,573 trait_pred,574 None,575 );576 return;577 }578579 hir::Node::Item(hir::Item {580 kind:581 hir::ItemKind::Struct(_, generics, _)582 | hir::ItemKind::Enum(_, generics, _)583 | hir::ItemKind::Union(_, generics, _)584 | hir::ItemKind::Trait { generics, .. }585 | hir::ItemKind::Impl(hir::Impl { generics, .. })586 | hir::ItemKind::Fn { generics, .. }587 | hir::ItemKind::TyAlias(_, generics, _)588 | hir::ItemKind::Const(_, generics, _, _)589 | hir::ItemKind::TraitAlias(_, _, generics, _),590 ..591 })592 | hir::Node::TraitItem(hir::TraitItem { generics, .. })593 | hir::Node::ImplItem(hir::ImplItem { generics, .. })594 if param_ty =>595 {596 // We skip the 0'th arg (self) because we do not want597 // to consider the predicate as not suggestible if the598 // self type is an arg position `impl Trait` -- instead,599 // we handle that by adding ` + Bound` below.600 // FIXME(compiler-errors): It would be nice to do the same601 // this that we do in `suggest_restriction` and pull the602 // `impl Trait` into a new generic if it shows up somewhere603 // else in the predicate.604 if !trait_pred.skip_binder().trait_ref.args[1..]605 .iter()606 .all(|g| g.is_suggestable(self.tcx, false))607 {608 return;609 }610 // Missing generic type parameter bound.611 let param_name = self_ty.to_string();612 let mut constraint = with_no_trimmed_paths!(613 trait_pred.print_modifiers_and_trait_path().to_string()614 );615616 if let Some((name, term)) = associated_ty {617 // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.618 // That should be extracted into a helper function.619 if let Some(stripped) = constraint.strip_suffix('>') {620 constraint = format!("{stripped}, {name} = {term}>");621 } else {622 constraint.push_str(&format!("<{name} = {term}>"));623 }624 }625626 if suggest_constraining_type_param(627 self.tcx,628 generics,629 err,630 ¶m_name,631 &constraint,632 Some(trait_pred.def_id()),633 None,634 ) {635 return;636 }637 }638639 hir::Node::TraitItem(hir::TraitItem {640 generics,641 kind: hir::TraitItemKind::Fn(..),642 ..643 })644 | hir::Node::ImplItem(hir::ImplItem {645 generics,646 impl_kind: hir::ImplItemImplKind::Inherent { .. },647 kind: hir::ImplItemKind::Fn(..),648 ..649 }) if finder.can_suggest_bound(generics) => {650 // Missing generic type parameter bound.651 suggest_arbitrary_trait_bound(652 self.tcx,653 generics,654 err,655 trait_pred,656 associated_ty,657 );658 }659 hir::Node::Item(hir::Item {660 kind:661 hir::ItemKind::Struct(_, generics, _)662 | hir::ItemKind::Enum(_, generics, _)663 | hir::ItemKind::Union(_, generics, _)664 | hir::ItemKind::Trait { generics, .. }665 | hir::ItemKind::Impl(hir::Impl { generics, .. })666 | hir::ItemKind::Fn { generics, .. }667 | hir::ItemKind::TyAlias(_, generics, _)668 | hir::ItemKind::Const(_, generics, _, _)669 | hir::ItemKind::TraitAlias(_, _, generics, _),670 ..671 }) if finder.can_suggest_bound(generics) => {672 // Missing generic type parameter bound.673 if suggest_arbitrary_trait_bound(674 self.tcx,675 generics,676 err,677 trait_pred,678 associated_ty,679 ) {680 return;681 }682 }683 hir::Node::Crate(..) => return,684685 _ => {}686 }687 body_id = self.tcx.local_parent(body_id);688 }689 }690691 /// Provide a suggestion to dereference arguments to functions and binary operators, if that692 /// would satisfy trait bounds.693 pub(super) fn suggest_dereferences(694 &self,695 obligation: &PredicateObligation<'tcx>,696 err: &mut Diag<'_>,697 trait_pred: ty::PolyTraitPredicate<'tcx>,698 ) -> bool {699 let mut code = obligation.cause.code();700 if let ObligationCauseCode::FunctionArg { arg_hir_id, call_hir_id, .. } = code701 && let Some(typeck_results) = &self.typeck_results702 && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id)703 && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)704 {705 // Suggest dereferencing the argument to a function/method call if possible706707 // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)708 let mut real_trait_pred = trait_pred;709 while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {710 code = parent_code;711 if let Some(parent_trait_pred) = parent_trait_pred {712 real_trait_pred = parent_trait_pred;713 }714 }715716 // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle717 // `ReBound`, and we don't particularly care about the regions.718 let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());719 if !self.can_eq(obligation.param_env, real_ty, arg_ty) {720 return false;721 }722723 // Potentially, we'll want to place our dereferences under a `&`. We don't try this for724 // `&mut`, since we can't be sure users will get the side-effects they want from it.725 // If this doesn't work, we'll try removing the `&` in `suggest_remove_reference`.726 // FIXME(dianne): this misses the case where users need both to deref and remove `&`s.727 // This method could be combined with `TypeErrCtxt::suggest_remove_reference` to handle728 // that, similar to what `FnCtxt::suggest_deref_or_ref` does.729 let (is_under_ref, base_ty, span) = match expr.kind {730 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, subexpr)731 if let &ty::Ref(region, base_ty, hir::Mutability::Not) = real_ty.kind() =>732 {733 (Some(region), base_ty, subexpr.span)734 }735 // Don't suggest `*&mut`, etc.736 hir::ExprKind::AddrOf(..) => return false,737 _ => (None, real_ty, obligation.cause.span),738 };739740 let autoderef = (self.autoderef_steps)(base_ty);741 let mut is_boxed = base_ty.is_box();742 if let Some(steps) = autoderef.into_iter().position(|(mut ty, obligations)| {743 // Ensure one of the following for dereferencing to be valid: we're passing by744 // reference, `ty` is `Copy`, or we're moving out of a (potentially nested) `Box`.745 let can_deref = is_under_ref.is_some()746 || self.type_is_copy_modulo_regions(obligation.param_env, ty)747 || ty.is_numeric() // for inference vars (presumably but not provably `Copy`)748 || is_boxed && self.type_is_sized_modulo_regions(obligation.param_env, ty);749 is_boxed &= ty.is_box();750751 // Re-add the `&` if necessary752 if let Some(region) = is_under_ref {753 ty = Ty::new_ref(self.tcx, region, ty, hir::Mutability::Not);754 }755756 // Remapping bound vars here757 let real_trait_pred_and_ty =758 real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));759 let obligation = self.mk_trait_obligation_with_new_self_ty(760 obligation.param_env,761 real_trait_pred_and_ty,762 );763764 can_deref765 && obligations766 .iter()767 .chain([&obligation])768 .all(|obligation| self.predicate_may_hold(obligation))769 }) && steps > 0770 {771 if span.in_external_macro(self.tcx.sess.source_map()) {772 return false;773 }774 let derefs = "*".repeat(steps);775 let msg = "consider dereferencing here";776777 let call_node = self.tcx.hir_node(*call_hir_id);778 let is_receiver = matches!(779 call_node,780 Node::Expr(hir::Expr {781 kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),782 ..783 })784 if receiver_expr.hir_id == *arg_hir_id785 );786 if is_receiver {787 err.multipart_suggestion(788 msg,789 vec![790 (span.shrink_to_lo(), format!("({derefs}")),791 (span.shrink_to_hi(), ")".to_string()),792 ],793 Applicability::MachineApplicable,794 )795 } else {796 err.span_suggestion_verbose(797 span.shrink_to_lo(),798 msg,799 derefs,800 Applicability::MachineApplicable,801 )802 };803 return true;804 }805 } else if let (806 ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. },807 predicate,808 ) = code.peel_derives_with_predicate()809 && let Some(typeck_results) = &self.typeck_results810 && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id)811 && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id)812 && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs)813 && let trait_pred = predicate.unwrap_or(trait_pred)814 // Only run this code on binary operators815 && hir::lang_items::BINARY_OPERATORS816 .iter()817 .filter_map(|&op| self.tcx.lang_items().get(op))818 .any(|op| {819 op == trait_pred.skip_binder().trait_ref.def_id820 })821 {822 // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible823 let trait_pred = predicate.unwrap_or(trait_pred);824 let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty());825 let lhs_autoderef = (self.autoderef_steps)(lhs_ty);826 let rhs_autoderef = (self.autoderef_steps)(rhs_ty);827 let first_lhs = lhs_autoderef.first().unwrap().clone();828 let first_rhs = rhs_autoderef.first().unwrap().clone();829 let mut autoderefs = lhs_autoderef830 .into_iter()831 .enumerate()832 .rev()833 .zip_longest(rhs_autoderef.into_iter().enumerate().rev())834 .map(|t| match t {835 EitherOrBoth::Both(a, b) => (a, b),836 EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())),837 EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b),838 })839 .rev();840 if let Some((lsteps, rsteps)) =841 autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| {842 // Create a new predicate with the dereferenced LHS and RHS843 // We simultaneously dereference both sides rather than doing them844 // one at a time to account for cases such as &Box<T> == &&T845 let trait_pred_and_ty = trait_pred.map_bound(|inner| {846 (847 ty::TraitPredicate {848 trait_ref: ty::TraitRef::new_from_args(849 self.tcx,850 inner.trait_ref.def_id,851 self.tcx.mk_args(852 &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]]853 .concat(),854 ),855 ),856 ..inner857 },858 l_ty,859 )860 });861 let obligation = self.mk_trait_obligation_with_new_self_ty(862 obligation.param_env,863 trait_pred_and_ty,864 );865 self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) {866 (_, 0) => (Some(lsteps), None),867 (0, _) => (None, Some(rsteps)),868 _ => (Some(lsteps), Some(rsteps)),869 })870 })871 {872 let make_sugg = |mut expr: &Expr<'_>, mut steps| {873 if expr.span.in_external_macro(self.tcx.sess.source_map()) {874 return None;875 }876 let mut prefix_span = expr.span.shrink_to_lo();877 let mut msg = "consider dereferencing here";878 if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind {879 msg = "consider removing the borrow and dereferencing instead";880 if let hir::ExprKind::AddrOf(..) = inner.kind {881 msg = "consider removing the borrows and dereferencing instead";882 }883 }884 while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind885 && steps > 0886 {887 prefix_span = prefix_span.with_hi(inner.span.lo());888 expr = inner;889 steps -= 1;890 }891 // Empty suggestions with empty spans ICE with debug assertions892 if steps == 0 {893 return Some((894 msg.trim_end_matches(" and dereferencing instead"),895 vec![(prefix_span, String::new())],896 ));897 }898 let derefs = "*".repeat(steps);899 let needs_parens = steps > 0 && expr_needs_parens(expr);900 let mut suggestion = if needs_parens {901 vec![902 (903 expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),904 format!("{derefs}("),905 ),906 (expr.span.shrink_to_hi(), ")".to_string()),907 ]908 } else {909 vec![(910 expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),911 format!("{derefs}"),912 )]913 };914 // Empty suggestions with empty spans ICE with debug assertions915 if !prefix_span.is_empty() {916 suggestion.push((prefix_span, String::new()));917 }918 Some((msg, suggestion))919 };920921 if let Some(lsteps) = lsteps922 && let Some(rsteps) = rsteps923 && lsteps > 0924 && rsteps > 0925 {926 let Some((_, mut suggestion)) = make_sugg(lhs, lsteps) else {927 return false;928 };929 let Some((_, mut rhs_suggestion)) = make_sugg(rhs, rsteps) else {930 return false;931 };932 suggestion.append(&mut rhs_suggestion);933 err.multipart_suggestion(934 "consider dereferencing both sides of the expression",935 suggestion,936 Applicability::MachineApplicable,937 );938 return true;939 } else if let Some(lsteps) = lsteps940 && lsteps > 0941 {942 let Some((msg, suggestion)) = make_sugg(lhs, lsteps) else {943 return false;944 };945 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);946 return true;947 } else if let Some(rsteps) = rsteps948 && rsteps > 0949 {950 let Some((msg, suggestion)) = make_sugg(rhs, rsteps) else {951 return false;952 };953 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);954 return true;955 }956 }957 }958 false959 }960961 /// Given a closure's `DefId`, return the given name of the closure.962 ///963 /// This doesn't account for reassignments, but it's only used for suggestions.964 fn get_closure_name(965 &self,966 def_id: DefId,967 err: &mut Diag<'_>,968 msg: Cow<'static, str>,969 ) -> Option<Symbol> {970 let get_name = |err: &mut Diag<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> {971 // Get the local name of this closure. This can be inaccurate because972 // of the possibility of reassignment, but this should be good enough.973 match &kind {974 hir::PatKind::Binding(hir::BindingMode::NONE, _, ident, None) => Some(ident.name),975 _ => {976 err.note(msg);977 None978 }979 }980 };981982 let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);983 match self.tcx.parent_hir_node(hir_id) {984 hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(local), .. }) => {985 get_name(err, &local.pat.kind)986 }987 // Different to previous arm because one is `&hir::Local` and the other988 // is `Box<hir::Local>`.989 hir::Node::LetStmt(local) => get_name(err, &local.pat.kind),990 _ => None,991 }992 }993994 /// We tried to apply the bound to an `fn` or closure. Check whether calling it would995 /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling996 /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.997 pub(super) fn suggest_fn_call(998 &self,999 obligation: &PredicateObligation<'tcx>,1000 err: &mut Diag<'_>,1001 trait_pred: ty::PolyTraitPredicate<'tcx>,1002 ) -> bool {1003 // It doesn't make sense to make this suggestion outside of typeck...1004 // (also autoderef will ICE...)1005 if self.typeck_results.is_none() {1006 return false;1007 }10081009 if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =1010 obligation.predicate.kind().skip_binder()1011 && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)1012 {1013 // Don't suggest calling to turn an unsized type into a sized type1014 return false;1015 }10161017 let self_ty = self.instantiate_binder_with_fresh_vars(1018 DUMMY_SP,1019 BoundRegionConversionTime::FnCall,1020 trait_pred.self_ty(),1021 );10221023 let Some((def_id_or_name, output, inputs)) =1024 self.extract_callable_info(obligation.cause.body_id, obligation.param_env, self_ty)1025 else {1026 return false;1027 };10281029 // Remapping bound vars here1030 let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));10311032 let new_obligation =1033 self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);1034 if !self.predicate_must_hold_modulo_regions(&new_obligation) {1035 return false;1036 }10371038 // If this is a zero-argument async closure directly passed as an argument1039 // and the expected type is `Future`, suggest using `async {}` block instead1040 // of `async || {}`1041 if let ty::CoroutineClosure(def_id, args) = *self_ty.kind()1042 && let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder()1043 && let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind()1044 && inputs.is_empty()1045 && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future)1046 && let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1047 && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) =1048 self.tcx.hir_node(*arg_hir_id)1049 && let Some(hir::Node::Expr(hir::Expr {1050 kind: hir::ExprKind::Closure(closure), ..1051 })) = self.tcx.hir_get_if_local(def_id)1052 && let hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async) = closure.kind1053 && let Some(arg_span) = closure.fn_arg_span1054 && obligation.cause.span.contains(arg_span)1055 {1056 let mut body = self.tcx.hir_body(closure.body).value;1057 let peeled = body.peel_blocks().peel_drop_temps();1058 if let hir::ExprKind::Closure(inner) = peeled.kind {1059 body = self.tcx.hir_body(inner.body).value;1060 }1061 if !matches!(body.peel_blocks().peel_drop_temps().kind, hir::ExprKind::Block(..)) {1062 return false;1063 }10641065 let sm = self.tcx.sess.source_map();1066 let removal_span = if let Ok(snippet) =1067 sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)))1068 && snippet.ends_with(' ')1069 {1070 // There's a space after `||`, include it in the removal1071 arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))1072 } else {1073 arg_span1074 };1075 err.span_suggestion_verbose(1076 removal_span,1077 "use `async {}` instead of `async || {}` to introduce an async block",1078 "",1079 Applicability::MachineApplicable,1080 );1081 return true;1082 }10831084 // Get the name of the callable and the arguments to be used in the suggestion.1085 let msg = match def_id_or_name {1086 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {1087 DefKind::Ctor(CtorOf::Struct, _) => {1088 Cow::from("use parentheses to construct this tuple struct")1089 }1090 DefKind::Ctor(CtorOf::Variant, _) => {1091 Cow::from("use parentheses to construct this tuple variant")1092 }1093 kind => Cow::from(format!(1094 "use parentheses to call this {}",1095 self.tcx.def_kind_descr(kind, def_id)1096 )),1097 },1098 DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")),1099 };11001101 let args = inputs1102 .into_iter()1103 .map(|ty| {1104 if ty.is_suggestable(self.tcx, false) {1105 format!("/* {ty} */")1106 } else {1107 "/* value */".to_string()1108 }1109 })1110 .collect::<Vec<_>>()1111 .join(", ");11121113 if let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1114 && obligation.cause.span.can_be_used_for_suggestions()1115 {1116 let span = obligation.cause.span;11171118 let arg_expr = match self.tcx.hir_node(*arg_hir_id) {1119 hir::Node::Expr(expr) => Some(expr),1120 _ => None,1121 };11221123 let is_closure_expr =1124 arg_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Closure(..)));11251126 // If the user wrote `|| {}()`, suggesting to call the closure would produce `(|| {}())()`,1127 // which doesn't help and is often outright wrong.1128 if args.is_empty()1129 && let Some(expr) = arg_expr1130 && let hir::ExprKind::Closure(closure) = expr.kind1131 {1132 let mut body = self.tcx.hir_body(closure.body).value;11331134 // Async closures desugar to a closure returning a coroutine1135 if let hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) =1136 closure.kind1137 {1138 let peeled = body.peel_blocks().peel_drop_temps();1139 if let hir::ExprKind::Closure(inner) = peeled.kind {1140 body = self.tcx.hir_body(inner.body).value;1141 }1142 }11431144 let peeled_body = body.peel_blocks().peel_drop_temps();1145 if let hir::ExprKind::Call(callee, call_args) = peeled_body.kind1146 && call_args.is_empty()1147 && let hir::ExprKind::Block(..) = callee.peel_blocks().peel_drop_temps().kind1148 {1149 return false;1150 }1151 }11521153 if is_closure_expr {1154 err.multipart_suggestions(1155 msg,1156 vec![vec![1157 (span.shrink_to_lo(), "(".to_string()),1158 (span.shrink_to_hi(), format!(")({args})")),1159 ]],1160 Applicability::HasPlaceholders,1161 );1162 } else {1163 err.span_suggestion_verbose(1164 span.shrink_to_hi(),1165 msg,1166 format!("({args})"),1167 Applicability::HasPlaceholders,1168 );1169 }1170 } else if let DefIdOrName::DefId(def_id) = def_id_or_name {1171 let name = match self.tcx.hir_get_if_local(def_id) {1172 Some(hir::Node::Expr(hir::Expr {1173 kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),1174 ..1175 })) => {1176 err.span_label(*fn_decl_span, "consider calling this closure");1177 let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else {1178 return false;1179 };1180 name.to_string()1181 }1182 Some(hir::Node::Item(hir::Item {1183 kind: hir::ItemKind::Fn { ident, .. }, ..1184 })) => {1185 err.span_label(ident.span, "consider calling this function");1186 ident.to_string()1187 }1188 Some(hir::Node::Ctor(..)) => {1189 let name = self.tcx.def_path_str(def_id);1190 err.span_label(1191 self.tcx.def_span(def_id),1192 format!("consider calling the constructor for `{name}`"),1193 );1194 name1195 }1196 _ => return false,1197 };1198 err.help(format!("{msg}: `{name}({args})`"));1199 }1200 true1201 }12021203 pub(super) fn suggest_cast_to_fn_pointer(1204 &self,1205 obligation: &PredicateObligation<'tcx>,1206 err: &mut Diag<'_>,1207 leaf_trait_predicate: ty::PolyTraitPredicate<'tcx>,1208 main_trait_predicate: ty::PolyTraitPredicate<'tcx>,1209 span: Span,1210 ) -> bool {1211 let &[candidate] = &self.find_similar_impl_candidates(leaf_trait_predicate)[..] else {1212 return false;1213 };1214 let candidate = candidate.trait_ref;12151216 if !matches!(1217 (candidate.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind(),),1218 (ty::FnPtr(..), ty::FnDef(..))1219 ) {1220 return false;1221 }12221223 let parenthesized_cast = |span: Span| {1224 vec![1225 (span.shrink_to_lo(), "(".to_string()),1226 (span.shrink_to_hi(), format!(" as {})", candidate.self_ty())),1227 ]1228 };1229 // Wrap method receivers and `&`-references in parens.1230 let suggestion = if self.tcx.sess.source_map().span_followed_by(span, ".").is_some() {1231 parenthesized_cast(span)1232 } else if let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) {1233 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1234 expr_finder.visit_expr(body.value);1235 if let Some(expr) = expr_finder.result1236 && let hir::ExprKind::AddrOf(_, _, expr) = expr.kind1237 {1238 parenthesized_cast(expr.span)1239 } else {1240 vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]1241 }1242 } else {1243 vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]1244 };12451246 let trait_ = self.tcx.short_string(candidate.print_trait_sugared(), err.long_ty_path());1247 let self_ty = self.tcx.short_string(candidate.self_ty(), err.long_ty_path());1248 err.multipart_suggestion(1249 format!(1250 "the trait `{trait_}` is implemented for fn pointer \1251 `{self_ty}`, try casting using `as`",1252 ),1253 suggestion,1254 Applicability::MaybeIncorrect,1255 );1256 true1257 }12581259 pub(super) fn check_for_binding_assigned_block_without_tail_expression(1260 &self,1261 obligation: &PredicateObligation<'tcx>,1262 err: &mut Diag<'_>,1263 trait_pred: ty::PolyTraitPredicate<'tcx>,1264 ) {1265 let mut span = obligation.cause.span;1266 while span.from_expansion() {1267 // Remove all the desugaring and macro contexts.1268 span.remove_mark();1269 }1270 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1271 let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) else {1272 return;1273 };1274 expr_finder.visit_expr(body.value);1275 let Some(expr) = expr_finder.result else {1276 return;1277 };1278 let Some(typeck) = &self.typeck_results else {1279 return;1280 };1281 let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else {1282 return;1283 };1284 if !ty.is_unit() {1285 return;1286 };1287 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {1288 return;1289 };1290 let Res::Local(hir_id) = path.res else {1291 return;1292 };1293 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {1294 return;1295 };1296 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =1297 self.tcx.parent_hir_node(pat.hir_id)1298 else {1299 return;1300 };1301 let hir::ExprKind::Block(block, None) = init.kind else {1302 return;1303 };1304 if block.expr.is_some() {1305 return;1306 }1307 let [.., stmt] = block.stmts else {1308 err.span_label(block.span, "this empty block is missing a tail expression");1309 return;1310 };1311 // FIXME expr and stmt have the same span if expr comes from expansion1312 // cc: https://github.com/rust-lang/rust/pull/147416#discussion_r24994075231313 if stmt.span.from_expansion() {1314 return;1315 }1316 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {1317 return;1318 };1319 let Some(ty) = typeck.expr_ty_opt(tail_expr) else {1320 err.span_label(block.span, "this block is missing a tail expression");1321 return;1322 };1323 let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty));1324 let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));13251326 let new_obligation =1327 self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);1328 if !matches!(tail_expr.kind, hir::ExprKind::Err(_))1329 && self.predicate_must_hold_modulo_regions(&new_obligation)1330 {1331 err.span_suggestion_short(1332 stmt.span.with_lo(tail_expr.span.hi()),1333 "remove this semicolon",1334 "",1335 Applicability::MachineApplicable,1336 );1337 } else {1338 err.span_label(block.span, "this block is missing a tail expression");1339 }1340 }13411342 pub(super) fn suggest_add_clone_to_arg(1343 &self,1344 obligation: &PredicateObligation<'tcx>,1345 err: &mut Diag<'_>,1346 trait_pred: ty::PolyTraitPredicate<'tcx>,1347 ) -> bool {1348 let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());1349 self.enter_forall(self_ty, |ty: Ty<'_>| {1350 let Some(generics) = self.tcx.hir_get_generics(obligation.cause.body_id) else {1351 return false;1352 };1353 let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };1354 let ty::Param(param) = inner_ty.kind() else { return false };1355 let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1356 else {1357 return false;1358 };13591360 let clone_trait = self.tcx.require_lang_item(LangItem::Clone, obligation.cause.span);1361 let has_clone = |ty| {1362 self.type_implements_trait(clone_trait, [ty], obligation.param_env)1363 .must_apply_modulo_regions()1364 };13651366 let existing_clone_call = match self.tcx.hir_node(*arg_hir_id) {1367 // It's just a variable. Propose cloning it.1368 Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) => None,1369 // It's already a call to `clone()`. We might be able to suggest1370 // adding a `+ Clone` bound, though.1371 Node::Expr(Expr {1372 kind:1373 hir::ExprKind::MethodCall(1374 hir::PathSegment { ident, .. },1375 _receiver,1376 [],1377 call_span,1378 ),1379 hir_id,1380 ..1381 }) if ident.name == sym::clone1382 && !call_span.from_expansion()1383 && !has_clone(*inner_ty) =>1384 {1385 // We only care about method calls corresponding to the real `Clone` trait.1386 let Some(typeck_results) = self.typeck_results.as_ref() else { return false };1387 let Some((DefKind::AssocFn, did)) = typeck_results.type_dependent_def(*hir_id)1388 else {1389 return false;1390 };1391 if self.tcx.trait_of_assoc(did) != Some(clone_trait) {1392 return false;1393 }1394 Some(ident.span)1395 }1396 _ => return false,1397 };13981399 let new_obligation = self.mk_trait_obligation_with_new_self_ty(1400 obligation.param_env,1401 trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),1402 );14031404 if self.predicate_may_hold(&new_obligation) && has_clone(ty) {1405 if !has_clone(param.to_ty(self.tcx)) {1406 suggest_constraining_type_param(1407 self.tcx,1408 generics,1409 err,1410 param.name.as_str(),1411 "Clone",1412 Some(clone_trait),1413 None,1414 );1415 }1416 if let Some(existing_clone_call) = existing_clone_call {1417 err.span_note(1418 existing_clone_call,1419 format!(1420 "this `clone()` copies the reference, \1421 which does not do anything, \1422 because `{inner_ty}` does not implement `Clone`"1423 ),1424 );1425 } else {1426 err.span_suggestion_verbose(1427 obligation.cause.span.shrink_to_hi(),1428 "consider using clone here",1429 ".clone()".to_string(),1430 Applicability::MaybeIncorrect,1431 );1432 }1433 return true;1434 }1435 false1436 })1437 }14381439 /// Extracts information about a callable type for diagnostics. This is a1440 /// heuristic -- it doesn't necessarily mean that a type is always callable,1441 /// because the callable type must also be well-formed to be called.1442 pub fn extract_callable_info(1443 &self,1444 body_id: LocalDefId,1445 param_env: ty::ParamEnv<'tcx>,1446 found: Ty<'tcx>,1447 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {1448 // Autoderef is useful here because sometimes we box callables, etc.1449 let Some((def_id_or_name, output, inputs)) =1450 (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() {1451 ty::FnPtr(sig_tys, _) => Some((1452 DefIdOrName::Name("function pointer"),1453 sig_tys.output(),1454 sig_tys.inputs(),1455 )),1456 ty::FnDef(def_id, _) => {1457 let fn_sig = found.fn_sig(self.tcx);1458 Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))1459 }1460 ty::Closure(def_id, args) => {1461 let fn_sig = args.as_closure().sig();1462 Some((1463 DefIdOrName::DefId(def_id),1464 fn_sig.output(),1465 fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),1466 ))1467 }1468 ty::CoroutineClosure(def_id, args) => {1469 let sig_parts = args.as_coroutine_closure().coroutine_closure_sig();1470 Some((1471 DefIdOrName::DefId(def_id),1472 sig_parts.map_bound(|sig| {1473 sig.to_coroutine(1474 self.tcx,1475 args.as_coroutine_closure().parent_args(),1476 // Just use infer vars here, since we don't really care1477 // what these types are, just that we're returning a coroutine.1478 self.next_ty_var(DUMMY_SP),1479 self.tcx.coroutine_for_closure(def_id),1480 self.next_ty_var(DUMMY_SP),1481 )1482 }),1483 sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),1484 ))1485 }1486 ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {1487 self.tcx1488 .item_self_bounds(def_id)1489 .instantiate(self.tcx, args)1490 .skip_norm_wip()1491 .iter()1492 .find_map(|pred| {1493 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()1494 && self1495 .tcx1496 .is_lang_item(proj.projection_term.def_id(), LangItem::FnOnceOutput)1497 // args tuple will always be args[1]1498 && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()1499 {1500 Some((1501 DefIdOrName::DefId(def_id),1502 pred.kind().rebind(proj.term.expect_type()),1503 pred.kind().rebind(args.as_slice()),1504 ))1505 } else {1506 None1507 }1508 })1509 }1510 ty::Dynamic(data, _) => data.iter().find_map(|pred| {1511 if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()1512 && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)1513 // for existential projection, args are shifted over by 11514 && let ty::Tuple(args) = proj.args.type_at(0).kind()1515 {1516 Some((1517 DefIdOrName::Name("trait object"),1518 pred.rebind(proj.term.expect_type()),1519 pred.rebind(args.as_slice()),1520 ))1521 } else {1522 None1523 }1524 }),1525 ty::Param(param) => {1526 let generics = self.tcx.generics_of(body_id);1527 let name = if generics.count() > param.index as usize1528 && let def = generics.param_at(param.index as usize, self.tcx)1529 && matches!(def.kind, ty::GenericParamDefKind::Type { .. })1530 && def.name == param.name1531 {1532 DefIdOrName::DefId(def.def_id)1533 } else {1534 DefIdOrName::Name("type parameter")1535 };1536 param_env.caller_bounds().iter().find_map(|pred| {1537 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()1538 && self1539 .tcx1540 .is_lang_item(proj.projection_term.def_id(), LangItem::FnOnceOutput)1541 && proj.projection_term.self_ty() == found1542 // args tuple will always be args[1]1543 && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()1544 {1545 Some((1546 name,1547 pred.kind().rebind(proj.term.expect_type()),1548 pred.kind().rebind(args.as_slice()),1549 ))1550 } else {1551 None1552 }1553 })1554 }1555 _ => None,1556 })1557 else {1558 return None;1559 };15601561 let output = self.instantiate_binder_with_fresh_vars(1562 DUMMY_SP,1563 BoundRegionConversionTime::FnCall,1564 output,1565 );1566 let inputs = inputs1567 .skip_binder()1568 .iter()1569 .map(|ty| {1570 self.instantiate_binder_with_fresh_vars(1571 DUMMY_SP,1572 BoundRegionConversionTime::FnCall,1573 inputs.rebind(*ty),1574 )1575 })1576 .collect();15771578 // We don't want to register any extra obligations, which should be1579 // implied by wf, but also because that would possibly result in1580 // erroneous errors later on.1581 let InferOk { value: output, obligations: _ } =1582 self.at(&ObligationCause::dummy(), param_env).normalize(Unnormalized::new_wip(output));15831584 if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }1585 }15861587 pub(super) fn where_clause_expr_matches_failed_self_ty(1588 &self,1589 obligation: &PredicateObligation<'tcx>,1590 old_self_ty: Ty<'tcx>,1591 ) -> bool {1592 let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code() else {1593 return true;1594 };1595 let (Some(typeck_results), Some(body)) = (1596 self.typeck_results.as_ref(),1597 self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id),1598 ) else {1599 return true;1600 };16011602 let mut expr_finder = FindExprBySpan::new(obligation.cause.span, self.tcx);1603 expr_finder.visit_expr(body.value);1604 let Some(expr) = expr_finder.result else {1605 return true;1606 };16071608 let inner_old_self_ty = match old_self_ty.kind() {1609 ty::Ref(_, inner_ty, _) => Some(*inner_ty),1610 _ => None,1611 };16121613 [typeck_results.expr_ty_adjusted_opt(expr)].into_iter().flatten().any(|expr_ty| {1614 self.can_eq(obligation.param_env, expr_ty, old_self_ty)1615 || inner_old_self_ty1616 .is_some_and(|inner_ty| self.can_eq(obligation.param_env, expr_ty, inner_ty))1617 })1618 }16191620 pub(super) fn suggest_add_reference_to_arg(1621 &self,1622 obligation: &PredicateObligation<'tcx>,1623 err: &mut Diag<'_>,1624 poly_trait_pred: ty::PolyTraitPredicate<'tcx>,1625 has_custom_message: bool,1626 ) -> bool {1627 let span = obligation.cause.span;1628 let param_env = obligation.param_env;16291630 let mk_result = |trait_pred_and_new_ty| {1631 let obligation =1632 self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);1633 self.predicate_must_hold_modulo_regions(&obligation)1634 };16351636 let code = match obligation.cause.code() {1637 ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code,1638 // FIXME(compiler-errors): This is kind of a mess, but required for obligations1639 // that come from a path expr to affect the *call* expr.1640 c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _)1641 if self.tcx.hir_span(*hir_id).lo() == span.lo() =>1642 {1643 // `hir_id` corresponds to the HIR node that introduced a `where`-clause obligation.1644 // If that obligation comes from a type in an associated method call, we need1645 // special handling here.1646 if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id)1647 && let hir::ExprKind::Call(base, _) = expr.kind1648 && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) = base.kind1649 && let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id)1650 && let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind1651 && ty.span == span1652 {1653 // We've encountered something like `&str::from("")`, where the intended code1654 // was likely `<&str>::from("")`. The former is interpreted as "call method1655 // `from` on `str` and borrow the result", while the latter means "call method1656 // `from` on `&str`".16571658 let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| {1659 (p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))1660 });1661 let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| {1662 (p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))1663 });16641665 let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);1666 let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);1667 let sugg_msg = |pre: &str| {1668 format!(1669 "you likely meant to call the associated function `{FN}` for type \1670 `&{pre}{TY}`, but the code as written calls associated function `{FN}` on \1671 type `{TY}`",1672 FN = segment.ident,1673 TY = poly_trait_pred.self_ty(),1674 )1675 };1676 match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) {1677 (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => {1678 err.multipart_suggestion(1679 sugg_msg(mtbl.prefix_str()),1680 vec![1681 (outer.span.shrink_to_lo(), "<".to_string()),1682 (span.shrink_to_hi(), ">".to_string()),1683 ],1684 Applicability::MachineApplicable,1685 );1686 }1687 (true, _, hir::Mutability::Mut) => {1688 // There's an associated function found on the immutable borrow of the1689 err.multipart_suggestion(1690 sugg_msg("mut "),1691 vec![1692 (outer.span.shrink_to_lo().until(span), "<&".to_string()),1693 (span.shrink_to_hi(), ">".to_string()),1694 ],1695 Applicability::MachineApplicable,1696 );1697 }1698 (_, true, hir::Mutability::Not) => {1699 err.multipart_suggestion(1700 sugg_msg(""),1701 vec![1702 (outer.span.shrink_to_lo().until(span), "<&mut ".to_string()),1703 (span.shrink_to_hi(), ">".to_string()),1704 ],1705 Applicability::MachineApplicable,1706 );1707 }1708 _ => {}1709 }1710 // If we didn't return early here, we would instead suggest `&&str::from("")`.1711 return false;1712 }1713 c1714 }1715 c if matches!(1716 span.ctxt().outer_expn_data().kind,1717 ExpnKind::Desugaring(DesugaringKind::ForLoop)1718 ) =>1719 {1720 c1721 }1722 _ => return false,1723 };17241725 // List of traits for which it would be nonsensical to suggest borrowing.1726 // For instance, immutable references are always Copy, so suggesting to1727 // borrow would always succeed, but it's probably not what the user wanted.1728 let mut never_suggest_borrow: Vec<_> =1729 [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized]1730 .iter()1731 .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item))1732 .collect();17331734 if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) {1735 never_suggest_borrow.push(def_id);1736 }17371738 // Try to apply the original trait bound by borrowing.1739 let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,1740 blacklist: &[DefId]|1741 -> bool {1742 if blacklist.contains(&old_pred.def_id()) {1743 return false;1744 }1745 // We map bounds to `&T` and `&mut T`1746 let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {1747 (1748 trait_pred,1749 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),1750 )1751 });1752 let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {1753 (1754 trait_pred,1755 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),1756 )1757 });17581759 let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);1760 let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);17611762 let (ref_inner_ty_satisfies_pred, ref_inner_ty_is_mut) =1763 if let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code()1764 && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()1765 {1766 (1767 mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))),1768 mutability.is_mut(),1769 )1770 } else {1771 (false, false)1772 };17731774 let is_immut = imm_ref_self_ty_satisfies_pred1775 || (ref_inner_ty_satisfies_pred && !ref_inner_ty_is_mut);1776 let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_is_mut;1777 if !is_immut && !is_mut {1778 return false;1779 }1780 let Ok(_snippet) = self.tcx.sess.source_map().span_to_snippet(span) else {1781 return false;1782 };1783 // We don't want a borrowing suggestion on the fields in structs1784 // ```1785 // #[derive(Clone)]1786 // struct Foo {1787 // the_foos: Vec<Foo>1788 // }1789 // ```1790 if !matches!(1791 span.ctxt().outer_expn_data().kind,1792 ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)1793 ) {1794 return false;1795 }1796 // We have a very specific type of error, where just borrowing this argument1797 // might solve the problem. In cases like this, the important part is the1798 // original type obligation, not the last one that failed, which is arbitrary.1799 // Because of this, we modify the error to refer to the original obligation and1800 // return early in the caller.18011802 let mut label = || {1803 // Special case `Sized` as `old_pred` will be the trait itself instead of1804 // `Sized` when the trait bound is the source of the error.1805 let is_sized = match obligation.predicate.kind().skip_binder() {1806 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {1807 self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)1808 }1809 _ => false,1810 };18111812 let msg = format!(1813 "the trait bound `{}` is not satisfied",1814 self.tcx.short_string(old_pred, err.long_ty_path()),1815 );1816 let self_ty_str = self.tcx.short_string(old_pred.self_ty(), err.long_ty_path());1817 let trait_path = self1818 .tcx1819 .short_string(old_pred.print_modifiers_and_trait_path(), err.long_ty_path());18201821 if has_custom_message {1822 let msg = if is_sized {1823 "the trait bound `Sized` is not satisfied".into()1824 } else {1825 msg1826 };1827 err.note(msg);1828 } else {1829 err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)];1830 }1831 if is_sized {1832 err.span_label(1833 span,1834 format!("the trait `Sized` is not implemented for `{self_ty_str}`"),1835 );1836 } else {1837 err.span_label(1838 span,1839 format!("the trait `{trait_path}` is not implemented for `{self_ty_str}`"),1840 );1841 }1842 };18431844 let mut sugg_prefixes = vec![];1845 if is_immut {1846 sugg_prefixes.push("&");1847 }1848 if is_mut {1849 sugg_prefixes.push("&mut ");1850 }1851 let sugg_msg = format!(1852 "consider{} borrowing here",1853 if is_mut && !is_immut { " mutably" } else { "" },1854 );18551856 // Issue #104961, we need to add parentheses properly for compound expressions1857 // for example, `x.starts_with("hi".to_string() + "you")`1858 // should be `x.starts_with(&("hi".to_string() + "you"))`1859 let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) else {1860 return false;1861 };1862 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1863 expr_finder.visit_expr(body.value);18641865 if let Some(ty) = expr_finder.ty_result {1866 if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(ty.hir_id)1867 && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, _)) = expr.kind1868 && ty.span == span1869 {1870 // We've encountered something like `str::from("")`, where the intended code1871 // was likely `<&str>::from("")`. #143393.1872 label();1873 err.multipart_suggestions(1874 sugg_msg,1875 sugg_prefixes.into_iter().map(|sugg_prefix| {1876 vec![1877 (span.shrink_to_lo(), format!("<{sugg_prefix}")),1878 (span.shrink_to_hi(), ">".to_string()),1879 ]1880 }),1881 Applicability::MaybeIncorrect,1882 );1883 return true;1884 }1885 return false;1886 }1887 let Some(expr) = expr_finder.result else {1888 return false;1889 };1890 if let hir::ExprKind::AddrOf(_, _, _) = expr.kind {1891 return false;1892 }1893 let old_self_ty = old_pred.skip_binder().self_ty();1894 if !old_self_ty.has_escaping_bound_vars()1895 && !self.where_clause_expr_matches_failed_self_ty(1896 obligation,1897 self.tcx.instantiate_bound_regions_with_erased(old_pred.self_ty()),1898 )1899 {1900 return false;1901 }1902 let needs_parens_post = expr_needs_parens(expr);1903 let needs_parens_pre = match self.tcx.parent_hir_node(expr.hir_id) {1904 Node::Expr(e)1905 if let hir::ExprKind::MethodCall(_, base, _, _) = e.kind1906 && base.hir_id == expr.hir_id =>1907 {1908 true1909 }1910 _ => false,1911 };19121913 label();1914 let suggestions = sugg_prefixes.into_iter().map(|sugg_prefix| {1915 match (needs_parens_pre, needs_parens_post) {1916 (false, false) => vec![(span.shrink_to_lo(), sugg_prefix.to_string())],1917 // We have something like `foo.bar()`, where we want to bororw foo, so we need1918 // to suggest `(&mut foo).bar()`.1919 (false, true) => vec![1920 (span.shrink_to_lo(), format!("{sugg_prefix}(")),1921 (span.shrink_to_hi(), ")".to_string()),1922 ],1923 // Issue #109436, we need to add parentheses properly for method calls1924 // for example, `foo.into()` should be `(&foo).into()`1925 (true, false) => vec![1926 (span.shrink_to_lo(), format!("({sugg_prefix}")),1927 (span.shrink_to_hi(), ")".to_string()),1928 ],1929 (true, true) => vec![1930 (span.shrink_to_lo(), format!("({sugg_prefix}(")),1931 (span.shrink_to_hi(), "))".to_string()),1932 ],1933 }1934 });1935 err.multipart_suggestions(sugg_msg, suggestions, Applicability::MaybeIncorrect);1936 return true;1937 };19381939 if let ObligationCauseCode::ImplDerived(cause) = &*code {1940 try_borrowing(cause.derived.parent_trait_pred, &[])1941 } else if let ObligationCauseCode::WhereClause(..)1942 | ObligationCauseCode::WhereClauseInExpr(..) = code1943 {1944 try_borrowing(poly_trait_pred, &never_suggest_borrow)1945 } else {1946 false1947 }1948 }19491950 // Suggest borrowing the type1951 pub(super) fn suggest_borrowing_for_object_cast(1952 &self,1953 err: &mut Diag<'_>,1954 obligation: &PredicateObligation<'tcx>,1955 self_ty: Ty<'tcx>,1956 target_ty: Ty<'tcx>,1957 ) {1958 let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else {1959 return;1960 };1961 let ty::Dynamic(predicates, _) = object_ty.kind() else {1962 return;1963 };1964 let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);19651966 for predicate in predicates.iter() {1967 if !self.predicate_must_hold_modulo_regions(1968 &obligation.with(self.tcx, predicate.with_self_ty(self.tcx, self_ref_ty)),1969 ) {1970 return;1971 }1972 }19731974 err.span_suggestion_verbose(1975 obligation.cause.span.shrink_to_lo(),1976 format!(1977 "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`"1978 ),1979 "&",1980 Applicability::MaybeIncorrect,1981 );1982 }19831984 /// Peel `&`-borrows from an expression, following through untyped let-bindings.1985 /// Returns a list of removable `&` layers (each with the span to remove and the1986 /// resulting type), plus an optional terminal [`hir::Param`] when the chain ends1987 /// at a function parameter (including async-fn desugared parameters).1988 fn peel_expr_refs(1989 &self,1990 mut expr: &'tcx hir::Expr<'tcx>,1991 mut ty: Ty<'tcx>,1992 ) -> (Vec<PeeledRef<'tcx>>, Option<&'tcx hir::Param<'tcx>>) {1993 let mut refs = Vec::new();1994 'outer: loop {1995 while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {1996 let span =1997 if let Some(borrowed_span) = borrowed.span.find_ancestor_inside(expr.span) {1998 expr.span.until(borrowed_span)1999 } else {2000 break 'outer;
Findings
✓ No findings reported for this file.