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::diagnostics;52use crate::error_reporting::TypeErrCtxt;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::ProjectionAliasTy<'_>>,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) && !tcx.features().return_type_notation())144 || tcx.lookup_stability(projection.kind).is_some_and(|stab| stab.is_unstable())145 })146 {147 return;148 }149 let generics = tcx.generics_of(item_id);150 // Given `fn foo(t: impl Trait)` where `Trait` requires assoc type `A`...151 if let Some((param, bound_str, fn_sig)) =152 fn_sig.zip(projection).and_then(|(sig, p)| match *p.projection_self_ty().kind() {153 // Shenanigans to get the `Trait` from the `impl Trait`.154 ty::Param(param) => {155 let param_def = generics.type_param(param, tcx);156 if param_def.kind.is_synthetic() {157 let bound_str =158 param_def.name.as_str().strip_prefix("impl ")?.trim_start().to_string();159 return Some((param_def, bound_str, sig));160 }161 None162 }163 _ => None,164 })165 {166 let type_param_name = hir_generics.params.next_type_param_name(Some(&bound_str));167 let trait_pred = trait_pred.fold_with(&mut ReplaceImplTraitFolder {168 tcx,169 param,170 replace_ty: ty::ParamTy::new(generics.count() as u32, Symbol::intern(&type_param_name))171 .to_ty(tcx),172 });173 if !trait_pred.is_suggestable(tcx, false) {174 return;175 }176 // We know we have an `impl Trait` that doesn't satisfy a required projection.177178 // Find all of the occurrences of `impl Trait` for `Trait` in the function arguments'179 // types. There should be at least one, but there might be *more* than one. In that180 // case we could just ignore it and try to identify which one needs the restriction,181 // but instead we choose to suggest replacing all instances of `impl Trait` with `T`182 // where `T: Trait`.183 let mut ty_spans = vec![];184 for input in fn_sig.decl.inputs {185 ReplaceImplTraitVisitor { ty_spans: &mut ty_spans, param_did: param.def_id }186 .visit_ty_unambig(input);187 }188 // The type param `T: Trait` we will suggest to introduce.189 let type_param = format!("{type_param_name}: {bound_str}");190191 let mut sugg = vec![192 if let Some(span) = hir_generics.span_for_param_suggestion() {193 (span, format!(", {type_param}"))194 } else {195 (hir_generics.span, format!("<{type_param}>"))196 },197 // `fn foo(t: impl Trait)`198 // ^ suggest `where <T as Trait>::A: Bound`199 predicate_constraint(hir_generics, trait_pred.upcast(tcx)),200 ];201 sugg.extend(ty_spans.into_iter().map(|s| (s, type_param_name.to_string())));202203 // Suggest `fn foo<T: Trait>(t: T) where <T as Trait>::A: Bound`.204 // FIXME: we should suggest `fn foo(t: impl Trait<A: Bound>)` instead.205 err.multipart_suggestion(206 "introduce a type parameter with a trait bound instead of using `impl Trait`",207 sugg,208 Applicability::MaybeIncorrect,209 );210 } else {211 if !trait_pred.is_suggestable(tcx, false) {212 return;213 }214 // Trivial case: `T` needs an extra bound: `T: Bound`.215 let (sp, suggestion) = match (216 hir_generics217 .params218 .iter()219 .find(|p| !matches!(p.kind, hir::GenericParamKind::Type { synthetic: true, .. })),220 super_traits,221 ) {222 (_, None) => predicate_constraint(hir_generics, trait_pred.upcast(tcx)),223 (None, Some((ident, []))) => (224 ident.span.shrink_to_hi(),225 format!(": {}", trait_pred.print_modifiers_and_trait_path()),226 ),227 (_, Some((_, [.., bounds]))) => (228 bounds.span().shrink_to_hi(),229 format!(" + {}", trait_pred.print_modifiers_and_trait_path()),230 ),231 (Some(_), Some((_, []))) => (232 hir_generics.span.shrink_to_hi(),233 format!(": {}", trait_pred.print_modifiers_and_trait_path()),234 ),235 };236237 err.span_suggestion_verbose(238 sp,239 format!("consider further restricting {msg}"),240 suggestion,241 Applicability::MachineApplicable,242 );243 }244}245246/// A single layer of `&` peeled from an expression, used by247/// [`TypeErrCtxt::peel_expr_refs`].248struct PeeledRef<'tcx> {249 /// The span covering the `&` (and any whitespace/mutability keyword) to remove.250 span: Span,251 /// The type after peeling this layer (and all prior layers).252 peeled_ty: Ty<'tcx>,253}254255impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {256 pub fn note_field_shadowed_by_private_candidate_in_cause(257 &self,258 err: &mut Diag<'_>,259 cause: &ObligationCause<'tcx>,260 param_env: ty::ParamEnv<'tcx>,261 ) {262 let mut hir_ids = FxHashSet::default();263 // Walk the parent chain so we can recover264 // the source expression from whichever layer carries them.265 let mut next_code = Some(cause.code());266 while let Some(cause_code) = next_code {267 match cause_code {268 ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. } => {269 hir_ids.insert(*lhs_hir_id);270 hir_ids.insert(*rhs_hir_id);271 }272 ObligationCauseCode::FunctionArg { arg_hir_id, .. }273 | ObligationCauseCode::ReturnValue(arg_hir_id)274 | ObligationCauseCode::AwaitableExpr(arg_hir_id)275 | ObligationCauseCode::BlockTailExpression(arg_hir_id, _)276 | ObligationCauseCode::UnOp { hir_id: arg_hir_id } => {277 hir_ids.insert(*arg_hir_id);278 }279 ObligationCauseCode::OpaqueReturnType(Some((_, hir_id))) => {280 hir_ids.insert(*hir_id);281 }282 _ => {}283 }284 next_code = cause_code.parent();285 }286287 if !cause.span.is_dummy()288 && let Some(body) = self.tcx.hir_maybe_body_owned_by(cause.body_id)289 {290 let mut expr_finder = FindExprBySpan::new(cause.span, self.tcx);291 expr_finder.visit_body(body);292 if let Some(expr) = expr_finder.result {293 hir_ids.insert(expr.hir_id);294 }295 }296297 // we will sort immediately by source order before emitting any diagnostics298 #[allow(rustc::potential_query_instability)]299 let mut hir_ids: Vec<_> = hir_ids.into_iter().collect();300 let source_map = self.tcx.sess.source_map();301 hir_ids.sort_by_cached_key(|hir_id| {302 let span = self.tcx.hir_span(*hir_id);303 let lo = source_map.lookup_byte_offset(span.lo());304 let hi = source_map.lookup_byte_offset(span.hi());305 (lo.sf.name.prefer_remapped_unconditionally().to_string(), lo.pos.0, hi.pos.0)306 });307308 for hir_id in hir_ids {309 self.note_field_shadowed_by_private_candidate(err, hir_id, param_env);310 }311 }312313 pub fn note_field_shadowed_by_private_candidate(314 &self,315 err: &mut Diag<'_>,316 hir_id: hir::HirId,317 param_env: ty::ParamEnv<'tcx>,318 ) {319 let Some(typeck_results) = &self.typeck_results else {320 return;321 };322 let Node::Expr(expr) = self.tcx.hir_node(hir_id) else {323 return;324 };325 let hir::ExprKind::Field(base_expr, field_ident) = expr.kind else {326 return;327 };328329 let Some(base_ty) = typeck_results.expr_ty_opt(base_expr) else {330 return;331 };332 let base_ty = self.resolve_vars_if_possible(base_ty);333 if base_ty.references_error() {334 return;335 }336337 let fn_body_hir_id = self.tcx.local_def_id_to_hir_id(typeck_results.hir_owner.def_id);338 let mut private_candidate: Option<(Ty<'tcx>, Ty<'tcx>, Span)> = None;339340 for (deref_base_ty, _) in (self.autoderef_steps)(base_ty) {341 let ty::Adt(base_def, args) = deref_base_ty.kind() else {342 continue;343 };344345 if base_def.is_enum() {346 continue;347 }348349 let (adjusted_ident, def_scope) =350 self.tcx.adjust_ident_and_get_scope(field_ident, base_def.did(), fn_body_hir_id);351352 let Some((_, field_def)) =353 base_def.non_enum_variant().fields.iter_enumerated().find(|(_, field)| {354 field.ident(self.tcx).normalize_to_macros_2_0() == adjusted_ident355 })356 else {357 continue;358 };359 let field_span = self360 .tcx361 .def_ident_span(field_def.did)362 .unwrap_or_else(|| self.tcx.def_span(field_def.did));363364 if field_def.vis.is_accessible_from(def_scope, self.tcx) {365 let accessible_field_ty = field_def.ty(self.tcx, args).skip_norm_wip();366 if let Some((private_base_ty, private_field_ty, private_field_span)) =367 private_candidate368 && !self.can_eq(param_env, private_field_ty, accessible_field_ty)369 {370 let private_struct_span = match private_base_ty.kind() {371 ty::Adt(private_base_def, _) => self372 .tcx373 .def_ident_span(private_base_def.did())374 .unwrap_or_else(|| self.tcx.def_span(private_base_def.did())),375 _ => DUMMY_SP,376 };377 let accessible_struct_span = self378 .tcx379 .def_ident_span(base_def.did())380 .unwrap_or_else(|| self.tcx.def_span(base_def.did()));381 let deref_impl_span = (typeck_results382 .expr_adjustments(base_expr)383 .iter()384 .filter(|adj| {385 matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))386 })387 .count()388 == 1)389 .then(|| {390 self.probe(|_| {391 let deref_trait_did =392 self.tcx.require_lang_item(LangItem::Deref, DUMMY_SP);393 let trait_ref =394 ty::TraitRef::new(self.tcx, deref_trait_did, [private_base_ty]);395 let obligation: Obligation<'tcx, ty::Predicate<'tcx>> =396 Obligation::new(397 self.tcx,398 ObligationCause::dummy(),399 param_env,400 trait_ref,401 );402 let Ok(Some(ImplSource::UserDefined(impl_data))) =403 SelectionContext::new(self)404 .select(&obligation.with(self.tcx, trait_ref))405 else {406 return None;407 };408 Some(self.tcx.def_span(impl_data.impl_def_id))409 })410 })411 .flatten();412413 let mut note_spans: MultiSpan = private_struct_span.into();414 if private_struct_span != DUMMY_SP {415 note_spans.push_span_label(private_struct_span, "in this struct");416 }417 if private_field_span != DUMMY_SP {418 note_spans.push_span_label(419 private_field_span,420 "if this field wasn't private, it would be accessible",421 );422 }423 if accessible_struct_span != DUMMY_SP {424 note_spans.push_span_label(425 accessible_struct_span,426 "this struct is accessible through auto-deref",427 );428 }429 if field_span != DUMMY_SP {430 note_spans431 .push_span_label(field_span, "this is the field that was accessed");432 }433 if let Some(deref_impl_span) = deref_impl_span434 && deref_impl_span != DUMMY_SP435 {436 note_spans.push_span_label(437 deref_impl_span,438 "the field was accessed through this `Deref`",439 );440 }441442 err.span_note(443 note_spans,444 format!(445 "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"446 ),447 );448 }449450 // we finally get to the accessible field,451 // so we can return early without checking the rest of the autoderef candidates452 return;453 }454455 private_candidate.get_or_insert((456 deref_base_ty,457 field_def.ty(self.tcx, args).skip_norm_wip(),458 field_span,459 ));460 }461 }462463 pub fn suggest_restricting_param_bound(464 &self,465 err: &mut Diag<'_>,466 trait_pred: ty::PolyTraitPredicate<'tcx>,467 associated_ty: Option<(&'static str, Ty<'tcx>)>,468 mut body_id: LocalDefId,469 ) {470 if trait_pred.skip_binder().polarity != ty::PredicatePolarity::Positive {471 return;472 }473474 let trait_pred = self.resolve_numeric_literals_with_default(trait_pred);475476 let self_ty = trait_pred.skip_binder().self_ty();477 let (param_ty, projection) = match *self_ty.kind() {478 ty::Param(_) => (true, None),479 ty::Alias(alias) => {480 if let Some(projection) = alias.try_to_projection() {481 (false, Some(projection))482 } else {483 (false, None)484 }485 }486 _ => (false, None),487 };488489 let mut finder = ParamFinder { .. };490 finder.visit_binder(&trait_pred);491492 // FIXME: Add check for trait bound that is already present, particularly `?Sized` so we493 // don't suggest `T: Sized + ?Sized`.494 loop {495 let node = self.tcx.hir_node_by_def_id(body_id);496 match node {497 hir::Node::Item(hir::Item {498 kind: hir::ItemKind::Trait { ident, generics, bounds, .. },499 ..500 }) if self_ty == self.tcx.types.self_param => {501 assert!(param_ty);502 // Restricting `Self` for a single method.503 suggest_restriction(504 self.tcx,505 body_id,506 generics,507 "`Self`",508 err,509 None,510 projection,511 trait_pred,512 Some((&ident, bounds)),513 );514 return;515 }516517 hir::Node::TraitItem(hir::TraitItem {518 generics,519 kind: hir::TraitItemKind::Fn(..),520 ..521 }) if self_ty == self.tcx.types.self_param => {522 assert!(param_ty);523 // Restricting `Self` for a single method.524 suggest_restriction(525 self.tcx, body_id, generics, "`Self`", err, None, projection, trait_pred,526 None,527 );528 return;529 }530531 hir::Node::TraitItem(hir::TraitItem {532 generics,533 kind: hir::TraitItemKind::Fn(fn_sig, ..),534 ..535 })536 | hir::Node::ImplItem(hir::ImplItem {537 generics,538 kind: hir::ImplItemKind::Fn(fn_sig, ..),539 ..540 })541 | hir::Node::Item(hir::Item {542 kind: hir::ItemKind::Fn { sig: fn_sig, generics, .. },543 ..544 }) if projection.is_some() => {545 // Missing restriction on associated type of type parameter (unmet projection).546 suggest_restriction(547 self.tcx,548 body_id,549 generics,550 "the associated type",551 err,552 Some(fn_sig),553 projection,554 trait_pred,555 None,556 );557 return;558 }559 hir::Node::Item(hir::Item {560 kind:561 hir::ItemKind::Trait { generics, .. }562 | hir::ItemKind::Impl(hir::Impl { generics, .. }),563 ..564 }) if projection.is_some() => {565 // Missing restriction on associated type of type parameter (unmet projection).566 suggest_restriction(567 self.tcx,568 body_id,569 generics,570 "the associated type",571 err,572 None,573 projection,574 trait_pred,575 None,576 );577 return;578 }579580 hir::Node::Item(hir::Item {581 kind:582 hir::ItemKind::Struct(_, generics, _)583 | hir::ItemKind::Enum(_, generics, _)584 | hir::ItemKind::Union(_, generics, _)585 | hir::ItemKind::Trait { generics, .. }586 | hir::ItemKind::Impl(hir::Impl { generics, .. })587 | hir::ItemKind::Fn { generics, .. }588 | hir::ItemKind::TyAlias(_, generics, _)589 | hir::ItemKind::Const(_, generics, _, _)590 | hir::ItemKind::TraitAlias(_, _, generics, _),591 ..592 })593 | hir::Node::TraitItem(hir::TraitItem { generics, .. })594 | hir::Node::ImplItem(hir::ImplItem { generics, .. })595 if param_ty =>596 {597 // We skip the 0'th arg (self) because we do not want598 // to consider the predicate as not suggestible if the599 // self type is an arg position `impl Trait` -- instead,600 // we handle that by adding ` + Bound` below.601 // FIXME(compiler-errors): It would be nice to do the same602 // this that we do in `suggest_restriction` and pull the603 // `impl Trait` into a new generic if it shows up somewhere604 // else in the predicate.605 if !trait_pred.skip_binder().trait_ref.args[1..]606 .iter()607 .all(|g| g.is_suggestable(self.tcx, false))608 {609 return;610 }611 // Missing generic type parameter bound.612 let param_name = self_ty.to_string();613 let mut constraint = with_no_trimmed_paths!(614 trait_pred.print_modifiers_and_trait_path().to_string()615 );616617 if let Some((name, term)) = associated_ty {618 // FIXME: this case overlaps with code in TyCtxt::note_and_explain_type_err.619 // That should be extracted into a helper function.620 if let Some(stripped) = constraint.strip_suffix('>') {621 constraint = format!("{stripped}, {name} = {term}>");622 } else {623 constraint.push_str(&format!("<{name} = {term}>"));624 }625 }626627 if suggest_constraining_type_param(628 self.tcx,629 generics,630 err,631 ¶m_name,632 &constraint,633 Some(trait_pred.def_id()),634 None,635 ) {636 return;637 }638 }639640 hir::Node::TraitItem(hir::TraitItem {641 generics,642 kind: hir::TraitItemKind::Fn(..),643 ..644 })645 | hir::Node::ImplItem(hir::ImplItem {646 generics,647 impl_kind: hir::ImplItemImplKind::Inherent { .. },648 kind: hir::ImplItemKind::Fn(..),649 ..650 }) if finder.can_suggest_bound(generics) => {651 // Missing generic type parameter bound.652 suggest_arbitrary_trait_bound(653 self.tcx,654 generics,655 err,656 trait_pred,657 associated_ty,658 );659 }660 hir::Node::Item(hir::Item {661 kind:662 hir::ItemKind::Struct(_, generics, _)663 | hir::ItemKind::Enum(_, generics, _)664 | hir::ItemKind::Union(_, generics, _)665 | hir::ItemKind::Trait { generics, .. }666 | hir::ItemKind::Impl(hir::Impl { generics, .. })667 | hir::ItemKind::Fn { generics, .. }668 | hir::ItemKind::TyAlias(_, generics, _)669 | hir::ItemKind::Const(_, generics, _, _)670 | hir::ItemKind::TraitAlias(_, _, generics, _),671 ..672 }) if finder.can_suggest_bound(generics) => {673 // Missing generic type parameter bound.674 if suggest_arbitrary_trait_bound(675 self.tcx,676 generics,677 err,678 trait_pred,679 associated_ty,680 ) {681 return;682 }683 }684 hir::Node::Crate(..) => return,685686 _ => {}687 }688 body_id = self.tcx.local_parent(body_id);689 }690 }691692 /// Provide a suggestion to dereference arguments to functions and binary operators, if that693 /// would satisfy trait bounds.694 pub(super) fn suggest_dereferences(695 &self,696 obligation: &PredicateObligation<'tcx>,697 err: &mut Diag<'_>,698 trait_pred: ty::PolyTraitPredicate<'tcx>,699 ) -> bool {700 let mut code = obligation.cause.code();701 if let ObligationCauseCode::FunctionArg { arg_hir_id, call_hir_id, .. } = code702 && let Some(typeck_results) = &self.typeck_results703 && let hir::Node::Expr(expr) = self.tcx.hir_node(*arg_hir_id)704 && let Some(arg_ty) = typeck_results.expr_ty_adjusted_opt(expr)705 {706 // Suggest dereferencing the argument to a function/method call if possible707708 // Get the root obligation, since the leaf obligation we have may be unhelpful (#87437)709 let mut real_trait_pred = trait_pred;710 while let Some((parent_code, parent_trait_pred)) = code.parent_with_predicate() {711 code = parent_code;712 if let Some(parent_trait_pred) = parent_trait_pred {713 real_trait_pred = parent_trait_pred;714 }715 }716717 // We `instantiate_bound_regions_with_erased` here because `make_subregion` does not handle718 // `ReBound`, and we don't particularly care about the regions.719 let real_ty = self.tcx.instantiate_bound_regions_with_erased(real_trait_pred.self_ty());720 if !self.can_eq(obligation.param_env, real_ty, arg_ty) {721 return false;722 }723724 // Potentially, we'll want to place our dereferences under a `&`. We don't try this for725 // `&mut`, since we can't be sure users will get the side-effects they want from it.726 // If this doesn't work, we'll try removing the `&` in `suggest_remove_reference`.727 // FIXME(dianne): this misses the case where users need both to deref and remove `&`s.728 // This method could be combined with `TypeErrCtxt::suggest_remove_reference` to handle729 // that, similar to what `FnCtxt::suggest_deref_or_ref` does.730 let (is_under_ref, base_ty, span) = match expr.kind {731 hir::ExprKind::AddrOf(hir::BorrowKind::Ref, hir::Mutability::Not, subexpr)732 if let &ty::Ref(region, base_ty, hir::Mutability::Not) = real_ty.kind() =>733 {734 (Some(region), base_ty, subexpr.span)735 }736 // Don't suggest `*&mut`, etc.737 hir::ExprKind::AddrOf(..) => return false,738 _ => (None, real_ty, obligation.cause.span),739 };740741 let autoderef = (self.autoderef_steps)(base_ty);742 let mut is_boxed = base_ty.is_box();743 if let Some(steps) = autoderef.into_iter().position(|(mut ty, obligations)| {744 // Ensure one of the following for dereferencing to be valid: we're passing by745 // reference, `ty` is `Copy`, or we're moving out of a (potentially nested) `Box`.746 let can_deref = is_under_ref.is_some()747 || self.type_is_copy_modulo_regions(obligation.param_env, ty)748 || ty.is_numeric() // for inference vars (presumably but not provably `Copy`)749 || is_boxed && self.type_is_sized_modulo_regions(obligation.param_env, ty);750 is_boxed &= ty.is_box();751752 // Re-add the `&` if necessary753 if let Some(region) = is_under_ref {754 ty = Ty::new_ref(self.tcx, region, ty, hir::Mutability::Not);755 }756757 // Remapping bound vars here758 let real_trait_pred_and_ty =759 real_trait_pred.map_bound(|inner_trait_pred| (inner_trait_pred, ty));760 let obligation = self.mk_trait_obligation_with_new_self_ty(761 obligation.param_env,762 real_trait_pred_and_ty,763 );764765 can_deref766 && obligations767 .iter()768 .chain([&obligation])769 .all(|obligation| self.predicate_may_hold(obligation))770 }) && steps > 0771 {772 if span.in_external_macro(self.tcx.sess.source_map()) {773 return false;774 }775 let derefs = "*".repeat(steps);776 let msg = "consider dereferencing here";777778 let call_node = self.tcx.hir_node(*call_hir_id);779 let is_receiver = matches!(780 call_node,781 Node::Expr(hir::Expr {782 kind: hir::ExprKind::MethodCall(_, receiver_expr, ..),783 ..784 })785 if receiver_expr.hir_id == *arg_hir_id786 );787 if is_receiver {788 err.multipart_suggestion(789 msg,790 vec![791 (span.shrink_to_lo(), format!("({derefs}")),792 (span.shrink_to_hi(), ")".to_string()),793 ],794 Applicability::MachineApplicable,795 )796 } else {797 err.span_suggestion_verbose(798 span.shrink_to_lo(),799 msg,800 derefs,801 Applicability::MachineApplicable,802 )803 };804 return true;805 }806 } else if let (807 ObligationCauseCode::BinOp { lhs_hir_id, rhs_hir_id, .. },808 predicate,809 ) = code.peel_derives_with_predicate()810 && let Some(typeck_results) = &self.typeck_results811 && let hir::Node::Expr(lhs) = self.tcx.hir_node(*lhs_hir_id)812 && let hir::Node::Expr(rhs) = self.tcx.hir_node(*rhs_hir_id)813 && let Some(rhs_ty) = typeck_results.expr_ty_opt(rhs)814 && let trait_pred = predicate.unwrap_or(trait_pred)815 // Only run this code on binary operators816 && hir::lang_items::BINARY_OPERATORS817 .iter()818 .filter_map(|&op| self.tcx.lang_items().get(op))819 .any(|op| {820 op == trait_pred.skip_binder().trait_ref.def_id821 })822 {823 // Suggest dereferencing the LHS, RHS, or both terms of a binop if possible824 let trait_pred = predicate.unwrap_or(trait_pred);825 let lhs_ty = self.tcx.instantiate_bound_regions_with_erased(trait_pred.self_ty());826 let lhs_autoderef = (self.autoderef_steps)(lhs_ty);827 let rhs_autoderef = (self.autoderef_steps)(rhs_ty);828 let first_lhs = lhs_autoderef.first().unwrap().clone();829 let first_rhs = rhs_autoderef.first().unwrap().clone();830 let mut autoderefs = lhs_autoderef831 .into_iter()832 .enumerate()833 .rev()834 .zip_longest(rhs_autoderef.into_iter().enumerate().rev())835 .map(|t| match t {836 EitherOrBoth::Both(a, b) => (a, b),837 EitherOrBoth::Left(a) => (a, (0, first_rhs.clone())),838 EitherOrBoth::Right(b) => ((0, first_lhs.clone()), b),839 })840 .rev();841 if let Some((lsteps, rsteps)) =842 autoderefs.find_map(|((lsteps, (l_ty, _)), (rsteps, (r_ty, _)))| {843 // Create a new predicate with the dereferenced LHS and RHS844 // We simultaneously dereference both sides rather than doing them845 // one at a time to account for cases such as &Box<T> == &&T846 let trait_pred_and_ty = trait_pred.map_bound(|inner| {847 (848 ty::TraitPredicate {849 trait_ref: ty::TraitRef::new_from_args(850 self.tcx,851 inner.trait_ref.def_id,852 self.tcx.mk_args(853 &[&[l_ty.into(), r_ty.into()], &inner.trait_ref.args[2..]]854 .concat(),855 ),856 ),857 ..inner858 },859 l_ty,860 )861 });862 let obligation = self.mk_trait_obligation_with_new_self_ty(863 obligation.param_env,864 trait_pred_and_ty,865 );866 self.predicate_may_hold(&obligation).then_some(match (lsteps, rsteps) {867 (_, 0) => (Some(lsteps), None),868 (0, _) => (None, Some(rsteps)),869 _ => (Some(lsteps), Some(rsteps)),870 })871 })872 {873 let make_sugg = |mut expr: &Expr<'_>, mut steps| {874 if expr.span.in_external_macro(self.tcx.sess.source_map()) {875 return None;876 }877 let mut prefix_span = expr.span.shrink_to_lo();878 let mut msg = "consider dereferencing here";879 if let hir::ExprKind::AddrOf(_, _, inner) = expr.kind {880 msg = "consider removing the borrow and dereferencing instead";881 if let hir::ExprKind::AddrOf(..) = inner.kind {882 msg = "consider removing the borrows and dereferencing instead";883 }884 }885 while let hir::ExprKind::AddrOf(_, _, inner) = expr.kind886 && steps > 0887 {888 prefix_span = prefix_span.with_hi(inner.span.lo());889 expr = inner;890 steps -= 1;891 }892 // Empty suggestions with empty spans ICE with debug assertions893 if steps == 0 {894 return Some((895 msg.trim_end_matches(" and dereferencing instead"),896 vec![(prefix_span, String::new())],897 ));898 }899 let derefs = "*".repeat(steps);900 let needs_parens = steps > 0 && expr_needs_parens(expr);901 let mut suggestion = if needs_parens {902 vec![903 (904 expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),905 format!("{derefs}("),906 ),907 (expr.span.shrink_to_hi(), ")".to_string()),908 ]909 } else {910 vec![(911 expr.span.with_lo(prefix_span.hi()).shrink_to_lo(),912 format!("{derefs}"),913 )]914 };915 // Empty suggestions with empty spans ICE with debug assertions916 if !prefix_span.is_empty() {917 suggestion.push((prefix_span, String::new()));918 }919 Some((msg, suggestion))920 };921922 if let Some(lsteps) = lsteps923 && let Some(rsteps) = rsteps924 && lsteps > 0925 && rsteps > 0926 {927 let Some((_, mut suggestion)) = make_sugg(lhs, lsteps) else {928 return false;929 };930 let Some((_, mut rhs_suggestion)) = make_sugg(rhs, rsteps) else {931 return false;932 };933 suggestion.append(&mut rhs_suggestion);934 err.multipart_suggestion(935 "consider dereferencing both sides of the expression",936 suggestion,937 Applicability::MachineApplicable,938 );939 return true;940 } else if let Some(lsteps) = lsteps941 && lsteps > 0942 {943 let Some((msg, suggestion)) = make_sugg(lhs, lsteps) else {944 return false;945 };946 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);947 return true;948 } else if let Some(rsteps) = rsteps949 && rsteps > 0950 {951 let Some((msg, suggestion)) = make_sugg(rhs, rsteps) else {952 return false;953 };954 err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);955 return true;956 }957 }958 }959 false960 }961962 /// Given a closure's `DefId`, return the given name of the closure.963 ///964 /// This doesn't account for reassignments, but it's only used for suggestions.965 fn get_closure_name(966 &self,967 def_id: DefId,968 err: &mut Diag<'_>,969 msg: Cow<'static, str>,970 ) -> Option<Symbol> {971 let get_name = |err: &mut Diag<'_>, kind: &hir::PatKind<'_>| -> Option<Symbol> {972 // Get the local name of this closure. This can be inaccurate because973 // of the possibility of reassignment, but this should be good enough.974 match &kind {975 hir::PatKind::Binding(hir::BindingMode::NONE, _, ident, None) => Some(ident.name),976 _ => {977 err.note(msg);978 None979 }980 }981 };982983 let hir_id = self.tcx.local_def_id_to_hir_id(def_id.as_local()?);984 match self.tcx.parent_hir_node(hir_id) {985 hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Let(local), .. }) => {986 get_name(err, &local.pat.kind)987 }988 // Different to previous arm because one is `&hir::Local` and the other989 // is `Box<hir::Local>`.990 hir::Node::LetStmt(local) => get_name(err, &local.pat.kind),991 _ => None,992 }993 }994995 /// We tried to apply the bound to an `fn` or closure. Check whether calling it would996 /// evaluate to a type that *would* satisfy the trait bound. If it would, suggest calling997 /// it: `bar(foo)` → `bar(foo())`. This case is *very* likely to be hit if `foo` is `async`.998 pub(super) fn suggest_fn_call(999 &self,1000 obligation: &PredicateObligation<'tcx>,1001 err: &mut Diag<'_>,1002 trait_pred: ty::PolyTraitPredicate<'tcx>,1003 ) -> bool {1004 // It doesn't make sense to make this suggestion outside of typeck...1005 // (also autoderef will ICE...)1006 if self.typeck_results.is_none() {1007 return false;1008 }10091010 if let ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) =1011 obligation.predicate.kind().skip_binder()1012 && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)1013 {1014 // Don't suggest calling to turn an unsized type into a sized type1015 return false;1016 }10171018 let self_ty = self.instantiate_binder_with_fresh_vars(1019 DUMMY_SP,1020 BoundRegionConversionTime::FnCall,1021 trait_pred.self_ty(),1022 );10231024 let Some((def_id_or_name, output, inputs)) =1025 self.extract_callable_info(obligation.cause.body_id, obligation.param_env, self_ty)1026 else {1027 return false;1028 };10291030 // Remapping bound vars here1031 let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output));10321033 let new_obligation =1034 self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);1035 if !self.predicate_must_hold_modulo_regions(&new_obligation) {1036 return false;1037 }10381039 // If this is a zero-argument async closure directly passed as an argument1040 // and the expected type is `Future`, suggest using `async {}` block instead1041 // of `async || {}`1042 if let ty::CoroutineClosure(def_id, args) = *self_ty.kind()1043 && let sig = args.as_coroutine_closure().coroutine_closure_sig().skip_binder()1044 && let ty::Tuple(inputs) = *sig.tupled_inputs_ty.kind()1045 && inputs.is_empty()1046 && self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Future)1047 && let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1048 && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) =1049 self.tcx.hir_node(*arg_hir_id)1050 && let Some(hir::Node::Expr(hir::Expr {1051 kind: hir::ExprKind::Closure(closure), ..1052 })) = self.tcx.hir_get_if_local(def_id)1053 && let hir::ClosureKind::CoroutineClosure(CoroutineDesugaring::Async) = closure.kind1054 && let Some(arg_span) = closure.fn_arg_span1055 && obligation.cause.span.contains(arg_span)1056 {1057 let mut body = self.tcx.hir_body(closure.body).value;1058 let peeled = body.peel_blocks().peel_drop_temps();1059 if let hir::ExprKind::Closure(inner) = peeled.kind {1060 body = self.tcx.hir_body(inner.body).value;1061 }1062 if !matches!(body.peel_blocks().peel_drop_temps().kind, hir::ExprKind::Block(..)) {1063 return false;1064 }10651066 let sm = self.tcx.sess.source_map();1067 let removal_span = if let Ok(snippet) =1068 sm.span_to_snippet(arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1)))1069 && snippet.ends_with(' ')1070 {1071 // There's a space after `||`, include it in the removal1072 arg_span.with_hi(arg_span.hi() + rustc_span::BytePos(1))1073 } else {1074 arg_span1075 };1076 err.span_suggestion_verbose(1077 removal_span,1078 "use `async {}` instead of `async || {}` to introduce an async block",1079 "",1080 Applicability::MachineApplicable,1081 );1082 return true;1083 }10841085 // Get the name of the callable and the arguments to be used in the suggestion.1086 let msg = match def_id_or_name {1087 DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) {1088 DefKind::Ctor(CtorOf::Struct, _) => {1089 Cow::from("use parentheses to construct this tuple struct")1090 }1091 DefKind::Ctor(CtorOf::Variant, _) => {1092 Cow::from("use parentheses to construct this tuple variant")1093 }1094 kind => Cow::from(format!(1095 "use parentheses to call this {}",1096 self.tcx.def_kind_descr(kind, def_id)1097 )),1098 },1099 DefIdOrName::Name(name) => Cow::from(format!("use parentheses to call this {name}")),1100 };11011102 let args = inputs1103 .into_iter()1104 .map(|ty| {1105 if ty.is_suggestable(self.tcx, false) {1106 format!("/* {ty} */")1107 } else {1108 "/* value */".to_string()1109 }1110 })1111 .collect::<Vec<_>>()1112 .join(", ");11131114 if let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1115 && obligation.cause.span.can_be_used_for_suggestions()1116 {1117 let span = obligation.cause.span;11181119 let arg_expr = match self.tcx.hir_node(*arg_hir_id) {1120 hir::Node::Expr(expr) => Some(expr),1121 _ => None,1122 };11231124 let is_closure_expr =1125 arg_expr.is_some_and(|expr| matches!(expr.kind, hir::ExprKind::Closure(..)));11261127 // If the user wrote `|| {}()`, suggesting to call the closure would produce `(|| {}())()`,1128 // which doesn't help and is often outright wrong.1129 if args.is_empty()1130 && let Some(expr) = arg_expr1131 && let hir::ExprKind::Closure(closure) = expr.kind1132 {1133 let mut body = self.tcx.hir_body(closure.body).value;11341135 // Async closures desugar to a closure returning a coroutine1136 if let hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) =1137 closure.kind1138 {1139 let peeled = body.peel_blocks().peel_drop_temps();1140 if let hir::ExprKind::Closure(inner) = peeled.kind {1141 body = self.tcx.hir_body(inner.body).value;1142 }1143 }11441145 let peeled_body = body.peel_blocks().peel_drop_temps();1146 if let hir::ExprKind::Call(callee, call_args) = peeled_body.kind1147 && call_args.is_empty()1148 && let hir::ExprKind::Block(..) = callee.peel_blocks().peel_drop_temps().kind1149 {1150 return false;1151 }1152 }11531154 if is_closure_expr {1155 err.multipart_suggestions(1156 msg,1157 vec![vec![1158 (span.shrink_to_lo(), "(".to_string()),1159 (span.shrink_to_hi(), format!(")({args})")),1160 ]],1161 Applicability::HasPlaceholders,1162 );1163 } else {1164 err.span_suggestion_verbose(1165 span.shrink_to_hi(),1166 msg,1167 format!("({args})"),1168 Applicability::HasPlaceholders,1169 );1170 }1171 } else if let DefIdOrName::DefId(def_id) = def_id_or_name {1172 let name = match self.tcx.hir_get_if_local(def_id) {1173 Some(hir::Node::Expr(hir::Expr {1174 kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }),1175 ..1176 })) => {1177 err.span_label(*fn_decl_span, "consider calling this closure");1178 let Some(name) = self.get_closure_name(def_id, err, msg.clone()) else {1179 return false;1180 };1181 name.to_string()1182 }1183 Some(hir::Node::Item(hir::Item {1184 kind: hir::ItemKind::Fn { ident, .. }, ..1185 })) => {1186 err.span_label(ident.span, "consider calling this function");1187 ident.to_string()1188 }1189 Some(hir::Node::Ctor(..)) => {1190 let name = self.tcx.def_path_str(def_id);1191 err.span_label(1192 self.tcx.def_span(def_id),1193 format!("consider calling the constructor for `{name}`"),1194 );1195 name1196 }1197 _ => return false,1198 };1199 err.help(format!("{msg}: `{name}({args})`"));1200 }1201 true1202 }12031204 pub(super) fn suggest_cast_to_fn_pointer(1205 &self,1206 obligation: &PredicateObligation<'tcx>,1207 err: &mut Diag<'_>,1208 leaf_trait_predicate: ty::PolyTraitPredicate<'tcx>,1209 main_trait_predicate: ty::PolyTraitPredicate<'tcx>,1210 span: Span,1211 ) -> bool {1212 let &[candidate] = &self.find_similar_impl_candidates(leaf_trait_predicate)[..] else {1213 return false;1214 };1215 let candidate = candidate.trait_ref;12161217 if !matches!(1218 (candidate.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind(),),1219 (ty::FnPtr(..), ty::FnDef(..))1220 ) {1221 return false;1222 }12231224 let parenthesized_cast = |span: Span| {1225 vec![1226 (span.shrink_to_lo(), "(".to_string()),1227 (span.shrink_to_hi(), format!(" as {})", candidate.self_ty())),1228 ]1229 };1230 // Wrap method receivers and `&`-references in parens.1231 let suggestion = if self.tcx.sess.source_map().span_followed_by(span, ".").is_some() {1232 parenthesized_cast(span)1233 } else if let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) {1234 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1235 expr_finder.visit_expr(body.value);1236 if let Some(expr) = expr_finder.result1237 && let hir::ExprKind::AddrOf(_, _, expr) = expr.kind1238 {1239 parenthesized_cast(expr.span)1240 } else {1241 vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]1242 }1243 } else {1244 vec![(span.shrink_to_hi(), format!(" as {}", candidate.self_ty()))]1245 };12461247 let trait_ = self.tcx.short_string(candidate.print_trait_sugared(), err.long_ty_path());1248 let self_ty = self.tcx.short_string(candidate.self_ty(), err.long_ty_path());1249 err.multipart_suggestion(1250 format!(1251 "the trait `{trait_}` is implemented for fn pointer \1252 `{self_ty}`, try casting using `as`",1253 ),1254 suggestion,1255 Applicability::MaybeIncorrect,1256 );1257 true1258 }12591260 pub(super) fn check_for_binding_assigned_block_without_tail_expression(1261 &self,1262 obligation: &PredicateObligation<'tcx>,1263 err: &mut Diag<'_>,1264 trait_pred: ty::PolyTraitPredicate<'tcx>,1265 ) {1266 let mut span = obligation.cause.span;1267 while span.from_expansion() {1268 // Remove all the desugaring and macro contexts.1269 span.remove_mark();1270 }1271 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1272 let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) else {1273 return;1274 };1275 expr_finder.visit_expr(body.value);1276 let Some(expr) = expr_finder.result else {1277 return;1278 };1279 let Some(typeck) = &self.typeck_results else {1280 return;1281 };1282 let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else {1283 return;1284 };1285 if !ty.is_unit() {1286 return;1287 };1288 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else {1289 return;1290 };1291 let Res::Local(hir_id) = path.res else {1292 return;1293 };1294 let hir::Node::Pat(pat) = self.tcx.hir_node(hir_id) else {1295 return;1296 };1297 let hir::Node::LetStmt(hir::LetStmt { ty: None, init: Some(init), .. }) =1298 self.tcx.parent_hir_node(pat.hir_id)1299 else {1300 return;1301 };1302 let hir::ExprKind::Block(block, None) = init.kind else {1303 return;1304 };1305 if block.expr.is_some() {1306 return;1307 }1308 let [.., stmt] = block.stmts else {1309 err.span_label(block.span, "this empty block is missing a tail expression");1310 return;1311 };1312 // FIXME expr and stmt have the same span if expr comes from expansion1313 // cc: https://github.com/rust-lang/rust/pull/147416#discussion_r24994075231314 if stmt.span.from_expansion() {1315 return;1316 }1317 let hir::StmtKind::Semi(tail_expr) = stmt.kind else {1318 return;1319 };1320 let Some(ty) = typeck.expr_ty_opt(tail_expr) else {1321 err.span_label(block.span, "this block is missing a tail expression");1322 return;1323 };1324 let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty));1325 let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));13261327 let new_obligation =1328 self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);1329 if !matches!(tail_expr.kind, hir::ExprKind::Err(_))1330 && self.predicate_must_hold_modulo_regions(&new_obligation)1331 {1332 err.span_suggestion_short(1333 stmt.span.with_lo(tail_expr.span.hi()),1334 "remove this semicolon",1335 "",1336 Applicability::MachineApplicable,1337 );1338 } else {1339 err.span_label(block.span, "this block is missing a tail expression");1340 }1341 }13421343 pub(super) fn suggest_add_clone_to_arg(1344 &self,1345 obligation: &PredicateObligation<'tcx>,1346 err: &mut Diag<'_>,1347 trait_pred: ty::PolyTraitPredicate<'tcx>,1348 ) -> bool {1349 let self_ty = self.resolve_vars_if_possible(trait_pred.self_ty());1350 self.enter_forall(self_ty, |ty: Ty<'_>| {1351 let Some(generics) = self.tcx.hir_get_generics(obligation.cause.body_id) else {1352 return false;1353 };1354 let ty::Ref(_, inner_ty, hir::Mutability::Not) = ty.kind() else { return false };1355 let ty::Param(param) = inner_ty.kind() else { return false };1356 let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code()1357 else {1358 return false;1359 };13601361 let clone_trait = self.tcx.require_lang_item(LangItem::Clone, obligation.cause.span);1362 let has_clone = |ty| {1363 self.type_implements_trait(clone_trait, [ty], obligation.param_env)1364 .must_apply_modulo_regions()1365 };13661367 let existing_clone_call = match self.tcx.hir_node(*arg_hir_id) {1368 // It's just a variable. Propose cloning it.1369 Node::Expr(Expr { kind: hir::ExprKind::Path(_), .. }) => None,1370 // It's already a call to `clone()`. We might be able to suggest1371 // adding a `+ Clone` bound, though.1372 Node::Expr(Expr {1373 kind:1374 hir::ExprKind::MethodCall(1375 hir::PathSegment { ident, .. },1376 _receiver,1377 [],1378 call_span,1379 ),1380 hir_id,1381 ..1382 }) if ident.name == sym::clone1383 && !call_span.from_expansion()1384 && !has_clone(*inner_ty) =>1385 {1386 // We only care about method calls corresponding to the real `Clone` trait.1387 let Some(typeck_results) = self.typeck_results.as_ref() else { return false };1388 let Some((DefKind::AssocFn, did)) = typeck_results.type_dependent_def(*hir_id)1389 else {1390 return false;1391 };1392 if self.tcx.trait_of_assoc(did) != Some(clone_trait) {1393 return false;1394 }1395 Some(ident.span)1396 }1397 _ => return false,1398 };13991400 let new_obligation = self.mk_trait_obligation_with_new_self_ty(1401 obligation.param_env,1402 trait_pred.map_bound(|trait_pred| (trait_pred, *inner_ty)),1403 );14041405 if self.predicate_may_hold(&new_obligation) && has_clone(ty) {1406 if !has_clone(param.to_ty(self.tcx)) {1407 suggest_constraining_type_param(1408 self.tcx,1409 generics,1410 err,1411 param.name.as_str(),1412 "Clone",1413 Some(clone_trait),1414 None,1415 );1416 }1417 if let Some(existing_clone_call) = existing_clone_call {1418 err.span_note(1419 existing_clone_call,1420 format!(1421 "this `clone()` copies the reference, \1422 which does not do anything, \1423 because `{inner_ty}` does not implement `Clone`"1424 ),1425 );1426 } else {1427 err.span_suggestion_verbose(1428 obligation.cause.span.shrink_to_hi(),1429 "consider using clone here",1430 ".clone()".to_string(),1431 Applicability::MaybeIncorrect,1432 );1433 }1434 return true;1435 }1436 false1437 })1438 }14391440 /// Extracts information about a callable type for diagnostics. This is a1441 /// heuristic -- it doesn't necessarily mean that a type is always callable,1442 /// because the callable type must also be well-formed to be called.1443 pub fn extract_callable_info(1444 &self,1445 body_id: LocalDefId,1446 param_env: ty::ParamEnv<'tcx>,1447 found: Ty<'tcx>,1448 ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> {1449 // Autoderef is useful here because sometimes we box callables, etc.1450 let Some((def_id_or_name, output, inputs)) =1451 (self.autoderef_steps)(found).into_iter().find_map(|(found, _)| match *found.kind() {1452 ty::FnPtr(sig_tys, _) => Some((1453 DefIdOrName::Name("function pointer"),1454 sig_tys.output(),1455 sig_tys.inputs(),1456 )),1457 ty::FnDef(def_id, _) => {1458 let fn_sig = found.fn_sig(self.tcx);1459 Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs()))1460 }1461 ty::Closure(def_id, args) => {1462 let fn_sig = args.as_closure().sig();1463 Some((1464 DefIdOrName::DefId(def_id),1465 fn_sig.output(),1466 fn_sig.inputs().map_bound(|inputs| inputs[0].tuple_fields().as_slice()),1467 ))1468 }1469 ty::CoroutineClosure(def_id, args) => {1470 let sig_parts = args.as_coroutine_closure().coroutine_closure_sig();1471 Some((1472 DefIdOrName::DefId(def_id),1473 sig_parts.map_bound(|sig| {1474 sig.to_coroutine(1475 self.tcx,1476 args.as_coroutine_closure().parent_args(),1477 // Just use infer vars here, since we don't really care1478 // what these types are, just that we're returning a coroutine.1479 self.next_ty_var(DUMMY_SP),1480 self.tcx.coroutine_for_closure(def_id),1481 self.next_ty_var(DUMMY_SP),1482 )1483 }),1484 sig_parts.map_bound(|sig| sig.tupled_inputs_ty.tuple_fields().as_slice()),1485 ))1486 }1487 ty::Alias(ty::AliasTy { kind: ty::Opaque { def_id }, args, .. }) => {1488 self.tcx1489 .item_self_bounds(def_id)1490 .instantiate(self.tcx, args)1491 .skip_norm_wip()1492 .iter()1493 .find_map(|pred| {1494 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()1495 && self1496 .tcx1497 .is_lang_item(proj.def_id(), LangItem::FnOnceOutput)1498 // args tuple will always be args[1]1499 && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()1500 {1501 Some((1502 DefIdOrName::DefId(def_id),1503 pred.kind().rebind(proj.term.expect_type()),1504 pred.kind().rebind(args.as_slice()),1505 ))1506 } else {1507 None1508 }1509 })1510 }1511 ty::Dynamic(data, _) => data.iter().find_map(|pred| {1512 if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder()1513 && self.tcx.is_lang_item(proj.def_id, LangItem::FnOnceOutput)1514 // for existential projection, args are shifted over by 11515 && let ty::Tuple(args) = proj.args.type_at(0).kind()1516 {1517 Some((1518 DefIdOrName::Name("trait object"),1519 pred.rebind(proj.term.expect_type()),1520 pred.rebind(args.as_slice()),1521 ))1522 } else {1523 None1524 }1525 }),1526 ty::Param(param) => {1527 let generics = self.tcx.generics_of(body_id);1528 let name = if generics.count() > param.index as usize1529 && let def = generics.param_at(param.index as usize, self.tcx)1530 && matches!(def.kind, ty::GenericParamDefKind::Type { .. })1531 && def.name == param.name1532 {1533 DefIdOrName::DefId(def.def_id)1534 } else {1535 DefIdOrName::Name("type parameter")1536 };1537 param_env.caller_bounds().iter().find_map(|pred| {1538 if let ty::ClauseKind::Projection(proj) = pred.kind().skip_binder()1539 && self1540 .tcx1541 .is_lang_item(proj.def_id(), LangItem::FnOnceOutput)1542 && proj.projection_term.self_ty() == found1543 // args tuple will always be args[1]1544 && let ty::Tuple(args) = proj.projection_term.args.type_at(1).kind()1545 {1546 Some((1547 name,1548 pred.kind().rebind(proj.term.expect_type()),1549 pred.kind().rebind(args.as_slice()),1550 ))1551 } else {1552 None1553 }1554 })1555 }1556 _ => None,1557 })1558 else {1559 return None;1560 };15611562 let output = self.instantiate_binder_with_fresh_vars(1563 DUMMY_SP,1564 BoundRegionConversionTime::FnCall,1565 output,1566 );1567 let inputs = inputs1568 .skip_binder()1569 .iter()1570 .map(|ty| {1571 self.instantiate_binder_with_fresh_vars(1572 DUMMY_SP,1573 BoundRegionConversionTime::FnCall,1574 inputs.rebind(*ty),1575 )1576 })1577 .collect();15781579 // We don't want to register any extra obligations, which should be1580 // implied by wf, but also because that would possibly result in1581 // erroneous errors later on.1582 let InferOk { value: output, obligations: _ } =1583 self.at(&ObligationCause::dummy(), param_env).normalize(Unnormalized::new_wip(output));15841585 if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) }1586 }15871588 pub(super) fn where_clause_expr_matches_failed_self_ty(1589 &self,1590 obligation: &PredicateObligation<'tcx>,1591 old_self_ty: Ty<'tcx>,1592 ) -> bool {1593 let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code() else {1594 return true;1595 };1596 let (Some(typeck_results), Some(body)) = (1597 self.typeck_results.as_ref(),1598 self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id),1599 ) else {1600 return true;1601 };16021603 let mut expr_finder = FindExprBySpan::new(obligation.cause.span, self.tcx);1604 expr_finder.visit_expr(body.value);1605 let Some(expr) = expr_finder.result else {1606 return true;1607 };16081609 let inner_old_self_ty = match old_self_ty.kind() {1610 ty::Ref(_, inner_ty, _) => Some(*inner_ty),1611 _ => None,1612 };16131614 [typeck_results.expr_ty_adjusted_opt(expr)].into_iter().flatten().any(|expr_ty| {1615 self.can_eq(obligation.param_env, expr_ty, old_self_ty)1616 || inner_old_self_ty1617 .is_some_and(|inner_ty| self.can_eq(obligation.param_env, expr_ty, inner_ty))1618 })1619 }16201621 pub(super) fn suggest_add_reference_to_arg(1622 &self,1623 obligation: &PredicateObligation<'tcx>,1624 err: &mut Diag<'_>,1625 poly_trait_pred: ty::PolyTraitPredicate<'tcx>,1626 has_custom_message: bool,1627 ) -> bool {1628 let span = obligation.cause.span;1629 let param_env = obligation.param_env;16301631 let mk_result = |trait_pred_and_new_ty| {1632 let obligation =1633 self.mk_trait_obligation_with_new_self_ty(param_env, trait_pred_and_new_ty);1634 self.predicate_must_hold_modulo_regions(&obligation)1635 };16361637 let code = match obligation.cause.code() {1638 ObligationCauseCode::FunctionArg { parent_code, .. } => parent_code,1639 // FIXME(compiler-errors): This is kind of a mess, but required for obligations1640 // that come from a path expr to affect the *call* expr.1641 c @ ObligationCauseCode::WhereClauseInExpr(_, _, hir_id, _)1642 if self.tcx.hir_span(*hir_id).lo() == span.lo() =>1643 {1644 // `hir_id` corresponds to the HIR node that introduced a `where`-clause obligation.1645 // If that obligation comes from a type in an associated method call, we need1646 // special handling here.1647 if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(*hir_id)1648 && let hir::ExprKind::Call(base, _) = expr.kind1649 && let hir::ExprKind::Path(hir::QPath::TypeRelative(ty, segment)) = base.kind1650 && let hir::Node::Expr(outer) = self.tcx.parent_hir_node(expr.hir_id)1651 && let hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mtbl, _) = outer.kind1652 && ty.span == span1653 {1654 // We've encountered something like `&str::from("")`, where the intended code1655 // was likely `<&str>::from("")`. The former is interpreted as "call method1656 // `from` on `str` and borrow the result", while the latter means "call method1657 // `from` on `&str`".16581659 let trait_pred_and_imm_ref = poly_trait_pred.map_bound(|p| {1660 (p, Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))1661 });1662 let trait_pred_and_mut_ref = poly_trait_pred.map_bound(|p| {1663 (p, Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, p.self_ty()))1664 });16651666 let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);1667 let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);1668 let sugg_msg = |pre: &str| {1669 format!(1670 "you likely meant to call the associated function `{FN}` for type \1671 `&{pre}{TY}`, but the code as written calls associated function `{FN}` on \1672 type `{TY}`",1673 FN = segment.ident,1674 TY = poly_trait_pred.self_ty(),1675 )1676 };1677 match (imm_ref_self_ty_satisfies_pred, mut_ref_self_ty_satisfies_pred, mtbl) {1678 (true, _, hir::Mutability::Not) | (_, true, hir::Mutability::Mut) => {1679 err.multipart_suggestion(1680 sugg_msg(mtbl.prefix_str()),1681 vec![1682 (outer.span.shrink_to_lo(), "<".to_string()),1683 (span.shrink_to_hi(), ">".to_string()),1684 ],1685 Applicability::MachineApplicable,1686 );1687 }1688 (true, _, hir::Mutability::Mut) => {1689 // There's an associated function found on the immutable borrow of the1690 err.multipart_suggestion(1691 sugg_msg("mut "),1692 vec![1693 (outer.span.shrink_to_lo().until(span), "<&".to_string()),1694 (span.shrink_to_hi(), ">".to_string()),1695 ],1696 Applicability::MachineApplicable,1697 );1698 }1699 (_, true, hir::Mutability::Not) => {1700 err.multipart_suggestion(1701 sugg_msg(""),1702 vec![1703 (outer.span.shrink_to_lo().until(span), "<&mut ".to_string()),1704 (span.shrink_to_hi(), ">".to_string()),1705 ],1706 Applicability::MachineApplicable,1707 );1708 }1709 _ => {}1710 }1711 // If we didn't return early here, we would instead suggest `&&str::from("")`.1712 return false;1713 }1714 c1715 }1716 c if matches!(1717 span.ctxt().outer_expn_data().kind,1718 ExpnKind::Desugaring(DesugaringKind::ForLoop)1719 ) =>1720 {1721 c1722 }1723 _ => return false,1724 };17251726 // List of traits for which it would be nonsensical to suggest borrowing.1727 // For instance, immutable references are always Copy, so suggesting to1728 // borrow would always succeed, but it's probably not what the user wanted.1729 let mut never_suggest_borrow: Vec<_> =1730 [LangItem::Copy, LangItem::Clone, LangItem::Unpin, LangItem::Sized]1731 .iter()1732 .filter_map(|lang_item| self.tcx.lang_items().get(*lang_item))1733 .collect();17341735 if let Some(def_id) = self.tcx.get_diagnostic_item(sym::Send) {1736 never_suggest_borrow.push(def_id);1737 }17381739 // Try to apply the original trait bound by borrowing.1740 let mut try_borrowing = |old_pred: ty::PolyTraitPredicate<'tcx>,1741 blacklist: &[DefId]|1742 -> bool {1743 if blacklist.contains(&old_pred.def_id()) {1744 return false;1745 }1746 // We map bounds to `&T` and `&mut T`1747 let trait_pred_and_imm_ref = old_pred.map_bound(|trait_pred| {1748 (1749 trait_pred,1750 Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),1751 )1752 });1753 let trait_pred_and_mut_ref = old_pred.map_bound(|trait_pred| {1754 (1755 trait_pred,1756 Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_static, trait_pred.self_ty()),1757 )1758 });17591760 let imm_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_imm_ref);1761 let mut_ref_self_ty_satisfies_pred = mk_result(trait_pred_and_mut_ref);17621763 let (ref_inner_ty_satisfies_pred, ref_inner_ty_is_mut) =1764 if let ObligationCauseCode::WhereClauseInExpr(..) = obligation.cause.code()1765 && let ty::Ref(_, ty, mutability) = old_pred.self_ty().skip_binder().kind()1766 {1767 (1768 mk_result(old_pred.map_bound(|trait_pred| (trait_pred, *ty))),1769 mutability.is_mut(),1770 )1771 } else {1772 (false, false)1773 };17741775 let is_immut = imm_ref_self_ty_satisfies_pred1776 || (ref_inner_ty_satisfies_pred && !ref_inner_ty_is_mut);1777 let is_mut = mut_ref_self_ty_satisfies_pred || ref_inner_ty_is_mut;1778 if !is_immut && !is_mut {1779 return false;1780 }1781 let Ok(_snippet) = self.tcx.sess.source_map().span_to_snippet(span) else {1782 return false;1783 };1784 // We don't want a borrowing suggestion on the fields in structs1785 // ```1786 // #[derive(Clone)]1787 // struct Foo {1788 // the_foos: Vec<Foo>1789 // }1790 // ```1791 if !matches!(1792 span.ctxt().outer_expn_data().kind,1793 ExpnKind::Root | ExpnKind::Desugaring(DesugaringKind::ForLoop)1794 ) {1795 return false;1796 }1797 // We have a very specific type of error, where just borrowing this argument1798 // might solve the problem. In cases like this, the important part is the1799 // original type obligation, not the last one that failed, which is arbitrary.1800 // Because of this, we modify the error to refer to the original obligation and1801 // return early in the caller.18021803 let mut label = || {1804 // Special case `Sized` as `old_pred` will be the trait itself instead of1805 // `Sized` when the trait bound is the source of the error.1806 let is_sized = match obligation.predicate.kind().skip_binder() {1807 ty::PredicateKind::Clause(ty::ClauseKind::Trait(trait_pred)) => {1808 self.tcx.is_lang_item(trait_pred.def_id(), LangItem::Sized)1809 }1810 _ => false,1811 };18121813 let msg = format!(1814 "the trait bound `{}` is not satisfied",1815 self.tcx.short_string(old_pred, err.long_ty_path()),1816 );1817 let self_ty_str = self.tcx.short_string(old_pred.self_ty(), err.long_ty_path());1818 let trait_path = self1819 .tcx1820 .short_string(old_pred.print_modifiers_and_trait_path(), err.long_ty_path());18211822 if has_custom_message {1823 let msg = if is_sized {1824 "the trait bound `Sized` is not satisfied".into()1825 } else {1826 msg1827 };1828 err.note(msg);1829 } else {1830 err.messages = vec![(rustc_errors::DiagMessage::from(msg), Style::NoStyle)];1831 }1832 if is_sized {1833 err.span_label(1834 span,1835 format!("the trait `Sized` is not implemented for `{self_ty_str}`"),1836 );1837 } else {1838 err.span_label(1839 span,1840 format!("the trait `{trait_path}` is not implemented for `{self_ty_str}`"),1841 );1842 }1843 };18441845 let mut sugg_prefixes = vec![];1846 if is_immut {1847 sugg_prefixes.push("&");1848 }1849 if is_mut {1850 sugg_prefixes.push("&mut ");1851 }1852 let sugg_msg = format!(1853 "consider{} borrowing here",1854 if is_mut && !is_immut { " mutably" } else { "" },1855 );18561857 // Issue #104961, we need to add parentheses properly for compound expressions1858 // for example, `x.starts_with("hi".to_string() + "you")`1859 // should be `x.starts_with(&("hi".to_string() + "you"))`1860 let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) else {1861 return false;1862 };1863 let mut expr_finder = FindExprBySpan::new(span, self.tcx);1864 expr_finder.visit_expr(body.value);18651866 if let Some(ty) = expr_finder.ty_result {1867 if let hir::Node::Expr(expr) = self.tcx.parent_hir_node(ty.hir_id)1868 && let hir::ExprKind::Path(hir::QPath::TypeRelative(_, _)) = expr.kind1869 && ty.span == span1870 {1871 // We've encountered something like `str::from("")`, where the intended code1872 // was likely `<&str>::from("")`. #143393.1873 label();1874 err.multipart_suggestions(1875 sugg_msg,1876 sugg_prefixes.into_iter().map(|sugg_prefix| {1877 vec![1878 (span.shrink_to_lo(), format!("<{sugg_prefix}")),1879 (span.shrink_to_hi(), ">".to_string()),1880 ]1881 }),1882 Applicability::MaybeIncorrect,1883 );1884 return true;1885 }1886 return false;1887 }1888 let Some(expr) = expr_finder.result else {1889 return false;1890 };1891 if let hir::ExprKind::AddrOf(_, _, _) = expr.kind {1892 return false;1893 }1894 let old_self_ty = old_pred.skip_binder().self_ty();1895 if !old_self_ty.has_escaping_bound_vars()1896 && !self.where_clause_expr_matches_failed_self_ty(1897 obligation,1898 self.tcx.instantiate_bound_regions_with_erased(old_pred.self_ty()),1899 )1900 {1901 return false;1902 }1903 let needs_parens_post = expr_needs_parens(expr);1904 let needs_parens_pre = match self.tcx.parent_hir_node(expr.hir_id) {1905 Node::Expr(e)1906 if let hir::ExprKind::MethodCall(_, base, _, _) = e.kind1907 && base.hir_id == expr.hir_id =>1908 {1909 true1910 }1911 _ => false,1912 };19131914 label();1915 let suggestions = sugg_prefixes.into_iter().map(|sugg_prefix| {1916 match (needs_parens_pre, needs_parens_post) {1917 (false, false) => vec![(span.shrink_to_lo(), sugg_prefix.to_string())],1918 // We have something like `foo.bar()`, where we want to bororw foo, so we need1919 // to suggest `(&mut foo).bar()`.1920 (false, true) => vec![1921 (span.shrink_to_lo(), format!("{sugg_prefix}(")),1922 (span.shrink_to_hi(), ")".to_string()),1923 ],1924 // Issue #109436, we need to add parentheses properly for method calls1925 // for example, `foo.into()` should be `(&foo).into()`1926 (true, false) => vec![1927 (span.shrink_to_lo(), format!("({sugg_prefix}")),1928 (span.shrink_to_hi(), ")".to_string()),1929 ],1930 (true, true) => vec![1931 (span.shrink_to_lo(), format!("({sugg_prefix}(")),1932 (span.shrink_to_hi(), "))".to_string()),1933 ],1934 }1935 });1936 err.multipart_suggestions(sugg_msg, suggestions, Applicability::MaybeIncorrect);1937 return true;1938 };19391940 if let ObligationCauseCode::ImplDerived(cause) = &*code {1941 try_borrowing(cause.derived.parent_trait_pred, &[])1942 } else if let ObligationCauseCode::WhereClause(..)1943 | ObligationCauseCode::WhereClauseInExpr(..) = code1944 {1945 try_borrowing(poly_trait_pred, &never_suggest_borrow)1946 } else {1947 false1948 }1949 }19501951 // Suggest borrowing the type1952 pub(super) fn suggest_borrowing_for_object_cast(1953 &self,1954 err: &mut Diag<'_>,1955 obligation: &PredicateObligation<'tcx>,1956 self_ty: Ty<'tcx>,1957 target_ty: Ty<'tcx>,1958 ) {1959 let ty::Ref(_, object_ty, hir::Mutability::Not) = target_ty.kind() else {1960 return;1961 };1962 let ty::Dynamic(predicates, _) = object_ty.kind() else {1963 return;1964 };1965 let self_ref_ty = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, self_ty);19661967 for predicate in predicates.iter() {1968 if !self.predicate_must_hold_modulo_regions(1969 &obligation.with(self.tcx, predicate.with_self_ty(self.tcx, self_ref_ty)),1970 ) {1971 return;1972 }1973 }19741975 err.span_suggestion_verbose(1976 obligation.cause.span.shrink_to_lo(),1977 format!(1978 "consider borrowing the value, since `&{self_ty}` can be coerced into `{target_ty}`"1979 ),1980 "&",1981 Applicability::MaybeIncorrect,1982 );1983 }19841985 /// Peel `&`-borrows from an expression, following through untyped let-bindings.1986 /// Returns a list of removable `&` layers (each with the span to remove and the1987 /// resulting type), plus an optional terminal [`hir::Param`] when the chain ends1988 /// at a function parameter (including async-fn desugared parameters).1989 fn peel_expr_refs(1990 &self,1991 mut expr: &'tcx hir::Expr<'tcx>,1992 mut ty: Ty<'tcx>,1993 ) -> (Vec<PeeledRef<'tcx>>, Option<&'tcx hir::Param<'tcx>>) {1994 let mut refs = Vec::new();1995 'outer: loop {1996 while let hir::ExprKind::AddrOf(_, _, borrowed) = expr.kind {1997 let span =1998 if let Some(borrowed_span) = borrowed.span.find_ancestor_inside(expr.span) {1999 expr.span.until(borrowed_span)2000 } else {
Findings
✓ No findings reported for this file.