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 {
30 impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
31 pub(super) fn report_mutability_error(
33 access_place: &Place<'tcx>,
35 the_place_err: &Place<'tcx>,
36 error_access: AccessKind,
40 "report_mutability_error(\
41 access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
43 access_place, span, the_place_err, error_access, location,
49 let access_place_desc = self.describe_place(access_place);
50 debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
53 Place::Local(local) => {
54 item_msg = format!("`{}`", access_place_desc.unwrap());
55 if let Place::Local(_) = access_place {
56 reason = ", as it is not declared as mutable".to_string();
58 let name = self.mir.local_decls[*local]
60 .expect("immutable unnamed local");
61 reason = format!(", as `{}` is not declared as mutable", name);
65 Place::Projection(box Projection {
67 elem: ProjectionElem::Field(upvar_index, _),
69 debug_assert!(is_closure_or_generator(
70 base.ty(self.mir, self.tcx).to_ty(self.tcx)
73 item_msg = format!("`{}`", access_place_desc.unwrap());
74 if access_place.is_upvar_field_projection(self.mir, &self.tcx).is_some() {
75 reason = ", as it is not declared as mutable".to_string();
77 let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
78 reason = format!(", as `{}` is not declared as mutable", name);
82 Place::Projection(box Projection {
84 elem: ProjectionElem::Deref,
86 if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() {
87 item_msg = format!("`{}`", access_place_desc.unwrap());
88 debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
89 debug_assert!(is_closure_or_generator(
90 the_place_err.ty(self.mir, self.tcx).to_ty(self.tcx)
93 reason = if access_place.is_upvar_field_projection(self.mir,
94 &self.tcx).is_some() {
95 ", as it is a captured variable in a `Fn` closure".to_string()
97 ", as `Fn` closures cannot mutate their captured variables".to_string()
100 if let Place::Local(local) = *base {
101 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
102 = self.mir.local_decls[local].is_user_variable {
111 item_msg = format!("`{}`", access_place_desc.unwrap());
112 reason = ", as it is immutable for the pattern guard".to_string();
115 if base.ty(self.mir, self.tcx).to_ty(self.tcx).is_region_ptr() {
120 if let Some(desc) = access_place_desc {
121 item_msg = format!("`{}`", desc);
122 reason = match error_access {
124 AccessKind::Mutate => format!(" which is behind a {}", pointer_type),
125 AccessKind::MutableBorrow => {
126 format!(", as it is behind a {}", pointer_type)
130 item_msg = format!("data in a {}", pointer_type);
131 reason = "".to_string();
136 Place::Promoted(_) => unreachable!(),
138 Place::Static(box Static { def_id, ty: _ }) => {
139 if let Place::Static(_) = access_place {
140 item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
141 reason = "".to_string();
143 item_msg = format!("`{}`", access_place_desc.unwrap());
144 let static_name = &self.tcx.item_name(*def_id);
145 reason = format!(", as `{}` is an immutable static item", static_name);
149 Place::Projection(box Projection {
151 elem: ProjectionElem::Index(_),
153 | Place::Projection(box Projection {
155 elem: ProjectionElem::ConstantIndex { .. },
157 | Place::Projection(box Projection {
159 elem: ProjectionElem::Subslice { .. },
161 | Place::Projection(box Projection {
163 elem: ProjectionElem::Downcast(..),
164 }) => bug!("Unexpected immutable place."),
167 debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
169 // `act` and `acted_on` are strings that let us abstract over
170 // the verbs used in some diagnostic messages.
174 let span = match error_access {
175 AccessKind::Move => {
177 .cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir);
182 AccessKind::Mutate => {
184 .cannot_assign(span, &(item_msg + &reason), Origin::Mir);
186 acted_on = "written";
189 AccessKind::MutableBorrow => {
190 act = "borrow as mutable";
191 acted_on = "borrowed as mutable";
193 let borrow_spans = self.borrow_spans(span, location);
194 let borrow_span = borrow_spans.args_or_use();
195 err = self.tcx.cannot_borrow_path_as_mutable_because(
201 borrow_spans.var_span_label(
204 "mutable borrow occurs due to use of `{}` in closure",
205 // always Some() if the message is printed.
206 self.describe_place(access_place).unwrap_or(String::new()),
213 debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
215 match the_place_err {
216 // We want to suggest users use `let mut` for local (user
217 // variable) mutations...
218 Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
219 // ... but it doesn't make sense to suggest it on
220 // variables that are `ref x`, `ref mut x`, `&self`,
221 // or `&mut self` (such variables are simply not
223 let local_decl = &self.mir.local_decls[*local];
224 assert_eq!(local_decl.mutability, Mutability::Not);
226 err.span_label(span, format!("cannot {ACT}", ACT = act));
228 local_decl.source_info.span,
229 "consider changing this to be mutable",
230 format!("mut {}", local_decl.name.unwrap()),
234 // Also suggest adding mut for upvars
235 Place::Projection(box Projection {
237 elem: ProjectionElem::Field(upvar_index, _),
239 debug_assert!(is_closure_or_generator(
240 base.ty(self.mir, self.tcx).to_ty(self.tcx)
243 err.span_label(span, format!("cannot {ACT}", ACT = act));
245 let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
247 .assert_crate_local();
248 let upvar_node_id = self.tcx.hir.hir_to_node_id(upvar_hir_id);
249 if let Some(hir::map::NodeBinding(pat)) = self.tcx.hir.find(upvar_node_id) {
250 if let hir::PatKind::Binding(
251 hir::BindingAnnotation::Unannotated,
259 "consider changing this to be mutable",
260 format!("mut {}", upvar_ident.name),
266 // complete hack to approximate old AST-borrowck
267 // diagnostic: if the span starts with a mutable borrow of
268 // a local variable, then just suggest the user remove it.
271 if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
272 snippet.starts_with("&mut ")
278 err.span_label(span, format!("cannot {ACT}", ACT = act));
279 err.span_label(span, "try removing `&mut` here");
282 Place::Projection(box Projection {
283 base: Place::Local(local),
284 elem: ProjectionElem::Deref,
286 if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
287 self.mir.local_decls[*local].is_user_variable
295 err.span_label(span, format!("cannot {ACT}", ACT = act));
297 "variables bound in patterns are immutable until the end of the pattern guard",
301 // We want to point out when a `&` can be readily replaced
304 // FIXME: can this case be generalized to work for an
305 // arbitrary base for the projection?
306 Place::Projection(box Projection {
307 base: Place::Local(local),
308 elem: ProjectionElem::Deref,
309 }) if self.mir.local_decls[*local].is_user_variable.is_some() =>
311 let local_decl = &self.mir.local_decls[*local];
312 let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
313 ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf) => {
314 Some(suggest_ampmut_self(self.tcx, local_decl))
317 ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
318 binding_mode: ty::BindingMode::BindByValue(_),
321 })) => Some(suggest_ampmut(
329 ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
330 binding_mode: ty::BindingMode::BindByReference(_),
333 let pattern_span = local_decl.source_info.span;
334 suggest_ref_mut(self.tcx, pattern_span)
335 .map(|replacement| (pattern_span, replacement))
339 ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),
341 ClearCrossCrate::Clear => bug!("saw cleared local state"),
344 let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
347 ("*const", "pointer")
350 if let Some((err_help_span, suggested_code)) = suggestion {
353 &format!("consider changing this to be a mutable {}", pointer_desc),
358 if let Some(name) = local_decl.name {
362 "`{NAME}` is a `{SIGIL}` {DESC}, \
363 so the data it refers to cannot be {ACTED_ON}",
365 SIGIL = pointer_sigil,
374 "cannot {ACT} through `{SIGIL}` {DESC}",
376 SIGIL = pointer_sigil,
383 Place::Projection(box Projection {
385 elem: ProjectionElem::Deref,
386 }) if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() =>
388 err.span_label(span, format!("cannot {ACT}", ACT = act));
391 "consider changing this to accept closures that implement `FnMut`"
396 err.span_label(span, format!("cannot {ACT}", ACT = act));
400 err.buffer(&mut self.errors_buffer);
404 fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
405 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
406 local_decl: &mir::LocalDecl<'tcx>,
407 ) -> (Span, String) {
408 let sp = local_decl.source_info.span;
409 (sp, match tcx.sess.codemap().span_to_snippet(sp) {
411 let lt_pos = snippet.find('\'');
412 if let Some(lt_pos) = lt_pos {
413 format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
415 "&mut self".to_string()
418 _ => "&mut self".to_string()
422 // When we want to suggest a user change a local variable to be a `&mut`, there
423 // are three potential "obvious" things to highlight:
425 // let ident [: Type] [= RightHandSideExpression];
426 // ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
429 // We can always fallback on highlighting the first. But chances are good that
430 // the user experience will be better if we highlight one of the others if possible;
431 // for example, if the RHS is present and the Type is not, then the type is going to
432 // be inferred *from* the RHS, which means we should highlight that (and suggest
433 // that they borrow the RHS mutably).
435 // This implementation attempts to emulate AST-borrowck prioritization
436 // by trying (3.), then (2.) and finally falling back on (1.).
437 fn suggest_ampmut<'cx, 'gcx, 'tcx>(
438 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
441 local_decl: &mir::LocalDecl<'tcx>,
442 opt_ty_info: Option<Span>,
443 ) -> (Span, String) {
444 let locations = mir.find_assignments(local);
445 if locations.len() > 0 {
446 let assignment_rhs_span = mir.source_info(locations[0]).span;
447 if let Ok(src) = tcx.sess.codemap().span_to_snippet(assignment_rhs_span) {
448 if let (true, Some(ws_pos)) = (
449 src.starts_with("&'"),
450 src.find(|c: char| -> bool { c.is_whitespace() }),
452 let lt_name = &src[1..ws_pos];
453 let ty = &src[ws_pos..];
454 return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
455 } else if src.starts_with('&') {
456 let borrowed_expr = src[1..].to_string();
457 return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
462 let highlight_span = match opt_ty_info {
463 // if this is a variable binding with an explicit type,
464 // try to highlight that for the suggestion.
465 Some(ty_span) => ty_span,
467 // otherwise, just highlight the span associated with
468 // the (MIR) LocalDecl.
469 None => local_decl.source_info.span,
472 if let Ok(src) = tcx.sess.codemap().span_to_snippet(highlight_span) {
473 if let (true, Some(ws_pos)) = (
474 src.starts_with("&'"),
475 src.find(|c: char| -> bool { c.is_whitespace() }),
477 let lt_name = &src[1..ws_pos];
478 let ty = &src[ws_pos..];
479 return (highlight_span, format!("&{} mut{}", lt_name, ty));
483 let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
484 assert_eq!(ty_mut.mutbl, hir::MutImmutable);
486 if local_decl.ty.is_region_ptr() {
487 format!("&mut {}", ty_mut.ty)
489 format!("*mut {}", ty_mut.ty)
493 fn is_closure_or_generator(ty: ty::Ty) -> bool {
494 ty.is_closure() || ty.is_generator()