1 use rustc_errors::{Applicability, Diagnostic};
3 use rustc_hir::intravisit::Visitor;
5 use rustc_middle::hir::map::Map;
6 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
7 use rustc_middle::ty::{self, Ty, TyCtxt};
10 mir::{self, BindingForm, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location},
12 use rustc_span::source_map::DesugaringKind;
13 use rustc_span::symbol::{kw, Symbol};
14 use rustc_span::{sym, BytePos, Span};
16 use crate::diagnostics::BorrowedContentSource;
17 use crate::MirBorrowckCtxt;
18 use rustc_const_eval::util::collect_writes::FindAssignments;
20 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
21 pub(crate) enum AccessKind {
26 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
27 pub(crate) fn report_mutability_error(
29 access_place: Place<'tcx>,
31 the_place_err: PlaceRef<'tcx>,
32 error_access: AccessKind,
36 "report_mutability_error(\
37 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
39 access_place, span, the_place_err, error_access, location,
45 let mut opt_source = None;
46 let access_place_desc = self.describe_any_place(access_place.as_ref());
47 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
50 PlaceRef { local, projection: [] } => {
51 item_msg = access_place_desc;
52 if access_place.as_local().is_some() {
53 reason = ", as it is not declared as mutable".to_string();
55 let name = self.local_names[local].expect("immutable unnamed local");
56 reason = format!(", as `{name}` is not declared as mutable");
62 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
64 debug_assert!(is_closure_or_generator(
65 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
68 let imm_borrow_derefed = self.upvars[upvar_index.index()]
72 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
74 // If the place is immutable then:
76 // - Either we deref an immutable ref to get to our final place.
77 // - We don't capture derefs of raw ptrs
78 // - Or the final place is immut because the root variable of the capture
79 // isn't marked mut and we should suggest that to the user.
80 if imm_borrow_derefed {
81 // If we deref an immutable ref then the suggestion here doesn't help.
84 item_msg = access_place_desc;
85 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
86 reason = ", as it is not declared as mutable".to_string();
88 let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
89 reason = format!(", as `{name}` is not declared as mutable");
94 PlaceRef { local, projection: [ProjectionElem::Deref] }
95 if self.body.local_decls[local].is_ref_for_guard() =>
97 item_msg = access_place_desc;
98 reason = ", as it is immutable for the pattern guard".to_string();
100 PlaceRef { local, projection: [ProjectionElem::Deref] }
101 if self.body.local_decls[local].is_ref_to_static() =>
103 if access_place.projection.len() == 1 {
104 item_msg = format!("immutable static item {access_place_desc}");
105 reason = String::new();
107 item_msg = access_place_desc;
108 let local_info = &self.body.local_decls[local].local_info;
109 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
110 let static_name = &self.infcx.tcx.item_name(def_id);
111 reason = format!(", as `{static_name}` is an immutable static item");
113 bug!("is_ref_to_static return true, but not ref to static?");
117 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
118 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
119 && proj_base.is_empty()
120 && !self.upvars.is_empty()
122 item_msg = access_place_desc;
124 self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr()
126 debug_assert!(is_closure_or_generator(
129 the_place_err.projection,
136 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
137 ", as it is a captured variable in a `Fn` closure".to_string()
139 ", as `Fn` closures cannot mutate their captured variables".to_string()
142 let source = self.borrowed_content_source(PlaceRef {
143 local: the_place_err.local,
144 projection: proj_base,
146 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
147 opt_source = Some(source);
148 if let Some(desc) = self.describe_place(access_place.as_ref()) {
149 item_msg = format!("`{desc}`");
150 reason = match error_access {
151 AccessKind::Mutate => format!(", which is behind {pointer_type}"),
152 AccessKind::MutableBorrow => {
153 format!(", as it is behind {pointer_type}")
157 item_msg = format!("data in {pointer_type}");
158 reason = String::new();
168 ProjectionElem::Index(_)
169 | ProjectionElem::ConstantIndex { .. }
170 | ProjectionElem::OpaqueCast { .. }
171 | ProjectionElem::Subslice { .. }
172 | ProjectionElem::Downcast(..),
174 } => bug!("Unexpected immutable place."),
177 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
179 // `act` and `acted_on` are strings that let us abstract over
180 // the verbs used in some diagnostic messages.
183 let mut suggest = true;
184 let mut mut_error = None;
187 let span = match error_access {
188 AccessKind::Mutate => {
189 err = self.cannot_assign(span, &(item_msg + &reason));
191 acted_on = "written";
194 AccessKind::MutableBorrow => {
195 act = "borrow as mutable";
196 acted_on = "borrowed as mutable";
198 let borrow_spans = self.borrow_spans(span, location);
199 let borrow_span = borrow_spans.args_or_use();
200 match the_place_err {
201 PlaceRef { local, projection: [] }
202 if self.body.local_decls[local].can_be_made_mutable() =>
204 let span = self.body.local_decls[local].source_info.span;
205 mut_error = Some(span);
206 if let Some((buffer, c)) = self.get_buffered_mut_error(span) {
207 // We've encountered a second (or more) attempt to mutably borrow an
208 // immutable binding, so the likely problem is with the binding
209 // declaration, not the use. We collect these in a single diagnostic
210 // and make the binding the primary span of the error.
214 err.replace_span_with(span, false);
215 err.span_label(span, "not mutable");
219 err = self.cannot_borrow_path_as_mutable_because(
227 err = self.cannot_borrow_path_as_mutable_because(
235 borrow_spans.var_span_label(
238 "mutable borrow occurs due to use of {} in closure",
239 self.describe_any_place(access_place.as_ref()),
248 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
250 match the_place_err {
251 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
253 // This is applicable when we have a deref of a field access to a deref of a local -
254 // something like `*((*_1).0`. The local that we get will be a reference to the
255 // struct we've got a field access of (it must be a reference since there's a deref
256 // after the field access).
262 ProjectionElem::Deref,
263 ProjectionElem::Field(field, _),
264 ProjectionElem::Deref,
267 err.span_label(span, format!("cannot {act}"));
269 if let Some(span) = get_mut_span_in_struct_field(
271 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
274 err.span_suggestion_verbose(
276 "consider changing this to be mutable",
278 Applicability::MaybeIncorrect,
283 // Suggest removing a `&mut` from the use of a mutable reference.
284 PlaceRef { local, projection: [] }
289 .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local]))
292 let decl = &self.body.local_decls[local];
293 err.span_label(span, format!("cannot {act}"));
294 if let Some(mir::Statement {
297 mir::StatementKind::Assign(box (
301 mir::BorrowKind::Mut { allow_two_phase_borrow: false },
306 }) = &self.body[location.block].statements.get(location.statement_index)
308 match decl.local_info {
309 Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
310 mir::VarBindingForm {
311 binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
312 opt_ty_info: Some(sp),
318 err.span_note(sp, "the binding is already a mutable borrow");
323 decl.source_info.span,
324 "the binding is already a mutable borrow",
329 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
331 if snippet.starts_with("&mut ") {
332 // We don't have access to the HIR to get accurate spans, but we can
333 // give a best effort structured suggestion.
334 err.span_suggestion_verbose(
335 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
336 "try removing `&mut` here",
338 Applicability::MachineApplicable,
341 // This can occur with things like `(&mut self).foo()`.
342 err.span_help(source_info.span, "try removing `&mut` here");
345 err.span_help(source_info.span, "try removing `&mut` here");
347 } else if decl.mutability == Mutability::Not {
350 Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(
351 hir::ImplicitSelfKind::MutRef
355 "as `Self` may be unsized, this call attempts to take `&mut &mut self`",
357 err.note("however, `&mut self` expands to `self: &mut Self`, therefore `self` cannot be borrowed mutably");
359 err.span_suggestion_verbose(
360 decl.source_info.span.shrink_to_lo(),
361 "consider making the binding mutable",
363 Applicability::MachineApplicable,
369 // We want to suggest users use `let mut` for local (user
370 // variable) mutations...
371 PlaceRef { local, projection: [] }
372 if self.body.local_decls[local].can_be_made_mutable() =>
374 // ... but it doesn't make sense to suggest it on
375 // variables that are `ref x`, `ref mut x`, `&self`,
376 // or `&mut self` (such variables are simply not
378 let local_decl = &self.body.local_decls[local];
379 assert_eq!(local_decl.mutability, Mutability::Not);
382 err.span_label(span, format!("cannot {act}"));
385 err.span_suggestion_verbose(
386 local_decl.source_info.span.shrink_to_lo(),
387 "consider changing this to be mutable",
389 Applicability::MachineApplicable,
391 let tcx = self.infcx.tcx;
392 if let ty::Closure(id, _) = *the_place_err.ty(self.body, tcx).ty.kind() {
393 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
398 // Also suggest adding mut for upvars
401 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
403 debug_assert!(is_closure_or_generator(
404 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
407 let captured_place = &self.upvars[upvar_index.index()].place;
409 err.span_label(span, format!("cannot {act}"));
411 let upvar_hir_id = captured_place.get_root_variable();
413 if let Some(Node::Pat(pat)) = self.infcx.tcx.hir().find(upvar_hir_id)
414 && let hir::PatKind::Binding(
415 hir::BindingAnnotation::NONE,
423 "consider changing this to be mutable",
424 format!("mut {}", upvar_ident.name),
425 Applicability::MachineApplicable,
429 let tcx = self.infcx.tcx;
430 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
431 && let ty::Closure(id, _) = *ty.kind()
433 self.show_mutating_upvar(tcx, id.expect_local(), the_place_err, &mut err);
437 // complete hack to approximate old AST-borrowck
438 // diagnostic: if the span starts with a mutable borrow of
439 // a local variable, then just suggest the user remove it.
440 PlaceRef { local: _, projection: [] }
446 .span_to_snippet(span)
447 .map_or(false, |snippet| snippet.starts_with("&mut ")) =>
449 err.span_label(span, format!("cannot {act}"));
452 "try removing `&mut` here",
454 Applicability::MaybeIncorrect,
458 PlaceRef { local, projection: [ProjectionElem::Deref] }
459 if self.body.local_decls[local].is_ref_for_guard() =>
461 err.span_label(span, format!("cannot {act}"));
463 "variables bound in patterns are immutable until the end of the pattern guard",
467 // We want to point out when a `&` can be readily replaced
470 // FIXME: can this case be generalized to work for an
471 // arbitrary base for the projection?
472 PlaceRef { local, projection: [ProjectionElem::Deref] }
473 if self.body.local_decls[local].is_user_variable() =>
475 let local_decl = &self.body.local_decls[local];
477 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
480 ("*const", "pointer")
483 match self.local_names[local] {
484 Some(name) if !local_decl.from_compiler_desugaring() => {
485 let label = match local_decl.local_info.as_deref().unwrap() {
486 LocalInfo::User(ClearCrossCrate::Set(
487 mir::BindingForm::ImplicitSelf(_),
489 let (span, suggestion) =
490 suggest_ampmut_self(self.infcx.tcx, local_decl);
491 Some((true, span, suggestion))
494 LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
495 mir::VarBindingForm {
496 binding_mode: ty::BindingMode::BindByValue(_),
501 // check if the RHS is from desugaring
502 let opt_assignment_rhs_span =
503 self.body.find_assignments(local).first().map(|&location| {
504 if let Some(mir::Statement {
507 mir::StatementKind::Assign(box (
509 mir::Rvalue::Use(mir::Operand::Copy(place)),
511 }) = self.body[location.block]
513 .get(location.statement_index)
515 self.body.local_decls[place.local].source_info.span
517 self.body.source_info(location).span
520 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
521 // on for loops, RHS points to the iterator part
522 Some(DesugaringKind::ForLoop) => {
523 self.suggest_similar_mut_method_for_for_loop(&mut err);
524 err.span_label(opt_assignment_rhs_span.unwrap(), format!(
525 "this iterator yields `{pointer_sigil}` {pointer_desc}s",
529 // don't create labels for compiler-generated spans
532 let label = if name != kw::SelfLower {
536 opt_assignment_rhs_span,
540 match local_decl.local_info.as_deref() {
541 Some(LocalInfo::User(ClearCrossCrate::Set(
542 mir::BindingForm::Var(mir::VarBindingForm {
547 let (span, sugg) = suggest_ampmut_self(
553 // explicit self (eg `self: &'a Self`)
557 opt_assignment_rhs_span,
567 LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
568 mir::VarBindingForm {
569 binding_mode: ty::BindingMode::BindByReference(_),
573 let pattern_span = local_decl.source_info.span;
574 suggest_ref_mut(self.infcx.tcx, pattern_span)
575 .map(|replacement| (true, pattern_span, replacement))
578 LocalInfo::User(ClearCrossCrate::Clear) => {
579 bug!("saw cleared local state")
586 Some((true, err_help_span, suggested_code)) => {
587 let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
589 err.span_suggestion_verbose(
592 "consider changing this to be a mutable {pointer_desc}"
595 Applicability::MachineApplicable,
597 } else if let Some(x) = local_trait {
598 err.span_suggestion_verbose(
601 "consider changing that to be a mutable {pointer_desc}"
604 Applicability::MachineApplicable,
608 Some((false, err_label_span, message)) => {
612 "consider changing this binding's type to be: `{message}`"
621 "`{name}` is a `{pointer_sigil}` {pointer_desc}, \
622 so the data it refers to cannot be {acted_on}",
629 format!("cannot {act} through `{pointer_sigil}` {pointer_desc}"),
635 PlaceRef { local, projection: [ProjectionElem::Deref] }
636 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
638 self.expected_fn_found_fn_mut_call(&mut err, span, act);
641 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
642 err.span_label(span, format!("cannot {act}"));
645 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
647 "trait `DerefMut` is required to modify through a dereference, \
648 but it is not implemented for `{ty}`",
651 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
653 "trait `IndexMut` is required to modify indexed content, \
654 but it is not implemented for `{ty}`",
656 self.suggest_map_index_mut_alternatives(ty, &mut err, span);
663 err.span_label(span, format!("cannot {act}"));
667 if let Some(span) = mut_error {
668 self.buffer_mut_error(span, err, count);
670 self.buffer_error(err);
674 fn suggest_map_index_mut_alternatives(&self, ty: Ty<'tcx>, err: &mut Diagnostic, span: Span) {
675 let Some(adt) = ty.ty_adt_def() else { return };
677 if self.infcx.tcx.is_diagnostic_item(sym::HashMap, did)
678 || self.infcx.tcx.is_diagnostic_item(sym::BTreeMap, did)
682 err: &'a mut Diagnostic,
686 impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
687 fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
688 hir::intravisit::walk_stmt(self, stmt);
689 let expr = match stmt.kind {
690 hir::StmtKind::Semi(expr) | hir::StmtKind::Expr(expr) => expr,
691 hir::StmtKind::Local(hir::Local { init: Some(expr), .. }) => expr,
696 if let hir::ExprKind::Assign(place, rv, _sp) = expr.kind
697 && let hir::ExprKind::Index(val, index) = place.kind
698 && (expr.span == self.assign_span || place.span == self.assign_span)
702 self.err.multipart_suggestions(
704 "to modify a `{}`, use `.get_mut()`, `.insert()` or the entry API",
708 vec![ // val.insert(index, rv);
710 val.span.shrink_to_hi().with_hi(index.span.lo()),
711 ".insert(".to_string(),
714 index.span.shrink_to_hi().with_hi(rv.span.lo()),
717 (rv.span.shrink_to_hi(), ")".to_string()),
719 vec![ // val.get_mut(index).map(|v| { *v = rv; });
721 val.span.shrink_to_hi().with_hi(index.span.lo()),
722 ".get_mut(".to_string(),
725 index.span.shrink_to_hi().with_hi(place.span.hi()),
726 ").map(|val| { *val".to_string(),
729 rv.span.shrink_to_hi(),
733 vec![ // let x = val.entry(index).or_insert(rv);
734 (val.span.shrink_to_lo(), "let val = ".to_string()),
736 val.span.shrink_to_hi().with_hi(index.span.lo()),
737 ".entry(".to_string(),
740 index.span.shrink_to_hi().with_hi(rv.span.lo()),
741 ").or_insert(".to_string(),
743 (rv.span.shrink_to_hi(), ")".to_string()),
746 Applicability::MachineApplicable,
748 self.suggested = true;
749 } else if let hir::ExprKind::MethodCall(_path, receiver, _, sp) = expr.kind
750 && let hir::ExprKind::Index(val, index) = receiver.kind
751 && expr.span == self.assign_span
753 // val[index].path(args..);
754 self.err.multipart_suggestion(
755 &format!("to modify a `{}` use `.get_mut()`", self.ty),
758 val.span.shrink_to_hi().with_hi(index.span.lo()),
759 ".get_mut(".to_string(),
762 index.span.shrink_to_hi().with_hi(receiver.span.hi()),
763 ").map(|val| val".to_string(),
765 (sp.shrink_to_hi(), ")".to_string()),
767 Applicability::MachineApplicable,
769 self.suggested = true;
773 let hir_map = self.infcx.tcx.hir();
774 let def_id = self.body.source.def_id();
775 let hir_id = hir_map.local_def_id_to_hir_id(def_id.as_local().unwrap());
776 let node = hir_map.find(hir_id);
777 let Some(hir::Node::Item(item)) = node else { return; };
778 let hir::ItemKind::Fn(.., body_id) = item.kind else { return; };
779 let body = self.infcx.tcx.hir().body(body_id);
780 let mut v = V { assign_span: span, err, ty, suggested: false };
784 "to modify a `{ty}`, use `.get_mut()`, `.insert()` or the entry API",
790 /// User cannot make signature of a trait mutable without changing the
791 /// trait. So we find if this error belongs to a trait and if so we move
792 /// suggestion to the trait or disable it if it is out of scope of this crate
793 fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
794 if self.body.local_kind(local) != LocalKind::Arg {
795 return (false, None);
797 let hir_map = self.infcx.tcx.hir();
798 let my_def = self.body.source.def_id();
799 let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
801 self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
803 return (false, None);
807 td.as_local().and_then(|tld| match hir_map.find_by_def_id(tld) {
808 Some(Node::Item(hir::Item {
809 kind: hir::ItemKind::Trait(_, _, _, _, items),
812 let mut f_in_trait_opt = None;
813 for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
814 let hi = fi.hir_id();
815 if !matches!(k, hir::AssocItemKind::Fn { .. }) {
818 if hir_map.name(hi) != hir_map.name(my_hir) {
821 f_in_trait_opt = Some(hi);
824 f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
825 Some(Node::TraitItem(hir::TraitItem {
827 hir::TraitItemKind::Fn(
828 hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
833 let hir::Ty { span, .. } = inputs[local.index() - 1];
844 // point to span of upvar making closure call require mutable borrow
845 fn show_mutating_upvar(
848 closure_local_def_id: hir::def_id::LocalDefId,
849 the_place_err: PlaceRef<'tcx>,
850 err: &mut Diagnostic,
852 let tables = tcx.typeck(closure_local_def_id);
853 let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
854 if let Some((span, closure_kind_origin)) =
855 &tables.closure_kind_origins().get(closure_hir_id)
857 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
858 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
859 let root_hir_id = upvar_id.var_path.hir_id;
860 // we have an origin for this closure kind starting at this root variable so it's safe to unwrap here
861 let captured_places =
862 tables.closure_min_captures[&closure_local_def_id].get(&root_hir_id).unwrap();
864 let origin_projection = closure_kind_origin
867 .map(|proj| proj.kind)
868 .collect::<Vec<_>>();
869 let mut capture_reason = String::new();
870 for captured_place in captured_places {
871 let captured_place_kinds = captured_place
875 .map(|proj| proj.kind)
876 .collect::<Vec<_>>();
877 if rustc_middle::ty::is_ancestor_or_same_capture(
878 &captured_place_kinds,
881 match captured_place.info.capture_kind {
882 ty::UpvarCapture::ByRef(
883 ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
885 capture_reason = format!("mutable borrow of `{upvar}`");
887 ty::UpvarCapture::ByValue => {
888 capture_reason = format!("possible mutation of `{upvar}`");
890 _ => bug!("upvar `{upvar}` borrowed, but not mutably"),
895 if capture_reason.is_empty() {
896 bug!("upvar `{upvar}` borrowed, but cannot find reason");
905 "calling `{}` requires mutable binding due to {}",
906 self.describe_place(the_place_err).unwrap(),
913 // Attempt to search similar mutable associated items for suggestion.
914 // In the future, attempt in all path but initially for RHS of for_loop
915 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut Diagnostic) {
918 ExprKind::{Block, Call, DropTemps, Match, MethodCall},
919 HirId, ImplItem, ImplItemKind, Item, ItemKind,
922 fn maybe_body_id_of_fn(hir_map: Map<'_>, id: HirId) -> Option<BodyId> {
923 match hir_map.find(id) {
924 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
925 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
931 let hir_map = self.infcx.tcx.hir();
932 let mir_body_hir_id = self.mir_hir_id();
933 if let Some(fn_body_id) = maybe_body_id_of_fn(hir_map, mir_body_hir_id) {
949 MethodCall(path_segment, _, _, span),
967 ) = hir_map.body(fn_body_id).value.kind
969 let opt_suggestions = self
972 .typeck(path_segment.hir_id.owner.def_id)
973 .type_dependent_def_id(*hir_id)
974 .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
975 .map(|def_id| self.infcx.tcx.associated_items(def_id))
978 .in_definition_order()
979 .map(|assoc_item_def| assoc_item_def.ident(self.infcx.tcx))
981 let original_method_ident = path_segment.ident;
982 original_method_ident != ident
985 .starts_with(&original_method_ident.name.to_string())
987 .map(|ident| format!("{ident}()"))
991 if let Some(mut suggestions) = opt_suggestions
992 && suggestions.peek().is_some()
994 err.span_suggestions(
996 "use mutable method",
998 Applicability::MaybeIncorrect,
1005 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
1006 fn expected_fn_found_fn_mut_call(&self, err: &mut Diagnostic, sp: Span, act: &str) {
1007 err.span_label(sp, format!("cannot {act}"));
1009 let hir = self.infcx.tcx.hir();
1010 let closure_id = self.mir_hir_id();
1011 let closure_span = self.infcx.tcx.def_span(self.mir_def_id());
1012 let fn_call_id = hir.parent_id(closure_id);
1013 let node = hir.get(fn_call_id);
1014 let def_id = hir.enclosing_body_owner(fn_call_id);
1015 let mut look_at_return = true;
1016 // If we can detect the expression to be an `fn` call where the closure was an argument,
1017 // we point at the `fn` definition argument...
1018 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
1022 .filter(|(_, arg)| arg.hir_id == closure_id)
1023 .map(|(pos, _)| pos)
1025 let tables = self.infcx.tcx.typeck(def_id);
1026 if let Some(ty::FnDef(def_id, _)) =
1027 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
1029 let arg = match hir.get_if_local(*def_id) {
1031 hir::Node::Item(hir::Item {
1032 ident, kind: hir::ItemKind::Fn(sig, ..), ..
1034 | hir::Node::TraitItem(hir::TraitItem {
1036 kind: hir::TraitItemKind::Fn(sig, _),
1039 | hir::Node::ImplItem(hir::ImplItem {
1041 kind: hir::ImplItemKind::Fn(sig, _),
1047 sig.decl.inputs.get(
1048 pos + if sig.decl.implicit_self.has_implicit_self() {
1055 .map(|arg| arg.span)
1056 .unwrap_or(ident.span),
1060 if let Some(span) = arg {
1061 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
1062 err.span_label(func.span, "expects `Fn` instead of `FnMut`");
1063 err.span_label(closure_span, "in this closure");
1064 look_at_return = false;
1069 if look_at_return && hir.get_return_block(closure_id).is_some() {
1070 // ...otherwise we are probably in the tail expression of the function, point at the
1072 match hir.get_by_def_id(hir.get_parent_item(fn_call_id).def_id) {
1073 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
1074 | hir::Node::TraitItem(hir::TraitItem {
1076 kind: hir::TraitItemKind::Fn(sig, _),
1079 | hir::Node::ImplItem(hir::ImplItem {
1081 kind: hir::ImplItemKind::Fn(sig, _),
1084 err.span_label(ident.span, "");
1086 sig.decl.output.span(),
1087 "change this to return `FnMut` instead of `Fn`",
1089 err.span_label(closure_span, "in this closure");
1097 pub fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
1098 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
1100 match local_decl.local_info.as_deref() {
1101 // Check if mutably borrowing a mutable reference.
1102 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
1103 mir::VarBindingForm {
1104 binding_mode: ty::BindingMode::BindByValue(Mutability::Not), ..
1106 )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
1107 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => {
1108 // Check if the user variable is a `&mut self` and we can therefore
1109 // suggest removing the `&mut`.
1111 // Deliberately fall into this case for all implicit self types,
1112 // so that we don't fall in to the next case with them.
1113 *kind == hir::ImplicitSelfKind::MutRef
1115 _ if Some(kw::SelfLower) == local_name => {
1116 // Otherwise, check if the name is the `self` keyword - in which case
1117 // we have an explicit self. Do the same thing in this case and check
1118 // for a `self: &mut Self` to suggest removing the `&mut`.
1119 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
1125 fn suggest_ampmut_self<'tcx>(
1127 local_decl: &mir::LocalDecl<'tcx>,
1128 ) -> (Span, String) {
1129 let sp = local_decl.source_info.span;
1132 match tcx.sess.source_map().span_to_snippet(sp) {
1134 let lt_pos = snippet.find('\'');
1135 if let Some(lt_pos) = lt_pos {
1136 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
1138 "&mut self".to_string()
1141 _ => "&mut self".to_string(),
1146 // When we want to suggest a user change a local variable to be a `&mut`, there
1147 // are three potential "obvious" things to highlight:
1149 // let ident [: Type] [= RightHandSideExpression];
1150 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
1153 // We can always fallback on highlighting the first. But chances are good that
1154 // the user experience will be better if we highlight one of the others if possible;
1155 // for example, if the RHS is present and the Type is not, then the type is going to
1156 // be inferred *from* the RHS, which means we should highlight that (and suggest
1157 // that they borrow the RHS mutably).
1159 // This implementation attempts to emulate AST-borrowck prioritization
1160 // by trying (3.), then (2.) and finally falling back on (1.).
1161 fn suggest_ampmut<'tcx>(
1163 local_decl: &mir::LocalDecl<'tcx>,
1164 opt_assignment_rhs_span: Option<Span>,
1165 opt_ty_info: Option<Span>,
1166 ) -> (bool, Span, String) {
1167 if let Some(assignment_rhs_span) = opt_assignment_rhs_span
1168 && let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span)
1170 let is_mutbl = |ty: &str| -> bool {
1171 if let Some(rest) = ty.strip_prefix("mut") {
1172 match rest.chars().next() {
1174 Some(c) if c.is_whitespace() => true,
1179 // e.g. `&mutablevar`
1186 if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
1187 let lt_name = &src[1..ws_pos];
1188 let ty = src[ws_pos..].trim_start();
1190 return (true, assignment_rhs_span, format!("&{lt_name} mut {ty}"));
1192 } else if let Some(stripped) = src.strip_prefix('&') {
1193 let stripped = stripped.trim_start();
1194 if !is_mutbl(stripped) {
1195 return (true, assignment_rhs_span, format!("&mut {stripped}"));
1200 let (suggestability, highlight_span) = match opt_ty_info {
1201 // if this is a variable binding with an explicit type,
1202 // try to highlight that for the suggestion.
1203 Some(ty_span) => (true, ty_span),
1205 // otherwise, just highlight the span associated with
1206 // the (MIR) LocalDecl.
1207 None => (false, local_decl.source_info.span),
1210 if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span)
1211 && let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace))
1213 let lt_name = &src[1..ws_pos];
1214 let ty = &src[ws_pos..];
1215 return (true, highlight_span, format!("&{lt_name} mut{ty}"));
1218 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
1219 assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
1223 if local_decl.ty.is_region_ptr() {
1224 format!("&mut {}", ty_mut.ty)
1226 format!("*mut {}", ty_mut.ty)
1231 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
1232 ty.is_closure() || ty.is_generator()
1235 /// Given a field that needs to be mutable, returns a span where the " mut " could go.
1236 /// This function expects the local to be a reference to a struct in order to produce a span.
1239 /// LL | s: &'a String
1240 /// | ^^^ returns a span taking up the space here
1242 fn get_mut_span_in_struct_field<'tcx>(
1247 // Expect our local to be a reference to a struct of some kind.
1248 if let ty::Ref(_, ty, _) = ty.kind()
1249 && let ty::Adt(def, _) = ty.kind()
1250 && let field = def.all_fields().nth(field.index())?
1251 // Use the HIR types to construct the diagnostic message.
1252 && let node = tcx.hir().find_by_def_id(field.did.as_local()?)?
1253 // Now we're dealing with the actual struct that we're going to suggest a change to,
1254 // we can expect a field that is an immutable reference to a type.
1255 && let hir::Node::Field(field) = node
1256 && let hir::TyKind::Ref(lt, hir::MutTy { mutbl: hir::Mutability::Not, ty }) = field.ty.kind
1258 return Some(lt.ident.span.between(ty.span));
1264 /// If possible, suggest replacing `ref` with `ref mut`.
1265 fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
1266 let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
1267 if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
1268 let replacement = format!("ref mut{}", &hi_src["ref".len()..]);