3 use rustc_middle::hir::map::Map;
4 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
5 use rustc_middle::ty::{self, Ty, TyCtxt};
9 self, BindingForm, ClearCrossCrate, ImplicitSelfKind, Local, LocalDecl, LocalInfo,
13 use rustc_span::source_map::DesugaringKind;
14 use rustc_span::symbol::{kw, Symbol};
15 use rustc_span::{BytePos, Span};
17 use crate::diagnostics::BorrowedContentSource;
18 use crate::MirBorrowckCtxt;
19 use rustc_const_eval::util::collect_writes::FindAssignments;
20 use rustc_errors::{Applicability, DiagnosticBuilder};
22 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
23 pub(crate) enum AccessKind {
28 impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
29 pub(crate) fn report_mutability_error(
31 access_place: Place<'tcx>,
33 the_place_err: PlaceRef<'tcx>,
34 error_access: AccessKind,
38 "report_mutability_error(\
39 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
41 access_place, span, the_place_err, error_access, location,
47 let mut opt_source = None;
48 let access_place_desc = self.describe_any_place(access_place.as_ref());
49 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
52 PlaceRef { local, projection: [] } => {
53 item_msg = access_place_desc;
54 if access_place.as_local().is_some() {
55 reason = ", as it is not declared as mutable".to_string();
57 let name = self.local_names[local].expect("immutable unnamed local");
58 reason = format!(", as `{}` is not declared as mutable", name);
64 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
66 debug_assert!(is_closure_or_generator(
67 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
70 let imm_borrow_derefed = self.upvars[upvar_index.index()]
74 .any(|ty| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)));
76 // If the place is immutable then:
78 // - Either we deref an immutable ref to get to our final place.
79 // - We don't capture derefs of raw ptrs
80 // - Or the final place is immut because the root variable of the capture
81 // isn't marked mut and we should suggest that to the user.
82 if imm_borrow_derefed {
83 // If we deref an immutable ref then the suggestion here doesn't help.
86 item_msg = access_place_desc;
87 if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
88 reason = ", as it is not declared as mutable".to_string();
90 let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
91 reason = format!(", as `{}` is not declared as mutable", name);
96 PlaceRef { local, projection: [ProjectionElem::Deref] }
97 if self.body.local_decls[local].is_ref_for_guard() =>
99 item_msg = access_place_desc;
100 reason = ", as it is immutable for the pattern guard".to_string();
102 PlaceRef { local, projection: [ProjectionElem::Deref] }
103 if self.body.local_decls[local].is_ref_to_static() =>
105 if access_place.projection.len() == 1 {
106 item_msg = format!("immutable static item {}", access_place_desc);
107 reason = String::new();
109 item_msg = access_place_desc;
110 let local_info = &self.body.local_decls[local].local_info;
111 if let Some(box LocalInfo::StaticRef { def_id, .. }) = *local_info {
112 let static_name = &self.infcx.tcx.item_name(def_id);
113 reason = format!(", as `{}` is an immutable static item", static_name);
115 bug!("is_ref_to_static return true, but not ref to static?");
119 PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
120 if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
121 && proj_base.is_empty()
122 && !self.upvars.is_empty()
124 item_msg = access_place_desc;
126 self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr()
128 debug_assert!(is_closure_or_generator(
131 the_place_err.projection,
138 reason = if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
139 ", as it is a captured variable in a `Fn` closure".to_string()
141 ", as `Fn` closures cannot mutate their captured variables".to_string()
144 let source = self.borrowed_content_source(PlaceRef {
145 local: the_place_err.local,
146 projection: proj_base,
148 let pointer_type = source.describe_for_immutable_place(self.infcx.tcx);
149 opt_source = Some(source);
150 if let Some(desc) = self.describe_place(access_place.as_ref()) {
151 item_msg = format!("`{}`", desc);
152 reason = match error_access {
153 AccessKind::Mutate => format!(", which is behind {}", pointer_type),
154 AccessKind::MutableBorrow => {
155 format!(", as it is behind {}", pointer_type)
159 item_msg = format!("data in {}", pointer_type);
160 reason = String::new();
170 ProjectionElem::Index(_)
171 | ProjectionElem::ConstantIndex { .. }
172 | ProjectionElem::Subslice { .. }
173 | ProjectionElem::Downcast(..),
175 } => bug!("Unexpected immutable place."),
178 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
180 // `act` and `acted_on` are strings that let us abstract over
181 // the verbs used in some diagnostic messages.
185 let span = match error_access {
186 AccessKind::Mutate => {
187 err = self.cannot_assign(span, &(item_msg + &reason));
189 acted_on = "written";
192 AccessKind::MutableBorrow => {
193 act = "borrow as mutable";
194 acted_on = "borrowed as mutable";
196 let borrow_spans = self.borrow_spans(span, location);
197 let borrow_span = borrow_spans.args_or_use();
198 err = self.cannot_borrow_path_as_mutable_because(borrow_span, &item_msg, &reason);
199 borrow_spans.var_span_label(
202 "mutable borrow occurs due to use of {} in closure",
203 self.describe_any_place(access_place.as_ref()),
211 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
213 match the_place_err {
214 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
216 // This is applicable when we have a deref of a field access to a deref of a local -
217 // something like `*((*_1).0`. The local that we get will be a reference to the
218 // struct we've got a field access of (it must be a reference since there's a deref
219 // after the field access).
225 ProjectionElem::Deref,
226 ProjectionElem::Field(field, _),
227 ProjectionElem::Deref,
230 err.span_label(span, format!("cannot {ACT}", ACT = act));
232 if let Some((span, message)) = annotate_struct_field(
234 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty,
239 "consider changing this to be mutable",
241 Applicability::MaybeIncorrect,
246 // Suggest removing a `&mut` from the use of a mutable reference.
247 PlaceRef { local, projection: [] }
252 .map(|l| mut_borrow_of_mutable_ref(l, self.local_names[local]))
255 let decl = &self.body.local_decls[local];
256 err.span_label(span, format!("cannot {ACT}", ACT = act));
257 if let Some(mir::Statement {
260 mir::StatementKind::Assign(box (
264 mir::BorrowKind::Mut { allow_two_phase_borrow: false },
269 }) = &self.body[location.block].statements.get(location.statement_index)
271 match decl.local_info {
272 Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::Var(
273 mir::VarBindingForm {
274 binding_mode: ty::BindingMode::BindByValue(Mutability::Not),
275 opt_ty_info: Some(sp),
280 err.span_note(sp, "the binding is already a mutable borrow");
284 decl.source_info.span,
285 "the binding is already a mutable borrow",
290 self.infcx.tcx.sess.source_map().span_to_snippet(source_info.span)
292 if snippet.starts_with("&mut ") {
293 // We don't have access to the HIR to get accurate spans, but we can
294 // give a best effort structured suggestion.
295 err.span_suggestion_verbose(
296 source_info.span.with_hi(source_info.span.lo() + BytePos(5)),
297 "try removing `&mut` here",
299 Applicability::MachineApplicable,
302 // This can occur with things like `(&mut self).foo()`.
303 err.span_help(source_info.span, "try removing `&mut` here");
306 err.span_help(source_info.span, "try removing `&mut` here");
308 } else if decl.mutability == Mutability::Not
311 Some(box LocalInfo::User(ClearCrossCrate::Set(BindingForm::ImplicitSelf(
312 ImplicitSelfKind::MutRef
316 err.span_suggestion_verbose(
317 decl.source_info.span.shrink_to_lo(),
318 "consider making the binding mutable",
320 Applicability::MachineApplicable,
325 // We want to suggest users use `let mut` for local (user
326 // variable) mutations...
327 PlaceRef { local, projection: [] }
328 if self.body.local_decls[local].can_be_made_mutable() =>
330 // ... but it doesn't make sense to suggest it on
331 // variables that are `ref x`, `ref mut x`, `&self`,
332 // or `&mut self` (such variables are simply not
334 let local_decl = &self.body.local_decls[local];
335 assert_eq!(local_decl.mutability, Mutability::Not);
337 err.span_label(span, format!("cannot {ACT}", ACT = act));
339 local_decl.source_info.span,
340 "consider changing this to be mutable",
341 format!("mut {}", self.local_names[local].unwrap()),
342 Applicability::MachineApplicable,
344 let tcx = self.infcx.tcx;
345 if let ty::Closure(id, _) = the_place_err.ty(self.body, tcx).ty.kind() {
346 self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
350 // Also suggest adding mut for upvars
353 projection: [proj_base @ .., ProjectionElem::Field(upvar_index, _)],
355 debug_assert!(is_closure_or_generator(
356 Place::ty_from(local, proj_base, self.body, self.infcx.tcx).ty
359 let captured_place = &self.upvars[upvar_index.index()].place;
361 err.span_label(span, format!("cannot {ACT}", ACT = act));
363 let upvar_hir_id = captured_place.get_root_variable();
365 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_hir_id) {
366 if let hir::PatKind::Binding(
367 hir::BindingAnnotation::Unannotated,
375 "consider changing this to be mutable",
376 format!("mut {}", upvar_ident.name),
377 Applicability::MachineApplicable,
382 let tcx = self.infcx.tcx;
383 if let ty::Ref(_, ty, Mutability::Mut) = the_place_err.ty(self.body, tcx).ty.kind()
385 if let ty::Closure(id, _) = ty.kind() {
386 self.show_mutating_upvar(tcx, id, the_place_err, &mut err);
391 // complete hack to approximate old AST-borrowck
392 // diagnostic: if the span starts with a mutable borrow of
393 // a local variable, then just suggest the user remove it.
394 PlaceRef { local: _, projection: [] }
396 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
397 snippet.starts_with("&mut ")
403 err.span_label(span, format!("cannot {ACT}", ACT = act));
406 "try removing `&mut` here",
408 Applicability::MaybeIncorrect,
412 PlaceRef { local, projection: [ProjectionElem::Deref] }
413 if self.body.local_decls[local].is_ref_for_guard() =>
415 err.span_label(span, format!("cannot {ACT}", ACT = act));
417 "variables bound in patterns are immutable until the end of the pattern guard",
421 // We want to point out when a `&` can be readily replaced
424 // FIXME: can this case be generalized to work for an
425 // arbitrary base for the projection?
426 PlaceRef { local, projection: [ProjectionElem::Deref] }
427 if self.body.local_decls[local].is_user_variable() =>
429 let local_decl = &self.body.local_decls[local];
431 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
434 ("*const", "pointer")
437 match self.local_names[local] {
438 Some(name) if !local_decl.from_compiler_desugaring() => {
439 let label = match local_decl.local_info.as_ref().unwrap() {
440 box LocalInfo::User(ClearCrossCrate::Set(
441 mir::BindingForm::ImplicitSelf(_),
443 let (span, suggestion) =
444 suggest_ampmut_self(self.infcx.tcx, local_decl);
445 Some((true, span, suggestion))
448 box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
449 mir::VarBindingForm {
450 binding_mode: ty::BindingMode::BindByValue(_),
455 // check if the RHS is from desugaring
456 let opt_assignment_rhs_span =
457 self.body.find_assignments(local).first().map(|&location| {
458 if let Some(mir::Statement {
461 mir::StatementKind::Assign(box (
463 mir::Rvalue::Use(mir::Operand::Copy(place)),
465 }) = self.body[location.block]
467 .get(location.statement_index)
469 self.body.local_decls[place.local].source_info.span
471 self.body.source_info(location).span
474 match opt_assignment_rhs_span.and_then(|s| s.desugaring_kind()) {
475 // on for loops, RHS points to the iterator part
476 Some(DesugaringKind::ForLoop) => {
477 self.suggest_similar_mut_method_for_for_loop(&mut err);
480 opt_assignment_rhs_span.unwrap(),
482 "this iterator yields `{SIGIL}` {DESC}s",
483 SIGIL = pointer_sigil,
488 // don't create labels for compiler-generated spans
491 let (span, suggestion) = suggest_ampmut(
494 opt_assignment_rhs_span,
497 Some((true, span, suggestion))
502 box LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
503 mir::VarBindingForm {
504 binding_mode: ty::BindingMode::BindByReference(_),
508 let pattern_span = local_decl.source_info.span;
509 suggest_ref_mut(self.infcx.tcx, pattern_span)
510 .map(|replacement| (true, pattern_span, replacement))
513 box LocalInfo::User(ClearCrossCrate::Clear) => {
514 bug!("saw cleared local state")
521 Some((true, err_help_span, suggested_code)) => {
522 let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
527 "consider changing this to be a mutable {}",
531 Applicability::MachineApplicable,
533 } else if let Some(x) = local_trait {
537 "consider changing that to be a mutable {}",
541 Applicability::MachineApplicable,
545 Some((false, err_label_span, message)) => {
546 err.span_label(err_label_span, &message);
553 "`{NAME}` is a `{SIGIL}` {DESC}, \
554 so the data it refers to cannot be {ACTED_ON}",
556 SIGIL = pointer_sigil,
566 "cannot {ACT} through `{SIGIL}` {DESC}",
568 SIGIL = pointer_sigil,
576 PlaceRef { local, projection: [ProjectionElem::Deref] }
577 if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
579 self.expected_fn_found_fn_mut_call(&mut err, span, act);
582 PlaceRef { local: _, projection: [.., ProjectionElem::Deref] } => {
583 err.span_label(span, format!("cannot {ACT}", ACT = act));
586 Some(BorrowedContentSource::OverloadedDeref(ty)) => {
588 "trait `DerefMut` is required to modify through a dereference, \
589 but it is not implemented for `{}`",
593 Some(BorrowedContentSource::OverloadedIndex(ty)) => {
595 "trait `IndexMut` is required to modify indexed content, \
596 but it is not implemented for `{}`",
605 err.span_label(span, format!("cannot {ACT}", ACT = act));
609 err.buffer(&mut self.errors_buffer);
612 /// User cannot make signature of a trait mutable without changing the
613 /// trait. So we find if this error belongs to a trait and if so we move
614 /// suggestion to the trait or disable it if it is out of scope of this crate
615 fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
616 if self.body.local_kind(local) != LocalKind::Arg {
617 return (false, None);
619 let hir_map = self.infcx.tcx.hir();
620 let my_def = self.body.source.def_id();
621 let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
622 let td = if let Some(a) =
623 self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
627 return (false, None);
631 td.as_local().and_then(|tld| {
632 let h = hir_map.local_def_id_to_hir_id(tld);
633 match hir_map.find(h) {
634 Some(Node::Item(hir::Item {
635 kind: hir::ItemKind::Trait(_, _, _, _, items),
638 let mut f_in_trait_opt = None;
639 for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
640 let hi = fi.hir_id();
641 if !matches!(k, hir::AssocItemKind::Fn { .. }) {
644 if hir_map.name(hi) != hir_map.name(my_hir) {
647 f_in_trait_opt = Some(hi);
650 f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
651 Some(Node::TraitItem(hir::TraitItem {
653 hir::TraitItemKind::Fn(
654 hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
659 let hir::Ty { span, .. } = inputs[local.index() - 1];
671 // point to span of upvar making closure call require mutable borrow
672 fn show_mutating_upvar(
675 id: &hir::def_id::DefId,
676 the_place_err: PlaceRef<'tcx>,
677 err: &mut DiagnosticBuilder<'_>,
679 let closure_local_def_id = id.expect_local();
680 let tables = tcx.typeck(closure_local_def_id);
681 let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
682 if let Some((span, closure_kind_origin)) =
683 &tables.closure_kind_origins().get(closure_hir_id)
685 let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
686 let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
687 let root_hir_id = upvar_id.var_path.hir_id;
688 // we have an origin for this closure kind starting at this root variable so it's safe to unwrap here
689 let captured_places = tables.closure_min_captures[id].get(&root_hir_id).unwrap();
691 let origin_projection = closure_kind_origin
694 .map(|proj| proj.kind)
695 .collect::<Vec<_>>();
696 let mut capture_reason = String::new();
697 for captured_place in captured_places {
698 let captured_place_kinds = captured_place
702 .map(|proj| proj.kind)
703 .collect::<Vec<_>>();
704 if rustc_middle::ty::is_ancestor_or_same_capture(
705 &captured_place_kinds,
708 match captured_place.info.capture_kind {
709 ty::UpvarCapture::ByRef(ty::UpvarBorrow {
710 kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
713 capture_reason = format!("mutable borrow of `{}`", upvar);
715 ty::UpvarCapture::ByValue(_) => {
716 capture_reason = format!("possible mutation of `{}`", upvar);
718 _ => bug!("upvar `{}` borrowed, but not mutably", upvar),
723 if capture_reason.is_empty() {
724 bug!("upvar `{}` borrowed, but cannot find reason", upvar);
733 "calling `{}` requires mutable binding due to {}",
734 self.describe_place(the_place_err).unwrap(),
741 // Attempt to search similar mutable associated items for suggestion.
742 // In the future, attempt in all path but initially for RHS of for_loop
743 fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>) {
746 ExprKind::{Block, Call, DropTemps, Match, MethodCall},
747 HirId, ImplItem, ImplItemKind, Item, ItemKind,
750 fn maybe_body_id_of_fn(hir_map: &Map<'_>, id: HirId) -> Option<BodyId> {
751 match hir_map.find(id) {
752 Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
753 | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
759 let hir_map = self.infcx.tcx.hir();
760 let mir_body_hir_id = self.mir_hir_id();
761 if let Some(fn_body_id) = maybe_body_id_of_fn(&hir_map, mir_body_hir_id) {
776 kind: MethodCall(path_segment, ..),
794 ) = hir_map.body(fn_body_id).value.kind
796 let opt_suggestions = path_segment
798 .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner))
799 .and_then(|typeck| typeck.type_dependent_def_id(*hir_id))
800 .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
801 .map(|def_id| self.infcx.tcx.associated_items(def_id))
804 .in_definition_order()
805 .map(|assoc_item_def| assoc_item_def.ident)
807 let original_method_ident = path_segment.ident;
808 original_method_ident != ident
811 .starts_with(&original_method_ident.name.to_string())
813 .map(|ident| format!("{}()", ident))
817 if let Some(mut suggestions) = opt_suggestions {
818 if suggestions.peek().is_some() {
819 err.span_suggestions(
820 path_segment.ident.span,
821 "use mutable method",
823 Applicability::MaybeIncorrect,
831 /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.
832 fn expected_fn_found_fn_mut_call(&self, err: &mut DiagnosticBuilder<'_>, sp: Span, act: &str) {
833 err.span_label(sp, format!("cannot {}", act));
835 let hir = self.infcx.tcx.hir();
836 let closure_id = self.mir_hir_id();
837 let fn_call_id = hir.get_parent_node(closure_id);
838 let node = hir.get(fn_call_id);
839 let item_id = hir.enclosing_body_owner(fn_call_id);
840 let mut look_at_return = true;
841 // If we can detect the expression to be an `fn` call where the closure was an argument,
842 // we point at the `fn` definition argument...
843 if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Call(func, args), .. }) = node {
847 .filter(|(_, arg)| arg.span == self.body.span)
850 let def_id = hir.local_def_id(item_id);
851 let tables = self.infcx.tcx.typeck(def_id);
852 if let Some(ty::FnDef(def_id, _)) =
853 tables.node_type_opt(func.hir_id).as_ref().map(|ty| ty.kind())
855 let arg = match hir.get_if_local(*def_id) {
857 hir::Node::Item(hir::Item {
858 ident, kind: hir::ItemKind::Fn(sig, ..), ..
860 | hir::Node::TraitItem(hir::TraitItem {
862 kind: hir::TraitItemKind::Fn(sig, _),
865 | hir::Node::ImplItem(hir::ImplItem {
867 kind: hir::ImplItemKind::Fn(sig, _),
874 pos + if sig.decl.implicit_self.has_implicit_self() {
882 .unwrap_or(ident.span),
886 if let Some(span) = arg {
887 err.span_label(span, "change this to accept `FnMut` instead of `Fn`");
888 err.span_label(func.span, "expects `Fn` instead of `FnMut`");
889 if self.infcx.tcx.sess.source_map().is_multiline(self.body.span) {
890 err.span_label(self.body.span, "in this closure");
892 look_at_return = false;
897 if look_at_return && hir.get_return_block(closure_id).is_some() {
898 // ...otherwise we are probably in the tail expression of the function, point at the
900 match hir.get(hir.get_parent_item(fn_call_id)) {
901 hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(sig, ..), .. })
902 | hir::Node::TraitItem(hir::TraitItem {
904 kind: hir::TraitItemKind::Fn(sig, _),
907 | hir::Node::ImplItem(hir::ImplItem {
909 kind: hir::ImplItemKind::Fn(sig, _),
912 err.span_label(ident.span, "");
914 sig.decl.output.span(),
915 "change this to return `FnMut` instead of `Fn`",
917 err.span_label(self.body.span, "in this closure");
925 fn mut_borrow_of_mutable_ref(local_decl: &LocalDecl<'_>, local_name: Option<Symbol>) -> bool {
926 debug!("local_info: {:?}, ty.kind(): {:?}", local_decl.local_info, local_decl.ty.kind());
928 match local_decl.local_info.as_deref() {
929 // Check if mutably borrowing a mutable reference.
930 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::Var(
931 mir::VarBindingForm {
932 binding_mode: ty::BindingMode::BindByValue(Mutability::Not), ..
934 )))) => matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut)),
935 Some(LocalInfo::User(ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(kind)))) => {
936 // Check if the user variable is a `&mut self` and we can therefore
937 // suggest removing the `&mut`.
939 // Deliberately fall into this case for all implicit self types,
940 // so that we don't fall in to the next case with them.
941 *kind == mir::ImplicitSelfKind::MutRef
943 _ if Some(kw::SelfLower) == local_name => {
944 // Otherwise, check if the name is the `self` keyword - in which case
945 // we have an explicit self. Do the same thing in this case and check
946 // for a `self: &mut Self` to suggest removing the `&mut`.
947 matches!(local_decl.ty.kind(), ty::Ref(_, _, hir::Mutability::Mut))
953 fn suggest_ampmut_self<'tcx>(
955 local_decl: &mir::LocalDecl<'tcx>,
956 ) -> (Span, String) {
957 let sp = local_decl.source_info.span;
960 match tcx.sess.source_map().span_to_snippet(sp) {
962 let lt_pos = snippet.find('\'');
963 if let Some(lt_pos) = lt_pos {
964 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
966 "&mut self".to_string()
969 _ => "&mut self".to_string(),
974 // When we want to suggest a user change a local variable to be a `&mut`, there
975 // are three potential "obvious" things to highlight:
977 // let ident [: Type] [= RightHandSideExpression];
978 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
981 // We can always fallback on highlighting the first. But chances are good that
982 // the user experience will be better if we highlight one of the others if possible;
983 // for example, if the RHS is present and the Type is not, then the type is going to
984 // be inferred *from* the RHS, which means we should highlight that (and suggest
985 // that they borrow the RHS mutably).
987 // This implementation attempts to emulate AST-borrowck prioritization
988 // by trying (3.), then (2.) and finally falling back on (1.).
989 fn suggest_ampmut<'tcx>(
991 local_decl: &mir::LocalDecl<'tcx>,
992 opt_assignment_rhs_span: Option<Span>,
993 opt_ty_info: Option<Span>,
994 ) -> (Span, String) {
995 if let Some(assignment_rhs_span) = opt_assignment_rhs_span {
996 if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
997 let is_mutbl = |ty: &str| -> bool {
998 if let Some(rest) = ty.strip_prefix("mut") {
999 match rest.chars().next() {
1001 Some(c) if c.is_whitespace() => true,
1006 // e.g. `&mutablevar`
1013 if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
1014 let lt_name = &src[1..ws_pos];
1015 let ty = src[ws_pos..].trim_start();
1017 return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
1019 } else if let Some(stripped) = src.strip_prefix('&') {
1020 let stripped = stripped.trim_start();
1021 if !is_mutbl(stripped) {
1022 return (assignment_rhs_span, format!("&mut {}", stripped));
1028 let highlight_span = match opt_ty_info {
1029 // if this is a variable binding with an explicit type,
1030 // try to highlight that for the suggestion.
1031 Some(ty_span) => ty_span,
1033 // otherwise, just highlight the span associated with
1034 // the (MIR) LocalDecl.
1035 None => local_decl.source_info.span,
1038 if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
1039 if let (true, Some(ws_pos)) = (src.starts_with("&'"), src.find(char::is_whitespace)) {
1040 let lt_name = &src[1..ws_pos];
1041 let ty = &src[ws_pos..];
1042 return (highlight_span, format!("&{} mut{}", lt_name, ty));
1046 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
1047 assert_eq!(ty_mut.mutbl, hir::Mutability::Not);
1050 if local_decl.ty.is_region_ptr() {
1051 format!("&mut {}", ty_mut.ty)
1053 format!("*mut {}", ty_mut.ty)
1058 fn is_closure_or_generator(ty: Ty<'_>) -> bool {
1059 ty.is_closure() || ty.is_generator()
1062 /// Adds a suggestion to a struct definition given a field access to a local.
1063 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
1066 /// LL | s: &'a String
1067 /// | ---------- use `&'a mut String` here to make mutable
1069 fn annotate_struct_field<'tcx>(
1073 ) -> Option<(Span, String)> {
1074 // Expect our local to be a reference to a struct of some kind.
1075 if let ty::Ref(_, ty, _) = ty.kind() {
1076 if let ty::Adt(def, _) = ty.kind() {
1077 let field = def.all_fields().nth(field.index())?;
1078 // Use the HIR types to construct the diagnostic message.
1079 let hir_id = tcx.hir().local_def_id_to_hir_id(field.did.as_local()?);
1080 let node = tcx.hir().find(hir_id)?;
1081 // Now we're dealing with the actual struct that we're going to suggest a change to,
1082 // we can expect a field that is an immutable reference to a type.
1083 if let hir::Node::Field(field) = node {
1084 if let hir::TyKind::Rptr(
1086 hir::MutTy { mutbl: hir::Mutability::Not, ref ty },
1089 // Get the snippets in two parts - the named lifetime (if there is one) and
1090 // type being referenced, that way we can reconstruct the snippet without loss
1092 let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
1093 let lifetime_snippet = if !lifetime.is_elided() {
1094 format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
1101 format!("&{}mut {}", lifetime_snippet, &*type_snippet,),
1111 /// If possible, suggest replacing `ref` with `ref mut`.
1112 fn suggest_ref_mut(tcx: TyCtxt<'_>, binding_span: Span) -> Option<String> {
1113 let hi_src = tcx.sess.source_map().span_to_snippet(binding_span).ok()?;
1114 if hi_src.starts_with("ref") && hi_src["ref".len()..].starts_with(rustc_lexer::is_whitespace) {
1115 let replacement = format!("ref mut{}", &hi_src["ref".len()..]);