1// ignore-tidy-filelength23use std::iter;4use std::ops::ControlFlow;56use either::Either;7use hir::{ClosureKind, Path};8use rustc_data_structures::fx::FxIndexSet;9use rustc_errors::codes::*;10use rustc_errors::{Applicability, Diag, MultiSpan, struct_span_code_err};11use rustc_hir as hir;12use rustc_hir::attrs::diagnostic::{CustomDiagnostic, FormatArgs};13use rustc_hir::def::{DefKind, Res};14use rustc_hir::intravisit::{Visitor, walk_block, walk_expr};15use rustc_hir::{16 CoroutineDesugaring, CoroutineKind, CoroutineSource, LangItem, PatField, find_attr,17};18use rustc_middle::bug;19use rustc_middle::hir::nested_filter::OnlyBodies;20use rustc_middle::mir::{21 self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,22 FakeBorrowKind, FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, MutBorrowKind,23 Operand, Place, PlaceRef, PlaceTy, ProjectionElem, Rvalue, Statement, StatementKind,24 Terminator, TerminatorKind, VarBindingForm, VarDebugInfoContents,25};26use rustc_middle::ty::print::PrintTraitRefExt as _;27use rustc_middle::ty::{28 self, PredicateKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, Upcast,29 suggest_constraining_type_params,30};31use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};32use rustc_span::def_id::{DefId, LocalDefId};33use rustc_span::hygiene::DesugaringKind;34use rustc_span::{BytePos, ExpnKind, Ident, MacroKind, Span, Symbol, kw, sym};35use rustc_trait_selection::error_reporting::InferCtxtErrorExt;36use rustc_trait_selection::error_reporting::traits::FindExprBySpan;37use rustc_trait_selection::error_reporting::traits::call_kind::CallKind;38use rustc_trait_selection::infer::InferCtxtExt;39use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;40use rustc_trait_selection::traits::{41 Obligation, ObligationCause, ObligationCtxt, supertrait_def_ids,42};43use tracing::{debug, instrument};4445use super::explain_borrow::{BorrowExplanation, LaterUseKind};46use super::{DescribePlaceOpt, RegionName, RegionNameSource, UseSpans};47use crate::borrow_set::{BorrowData, TwoPhaseActivation};48use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;49use crate::diagnostics::{CapturedMessageOpt, call_kind, find_all_local_uses};50use crate::{InitializationRequiringAction, MirBorrowckCtxt, WriteKind, borrowck_errors};5152#[derive(Debug)]53struct MoveSite {54 /// Index of the "move out" that we found. The `MoveData` can55 /// then tell us where the move occurred.56 moi: MoveOutIndex,5758 /// `true` if we traversed a back edge while walking from the point59 /// of error to the move site.60 traversed_back_edge: bool,61}6263/// Which case a StorageDeadOrDrop is for.64#[derive(Copy, Clone, PartialEq, Eq, Debug)]65enum StorageDeadOrDrop<'tcx> {66 LocalStorageDead,67 BoxedStorageDead,68 Destructor(Ty<'tcx>),69}7071impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {72 pub(crate) fn report_use_of_moved_or_uninitialized(73 &mut self,74 location: Location,75 desired_action: InitializationRequiringAction,76 (moved_place, used_place, span): (PlaceRef<'tcx>, PlaceRef<'tcx>, Span),77 mpi: MovePathIndex,78 ) {79 debug!(80 "report_use_of_moved_or_uninitialized: location={:?} desired_action={:?} \81 moved_place={:?} used_place={:?} span={:?} mpi={:?}",82 location, desired_action, moved_place, used_place, span, mpi83 );8485 let use_spans =86 self.move_spans(moved_place, location).or_else(|| self.borrow_spans(span, location));87 let span = use_spans.args_or_use();8889 let (move_site_vec, maybe_reinitialized_locations) = self.get_moved_indexes(location, mpi);90 debug!(91 "report_use_of_moved_or_uninitialized: move_site_vec={:?} use_spans={:?}",92 move_site_vec, use_spans93 );94 let move_out_indices: Vec<_> =95 move_site_vec.iter().map(|move_site| move_site.moi).collect();9697 if move_out_indices.is_empty() {98 let root_local = used_place.local;99100 if !self.uninitialized_error_reported.insert(root_local) {101 debug!(102 "report_use_of_moved_or_uninitialized place: error about {:?} suppressed",103 root_local104 );105 return;106 }107108 let err = self.report_use_of_uninitialized(109 mpi,110 used_place,111 moved_place,112 desired_action,113 span,114 use_spans,115 );116 self.buffer_error(err);117 } else {118 if let Some((reported_place, _)) = self.has_move_error(&move_out_indices) {119 if used_place.is_prefix_of(*reported_place) {120 debug!(121 "report_use_of_moved_or_uninitialized place: error suppressed mois={:?}",122 move_out_indices123 );124 return;125 }126 }127128 let is_partial_move = move_site_vec.iter().any(|move_site| {129 let move_out = self.move_data.moves[(*move_site).moi];130 let moved_place = &self.move_data.move_paths[move_out.path].place;131 // `*(_1)` where `_1` is a `Box` is actually a move out.132 let is_box_move = moved_place.as_ref().projection == [ProjectionElem::Deref]133 && self.body.local_decls[moved_place.local].ty.is_box();134135 !is_box_move136 && used_place != moved_place.as_ref()137 && used_place.is_prefix_of(moved_place.as_ref())138 });139140 let partial_str = if is_partial_move { "partial " } else { "" };141 let partially_str = if is_partial_move { "partially " } else { "" };142143 let (on_move_message, on_move_label, on_move_notes) = if let ty::Adt(item_def, args) =144 self.body.local_decls[moved_place.local].ty.kind()145 && let Some(Some(directive)) = find_attr!(self.infcx.tcx, item_def.did(), OnMove { directive, .. } => directive)146 {147 let this = self.infcx.tcx.item_name(item_def.did()).to_string();148 let mut generic_args: Vec<_> = self149 .infcx150 .tcx151 .generics_of(item_def.did())152 .own_params153 .iter()154 .filter_map(|param| Some((param.name, args[param.index as usize].to_string())))155 .collect();156 generic_args.push((kw::SelfUpper, this.clone()));157158 let args = FormatArgs { this, generic_args, .. };159 let CustomDiagnostic { message, label, notes, parent_label: _ } =160 directive.eval(None, &args);161162 (message, label, notes)163 } else {164 (None, None, Vec::new())165 };166167 let mut err = self.cannot_act_on_moved_value(168 span,169 desired_action.as_noun(),170 partially_str,171 self.describe_place_with_options(172 moved_place,173 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },174 ),175 on_move_message,176 );177178 for note in on_move_notes {179 err.note(note);180 }181182 let reinit_spans = maybe_reinitialized_locations183 .iter()184 .take(3)185 .map(|loc| {186 self.move_spans(self.move_data.move_paths[mpi].place.as_ref(), *loc)187 .args_or_use()188 })189 .collect::<Vec<Span>>();190191 let reinits = maybe_reinitialized_locations.len();192 if reinits == 1 {193 err.span_label(reinit_spans[0], "this reinitialization might get skipped");194 } else if reinits > 1 {195 err.span_note(196 MultiSpan::from_spans(reinit_spans),197 if reinits <= 3 {198 format!("these {reinits} reinitializations might get skipped")199 } else {200 format!(201 "these 3 reinitializations and {} other{} might get skipped",202 reinits - 3,203 if reinits == 4 { "" } else { "s" }204 )205 },206 );207 }208209 let closure = self.add_moved_or_invoked_closure_note(location, used_place, &mut err);210211 let mut is_loop_move = false;212 let mut seen_spans = FxIndexSet::default();213214 for move_site in &move_site_vec {215 let move_out = self.move_data.moves[(*move_site).moi];216 let moved_place = &self.move_data.move_paths[move_out.path].place;217218 let move_spans = self.move_spans(moved_place.as_ref(), move_out.source);219 let move_span = move_spans.args_or_use();220221 let is_move_msg = move_spans.for_closure();222223 let is_loop_message = location == move_out.source || move_site.traversed_back_edge;224225 if location == move_out.source {226 is_loop_move = true;227 }228229 let mut has_suggest_reborrow = false;230 if !seen_spans.contains(&move_span) {231 self.suggest_ref_or_clone(232 mpi,233 &mut err,234 move_spans,235 moved_place.as_ref(),236 &mut has_suggest_reborrow,237 closure,238 );239240 let msg_opt = CapturedMessageOpt {241 is_partial_move,242 is_loop_message,243 is_move_msg,244 is_loop_move,245 has_suggest_reborrow,246 maybe_reinitialized_locations_is_empty: maybe_reinitialized_locations247 .is_empty(),248 };249 self.explain_captures(250 &mut err,251 span,252 move_span,253 move_spans,254 *moved_place,255 msg_opt,256 );257 }258 seen_spans.insert(move_span);259 }260261 use_spans.var_path_only_subdiag(&mut err, desired_action);262263 if !is_loop_move {264 err.span_label(265 span,266 format!(267 "value {} here after {partial_str}move",268 desired_action.as_verb_in_past_tense(),269 ),270 );271 }272273 let ty = used_place.ty(self.body, self.infcx.tcx).ty;274 let needs_note = match ty.kind() {275 ty::Closure(id, _) => {276 self.infcx.tcx.closure_kind_origin(id.expect_local()).is_none()277 }278 _ => true,279 };280281 let mpi = self.move_data.moves[move_out_indices[0]].path;282 let place = &self.move_data.move_paths[mpi].place;283 let ty = place.ty(self.body, self.infcx.tcx).ty;284285 if self.infcx.param_env.caller_bounds().iter().any(|c| {286 c.as_trait_clause().is_some_and(|pred| {287 pred.skip_binder().self_ty() == ty && self.infcx.tcx.is_fn_trait(pred.def_id())288 })289 }) {290 // Suppress the next suggestion since we don't want to put more bounds onto291 // something that already has `Fn`-like bounds (or is a closure), so we can't292 // restrict anyways.293 } else {294 let copy_did = self.infcx.tcx.require_lang_item(LangItem::Copy, span);295 self.suggest_adding_bounds(&mut err, ty, copy_did, span);296 }297298 let opt_name = self.describe_place_with_options(299 place.as_ref(),300 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },301 );302 let note_msg = match opt_name {303 Some(name) => format!("`{name}`"),304 None => "value".to_owned(),305 };306 if needs_note {307 if let Some(local) = place.as_local() {308 let span = self.body.local_decls[local].source_info.span;309 if let Some(on_move_label) = on_move_label {310 err.span_label(span, on_move_label);311 } else {312 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Label {313 is_partial_move,314 ty,315 place: ¬e_msg,316 span,317 });318 }319 } else {320 err.subdiagnostic(crate::session_diagnostics::TypeNoCopy::Note {321 is_partial_move,322 ty,323 place: ¬e_msg,324 });325 };326 }327328 if let UseSpans::FnSelfUse {329 kind: CallKind::DerefCoercion { deref_target_span, deref_target_ty, .. },330 ..331 } = use_spans332 {333 err.note(format!(334 "{} occurs due to deref coercion to `{deref_target_ty}`",335 desired_action.as_noun(),336 ));337338 // Check first whether the source is accessible (issue #87060)339 if let Some(deref_target_span) = deref_target_span340 && self.infcx.tcx.sess.source_map().is_span_accessible(deref_target_span)341 {342 err.span_note(deref_target_span, "deref defined here");343 }344 }345346 self.buffer_move_error(move_out_indices, (used_place, err));347 }348 }349350 fn suggest_ref_or_clone(351 &self,352 mpi: MovePathIndex,353 err: &mut Diag<'infcx>,354 move_spans: UseSpans<'tcx>,355 moved_place: PlaceRef<'tcx>,356 has_suggest_reborrow: &mut bool,357 moved_or_invoked_closure: bool,358 ) {359 let move_span = match move_spans {360 UseSpans::ClosureUse { capture_kind_span, .. } => capture_kind_span,361 _ => move_spans.args_or_use(),362 };363 struct ExpressionFinder<'hir> {364 expr_span: Span,365 expr: Option<&'hir hir::Expr<'hir>>,366 pat: Option<&'hir hir::Pat<'hir>>,367 parent_pat: Option<&'hir hir::Pat<'hir>>,368 tcx: TyCtxt<'hir>,369 }370 impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {371 type NestedFilter = OnlyBodies;372373 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {374 self.tcx375 }376377 fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {378 if e.span == self.expr_span {379 self.expr = Some(e);380 }381 hir::intravisit::walk_expr(self, e);382 }383 fn visit_pat(&mut self, p: &'hir hir::Pat<'hir>) {384 if p.span == self.expr_span {385 self.pat = Some(p);386 }387 if let hir::PatKind::Binding(hir::BindingMode::NONE, _, i, sub) = p.kind {388 if i.span == self.expr_span || p.span == self.expr_span {389 self.pat = Some(p);390 }391 // Check if we are in a situation of `ident @ ident` where we want to suggest392 // `ref ident @ ref ident` or `ref ident @ Struct { ref ident }`.393 if let Some(subpat) = sub394 && self.pat.is_none()395 {396 self.visit_pat(subpat);397 if self.pat.is_some() {398 self.parent_pat = Some(p);399 }400 return;401 }402 }403 hir::intravisit::walk_pat(self, p);404 }405 }406 let tcx = self.infcx.tcx;407 if let Some(body) = tcx.hir_maybe_body_owned_by(self.mir_def_id()) {408 let expr = body.value;409 let place = &self.move_data.move_paths[mpi].place;410 let span = place.as_local().map(|local| self.body.local_decls[local].source_info.span);411 let mut finder = ExpressionFinder {412 expr_span: move_span,413 expr: None,414 pat: None,415 parent_pat: None,416 tcx,417 };418 finder.visit_expr(expr);419 if let Some(span) = span420 && let Some(expr) = finder.expr421 {422 for (_, expr) in tcx.hir_parent_iter(expr.hir_id) {423 if let hir::Node::Expr(expr) = expr {424 if expr.span.contains(span) {425 // If the let binding occurs within the same loop, then that426 // loop isn't relevant, like in the following, the outermost `loop`427 // doesn't play into `x` being moved.428 // ```429 // loop {430 // let x = String::new();431 // loop {432 // foo(x);433 // }434 // }435 // ```436 break;437 }438 if let hir::ExprKind::Loop(.., loop_span) = expr.kind {439 err.span_label(loop_span, "inside of this loop");440 }441 }442 }443 let typeck = self.infcx.tcx.typeck(self.mir_def_id());444 let parent = self.infcx.tcx.parent_hir_node(expr.hir_id);445 let (def_id, args, offset) = if let hir::Node::Expr(parent_expr) = parent446 && let hir::ExprKind::MethodCall(_, _, args, _) = parent_expr.kind447 {448 let def_id = typeck.type_dependent_def_id(parent_expr.hir_id);449 (def_id, args, 1)450 } else if let hir::Node::Expr(parent_expr) = parent451 && let hir::ExprKind::Call(call, args) = parent_expr.kind452 && let ty::FnDef(def_id, _) = typeck.node_type(call.hir_id).kind()453 {454 (Some(*def_id), args, 0)455 } else {456 (None, &[][..], 0)457 };458 let ty = place.ty(self.body, self.infcx.tcx).ty;459460 let mut can_suggest_clone = true;461 if let Some(def_id) = def_id462 && let Some(pos) = args.iter().position(|arg| arg.hir_id == expr.hir_id)463 {464 // The move occurred as one of the arguments to a function call. Is that465 // argument generic? `def_id` can't be a closure here, so using `fn_sig` is fine466 let arg_param = if self.infcx.tcx.def_kind(def_id).is_fn_like()467 && let sig =468 self.infcx.tcx.fn_sig(def_id).instantiate_identity().skip_binder()469 && let Some(arg_ty) = sig.inputs().get(pos + offset)470 && let ty::Param(arg_param) = arg_ty.kind()471 {472 Some(arg_param)473 } else {474 None475 };476477 // If the moved value is a mut reference, it is used in a478 // generic function and it's type is a generic param, it can be479 // reborrowed to avoid moving.480 // for example:481 // struct Y(u32);482 // x's type is '& mut Y' and it is used in `fn generic<T>(x: T) {}`.483 if let ty::Ref(_, _, hir::Mutability::Mut) = ty.kind()484 && arg_param.is_some()485 {486 *has_suggest_reborrow = true;487 self.suggest_reborrow(err, expr.span, moved_place);488 return;489 }490491 // If the moved place is used generically by the callee and a reference to it492 // would still satisfy any bounds on its type, suggest borrowing.493 if let Some(¶m) = arg_param494 && let hir::Node::Expr(call_expr) = parent495 && let Some(ref_mutability) = self.suggest_borrow_generic_arg(496 err,497 typeck,498 call_expr,499 def_id,500 param,501 moved_place,502 pos + offset,503 ty,504 expr.span,505 )506 {507 can_suggest_clone = ref_mutability.is_mut();508 } else if let Some(local_def_id) = def_id.as_local()509 && let node = self.infcx.tcx.hir_node_by_def_id(local_def_id)510 && let Some(fn_decl) = node.fn_decl()511 && let Some(ident) = node.ident()512 && let Some(arg) = fn_decl.inputs.get(pos + offset)513 {514 // If we can't suggest borrowing in the call, but the function definition515 // is local, instead offer changing the function to borrow that argument.516 let mut span: MultiSpan = arg.span.into();517 span.push_span_label(518 arg.span,519 "this parameter takes ownership of the value".to_string(),520 );521 let descr = match node.fn_kind() {522 Some(hir::intravisit::FnKind::ItemFn(..)) | None => "function",523 Some(hir::intravisit::FnKind::Method(..)) => "method",524 Some(hir::intravisit::FnKind::Closure) => "closure",525 };526 span.push_span_label(ident.span, format!("in this {descr}"));527 err.span_note(528 span,529 format!(530 "consider changing this parameter type in {descr} `{ident}` to \531 borrow instead if owning the value isn't necessary",532 ),533 );534 }535 }536 if let hir::Node::Expr(parent_expr) = parent537 && let hir::ExprKind::Call(call_expr, _) = parent_expr.kind538 && let hir::ExprKind::Path(qpath) = call_expr.kind539 && tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter)540 {541 // Do not suggest `.clone()` in a `for` loop, we already suggest borrowing.542 } else if let UseSpans::FnSelfUse { kind: CallKind::Normal { .. }, .. } = move_spans543 {544 // We already suggest cloning for these cases in `explain_captures`.545 } else if moved_or_invoked_closure {546 // Do not suggest `closure.clone()()`.547 } else if let UseSpans::ClosureUse {548 closure_kind:549 ClosureKind::Coroutine(CoroutineKind::Desugared(_, CoroutineSource::Block)),550 ..551 } = move_spans552 && can_suggest_clone553 {554 self.suggest_cloning(err, place.as_ref(), ty, expr, Some(move_spans));555 } else if self.suggest_hoisting_call_outside_loop(err, expr) && can_suggest_clone {556 // The place where the type moves would be misleading to suggest clone.557 // #121466558 self.suggest_cloning(err, place.as_ref(), ty, expr, Some(move_spans));559 }560 }561562 self.suggest_ref_for_dbg_args(expr, place, move_span, err);563564 // it's useless to suggest inserting `ref` when the span don't comes from local code565 if let Some(pat) = finder.pat566 && !move_span.is_dummy()567 && !self.infcx.tcx.sess.source_map().is_imported(move_span)568 {569 let mut sugg = vec![(pat.span.shrink_to_lo(), "ref ".to_string())];570 if let Some(pat) = finder.parent_pat {571 sugg.insert(0, (pat.span.shrink_to_lo(), "ref ".to_string()));572 }573 err.multipart_suggestion(574 "borrow this binding in the pattern to avoid moving the value",575 sugg,576 Applicability::MachineApplicable,577 );578 }579 }580 }581582 // for dbg!(x) which may take ownership, suggest dbg!(&x) instead583 fn suggest_ref_for_dbg_args(584 &self,585 body: &hir::Expr<'_>,586 place: &Place<'tcx>,587 move_span: Span,588 err: &mut Diag<'infcx>,589 ) {590 let var_info = self.body.var_debug_info.iter().find(|info| match info.value {591 VarDebugInfoContents::Place(ref p) => p == place,592 _ => false,593 });594 let Some(var_info) = var_info else { return };595 let arg_name = var_info.name;596 struct MatchArgFinder<'tcx> {597 tcx: TyCtxt<'tcx>,598 move_span: Span,599 arg_name: Symbol,600 match_arg_span: Option<Span> = None,601 }602 impl Visitor<'_> for MatchArgFinder<'_> {603 fn visit_expr(&mut self, e: &hir::Expr<'_>) {604 // dbg! is expanded into a match pattern, we need to find the right argument span605 if let hir::ExprKind::Match(scrutinee, ..) = &e.kind606 && let hir::ExprKind::Tup(args) = scrutinee.kind607 && e.span.macro_backtrace().any(|expn| {608 expn.macro_def_id.is_some_and(|macro_def_id| {609 self.tcx.is_diagnostic_item(sym::dbg_macro, macro_def_id)610 })611 })612 {613 for arg in args {614 if let hir::ExprKind::Path(hir::QPath::Resolved(615 _,616 path @ Path { segments: [seg], .. },617 )) = &arg.kind618 && seg.ident.name == self.arg_name619 && self.move_span.source_equal(arg.span)620 {621 self.match_arg_span = Some(path.span);622 return;623 }624 }625 }626 hir::intravisit::walk_expr(self, e);627 }628 }629630 let mut finder = MatchArgFinder { tcx: self.infcx.tcx, move_span, arg_name, .. };631 finder.visit_expr(body);632 if let Some(macro_arg_span) = finder.match_arg_span {633 err.span_suggestion_verbose(634 macro_arg_span.shrink_to_lo(),635 "consider borrowing instead of transferring ownership",636 "&",637 Applicability::MachineApplicable,638 );639 }640 }641642 pub(crate) fn suggest_reborrow(643 &self,644 err: &mut Diag<'infcx>,645 span: Span,646 moved_place: PlaceRef<'tcx>,647 ) {648 err.span_suggestion_verbose(649 span.shrink_to_lo(),650 format!(651 "consider creating a fresh reborrow of {} here",652 self.describe_place(moved_place)653 .map(|n| format!("`{n}`"))654 .unwrap_or_else(|| "the mutable reference".to_string()),655 ),656 "&mut *",657 Applicability::MachineApplicable,658 );659 }660661 /// If a place is used after being moved as an argument to a function, the function is generic662 /// in that argument, and a reference to the argument's type would still satisfy the function's663 /// bounds, suggest borrowing. This covers, e.g., borrowing an `impl Fn()` argument being passed664 /// in an `impl FnOnce()` position.665 /// Returns `Some(mutability)` when suggesting to borrow with mutability `mutability`, or `None`666 /// if no suggestion is made.667 fn suggest_borrow_generic_arg(668 &self,669 err: &mut Diag<'_>,670 typeck: &ty::TypeckResults<'tcx>,671 call_expr: &hir::Expr<'tcx>,672 callee_did: DefId,673 param: ty::ParamTy,674 moved_place: PlaceRef<'tcx>,675 moved_arg_pos: usize,676 moved_arg_ty: Ty<'tcx>,677 place_span: Span,678 ) -> Option<ty::Mutability> {679 let tcx = self.infcx.tcx;680 let sig = tcx.fn_sig(callee_did).instantiate_identity().skip_binder();681 let clauses = tcx.predicates_of(callee_did);682683 let generic_args = match call_expr.kind {684 // For method calls, generic arguments are attached to the call node.685 hir::ExprKind::MethodCall(..) => typeck.node_args_opt(call_expr.hir_id)?,686 // For normal calls, generic arguments are in the callee's type.687 // This diagnostic is only run for `FnDef` callees.688 hir::ExprKind::Call(callee, _)689 if let &ty::FnDef(_, args) = typeck.node_type(callee.hir_id).kind() =>690 {691 args692 }693 _ => return None,694 };695696 // First, is there at least one method on one of `param`'s trait bounds?697 // This keeps us from suggesting borrowing the argument to `mem::drop`, e.g.698 if !clauses.instantiate_identity(tcx).predicates.iter().any(|clause| {699 clause.as_trait_clause().is_some_and(|tc| {700 tc.self_ty().skip_binder().is_param(param.index)701 && tc.polarity() == ty::PredicatePolarity::Positive702 && supertrait_def_ids(tcx, tc.def_id())703 .flat_map(|trait_did| tcx.associated_items(trait_did).in_definition_order())704 .any(|item| item.is_method())705 })706 }) {707 return None;708 }709710 // Try borrowing a shared reference first, then mutably.711 if let Some(mutbl) = [ty::Mutability::Not, ty::Mutability::Mut].into_iter().find(|&mutbl| {712 let re = self.infcx.tcx.lifetimes.re_erased;713 let ref_ty = Ty::new_ref(self.infcx.tcx, re, moved_arg_ty, mutbl);714715 // Ensure that substituting `ref_ty` in the callee's signature doesn't break716 // other inputs or the return type.717 let new_args = tcx.mk_args_from_iter(generic_args.iter().enumerate().map(718 |(i, arg)| {719 if i == param.index as usize { ref_ty.into() } else { arg }720 },721 ));722 let can_subst = |ty: Ty<'tcx>| {723 // Normalize before comparing to see through type aliases and projections.724 let old_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, generic_args);725 let new_ty = ty::EarlyBinder::bind(ty).instantiate(tcx, new_args);726 if let Ok(old_ty) = tcx.try_normalize_erasing_regions(727 self.infcx.typing_env(self.infcx.param_env),728 old_ty,729 ) && let Ok(new_ty) = tcx.try_normalize_erasing_regions(730 self.infcx.typing_env(self.infcx.param_env),731 new_ty,732 ) {733 old_ty == new_ty734 } else {735 false736 }737 };738 if !can_subst(sig.output())739 || sig740 .inputs()741 .iter()742 .enumerate()743 .any(|(i, &input_ty)| i != moved_arg_pos && !can_subst(input_ty))744 {745 return false;746 }747748 // Test the callee's predicates, substituting in `ref_ty` for the moved argument type.749 clauses.instantiate(tcx, new_args).predicates.iter().all(|clause| {750 // Normalize before testing to see through type aliases and projections.751 let normalized = tcx752 .try_normalize_erasing_regions(753 self.infcx.typing_env(self.infcx.param_env),754 *clause,755 )756 .unwrap_or_else(|_| clause.skip_norm_wip());757 self.infcx.predicate_must_hold_modulo_regions(&Obligation::new(758 tcx,759 ObligationCause::dummy(),760 self.infcx.param_env,761 normalized,762 ))763 })764 }) {765 let place_desc = if let Some(desc) = self.describe_place(moved_place) {766 format!("`{desc}`")767 } else {768 "here".to_owned()769 };770 err.span_suggestion_verbose(771 place_span.shrink_to_lo(),772 format!("consider {}borrowing {place_desc}", mutbl.mutably_str()),773 mutbl.ref_prefix_str(),774 Applicability::MaybeIncorrect,775 );776 Some(mutbl)777 } else {778 None779 }780 }781782 fn report_use_of_uninitialized(783 &self,784 mpi: MovePathIndex,785 used_place: PlaceRef<'tcx>,786 moved_place: PlaceRef<'tcx>,787 desired_action: InitializationRequiringAction,788 span: Span,789 use_spans: UseSpans<'tcx>,790 ) -> Diag<'infcx> {791 // We need all statements in the body where the binding was assigned to later find all792 // the branching code paths where the binding *wasn't* assigned to.793 let inits = &self.move_data.init_path_map[mpi];794 let move_path = &self.move_data.move_paths[mpi];795 let decl_span = self.body.local_decls[move_path.place.local].source_info.span;796 let mut spans_set = FxIndexSet::default();797 for init_idx in inits {798 let init = &self.move_data.inits[*init_idx];799 let span = init.span(self.body);800 if !span.is_dummy() {801 spans_set.insert(span);802 }803 }804 let spans: Vec<_> = spans_set.into_iter().collect();805806 let (name, desc) = match self.describe_place_with_options(807 moved_place,808 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },809 ) {810 Some(name) => (format!("`{name}`"), format!("`{name}` ")),811 None => ("the variable".to_string(), String::new()),812 };813 let path = match self.describe_place_with_options(814 used_place,815 DescribePlaceOpt { including_downcast: true, including_tuple_field: true },816 ) {817 Some(name) => format!("`{name}`"),818 None => "value".to_string(),819 };820821 // We use the statements were the binding was initialized, and inspect the HIR to look822 // for the branching codepaths that aren't covered, to point at them.823 let tcx = self.infcx.tcx;824 let body = tcx.hir_body_owned_by(self.mir_def_id());825 let mut visitor = ConditionVisitor { tcx, spans, name, errors: vec![] };826 visitor.visit_body(&body);827 let spans = visitor.spans;828829 let mut show_assign_sugg = false;830 let isnt_initialized = if let InitializationRequiringAction::PartialAssignment831 | InitializationRequiringAction::Assignment = desired_action832 {833 // The same error is emitted for bindings that are *sometimes* initialized and the ones834 // that are *partially* initialized by assigning to a field of an uninitialized835 // binding. We differentiate between them for more accurate wording here.836 "isn't fully initialized"837 } else if !spans.iter().any(|i| {838 // We filter these to avoid misleading wording in cases like the following,839 // where `x` has an `init`, but it is in the same place we're looking at:840 // ```841 // let x;842 // x += 1;843 // ```844 !i.contains(span)845 // We filter these to avoid incorrect main message on `match-cfg-fake-edges.rs`846 && !visitor847 .errors848 .iter()849 .map(|(sp, _)| *sp)850 .any(|sp| span < sp && !sp.contains(span))851 }) {852 show_assign_sugg = true;853 "isn't initialized"854 } else {855 "is possibly-uninitialized"856 };857858 let used = desired_action.as_general_verb_in_past_tense();859 let mut err = struct_span_code_err!(860 self.dcx(),861 span,862 E0381,863 "{used} binding {desc}{isnt_initialized}"864 );865 use_spans.var_path_only_subdiag(&mut err, desired_action);866867 if let InitializationRequiringAction::PartialAssignment868 | InitializationRequiringAction::Assignment = desired_action869 {870 err.help(871 "partial initialization isn't supported, fully initialize the binding with a \872 default value and mutate it, or use `std::mem::MaybeUninit`",873 );874 }875 err.span_label(span, format!("{path} {used} here but it {isnt_initialized}"));876877 let mut shown = false;878 for (sp, label) in visitor.errors {879 if sp < span && !sp.overlaps(span) {880 // When we have a case like `match-cfg-fake-edges.rs`, we don't want to mention881 // match arms coming after the primary span because they aren't relevant:882 // ```883 // let x;884 // match y {885 // _ if { x = 2; true } => {}886 // _ if {887 // x; //~ ERROR888 // false889 // } => {}890 // _ => {} // We don't want to point to this.891 // };892 // ```893 err.span_label(sp, label);894 shown = true;895 }896 }897 if !shown {898 for sp in &spans {899 if *sp < span && !sp.overlaps(span) {900 err.span_label(*sp, "binding initialized here in some conditions");901 }902 }903 }904905 err.span_label(decl_span, "binding declared here but left uninitialized");906 if show_assign_sugg {907 struct LetVisitor {908 decl_span: Span,909 sugg: Option<(Span, bool)>,910 }911912 impl<'v> Visitor<'v> for LetVisitor {913 fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) {914 if self.sugg.is_some() {915 return;916 }917918 // FIXME: We make sure that this is a normal top-level binding,919 // but we could suggest `todo!()` for all uninitialized bindings in the pattern920 if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) =921 &ex.kind922 && let hir::PatKind::Binding(binding_mode, ..) = pat.kind923 && span.contains(self.decl_span)924 {925 // Insert after the whole binding pattern so suggestions stay valid for926 // bindings with `@` subpatterns like `ref mut x @ v`.927 let strip_ref = matches!(binding_mode.0, hir::ByRef::Yes(..));928 self.sugg =929 ty.map_or(Some((pat.span, strip_ref)), |ty| Some((ty.span, strip_ref)));930 }931 hir::intravisit::walk_stmt(self, ex);932 }933 }934935 let mut visitor = LetVisitor { decl_span, sugg: None };936 visitor.visit_body(&body);937 if let Some((span, strip_ref)) = visitor.sugg {938 self.suggest_assign_value(&mut err, moved_place, span, strip_ref);939 }940 }941 err942 }943944 fn suggest_assign_value(945 &self,946 err: &mut Diag<'_>,947 moved_place: PlaceRef<'tcx>,948 sugg_span: Span,949 strip_ref: bool,950 ) {951 let mut ty = moved_place.ty(self.body, self.infcx.tcx).ty;952 if strip_ref && let ty::Ref(_, inner, _) = ty.kind() {953 ty = *inner;954 }955 debug!("ty: {:?}, kind: {:?}", ty, ty.kind());956957 let Some(assign_value) = self.infcx.err_ctxt().ty_kind_suggestion(self.infcx.param_env, ty)958 else {959 return;960 };961962 err.span_suggestion_verbose(963 sugg_span.shrink_to_hi(),964 "consider assigning a value",965 format!(" = {assign_value}"),966 Applicability::MaybeIncorrect,967 );968 }969970 /// In a move error that occurs on a call within a loop, we try to identify cases where cloning971 /// the value would lead to a logic error. We infer these cases by seeing if the moved value is972 /// part of the logic to break the loop, either through an explicit `break` or if the expression973 /// is part of a `while let`.974 fn suggest_hoisting_call_outside_loop(&self, err: &mut Diag<'_>, expr: &hir::Expr<'_>) -> bool {975 let tcx = self.infcx.tcx;976 let mut can_suggest_clone = true;977978 // If the moved value is a locally declared binding, we'll look upwards on the expression979 // tree until the scope where it is defined, and no further, as suggesting to move the980 // expression beyond that point would be illogical.981 let local_hir_id = if let hir::ExprKind::Path(hir::QPath::Resolved(982 _,983 hir::Path { res: hir::def::Res::Local(local_hir_id), .. },984 )) = expr.kind985 {986 Some(local_hir_id)987 } else {988 // This case would be if the moved value comes from an argument binding, we'll just989 // look within the entire item, that's fine.990 None991 };992993 /// This will allow us to look for a specific `HirId`, in our case `local_hir_id` where the994 /// binding was declared, within any other expression. We'll use it to search for the995 /// binding declaration within every scope we inspect.996 struct Finder {997 hir_id: hir::HirId,998 }999 impl<'hir> Visitor<'hir> for Finder {1000 type Result = ControlFlow<()>;1001 fn visit_pat(&mut self, pat: &'hir hir::Pat<'hir>) -> Self::Result {1002 if pat.hir_id == self.hir_id {1003 return ControlFlow::Break(());1004 }1005 hir::intravisit::walk_pat(self, pat)1006 }1007 fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) -> Self::Result {1008 if ex.hir_id == self.hir_id {1009 return ControlFlow::Break(());1010 }1011 hir::intravisit::walk_expr(self, ex)1012 }1013 }1014 // The immediate HIR parent of the moved expression. We'll look for it to be a call.1015 let mut parent = None;1016 // The top-most loop where the moved expression could be moved to a new binding.1017 let mut outer_most_loop: Option<&hir::Expr<'_>> = None;1018 for (_, node) in tcx.hir_parent_iter(expr.hir_id) {1019 let e = match node {1020 hir::Node::Expr(e) => e,1021 hir::Node::LetStmt(hir::LetStmt { els: Some(els), .. }) => {1022 let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] };1023 finder.visit_block(els);1024 if !finder.found_breaks.is_empty() {1025 // Don't suggest clone as it could be will likely end in an infinite1026 // loop.1027 // let Some(_) = foo(non_copy.clone()) else { break; }1028 // --- ^^^^^^^^ -----1029 can_suggest_clone = false;1030 }1031 continue;1032 }1033 _ => continue,1034 };1035 if let Some(&hir_id) = local_hir_id {1036 if (Finder { hir_id }).visit_expr(e).is_break() {1037 // The current scope includes the declaration of the binding we're accessing, we1038 // can't look up any further for loops.1039 break;1040 }1041 }1042 if parent.is_none() {1043 parent = Some(e);1044 }1045 match e.kind {1046 hir::ExprKind::Let(_) => {1047 match tcx.parent_hir_node(e.hir_id) {1048 hir::Node::Expr(hir::Expr {1049 kind: hir::ExprKind::If(cond, ..), ..1050 }) => {1051 if (Finder { hir_id: expr.hir_id }).visit_expr(cond).is_break() {1052 // The expression where the move error happened is in a `while let`1053 // condition Don't suggest clone as it will likely end in an1054 // infinite loop.1055 // while let Some(_) = foo(non_copy.clone()) { }1056 // --------- ^^^^^^^^1057 can_suggest_clone = false;1058 }1059 }1060 _ => {}1061 }1062 }1063 hir::ExprKind::Loop(..) => {1064 outer_most_loop = Some(e);1065 }1066 _ => {}1067 }1068 }1069 let loop_count: usize = tcx1070 .hir_parent_iter(expr.hir_id)1071 .map(|(_, node)| match node {1072 hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Loop(..), .. }) => 1,1073 _ => 0,1074 })1075 .sum();10761077 let sm = tcx.sess.source_map();1078 if let Some(in_loop) = outer_most_loop {1079 let mut finder = BreakFinder { found_breaks: vec![], found_continues: vec![] };1080 finder.visit_expr(in_loop);1081 // All of the spans for `break` and `continue` expressions.1082 let spans = finder1083 .found_breaks1084 .iter()1085 .chain(finder.found_continues.iter())1086 .map(|(_, span)| *span)1087 .filter(|span| {1088 !matches!(1089 span.desugaring_kind(),1090 Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop)1091 )1092 })1093 .collect::<Vec<Span>>();1094 // All of the spans for the loops above the expression with the move error.1095 let loop_spans: Vec<_> = tcx1096 .hir_parent_iter(expr.hir_id)1097 .filter_map(|(_, node)| match node {1098 hir::Node::Expr(hir::Expr { span, kind: hir::ExprKind::Loop(..), .. }) => {1099 Some(*span)1100 }1101 _ => None,1102 })1103 .collect();1104 // It is possible that a user written `break` or `continue` is in the wrong place. We1105 // point them out at the user for them to make a determination. (#92531)1106 if !spans.is_empty() && loop_count > 1 {1107 // Getting fancy: if the spans of the loops *do not* overlap, we only use the line1108 // number when referring to them. If there *are* overlaps (multiple loops on the1109 // same line) then we use the more verbose span output (`file.rs:col:ll`).1110 let mut lines: Vec<_> =1111 loop_spans.iter().map(|sp| sm.lookup_char_pos(sp.lo()).line).collect();1112 lines.sort();1113 lines.dedup();1114 let fmt_span = |span: Span| {1115 if lines.len() == loop_spans.len() {1116 format!("line {}", sm.lookup_char_pos(span.lo()).line)1117 } else {1118 sm.span_to_diagnostic_string(span)1119 }1120 };1121 let mut spans: MultiSpan = spans.into();1122 // Point at all the `continue`s and explicit `break`s in the relevant loops.1123 for (desc, elements) in [1124 ("`break` exits", &finder.found_breaks),1125 ("`continue` advances", &finder.found_continues),1126 ] {1127 for (destination, sp) in elements {1128 if let Ok(hir_id) = destination.target_id1129 && let hir::Node::Expr(expr) = tcx.hir_node(hir_id)1130 && !matches!(1131 sp.desugaring_kind(),1132 Some(DesugaringKind::ForLoop | DesugaringKind::WhileLoop)1133 )1134 {1135 spans.push_span_label(1136 *sp,1137 format!("this {desc} the loop at {}", fmt_span(expr.span)),1138 );1139 }1140 }1141 }1142 // Point at all the loops that are between this move and the parent item.1143 for span in loop_spans {1144 spans.push_span_label(sm.guess_head_span(span), "");1145 }11461147 // note: verify that your loop breaking logic is correct1148 // --> $DIR/nested-loop-moved-value-wrong-continue.rs:41:171149 // |1150 // 28 | for foo in foos {1151 // | ---------------1152 // ...1153 // 33 | for bar in &bars {1154 // | ----------------1155 // ...1156 // 41 | continue;1157 // | ^^^^^^^^ this `continue` advances the loop at line 331158 err.span_note(spans, "verify that your loop breaking logic is correct");1159 }1160 if let Some(parent) = parent1161 && let hir::ExprKind::MethodCall(..) | hir::ExprKind::Call(..) = parent.kind1162 {1163 // FIXME: We could check that the call's *parent* takes `&mut val` to make the1164 // suggestion more targeted to the `mk_iter(val).next()` case. Maybe do that only to1165 // check for whether to suggest `let value` or `let mut value`.11661167 let span = in_loop.span;1168 if !finder.found_breaks.is_empty()1169 && let Ok(value) = sm.span_to_snippet(parent.span)1170 {1171 // We know with high certainty that this move would affect the early return of a1172 // loop, so we suggest moving the expression with the move out of the loop.1173 let indent = if let Some(indent) = sm.indentation_before(span) {1174 format!("\n{indent}")1175 } else {1176 " ".to_string()1177 };1178 err.multipart_suggestion(1179 "consider moving the expression out of the loop so it is only moved once",1180 vec![1181 (span.shrink_to_lo(), format!("let mut value = {value};{indent}")),1182 (parent.span, "value".to_string()),1183 ],1184 Applicability::MaybeIncorrect,1185 );1186 }1187 }1188 }1189 can_suggest_clone1190 }11911192 /// We have `S { foo: val, ..base }`, and we suggest instead writing1193 /// `S { foo: val, bar: base.bar.clone(), .. }` when valid.1194 fn suggest_cloning_on_functional_record_update(1195 &self,1196 err: &mut Diag<'_>,1197 ty: Ty<'tcx>,1198 expr: &hir::Expr<'_>,1199 ) {1200 let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());1201 let hir::ExprKind::Struct(struct_qpath, fields, hir::StructTailExpr::Base(base)) =1202 expr.kind1203 else {1204 return;1205 };1206 let hir::QPath::Resolved(_, path) = struct_qpath else { return };1207 let hir::def::Res::Def(_, def_id) = path.res else { return };1208 let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id) else { return };1209 let ty::Adt(def, args) = expr_ty.kind() else { return };1210 let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = base.kind else { return };1211 let (hir::def::Res::Local(_)1212 | hir::def::Res::Def(1213 DefKind::Const { .. }1214 | DefKind::ConstParam1215 | DefKind::Static { .. }1216 | DefKind::AssocConst { .. },1217 _,1218 )) = path.res1219 else {1220 return;1221 };1222 let Ok(base_str) = self.infcx.tcx.sess.source_map().span_to_snippet(base.span) else {1223 return;1224 };12251226 // 1. look for the fields of type `ty`.1227 // 2. check if they are clone and add them to suggestion1228 // 3. check if there are any values left to `..` and remove it if not1229 // 4. emit suggestion to clone the field directly as `bar: base.bar.clone()`12301231 let mut final_field_count = fields.len();1232 let Some(variant) = def.variants().iter().find(|variant| variant.def_id == def_id) else {1233 // When we have an enum, look for the variant that corresponds to the variant the user1234 // wrote.1235 return;1236 };1237 let mut sugg = vec![];1238 for field in &variant.fields {1239 // In practice unless there are more than one field with the same type, we'll be1240 // suggesting a single field at a type, because we don't aggregate multiple borrow1241 // checker errors involving the functional record update syntax into a single one.1242 let field_ty = field.ty(self.infcx.tcx, args);1243 let ident = field.ident(self.infcx.tcx);1244 if field_ty == ty && fields.iter().all(|field| field.ident.name != ident.name) {1245 // Suggest adding field and cloning it.1246 sugg.push(format!("{ident}: {base_str}.{ident}.clone()"));1247 final_field_count += 1;1248 }1249 }1250 let (span, sugg) = match fields {1251 [.., last] => (1252 if final_field_count == variant.fields.len() {1253 // We'll remove the `..base` as there aren't any fields left.1254 last.span.shrink_to_hi().with_hi(base.span.hi())1255 } else {1256 last.span.shrink_to_hi()1257 },1258 format!(", {}", sugg.join(", ")),1259 ),1260 // Account for no fields in suggestion span.1261 [] => (1262 expr.span.with_lo(struct_qpath.span().hi()),1263 if final_field_count == variant.fields.len() {1264 // We'll remove the `..base` as there aren't any fields left.1265 format!(" {{ {} }}", sugg.join(", "))1266 } else {1267 format!(" {{ {}, ..{base_str} }}", sugg.join(", "))1268 },1269 ),1270 };1271 let prefix = if !self.implements_clone(ty) {1272 let msg = format!("`{ty}` doesn't implement `Copy` or `Clone`");1273 if let ty::Adt(def, _) = ty.kind() {1274 err.span_note(self.infcx.tcx.def_span(def.did()), msg);1275 } else {1276 err.note(msg);1277 }1278 format!("if `{ty}` implemented `Clone`, you could ")1279 } else {1280 String::new()1281 };1282 let msg = format!(1283 "{prefix}clone the value from the field instead of using the functional record update \1284 syntax",1285 );1286 err.span_suggestion_verbose(span, msg, sugg, Applicability::MachineApplicable);1287 }12881289 pub(crate) fn suggest_cloning(1290 &self,1291 err: &mut Diag<'_>,1292 place: PlaceRef<'tcx>,1293 ty: Ty<'tcx>,1294 expr: &'tcx hir::Expr<'tcx>,1295 use_spans: Option<UseSpans<'tcx>>,1296 ) {1297 if let hir::ExprKind::Struct(_, _, hir::StructTailExpr::Base(_)) = expr.kind {1298 // We have `S { foo: val, ..base }`. In `check_aggregate_rvalue` we have a single1299 // `Location` that covers both the `S { ... }` literal, all of its fields and the1300 // `base`. If the move happens because of `S { foo: val, bar: base.bar }` the `expr`1301 // will already be correct. Instead, we see if we can suggest writing.1302 self.suggest_cloning_on_functional_record_update(err, ty, expr);1303 return;1304 }13051306 if self.implements_clone(ty) {1307 if self.in_move_closure(expr) {1308 if let Some(name) = self.describe_place(place) {1309 self.suggest_clone_of_captured_var_in_move_closure(err, &name, use_spans);1310 }1311 } else {1312 self.suggest_cloning_inner(err, ty, expr);1313 }1314 } else if let ty::Adt(def, args) = ty.kind()1315 && let Some(local_did) = def.did().as_local()1316 && def.variants().iter().all(|variant| {1317 variant1318 .fields1319 .iter()1320 .all(|field| self.implements_clone(field.ty(self.infcx.tcx, args)))1321 })1322 {1323 let ty_span = self.infcx.tcx.def_span(def.did());1324 let mut span: MultiSpan = ty_span.into();1325 let mut derive_clone = false;1326 self.infcx.tcx.for_each_relevant_impl(1327 self.infcx.tcx.lang_items().clone_trait().unwrap(),1328 ty,1329 |def_id| {1330 if self.infcx.tcx.is_automatically_derived(def_id) {1331 derive_clone = true;1332 span.push_span_label(1333 self.infcx.tcx.def_span(def_id),1334 "derived `Clone` adds implicit bounds on type parameters",1335 );1336 if let Some(generics) = self.infcx.tcx.hir_get_generics(local_did) {1337 for param in generics.params {1338 if let hir::GenericParamKind::Type { .. } = param.kind {1339 span.push_span_label(1340 param.span,1341 format!(1342 "introduces an implicit `{}: Clone` bound",1343 param.name.ident()1344 ),1345 );1346 }1347 }1348 }1349 }1350 },1351 );1352 let msg = if !derive_clone {1353 span.push_span_label(1354 ty_span,1355 format!(1356 "consider {}implementing `Clone` for this type",1357 if derive_clone { "manually " } else { "" }1358 ),1359 );1360 format!("if `{ty}` implemented `Clone`, you could clone the value")1361 } else {1362 format!("if all bounds were met, you could clone the value")1363 };1364 span.push_span_label(expr.span, "you could clone this value");1365 err.span_note(span, msg);1366 if derive_clone {1367 err.help("consider manually implementing `Clone` to avoid undesired bounds");1368 }1369 } else if let ty::Param(param) = ty.kind()1370 && let Some(_clone_trait_def) = self.infcx.tcx.lang_items().clone_trait()1371 && let generics = self.infcx.tcx.generics_of(self.mir_def_id())1372 && let generic_param = generics.type_param(*param, self.infcx.tcx)1373 && let param_span = self.infcx.tcx.def_span(generic_param.def_id)1374 && if let Some(UseSpans::FnSelfUse { kind, .. }) = use_spans1375 && let CallKind::FnCall { fn_trait_id, self_ty } = kind1376 && let ty::Param(_) = self_ty.kind()1377 && ty == self_ty1378 && self.infcx.tcx.fn_trait_kind_from_def_id(fn_trait_id).is_some()1379 {1380 // Do not suggest `F: FnOnce() + Clone`.1381 false1382 } else {1383 true1384 }1385 {1386 let mut span: MultiSpan = param_span.into();1387 span.push_span_label(1388 param_span,1389 "consider constraining this type parameter with `Clone`",1390 );1391 span.push_span_label(expr.span, "you could clone this value");1392 err.span_help(1393 span,1394 format!("if `{ty}` implemented `Clone`, you could clone the value"),1395 );1396 } else if let ty::Adt(_, _) = ty.kind()1397 && let Some(clone_trait) = self.infcx.tcx.lang_items().clone_trait()1398 {1399 // For cases like `Option<NonClone>`, where `Option<T>: Clone` if `T: Clone`, we point1400 // at the types that should be `Clone`.1401 let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);1402 let cause = ObligationCause::misc(expr.span, self.mir_def_id());1403 ocx.register_bound(cause, self.infcx.param_env, ty, clone_trait);1404 let errors = ocx.evaluate_obligations_error_on_ambiguity();1405 if errors.iter().all(|error| {1406 match error.obligation.predicate.as_clause().and_then(|c| c.as_trait_clause()) {1407 Some(clause) => match clause.self_ty().skip_binder().kind() {1408 ty::Adt(def, _) => def.did().is_local() && clause.def_id() == clone_trait,1409 _ => false,1410 },1411 None => false,1412 }1413 }) {1414 let mut type_spans = vec![];1415 let mut types = FxIndexSet::default();1416 for clause in errors1417 .iter()1418 .filter_map(|e| e.obligation.predicate.as_clause())1419 .filter_map(|c| c.as_trait_clause())1420 {1421 let ty::Adt(def, _) = clause.self_ty().skip_binder().kind() else { continue };1422 type_spans.push(self.infcx.tcx.def_span(def.did()));1423 types.insert(1424 self.infcx1425 .tcx1426 .short_string(clause.self_ty().skip_binder(), &mut err.long_ty_path()),1427 );1428 }1429 let mut span: MultiSpan = type_spans.clone().into();1430 for sp in type_spans {1431 span.push_span_label(sp, "consider implementing `Clone` for this type");1432 }1433 span.push_span_label(expr.span, "you could clone this value");1434 let types: Vec<_> = types.into_iter().collect();1435 let msg = match &types[..] {1436 [only] => format!("`{only}`"),1437 [head @ .., last] => format!(1438 "{} and `{last}`",1439 head.iter().map(|t| format!("`{t}`")).collect::<Vec<_>>().join(", ")1440 ),1441 [] => unreachable!(),1442 };1443 err.span_note(1444 span,1445 format!("if {msg} implemented `Clone`, you could clone the value"),1446 );1447 }1448 }1449 }14501451 pub(crate) fn implements_clone(&self, ty: Ty<'tcx>) -> bool {1452 let Some(clone_trait_def) = self.infcx.tcx.lang_items().clone_trait() else { return false };1453 self.infcx1454 .type_implements_trait(clone_trait_def, [ty], self.infcx.param_env)1455 .must_apply_modulo_regions()1456 }14571458 /// Given an expression, check if it is a method call `foo.clone()`, where `foo` and1459 /// `foo.clone()` both have the same type, returning the span for `.clone()` if so.1460 pub(crate) fn clone_on_reference(&self, expr: &hir::Expr<'_>) -> Option<Span> {1461 let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());1462 if let hir::ExprKind::MethodCall(segment, rcvr, args, span) = expr.kind1463 && let Some(expr_ty) = typeck_results.node_type_opt(expr.hir_id)1464 && let Some(rcvr_ty) = typeck_results.node_type_opt(rcvr.hir_id)1465 && rcvr_ty == expr_ty1466 && segment.ident.name == sym::clone1467 && args.is_empty()1468 {1469 Some(span)1470 } else {1471 None1472 }1473 }14741475 fn in_move_closure(&self, expr: &hir::Expr<'_>) -> bool {1476 for (_, node) in self.infcx.tcx.hir_parent_iter(expr.hir_id) {1477 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) = node1478 && let hir::CaptureBy::Value { .. } = closure.capture_clause1479 {1480 // `move || x.clone()` will not work. FIXME: suggest `let y = x.clone(); move || y`1481 return true;1482 }1483 }1484 false1485 }14861487 fn suggest_cloning_inner(1488 &self,1489 err: &mut Diag<'_>,1490 ty: Ty<'tcx>,1491 expr: &hir::Expr<'_>,1492 ) -> bool {1493 let tcx = self.infcx.tcx;14941495 // Don't suggest `.clone()` in a derive macro expansion.1496 if let ExpnKind::Macro(MacroKind::Derive, _) = self.body.span.ctxt().outer_expn_data().kind1497 {1498 return false;1499 }1500 if let Some(_) = self.clone_on_reference(expr) {1501 // Avoid redundant clone suggestion already suggested in `explain_captures`.1502 // See `tests/ui/moves/needs-clone-through-deref.rs`1503 return false;1504 }1505 // We don't want to suggest `.clone()` in a move closure, since the value has already been1506 // captured.1507 if self.in_move_closure(expr) {1508 return false;1509 }1510 // We also don't want to suggest cloning a closure itself, since the value has already been1511 // captured.1512 if let hir::ExprKind::Closure(_) = expr.kind {1513 return false;1514 }1515 // Try to find predicates on *generic params* that would allow copying `ty`1516 let mut suggestion =1517 if let Some(symbol) = tcx.hir_maybe_get_struct_pattern_shorthand_field(expr) {1518 format!(": {symbol}.clone()")1519 } else {1520 ".clone()".to_owned()1521 };1522 let mut sugg = Vec::with_capacity(2);1523 let mut inner_expr = expr;1524 let mut is_raw_ptr = false;1525 let typeck_result = self.infcx.tcx.typeck(self.mir_def_id());1526 // Remove uses of `&` and `*` when suggesting `.clone()`.1527 while let hir::ExprKind::AddrOf(.., inner) | hir::ExprKind::Unary(hir::UnOp::Deref, inner) =1528 &inner_expr.kind1529 {1530 if let hir::ExprKind::AddrOf(_, hir::Mutability::Mut, _) = inner_expr.kind {1531 // We assume that `&mut` refs are desired for their side-effects, so cloning the1532 // value wouldn't do what the user wanted.1533 return false;1534 }1535 inner_expr = inner;1536 if let Some(inner_type) = typeck_result.node_type_opt(inner.hir_id) {1537 if matches!(inner_type.kind(), ty::RawPtr(..)) {1538 is_raw_ptr = true;1539 break;1540 }1541 }1542 }1543 // Cloning the raw pointer doesn't make sense in some cases and would cause a type mismatch1544 // error. (see #126863)1545 if inner_expr.span.lo() != expr.span.lo() && !is_raw_ptr {1546 // Remove "(*" or "(&"1547 sugg.push((expr.span.with_hi(inner_expr.span.lo()), String::new()));1548 }1549 // Check whether `expr` is surrounded by parentheses or not.1550 let span = if inner_expr.span.hi() != expr.span.hi() {1551 // Account for `(*x)` to suggest `x.clone()`.1552 if is_raw_ptr {1553 expr.span.shrink_to_hi()1554 } else {1555 // Remove the close parenthesis ")"1556 expr.span.with_lo(inner_expr.span.hi())1557 }1558 } else {1559 if is_raw_ptr {1560 sugg.push((expr.span.shrink_to_lo(), "(".to_string()));1561 suggestion = ").clone()".to_string();1562 }1563 expr.span.shrink_to_hi()1564 };1565 sugg.push((span, suggestion));1566 let msg = if let ty::Adt(def, _) = ty.kind()1567 && [tcx.get_diagnostic_item(sym::Arc), tcx.get_diagnostic_item(sym::Rc)]1568 .contains(&Some(def.did()))1569 {1570 "clone the value to increment its reference count"1571 } else {1572 "consider cloning the value if the performance cost is acceptable"1573 };1574 err.multipart_suggestion(msg, sugg, Applicability::MachineApplicable);1575 true1576 }15771578 fn suggest_adding_bounds(&self, err: &mut Diag<'_>, ty: Ty<'tcx>, def_id: DefId, span: Span) {1579 let tcx = self.infcx.tcx;1580 let generics = tcx.generics_of(self.mir_def_id());15811582 let Some(hir_generics) =1583 tcx.hir_get_generics(tcx.typeck_root_def_id_local(self.mir_def_id()))1584 else {1585 return;1586 };1587 // Try to find predicates on *generic params* that would allow copying `ty`1588 let ocx = ObligationCtxt::new_with_diagnostics(self.infcx);1589 let cause = ObligationCause::misc(span, self.mir_def_id());15901591 ocx.register_bound(cause, self.infcx.param_env, ty, def_id);1592 let errors = ocx.evaluate_obligations_error_on_ambiguity();15931594 // Only emit suggestion if all required predicates are on generic1595 let predicates: Result<Vec<_>, _> = errors1596 .into_iter()1597 .map(|err| match err.obligation.predicate.kind().skip_binder() {1598 PredicateKind::Clause(ty::ClauseKind::Trait(predicate)) => {1599 match *predicate.self_ty().kind() {1600 ty::Param(param_ty) => Ok((1601 generics.type_param(param_ty, tcx),1602 predicate.trait_ref.print_trait_sugared().to_string(),1603 Some(predicate.trait_ref.def_id),1604 )),1605 _ => Err(()),1606 }1607 }1608 _ => Err(()),1609 })1610 .collect();16111612 if let Ok(predicates) = predicates {1613 suggest_constraining_type_params(1614 tcx,1615 hir_generics,1616 err,1617 predicates.iter().map(|(param, constraint, def_id)| {1618 (param.name.as_str(), &**constraint, *def_id)1619 }),1620 None,1621 );1622 }1623 }16241625 pub(crate) fn report_move_out_while_borrowed(1626 &mut self,1627 location: Location,1628 (place, span): (Place<'tcx>, Span),1629 borrow: &BorrowData<'tcx>,1630 ) {1631 debug!(1632 "report_move_out_while_borrowed: location={:?} place={:?} span={:?} borrow={:?}",1633 location, place, span, borrow1634 );1635 let value_msg = self.describe_any_place(place.as_ref());1636 let borrow_msg = self.describe_any_place(borrow.borrowed_place.as_ref());16371638 let borrow_spans = self.retrieve_borrow_spans(borrow);1639 let borrow_span = borrow_spans.args_or_use();16401641 let move_spans = self.move_spans(place.as_ref(), location);1642 let span = move_spans.args_or_use();16431644 let mut err = self.cannot_move_when_borrowed(1645 span,1646 borrow_span,1647 &self.describe_any_place(place.as_ref()),1648 &borrow_msg,1649 &value_msg,1650 );1651 self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);16521653 borrow_spans.var_path_only_subdiag(&mut err, crate::InitializationRequiringAction::Borrow);16541655 move_spans.var_subdiag(&mut err, None, |kind, var_span| {1656 use crate::session_diagnostics::CaptureVarCause::*;1657 match kind {1658 hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },1659 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {1660 MoveUseInClosure { var_span }1661 }1662 }1663 });16641665 self.explain_why_borrow_contains_point(location, borrow, None)1666 .add_explanation_to_diagnostic(&self, &mut err, "", Some(borrow_span), None);1667 self.suggest_copy_for_type_in_cloned_ref(&mut err, place);1668 let typeck_results = self.infcx.tcx.typeck(self.mir_def_id());1669 if let Some(expr) = self.find_expr(borrow_span) {1670 // This is a borrow span, so we want to suggest cloning the referent.1671 if let hir::ExprKind::AddrOf(_, _, borrowed_expr) = expr.kind1672 && let Some(ty) = typeck_results.expr_ty_opt(borrowed_expr)1673 {1674 self.suggest_cloning(&mut err, place.as_ref(), ty, borrowed_expr, Some(move_spans));1675 } else if typeck_results.expr_adjustments(expr).first().is_some_and(|adj| {1676 matches!(1677 adj.kind,1678 ty::adjustment::Adjust::Borrow(ty::adjustment::AutoBorrow::Ref(1679 ty::adjustment::AutoBorrowMutability::Not1680 | ty::adjustment::AutoBorrowMutability::Mut {1681 allow_two_phase_borrow: ty::adjustment::AllowTwoPhase::No1682 }1683 ))1684 )1685 }) && let Some(ty) = typeck_results.expr_ty_opt(expr)1686 {1687 self.suggest_cloning(&mut err, place.as_ref(), ty, expr, Some(move_spans));1688 }1689 }1690 self.buffer_error(err);1691 }16921693 pub(crate) fn report_use_while_mutably_borrowed(1694 &self,1695 location: Location,1696 (place, _span): (Place<'tcx>, Span),1697 borrow: &BorrowData<'tcx>,1698 ) -> Diag<'infcx> {1699 let borrow_spans = self.retrieve_borrow_spans(borrow);1700 let borrow_span = borrow_spans.args_or_use();17011702 // Conflicting borrows are reported separately, so only check for move1703 // captures.1704 let use_spans = self.move_spans(place.as_ref(), location);1705 let span = use_spans.var_or_use();17061707 // If the attempted use is in a closure then we do not care about the path span of the1708 // place we are currently trying to use we call `var_span_label` on `borrow_spans` to1709 // annotate if the existing borrow was in a closure.1710 let mut err = self.cannot_use_when_mutably_borrowed(1711 span,1712 &self.describe_any_place(place.as_ref()),1713 borrow_span,1714 &self.describe_any_place(borrow.borrowed_place.as_ref()),1715 );1716 self.note_due_to_edition_2024_opaque_capture_rules(borrow, &mut err);17171718 borrow_spans.var_subdiag(&mut err, Some(borrow.kind), |kind, var_span| {1719 use crate::session_diagnostics::CaptureVarCause::*;1720 let place = &borrow.borrowed_place;1721 let desc_place = self.describe_any_place(place.as_ref());1722 match kind {1723 hir::ClosureKind::Coroutine(_) => {1724 BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }1725 }1726 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {1727 BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }1728 }1729 }1730 });17311732 self.explain_why_borrow_contains_point(location, borrow, None)1733 .add_explanation_to_diagnostic(&self, &mut err, "", None, None);1734 err1735 }17361737 pub(crate) fn report_conflicting_borrow(1738 &self,1739 location: Location,1740 (place, span): (Place<'tcx>, Span),1741 gen_borrow_kind: BorrowKind,1742 issued_borrow: &BorrowData<'tcx>,1743 ) -> Diag<'infcx> {1744 let issued_spans = self.retrieve_borrow_spans(issued_borrow);1745 let issued_span = issued_spans.args_or_use();17461747 let borrow_spans = self.borrow_spans(span, location);1748 let span = borrow_spans.args_or_use();17491750 let container_name = if issued_spans.for_coroutine() || borrow_spans.for_coroutine() {1751 "coroutine"1752 } else {1753 "closure"1754 };17551756 let (desc_place, msg_place, msg_borrow, union_type_name) =1757 self.describe_place_for_conflicting_borrow(place, issued_borrow.borrowed_place);17581759 let explanation = self.explain_why_borrow_contains_point(location, issued_borrow, None);1760 let second_borrow_desc = if explanation.is_explained() { "second " } else { "" };17611762 // FIXME: supply non-"" `opt_via` when appropriate1763 let first_borrow_desc;1764 let mut err = match (gen_borrow_kind, issued_borrow.kind) {1765 (1766 BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),1767 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },1768 ) => {1769 first_borrow_desc = "mutable ";1770 let mut err = self.cannot_reborrow_already_borrowed(1771 span,1772 &desc_place,1773 &msg_place,1774 "immutable",1775 issued_span,1776 "it",1777 "mutable",1778 &msg_borrow,1779 None,1780 );1781 self.suggest_slice_method_if_applicable(1782 &mut err,1783 place,1784 issued_borrow.borrowed_place,1785 span,1786 issued_span,1787 );1788 err1789 }1790 (1791 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },1792 BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),1793 ) => {1794 first_borrow_desc = "immutable ";1795 let mut err = self.cannot_reborrow_already_borrowed(1796 span,1797 &desc_place,1798 &msg_place,1799 "mutable",1800 issued_span,1801 "it",1802 "immutable",1803 &msg_borrow,1804 None,1805 );1806 self.suggest_slice_method_if_applicable(1807 &mut err,1808 place,1809 issued_borrow.borrowed_place,1810 span,1811 issued_span,1812 );1813 self.suggest_binding_for_closure_capture_self(&mut err, &issued_spans);1814 self.suggest_using_closure_argument_instead_of_capture(1815 &mut err,1816 issued_borrow.borrowed_place,1817 &issued_spans,1818 );1819 err1820 }18211822 (1823 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },1824 BorrowKind::Mut { kind: MutBorrowKind::Default | MutBorrowKind::TwoPhaseBorrow },1825 ) => {1826 first_borrow_desc = "first ";1827 let mut err = self.cannot_mutably_borrow_multiply(1828 span,1829 &desc_place,1830 &msg_place,1831 issued_span,1832 &msg_borrow,1833 None,1834 );1835 self.suggest_slice_method_if_applicable(1836 &mut err,1837 place,1838 issued_borrow.borrowed_place,1839 span,1840 issued_span,1841 );1842 self.suggest_using_closure_argument_instead_of_capture(1843 &mut err,1844 issued_borrow.borrowed_place,1845 &issued_spans,1846 );1847 self.explain_iterator_advancement_in_for_loop_if_applicable(1848 &mut err,1849 span,1850 &issued_spans,1851 );1852 err1853 }18541855 (1856 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },1857 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },1858 ) => {1859 first_borrow_desc = "first ";1860 self.cannot_uniquely_borrow_by_two_closures(span, &desc_place, issued_span, None)1861 }18621863 (BorrowKind::Mut { .. }, BorrowKind::Fake(FakeBorrowKind::Shallow)) => {1864 if let Some(immutable_section_description) =1865 self.classify_immutable_section(issued_borrow.assigned_place)1866 {1867 let mut err = self.cannot_mutate_in_immutable_section(1868 span,1869 issued_span,1870 &desc_place,1871 immutable_section_description,1872 "mutably borrow",1873 );1874 borrow_spans.var_subdiag(1875 &mut err,1876 Some(BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }),1877 |kind, var_span| {1878 use crate::session_diagnostics::CaptureVarCause::*;1879 match kind {1880 hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {1881 place: desc_place,1882 var_span,1883 is_single_var: true,1884 },1885 hir::ClosureKind::Closure1886 | hir::ClosureKind::CoroutineClosure(_) => BorrowUsePlaceClosure {1887 place: desc_place,1888 var_span,1889 is_single_var: true,1890 },1891 }1892 },1893 );1894 return err;1895 } else {1896 first_borrow_desc = "immutable ";1897 self.cannot_reborrow_already_borrowed(1898 span,1899 &desc_place,1900 &msg_place,1901 "mutable",1902 issued_span,1903 "it",1904 "immutable",1905 &msg_borrow,1906 None,1907 )1908 }1909 }19101911 (BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }, _) => {1912 first_borrow_desc = "first ";1913 self.cannot_uniquely_borrow_by_one_closure(1914 span,1915 container_name,1916 &desc_place,1917 "",1918 issued_span,1919 "it",1920 "",1921 None,1922 )1923 }19241925 (1926 BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),1927 BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture },1928 ) => {1929 first_borrow_desc = "first ";1930 self.cannot_reborrow_already_uniquely_borrowed(1931 span,1932 container_name,1933 &desc_place,1934 "",1935 "immutable",1936 issued_span,1937 "",1938 None,1939 second_borrow_desc,1940 )1941 }19421943 (BorrowKind::Mut { .. }, BorrowKind::Mut { kind: MutBorrowKind::ClosureCapture }) => {1944 first_borrow_desc = "first ";1945 self.cannot_reborrow_already_uniquely_borrowed(1946 span,1947 container_name,1948 &desc_place,1949 "",1950 "mutable",1951 issued_span,1952 "",1953 None,1954 second_borrow_desc,1955 )1956 }19571958 (1959 BorrowKind::Shared | BorrowKind::Fake(FakeBorrowKind::Deep),1960 BorrowKind::Shared | BorrowKind::Fake(_),1961 )1962 | (1963 BorrowKind::Fake(FakeBorrowKind::Shallow),1964 BorrowKind::Mut { .. } | BorrowKind::Shared | BorrowKind::Fake(_),1965 ) => {1966 unreachable!()1967 }1968 };1969 self.note_due_to_edition_2024_opaque_capture_rules(issued_borrow, &mut err);19701971 if issued_spans == borrow_spans {1972 borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {1973 use crate::session_diagnostics::CaptureVarCause::*;1974 match kind {1975 hir::ClosureKind::Coroutine(_) => BorrowUsePlaceCoroutine {1976 place: desc_place,1977 var_span,1978 is_single_var: false,1979 },1980 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {1981 BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }1982 }1983 }1984 });1985 } else {1986 issued_spans.var_subdiag(&mut err, Some(issued_borrow.kind), |kind, var_span| {1987 use crate::session_diagnostics::CaptureVarCause::*;1988 let borrow_place = &issued_borrow.borrowed_place;1989 let borrow_place_desc = self.describe_any_place(borrow_place.as_ref());1990 match kind {1991 hir::ClosureKind::Coroutine(_) => {1992 FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }1993 }1994 hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {1995 FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }1996 }1997 }1998 });19992000 borrow_spans.var_subdiag(&mut err, Some(gen_borrow_kind), |kind, var_span| {
Findings
✓ No findings reported for this file.