1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 use rustc::mir::{self, BindingForm, ClearCrossCrate, Local, Location, Mir};
13 use rustc::mir::{Mutability, Place, Projection, ProjectionElem, Static};
14 use rustc::ty::{self, TyCtxt};
15 use rustc_data_structures::indexed_vec::Idx;
18 use borrow_check::MirBorrowckCtxt;
19 use util::borrowck_errors::{BorrowckErrors, Origin};
20 use util::collect_writes::FindAssignments;
21 use util::suggest_ref_mut;
23 #[derive(Copy, Clone, Debug)]
24 pub(super) enum AccessKind {
29 impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
30 pub(super) fn report_mutability_error(
32 access_place: &Place<'tcx>,
34 the_place_err: &Place<'tcx>,
35 error_access: AccessKind,
41 let access_place_desc = self.describe_place(access_place);
44 Place::Local(local) => {
45 item_msg = format!("`{}`", access_place_desc.unwrap());
46 if let Place::Local(_) = access_place {
47 reason = ", as it is not declared as mutable".to_string();
49 let name = self.mir.local_decls[*local]
51 .expect("immutable unnamed local");
52 reason = format!(", as `{}` is not declared as mutable", name);
56 Place::Projection(box Projection {
58 elem: ProjectionElem::Field(upvar_index, _),
60 debug_assert!(is_closure_or_generator(
61 base.ty(self.mir, self.tcx).to_ty(self.tcx)
64 item_msg = format!("`{}`", access_place_desc.unwrap());
65 if self.is_upvar(access_place) {
66 reason = ", as it is not declared as mutable".to_string();
68 let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
69 reason = format!(", as `{}` is not declared as mutable", name);
73 Place::Projection(box Projection {
75 elem: ProjectionElem::Deref,
77 if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() {
78 item_msg = format!("`{}`", access_place_desc.unwrap());
79 debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
80 debug_assert!(is_closure_or_generator(
81 the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx)
84 reason = if self.is_upvar(access_place) {
85 ", as it is a captured variable in a `Fn` closure".to_string()
87 format!(", as `Fn` closures cannot mutate their captured variables")
90 if let Place::Local(local) = *base {
91 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
92 = self.mir.local_decls[local].is_user_variable {
101 item_msg = format!("`{}`", access_place_desc.unwrap());
102 reason = format!(", as it is immutable for the pattern guard");
105 if base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() {
110 if let Some(desc) = access_place_desc {
111 item_msg = format!("`{}`", desc);
112 reason = match error_access {
113 AccessKind::Mutate => format!(" which is behind a {}", pointer_type),
114 AccessKind::MutableBorrow => {
115 format!(", as it is behind a {}", pointer_type)
119 item_msg = format!("data in a {}", pointer_type);
120 reason = "".to_string();
125 Place::Promoted(_) => unreachable!(),
127 Place::Static(box Static { def_id, ty: _ }) => {
128 if let Place::Static(_) = access_place {
129 item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
130 reason = "".to_string();
132 item_msg = format!("`{}`", access_place_desc.unwrap());
133 let static_name = &self.tcx.item_name(*def_id);
134 reason = format!(", as `{}` is an immutable static item", static_name);
138 Place::Projection(box Projection {
140 elem: ProjectionElem::Index(_),
142 | Place::Projection(box Projection {
144 elem: ProjectionElem::ConstantIndex { .. },
146 | Place::Projection(box Projection {
148 elem: ProjectionElem::Subslice { .. },
150 | Place::Projection(box Projection {
152 elem: ProjectionElem::Downcast(..),
153 }) => bug!("Unexpected immutable place."),
156 // `act` and `acted_on` are strings that let us abstract over
157 // the verbs used in some diagnostic messages.
162 let span = match error_access {
163 AccessKind::Mutate => {
165 .cannot_assign(span, &(item_msg + &reason), Origin::Mir);
167 acted_on = "written";
170 AccessKind::MutableBorrow => {
171 act = "borrow as mutable";
172 acted_on = "borrowed as mutable";
174 let closure_span = self.find_closure_span(span, location);
175 if let Some((args, var)) = closure_span {
176 err = self.tcx.cannot_borrow_path_as_mutable_because(
185 "mutable borrow occurs due to use of `{}` in closure",
186 self.describe_place(access_place).unwrap(),
191 err = self.tcx.cannot_borrow_path_as_mutable_because(
202 match the_place_err {
203 // We want to suggest users use `let mut` for local (user
204 // variable) mutations...
205 Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
206 // ... but it doesn't make sense to suggest it on
207 // variables that are `ref x`, `ref mut x`, `&self`,
208 // or `&mut self` (such variables are simply not
210 let local_decl = &self.mir.local_decls[*local];
211 assert_eq!(local_decl.mutability, Mutability::Not);
213 err.span_label(span, format!("cannot {ACT}", ACT = act));
215 local_decl.source_info.span,
216 "consider changing this to be mutable",
217 format!("mut {}", local_decl.name.unwrap()),
221 // Also suggest adding mut for upvars
222 Place::Projection(box Projection {
224 elem: ProjectionElem::Field(upvar_index, _),
226 debug_assert!(is_closure_or_generator(
227 base.ty(self.mir, self.tcx).to_ty(self.tcx)
230 err.span_label(span, format!("cannot {ACT}", ACT = act));
232 let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
234 .assert_crate_local();
235 let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id);
236 if let Some(hir::map::NodeBinding(pat)) = self.tcx.hir.find(upvar_node_id) {
237 if let hir::PatKind::Binding(
238 hir::BindingAnnotation::Unannotated,
246 "consider changing this to be mutable",
247 format!("mut {}", upvar_ident.name),
253 // complete hack to approximate old AST-borrowck
254 // diagnostic: if the span starts with a mutable borrow of
255 // a local variable, then just suggest the user remove it.
258 if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
259 snippet.starts_with("&mut ")
265 err.span_label(span, format!("cannot {ACT}", ACT = act));
266 err.span_label(span, "try removing `&mut` here");
269 Place::Projection(box Projection {
270 base: Place::Local(local),
271 elem: ProjectionElem::Deref,
273 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
274 self.mir.local_decls[*local].is_user_variable
282 err.span_label(span, format!("cannot {ACT}", ACT = act));
284 "variables bound in patterns are immutable until the end of the pattern guard",
288 // We want to point out when a `&` can be readily replaced
291 // FIXME: can this case be generalized to work for an
292 // arbitrary base for the projection?
293 Place::Projection(box Projection {
294 base: Place::Local(local),
295 elem: ProjectionElem::Deref,
296 }) if self.mir.local_decls[*local].is_user_variable.is_some() =>
298 let local_decl = &self.mir.local_decls[*local];
299 let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
300 ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => {
301 Some(suggest_ampmut_self(local_decl))
304 ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
305 binding_mode: ty::BindingMode::BindByValue(_),
308 })) => Some(suggest_ampmut(
316 ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
317 binding_mode: ty::BindingMode::BindByReference(_),
319 })) => suggest_ref_mut(self.tcx, local_decl.source_info.span),
322 ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),
324 ClearCrossCrate::Clear => bug!("saw cleared local state"),
327 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
330 ("*const", "pointer")
333 if let Some((err_help_span, suggested_code)) = suggestion {
336 &format!("consider changing this to be a mutable {}", pointer_desc),
341 if let Some(name) = local_decl.name {
345 "`{NAME}` is a `{SIGIL}` {DESC}, \
346 so the data it refers to cannot be {ACTED_ON}",
348 SIGIL = pointer_sigil,
357 "cannot {ACT} through `{SIGIL}` {DESC}",
359 SIGIL = pointer_sigil,
366 Place::Projection(box Projection {
368 elem: ProjectionElem::Deref,
369 }) if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() =>
371 err.span_label(span, format!("cannot {ACT}", ACT = act));
374 "consider changing this to accept closures that implement `FnMut`"
379 err.span_label(span, format!("cannot {ACT}", ACT = act));
383 err.buffer(&mut self.errors_buffer);
386 // Does this place refer to what the user sees as an upvar
387 fn is_upvar(&self, place: &Place<'tcx>) -> bool {
389 Place::Projection(box Projection {
391 elem: ProjectionElem::Field(_, _),
393 let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
394 is_closure_or_generator(base_ty)
396 Place::Projection(box Projection {
398 Place::Projection(box Projection {
400 elem: ProjectionElem::Field(upvar_index, _),
402 elem: ProjectionElem::Deref,
404 let base_ty = base.ty(self.mir, self.tcx).to_ty(self.tcx);
405 is_closure_or_generator(base_ty) && self.mir.upvar_decls[upvar_index.index()].by_ref
412 fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(local_decl: &mir::LocalDecl<'tcx>) -> (Span, String) {
413 (local_decl.source_info.span, "&mut self".to_string())
416 // When we want to suggest a user change a local variable to be a `&mut`, there
417 // are three potential "obvious" things to highlight:
419 // let ident [: Type] [= RightHandSideExpression];
420 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
423 // We can always fallback on highlighting the first. But chances are good that
424 // the user experience will be better if we highlight one of the others if possible;
425 // for example, if the RHS is present and the Type is not, then the type is going to
426 // be inferred *from* the RHS, which means we should highlight that (and suggest
427 // that they borrow the RHS mutably).
429 // This implementation attempts to emulate AST-borrowck prioritization
430 // by trying (3.), then (2.) and finally falling back on (1.).
431 fn suggest_ampmut<'cx, 'gcx, 'tcx>(
432 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
435 local_decl: &mir::LocalDecl<'tcx>,
436 opt_ty_info: Option<Span>,
437 ) -> (Span, String) {
438 let locations = mir.find_assignments(local);
439 if locations.len() > 0 {
440 let assignment_rhs_span = mir.source_info(locations[0]).span;
441 let snippet = tcx.sess.codemap().span_to_snippet(assignment_rhs_span);
442 if let Ok(src) = snippet {
443 if src.starts_with('&') {
444 let borrowed_expr = src[1..].to_string();
445 return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
450 let highlight_span = match opt_ty_info {
451 // if this is a variable binding with an explicit type,
452 // try to highlight that for the suggestion.
453 Some(ty_span) => ty_span,
455 // otherwise, just highlight the span associated with
456 // the (MIR) LocalDecl.
457 None => local_decl.source_info.span,
460 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
461 assert_eq!(ty_mut.mutbl, hir::MutImmutable);
462 if local_decl.ty.is_region_ptr() {
463 (highlight_span, format!("&mut {}", ty_mut.ty))
465 (highlight_span, format!("*mut {}", ty_mut.ty))
469 fn is_closure_or_generator(ty: ty::Ty) -> bool {
470 ty.is_closure() || ty.is_generator()