3 use rustc::mir::{self, BindingForm, Constant, ClearCrossCrate, Local, Location, Mir};
5 Mutability, Operand, Place, PlaceBase, Projection, ProjectionElem, Static, StaticKind,
7 use rustc::mir::{Terminator, TerminatorKind};
8 use rustc::ty::{self, Const, DefIdTree, TyS, TyKind, TyCtxt};
9 use rustc_data_structures::indexed_vec::Idx;
11 use syntax_pos::symbol::keywords;
13 use crate::dataflow::move_paths::InitLocation;
14 use crate::borrow_check::MirBorrowckCtxt;
15 use crate::util::borrowck_errors::{BorrowckErrors, Origin};
16 use crate::util::collect_writes::FindAssignments;
17 use crate::util::suggest_ref_mut;
18 use rustc_errors::Applicability;
20 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
21 pub(super) enum AccessKind {
27 impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
28 pub(super) fn report_mutability_error(
30 access_place: &Place<'tcx>,
32 the_place_err: &Place<'tcx>,
33 error_access: AccessKind,
37 "report_mutability_error(\
38 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
40 access_place, span, the_place_err, error_access, location,
46 let access_place_desc = self.describe_place(access_place);
47 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
50 Place::Base(PlaceBase::Local(local)) => {
51 item_msg = format!("`{}`", access_place_desc.unwrap());
52 if let Place::Base(PlaceBase::Local(_)) = access_place {
53 reason = ", as it is not declared as mutable".to_string();
55 let name = self.mir.local_decls[*local]
57 .expect("immutable unnamed local");
58 reason = format!(", as `{}` is not declared as mutable", name);
62 Place::Projection(box Projection {
64 elem: ProjectionElem::Field(upvar_index, _),
66 debug_assert!(is_closure_or_generator(
67 base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
70 item_msg = format!("`{}`", access_place_desc.unwrap());
71 if access_place.is_upvar_field_projection(self.mir, &self.infcx.tcx).is_some() {
72 reason = ", as it is not declared as mutable".to_string();
74 let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
75 reason = format!(", as `{}` is not declared as mutable", name);
79 Place::Projection(box Projection {
81 elem: ProjectionElem::Deref,
83 if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
84 !self.mir.upvar_decls.is_empty() {
85 item_msg = format!("`{}`", access_place_desc.unwrap());
86 debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
87 debug_assert!(is_closure_or_generator(
88 the_place_err.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
91 reason = if access_place.is_upvar_field_projection(self.mir,
92 &self.infcx.tcx).is_some() {
93 ", as it is a captured variable in a `Fn` closure".to_string()
95 ", as `Fn` closures cannot mutate their captured variables".to_string()
98 if let Place::Base(PlaceBase::Local(local)) = *base {
99 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
100 = self.mir.local_decls[local].is_user_variable {
109 item_msg = format!("`{}`", access_place_desc.unwrap());
110 reason = ", as it is immutable for the pattern guard".to_string();
113 if base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx).is_region_ptr() {
118 if let Some(desc) = access_place_desc {
119 item_msg = format!("`{}`", desc);
120 reason = match error_access {
122 AccessKind::Mutate => format!(" which is behind a {}", pointer_type),
123 AccessKind::MutableBorrow => {
124 format!(", as it is behind a {}", pointer_type)
128 item_msg = format!("data in a {}", pointer_type);
129 reason = String::new();
134 Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Promoted(_), .. })) =>
137 Place::Base(PlaceBase::Static(box Static { kind: StaticKind::Static(def_id), .. })) => {
138 if let Place::Base(PlaceBase::Static(_)) = access_place {
139 item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
140 reason = String::new();
142 item_msg = format!("`{}`", access_place_desc.unwrap());
143 let static_name = &self.infcx.tcx.item_name(*def_id);
144 reason = format!(", as `{}` is an immutable static item", static_name);
148 Place::Projection(box Projection {
150 elem: ProjectionElem::Index(_),
152 | Place::Projection(box Projection {
154 elem: ProjectionElem::ConstantIndex { .. },
156 | Place::Projection(box Projection {
158 elem: ProjectionElem::Subslice { .. },
160 | Place::Projection(box Projection {
162 elem: ProjectionElem::Downcast(..),
163 }) => bug!("Unexpected immutable place."),
166 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
168 // `act` and `acted_on` are strings that let us abstract over
169 // the verbs used in some diagnostic messages.
173 let span = match error_access {
174 AccessKind::Move => {
176 .cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir);
177 err.span_label(span, "cannot move");
178 err.buffer(&mut self.errors_buffer);
181 AccessKind::Mutate => {
183 .cannot_assign(span, &(item_msg + &reason), Origin::Mir);
185 acted_on = "written";
188 AccessKind::MutableBorrow => {
189 act = "borrow as mutable";
190 acted_on = "borrowed as mutable";
192 let borrow_spans = self.borrow_spans(span, location);
193 let borrow_span = borrow_spans.args_or_use();
194 err = self.infcx.tcx.cannot_borrow_path_as_mutable_because(
200 borrow_spans.var_span_label(
203 "mutable borrow occurs due to use of `{}` in closure",
204 // always Some() if the message is printed.
205 self.describe_place(access_place).unwrap_or_default(),
212 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
214 match the_place_err {
215 // Suggest making an existing shared borrow in a struct definition a mutable borrow.
217 // This is applicable when we have a deref of a field access to a deref of a local -
218 // something like `*((*_1).0`. The local that we get will be a reference to the
219 // struct we've got a field access of (it must be a reference since there's a deref
220 // after the field access).
221 Place::Projection(box Projection {
222 base: Place::Projection(box Projection {
223 base: Place::Projection(box Projection {
225 elem: ProjectionElem::Deref,
227 elem: ProjectionElem::Field(field, _),
229 elem: ProjectionElem::Deref,
231 err.span_label(span, format!("cannot {ACT}", ACT = act));
233 if let Some((span, message)) = annotate_struct_field(
235 base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx),
240 "consider changing this to be mutable",
242 Applicability::MaybeIncorrect,
247 // Suggest removing a `&mut` from the use of a mutable reference.
248 Place::Base(PlaceBase::Local(local))
250 self.mir.local_decls.get(*local).map(|local_decl| {
251 if let ClearCrossCrate::Set(
252 mir::BindingForm::ImplicitSelf(kind)
253 ) = local_decl.is_user_variable.as_ref().unwrap() {
254 // Check if the user variable is a `&mut self` and we can therefore
255 // suggest removing the `&mut`.
257 // Deliberately fall into this case for all implicit self types,
258 // so that we don't fall in to the next case with them.
259 *kind == mir::ImplicitSelfKind::MutRef
260 } else if Some(keywords::SelfLower.name()) == local_decl.name {
261 // Otherwise, check if the name is the self kewyord - in which case
262 // we have an explicit self. Do the same thing in this case and check
263 // for a `self: &mut Self` to suggest removing the `&mut`.
264 if let ty::TyKind::Ref(
265 _, _, hir::Mutability::MutMutable
266 ) = local_decl.ty.sty {
277 err.span_label(span, format!("cannot {ACT}", ACT = act));
278 err.span_label(span, "try removing `&mut` here");
281 // We want to suggest users use `let mut` for local (user
282 // variable) mutations...
283 Place::Base(PlaceBase::Local(local))
284 if self.mir.local_decls[*local].can_be_made_mutable() => {
285 // ... but it doesn't make sense to suggest it on
286 // variables that are `ref x`, `ref mut x`, `&self`,
287 // or `&mut self` (such variables are simply not
289 let local_decl = &self.mir.local_decls[*local];
290 assert_eq!(local_decl.mutability, Mutability::Not);
292 err.span_label(span, format!("cannot {ACT}", ACT = act));
294 local_decl.source_info.span,
295 "consider changing this to be mutable",
296 format!("mut {}", local_decl.name.unwrap()),
297 Applicability::MachineApplicable,
301 // Also suggest adding mut for upvars
302 Place::Projection(box Projection {
304 elem: ProjectionElem::Field(upvar_index, _),
306 debug_assert!(is_closure_or_generator(
307 base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
310 err.span_label(span, format!("cannot {ACT}", ACT = act));
312 let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
314 .assert_crate_local();
315 let upvar_node_id = self.infcx.tcx.hir().hir_to_node_id(upvar_hir_id);
316 if let Some(Node::Binding(pat)) = self.infcx.tcx.hir().find(upvar_node_id) {
317 if let hir::PatKind::Binding(
318 hir::BindingAnnotation::Unannotated,
326 "consider changing this to be mutable",
327 format!("mut {}", upvar_ident.name),
328 Applicability::MachineApplicable,
334 // complete hack to approximate old AST-borrowck
335 // diagnostic: if the span starts with a mutable borrow of
336 // a local variable, then just suggest the user remove it.
337 Place::Base(PlaceBase::Local(_))
339 if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
340 snippet.starts_with("&mut ")
346 err.span_label(span, format!("cannot {ACT}", ACT = act));
347 err.span_label(span, "try removing `&mut` here");
350 Place::Projection(box Projection {
351 base: Place::Base(PlaceBase::Local(local)),
352 elem: ProjectionElem::Deref,
354 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
355 self.mir.local_decls[*local].is_user_variable
363 err.span_label(span, format!("cannot {ACT}", ACT = act));
365 "variables bound in patterns are immutable until the end of the pattern guard",
369 // We want to point out when a `&` can be readily replaced
372 // FIXME: can this case be generalized to work for an
373 // arbitrary base for the projection?
374 Place::Projection(box Projection {
375 base: Place::Base(PlaceBase::Local(local)),
376 elem: ProjectionElem::Deref,
377 }) if self.mir.local_decls[*local].is_user_variable.is_some() =>
379 let local_decl = &self.mir.local_decls[*local];
380 let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
381 ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_)) => {
382 Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
385 ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
386 binding_mode: ty::BindingMode::BindByValue(_),
389 })) => Some(suggest_ampmut(
397 ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
398 binding_mode: ty::BindingMode::BindByReference(_),
401 let pattern_span = local_decl.source_info.span;
402 suggest_ref_mut(self.infcx.tcx, pattern_span)
403 .map(|replacement| (pattern_span, replacement))
406 ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),
408 ClearCrossCrate::Clear => bug!("saw cleared local state"),
411 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
414 ("*const", "pointer")
417 if let Some((err_help_span, suggested_code)) = suggestion {
420 &format!("consider changing this to be a mutable {}", pointer_desc),
422 Applicability::MachineApplicable,
426 if let Some(name) = local_decl.name {
430 "`{NAME}` is a `{SIGIL}` {DESC}, \
431 so the data it refers to cannot be {ACTED_ON}",
433 SIGIL = pointer_sigil,
442 "cannot {ACT} through `{SIGIL}` {DESC}",
444 SIGIL = pointer_sigil,
451 Place::Projection(box Projection {
453 elem: ProjectionElem::Deref,
454 }) if *base == Place::Base(PlaceBase::Local(Local::new(1))) &&
455 !self.mir.upvar_decls.is_empty() =>
457 err.span_label(span, format!("cannot {ACT}", ACT = act));
460 "consider changing this to accept closures that implement `FnMut`"
464 Place::Projection(box Projection {
465 base: Place::Base(PlaceBase::Local(local)),
466 elem: ProjectionElem::Deref,
467 }) if error_access == AccessKind::MutableBorrow => {
468 err.span_label(span, format!("cannot {ACT}", ACT = act));
470 let mpi = self.move_data.rev_lookup.find_local(*local);
471 for i in self.move_data.init_path_map[mpi].iter() {
472 if let InitLocation::Statement(location) = self.move_data.inits[*i].location {
475 kind: TerminatorKind::Call {
476 func: Operand::Constant(box Constant {
479 sty: TyKind::FnDef(id, substs),
490 ) = &self.mir.basic_blocks()[location.block].terminator {
491 let index_trait = self.infcx.tcx.lang_items().index_trait();
492 if self.infcx.tcx.parent(id) == index_trait {
493 let mut found = false;
494 self.infcx.tcx.for_each_relevant_impl(
495 self.infcx.tcx.lang_items().index_mut_trait().unwrap(),
502 let extra = if found {
505 format!(", but it is not implemented for `{}`",
511 "trait `IndexMut` is required to modify indexed content{}",
522 err.span_label(span, format!("cannot {ACT}", ACT = act));
526 err.buffer(&mut self.errors_buffer);
530 fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
531 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
532 local_decl: &mir::LocalDecl<'tcx>,
533 ) -> (Span, String) {
534 let sp = local_decl.source_info.span;
535 (sp, match tcx.sess.source_map().span_to_snippet(sp) {
537 let lt_pos = snippet.find('\'');
538 if let Some(lt_pos) = lt_pos {
539 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
541 "&mut self".to_string()
544 _ => "&mut self".to_string()
548 // When we want to suggest a user change a local variable to be a `&mut`, there
549 // are three potential "obvious" things to highlight:
551 // let ident [: Type] [= RightHandSideExpression];
552 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
555 // We can always fallback on highlighting the first. But chances are good that
556 // the user experience will be better if we highlight one of the others if possible;
557 // for example, if the RHS is present and the Type is not, then the type is going to
558 // be inferred *from* the RHS, which means we should highlight that (and suggest
559 // that they borrow the RHS mutably).
561 // This implementation attempts to emulate AST-borrowck prioritization
562 // by trying (3.), then (2.) and finally falling back on (1.).
563 fn suggest_ampmut<'cx, 'gcx, 'tcx>(
564 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
567 local_decl: &mir::LocalDecl<'tcx>,
568 opt_ty_info: Option<Span>,
569 ) -> (Span, String) {
570 let locations = mir.find_assignments(local);
571 if !locations.is_empty() {
572 let assignment_rhs_span = mir.source_info(locations[0]).span;
573 if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
574 if let (true, Some(ws_pos)) = (
575 src.starts_with("&'"),
576 src.find(|c: char| -> bool { c.is_whitespace() }),
578 let lt_name = &src[1..ws_pos];
579 let ty = &src[ws_pos..];
580 return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
581 } else if src.starts_with('&') {
582 let borrowed_expr = &src[1..];
583 return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
588 let highlight_span = match opt_ty_info {
589 // if this is a variable binding with an explicit type,
590 // try to highlight that for the suggestion.
591 Some(ty_span) => ty_span,
593 // otherwise, just highlight the span associated with
594 // the (MIR) LocalDecl.
595 None => local_decl.source_info.span,
598 if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
599 if let (true, Some(ws_pos)) = (
600 src.starts_with("&'"),
601 src.find(|c: char| -> bool { c.is_whitespace() }),
603 let lt_name = &src[1..ws_pos];
604 let ty = &src[ws_pos..];
605 return (highlight_span, format!("&{} mut{}", lt_name, ty));
609 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
610 assert_eq!(ty_mut.mutbl, hir::MutImmutable);
612 if local_decl.ty.is_region_ptr() {
613 format!("&mut {}", ty_mut.ty)
615 format!("*mut {}", ty_mut.ty)
619 fn is_closure_or_generator(ty: ty::Ty<'_>) -> bool {
620 ty.is_closure() || ty.is_generator()
623 /// Adds a suggestion to a struct definition given a field access to a local.
624 /// This function expects the local to be a reference to a struct in order to produce a suggestion.
627 /// LL | s: &'a String
628 /// | ---------- use `&'a mut String` here to make mutable
630 fn annotate_struct_field(
631 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
634 ) -> Option<(Span, String)> {
635 // Expect our local to be a reference to a struct of some kind.
636 if let ty::TyKind::Ref(_, ty, _) = ty.sty {
637 if let ty::TyKind::Adt(def, _) = ty.sty {
638 let field = def.all_fields().nth(field.index())?;
639 // Use the HIR types to construct the diagnostic message.
640 let hir_id = tcx.hir().as_local_hir_id(field.did)?;
641 let node = tcx.hir().find_by_hir_id(hir_id)?;
642 // Now we're dealing with the actual struct that we're going to suggest a change to,
643 // we can expect a field that is an immutable reference to a type.
644 if let hir::Node::Field(field) = node {
645 if let hir::TyKind::Rptr(lifetime, hir::MutTy {
646 mutbl: hir::Mutability::MutImmutable,
649 // Get the snippets in two parts - the named lifetime (if there is one) and
650 // type being referenced, that way we can reconstruct the snippet without loss
652 let type_snippet = tcx.sess.source_map().span_to_snippet(ty.span).ok()?;
653 let lifetime_snippet = if !lifetime.is_elided() {
654 format!("{} ", tcx.sess.source_map().span_to_snippet(lifetime.span).ok()?)
663 lifetime_snippet, &*type_snippet,