2018-07-15 15:11:29 +01:00
|
|
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
|
//
|
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
|
// except according to those terms.
|
|
|
|
|
|
|
|
|
|
use rustc::hir;
|
2018-08-25 15:56:16 +01:00
|
|
|
use rustc::hir::Node;
|
2018-08-30 19:46:27 +02:00
|
|
|
use rustc::mir::{self, BindingForm, Constant, ClearCrossCrate, Local, Location, Mir};
|
|
|
|
|
use rustc::mir::{Mutability, Operand, Place, Projection, ProjectionElem, Static, Terminator};
|
|
|
|
|
use rustc::mir::TerminatorKind;
|
|
|
|
|
use rustc::ty::{self, Const, DefIdTree, TyS, TyKind, TyCtxt};
|
2018-07-15 15:11:29 +01:00
|
|
|
use rustc_data_structures::indexed_vec::Idx;
|
|
|
|
|
use syntax_pos::Span;
|
|
|
|
|
|
2018-08-30 18:14:54 +02:00
|
|
|
use dataflow::move_paths::InitLocation;
|
2018-07-15 15:11:29 +01:00
|
|
|
use borrow_check::MirBorrowckCtxt;
|
|
|
|
|
use util::borrowck_errors::{BorrowckErrors, Origin};
|
|
|
|
|
use util::collect_writes::FindAssignments;
|
|
|
|
|
use util::suggest_ref_mut;
|
2018-09-15 03:18:29 +03:00
|
|
|
use rustc_errors::Applicability;
|
2018-07-15 15:11:29 +01:00
|
|
|
|
2018-08-30 19:46:27 +02:00
|
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
2018-07-15 15:11:29 +01:00
|
|
|
pub(super) enum AccessKind {
|
|
|
|
|
MutableBorrow,
|
|
|
|
|
Mutate,
|
2018-07-25 01:34:17 +02:00
|
|
|
Move,
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a, 'gcx, 'tcx> MirBorrowckCtxt<'a, 'gcx, 'tcx> {
|
|
|
|
|
pub(super) fn report_mutability_error(
|
|
|
|
|
&mut self,
|
|
|
|
|
access_place: &Place<'tcx>,
|
|
|
|
|
span: Span,
|
|
|
|
|
the_place_err: &Place<'tcx>,
|
|
|
|
|
error_access: AccessKind,
|
|
|
|
|
location: Location,
|
|
|
|
|
) {
|
2018-08-07 15:37:32 +02:00
|
|
|
debug!(
|
|
|
|
|
"report_mutability_error(\
|
|
|
|
|
access_place={:?}, span={:?}, the_place_err={:?}, error_access={:?}, location={:?},\
|
|
|
|
|
)",
|
|
|
|
|
access_place, span, the_place_err, error_access, location,
|
|
|
|
|
);
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
let mut err;
|
2018-07-15 15:51:35 +01:00
|
|
|
let item_msg;
|
|
|
|
|
let reason;
|
|
|
|
|
let access_place_desc = self.describe_place(access_place);
|
2018-08-07 15:37:32 +02:00
|
|
|
debug!("report_mutability_error: access_place_desc={:?}", access_place_desc);
|
2018-07-15 15:51:35 +01:00
|
|
|
|
|
|
|
|
match the_place_err {
|
|
|
|
|
Place::Local(local) => {
|
|
|
|
|
item_msg = format!("`{}`", access_place_desc.unwrap());
|
|
|
|
|
if let Place::Local(_) = access_place {
|
|
|
|
|
reason = ", as it is not declared as mutable".to_string();
|
|
|
|
|
} else {
|
|
|
|
|
let name = self.mir.local_decls[*local]
|
|
|
|
|
.name
|
|
|
|
|
.expect("immutable unnamed local");
|
|
|
|
|
reason = format!(", as `{}` is not declared as mutable", name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base,
|
|
|
|
|
elem: ProjectionElem::Field(upvar_index, _),
|
|
|
|
|
}) => {
|
|
|
|
|
debug_assert!(is_closure_or_generator(
|
2018-09-11 16:38:35 +02:00
|
|
|
base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
|
2018-07-15 15:51:35 +01:00
|
|
|
));
|
|
|
|
|
|
|
|
|
|
item_msg = format!("`{}`", access_place_desc.unwrap());
|
2018-09-11 16:38:35 +02:00
|
|
|
if access_place.is_upvar_field_projection(self.mir, &self.infcx.tcx).is_some() {
|
2018-07-15 15:51:35 +01:00
|
|
|
reason = ", as it is not declared as mutable".to_string();
|
|
|
|
|
} else {
|
|
|
|
|
let name = self.mir.upvar_decls[upvar_index.index()].debug_name;
|
|
|
|
|
reason = format!(", as `{}` is not declared as mutable", name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base,
|
|
|
|
|
elem: ProjectionElem::Deref,
|
|
|
|
|
}) => {
|
|
|
|
|
if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() {
|
|
|
|
|
item_msg = format!("`{}`", access_place_desc.unwrap());
|
|
|
|
|
debug_assert!(self.mir.local_decls[Local::new(1)].ty.is_region_ptr());
|
|
|
|
|
debug_assert!(is_closure_or_generator(
|
2018-09-11 16:38:35 +02:00
|
|
|
the_place_err.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
|
2018-07-15 15:51:35 +01:00
|
|
|
));
|
|
|
|
|
|
2018-08-07 17:06:21 +02:00
|
|
|
reason = if access_place.is_upvar_field_projection(self.mir,
|
2018-09-11 16:38:35 +02:00
|
|
|
&self.infcx.tcx).is_some() {
|
2018-07-15 15:51:35 +01:00
|
|
|
", as it is a captured variable in a `Fn` closure".to_string()
|
|
|
|
|
} else {
|
2018-07-28 14:40:32 +02:00
|
|
|
", as `Fn` closures cannot mutate their captured variables".to_string()
|
2018-07-15 15:51:35 +01:00
|
|
|
}
|
|
|
|
|
} else if {
|
|
|
|
|
if let Place::Local(local) = *base {
|
|
|
|
|
if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard))
|
|
|
|
|
= self.mir.local_decls[local].is_user_variable {
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
} {
|
|
|
|
|
item_msg = format!("`{}`", access_place_desc.unwrap());
|
2018-07-28 14:40:32 +02:00
|
|
|
reason = ", as it is immutable for the pattern guard".to_string();
|
2018-07-15 15:51:35 +01:00
|
|
|
} else {
|
|
|
|
|
let pointer_type =
|
2018-09-11 16:38:35 +02:00
|
|
|
if base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx).is_region_ptr() {
|
2018-07-15 15:51:35 +01:00
|
|
|
"`&` reference"
|
|
|
|
|
} else {
|
|
|
|
|
"`*const` pointer"
|
|
|
|
|
};
|
|
|
|
|
if let Some(desc) = access_place_desc {
|
|
|
|
|
item_msg = format!("`{}`", desc);
|
|
|
|
|
reason = match error_access {
|
2018-07-25 01:34:17 +02:00
|
|
|
AccessKind::Move |
|
2018-07-15 15:51:35 +01:00
|
|
|
AccessKind::Mutate => format!(" which is behind a {}", pointer_type),
|
|
|
|
|
AccessKind::MutableBorrow => {
|
|
|
|
|
format!(", as it is behind a {}", pointer_type)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
item_msg = format!("data in a {}", pointer_type);
|
2018-08-23 10:14:52 +02:00
|
|
|
reason = String::new();
|
2018-07-15 15:51:35 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-22 13:30:16 +02:00
|
|
|
Place::Promoted(_) => unreachable!(),
|
|
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
Place::Static(box Static { def_id, ty: _ }) => {
|
|
|
|
|
if let Place::Static(_) = access_place {
|
|
|
|
|
item_msg = format!("immutable static item `{}`", access_place_desc.unwrap());
|
2018-08-23 10:14:52 +02:00
|
|
|
reason = String::new();
|
2018-07-15 15:51:35 +01:00
|
|
|
} else {
|
|
|
|
|
item_msg = format!("`{}`", access_place_desc.unwrap());
|
2018-09-11 16:38:35 +02:00
|
|
|
let static_name = &self.infcx.tcx.item_name(*def_id);
|
2018-07-15 15:51:35 +01:00
|
|
|
reason = format!(", as `{}` is an immutable static item", static_name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base: _,
|
|
|
|
|
elem: ProjectionElem::Index(_),
|
|
|
|
|
})
|
|
|
|
|
| Place::Projection(box Projection {
|
|
|
|
|
base: _,
|
|
|
|
|
elem: ProjectionElem::ConstantIndex { .. },
|
|
|
|
|
})
|
|
|
|
|
| Place::Projection(box Projection {
|
|
|
|
|
base: _,
|
|
|
|
|
elem: ProjectionElem::Subslice { .. },
|
|
|
|
|
})
|
|
|
|
|
| Place::Projection(box Projection {
|
|
|
|
|
base: _,
|
|
|
|
|
elem: ProjectionElem::Downcast(..),
|
|
|
|
|
}) => bug!("Unexpected immutable place."),
|
|
|
|
|
}
|
2018-07-15 15:11:29 +01:00
|
|
|
|
2018-08-07 15:37:32 +02:00
|
|
|
debug!("report_mutability_error: item_msg={:?}, reason={:?}", item_msg, reason);
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
// `act` and `acted_on` are strings that let us abstract over
|
|
|
|
|
// the verbs used in some diagnostic messages.
|
|
|
|
|
let act;
|
|
|
|
|
let acted_on;
|
|
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
let span = match error_access {
|
2018-07-25 01:34:17 +02:00
|
|
|
AccessKind::Move => {
|
2018-09-11 16:38:35 +02:00
|
|
|
err = self.infcx.tcx
|
2018-07-25 01:34:17 +02:00
|
|
|
.cannot_move_out_of(span, &(item_msg + &reason), Origin::Mir);
|
|
|
|
|
act = "move";
|
|
|
|
|
acted_on = "moved";
|
|
|
|
|
span
|
|
|
|
|
}
|
2018-07-15 15:11:29 +01:00
|
|
|
AccessKind::Mutate => {
|
2018-09-11 16:38:35 +02:00
|
|
|
err = self.infcx.tcx
|
2018-07-15 15:51:35 +01:00
|
|
|
.cannot_assign(span, &(item_msg + &reason), Origin::Mir);
|
2018-07-15 15:11:29 +01:00
|
|
|
act = "assign";
|
|
|
|
|
acted_on = "written";
|
2018-07-15 15:51:35 +01:00
|
|
|
span
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|
|
|
|
|
AccessKind::MutableBorrow => {
|
|
|
|
|
act = "borrow as mutable";
|
|
|
|
|
acted_on = "borrowed as mutable";
|
2018-07-15 15:51:35 +01:00
|
|
|
|
2018-08-01 20:38:02 +01:00
|
|
|
let borrow_spans = self.borrow_spans(span, location);
|
|
|
|
|
let borrow_span = borrow_spans.args_or_use();
|
2018-09-11 16:38:35 +02:00
|
|
|
err = self.infcx.tcx.cannot_borrow_path_as_mutable_because(
|
2018-08-01 20:38:02 +01:00
|
|
|
borrow_span,
|
|
|
|
|
&item_msg,
|
|
|
|
|
&reason,
|
|
|
|
|
Origin::Mir,
|
|
|
|
|
);
|
|
|
|
|
borrow_spans.var_span_label(
|
|
|
|
|
&mut err,
|
|
|
|
|
format!(
|
|
|
|
|
"mutable borrow occurs due to use of `{}` in closure",
|
|
|
|
|
// always Some() if the message is printed.
|
|
|
|
|
self.describe_place(access_place).unwrap_or(String::new()),
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
borrow_span
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|
2018-07-15 15:51:35 +01:00
|
|
|
};
|
2018-07-15 15:11:29 +01:00
|
|
|
|
2018-08-07 15:37:32 +02:00
|
|
|
debug!("report_mutability_error: act={:?}, acted_on={:?}", act, acted_on);
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
match the_place_err {
|
|
|
|
|
// We want to suggest users use `let mut` for local (user
|
|
|
|
|
// variable) mutations...
|
|
|
|
|
Place::Local(local) if self.mir.local_decls[*local].can_be_made_mutable() => {
|
|
|
|
|
// ... but it doesn't make sense to suggest it on
|
|
|
|
|
// variables that are `ref x`, `ref mut x`, `&self`,
|
|
|
|
|
// or `&mut self` (such variables are simply not
|
2018-07-15 15:51:35 +01:00
|
|
|
// mutable).
|
2018-07-15 15:11:29 +01:00
|
|
|
let local_decl = &self.mir.local_decls[*local];
|
|
|
|
|
assert_eq!(local_decl.mutability, Mutability::Not);
|
|
|
|
|
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
2018-09-15 03:18:29 +03:00
|
|
|
err.span_suggestion_with_applicability(
|
2018-07-15 15:11:29 +01:00
|
|
|
local_decl.source_info.span,
|
|
|
|
|
"consider changing this to be mutable",
|
|
|
|
|
format!("mut {}", local_decl.name.unwrap()),
|
2018-09-17 03:16:08 +03:00
|
|
|
Applicability::MachineApplicable,
|
2018-07-15 15:11:29 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
// Also suggest adding mut for upvars
|
|
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base,
|
|
|
|
|
elem: ProjectionElem::Field(upvar_index, _),
|
|
|
|
|
}) => {
|
|
|
|
|
debug_assert!(is_closure_or_generator(
|
2018-09-11 16:38:35 +02:00
|
|
|
base.ty(self.mir, self.infcx.tcx).to_ty(self.infcx.tcx)
|
2018-07-15 15:51:35 +01:00
|
|
|
));
|
|
|
|
|
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
|
|
|
|
|
|
|
|
|
let upvar_hir_id = self.mir.upvar_decls[upvar_index.index()]
|
|
|
|
|
.var_hir_id
|
|
|
|
|
.assert_crate_local();
|
2018-09-11 16:38:35 +02:00
|
|
|
let upvar_node_id = self.infcx.tcx.hir.hir_to_node_id(upvar_hir_id);
|
|
|
|
|
if let Some(Node::Binding(pat)) = self.infcx.tcx.hir.find(upvar_node_id) {
|
2018-07-15 15:51:35 +01:00
|
|
|
if let hir::PatKind::Binding(
|
|
|
|
|
hir::BindingAnnotation::Unannotated,
|
|
|
|
|
_,
|
|
|
|
|
upvar_ident,
|
|
|
|
|
_,
|
|
|
|
|
) = pat.node
|
|
|
|
|
{
|
2018-09-15 03:18:29 +03:00
|
|
|
err.span_suggestion_with_applicability(
|
2018-07-15 15:51:35 +01:00
|
|
|
upvar_ident.span,
|
|
|
|
|
"consider changing this to be mutable",
|
|
|
|
|
format!("mut {}", upvar_ident.name),
|
2018-09-17 03:16:08 +03:00
|
|
|
Applicability::MachineApplicable,
|
2018-07-15 15:51:35 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
// complete hack to approximate old AST-borrowck
|
|
|
|
|
// diagnostic: if the span starts with a mutable borrow of
|
|
|
|
|
// a local variable, then just suggest the user remove it.
|
|
|
|
|
Place::Local(_)
|
|
|
|
|
if {
|
2018-09-11 16:38:35 +02:00
|
|
|
if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
|
2018-07-15 15:11:29 +01:00
|
|
|
snippet.starts_with("&mut ")
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
} =>
|
|
|
|
|
{
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
|
|
|
|
err.span_label(span, "try removing `&mut` here");
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base: Place::Local(local),
|
|
|
|
|
elem: ProjectionElem::Deref,
|
|
|
|
|
}) if {
|
|
|
|
|
if let Some(ClearCrossCrate::Set(BindingForm::RefForGuard)) =
|
|
|
|
|
self.mir.local_decls[*local].is_user_variable
|
|
|
|
|
{
|
|
|
|
|
true
|
|
|
|
|
} else {
|
|
|
|
|
false
|
|
|
|
|
}
|
|
|
|
|
} =>
|
|
|
|
|
{
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
|
|
|
|
err.note(
|
|
|
|
|
"variables bound in patterns are immutable until the end of the pattern guard",
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
// We want to point out when a `&` can be readily replaced
|
|
|
|
|
// with an `&mut`.
|
|
|
|
|
//
|
|
|
|
|
// FIXME: can this case be generalized to work for an
|
|
|
|
|
// arbitrary base for the projection?
|
|
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base: Place::Local(local),
|
|
|
|
|
elem: ProjectionElem::Deref,
|
2018-07-15 15:51:35 +01:00
|
|
|
}) if self.mir.local_decls[*local].is_user_variable.is_some() =>
|
|
|
|
|
{
|
2018-07-15 15:11:29 +01:00
|
|
|
let local_decl = &self.mir.local_decls[*local];
|
|
|
|
|
let suggestion = match local_decl.is_user_variable.as_ref().unwrap() {
|
2018-10-01 17:46:04 +02:00
|
|
|
ClearCrossCrate::Set(mir::BindingForm::ImplicitSelf(_)) => {
|
2018-09-11 16:38:35 +02:00
|
|
|
Some(suggest_ampmut_self(self.infcx.tcx, local_decl))
|
2018-07-15 15:51:35 +01:00
|
|
|
}
|
2018-07-15 15:11:29 +01:00
|
|
|
|
|
|
|
|
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
|
|
|
|
|
binding_mode: ty::BindingMode::BindByValue(_),
|
|
|
|
|
opt_ty_info,
|
|
|
|
|
..
|
|
|
|
|
})) => Some(suggest_ampmut(
|
2018-09-11 16:38:35 +02:00
|
|
|
self.infcx.tcx,
|
2018-07-15 15:11:29 +01:00
|
|
|
self.mir,
|
|
|
|
|
*local,
|
|
|
|
|
local_decl,
|
|
|
|
|
*opt_ty_info,
|
|
|
|
|
)),
|
|
|
|
|
|
|
|
|
|
ClearCrossCrate::Set(mir::BindingForm::Var(mir::VarBindingForm {
|
|
|
|
|
binding_mode: ty::BindingMode::BindByReference(_),
|
|
|
|
|
..
|
2018-08-07 01:02:39 -07:00
|
|
|
})) => {
|
|
|
|
|
let pattern_span = local_decl.source_info.span;
|
2018-09-11 16:38:35 +02:00
|
|
|
suggest_ref_mut(self.infcx.tcx, pattern_span)
|
2018-08-07 01:02:39 -07:00
|
|
|
.map(|replacement| (pattern_span, replacement))
|
|
|
|
|
}
|
2018-07-15 15:11:29 +01:00
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
//
|
|
|
|
|
ClearCrossCrate::Set(mir::BindingForm::RefForGuard) => unreachable!(),
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
ClearCrossCrate::Clear => bug!("saw cleared local state"),
|
|
|
|
|
};
|
|
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
let (pointer_sigil, pointer_desc) = if local_decl.ty.is_region_ptr() {
|
|
|
|
|
("&", "reference")
|
|
|
|
|
} else {
|
|
|
|
|
("*const", "pointer")
|
|
|
|
|
};
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
if let Some((err_help_span, suggested_code)) = suggestion {
|
2018-09-15 03:18:29 +03:00
|
|
|
err.span_suggestion_with_applicability(
|
2018-07-15 15:11:29 +01:00
|
|
|
err_help_span,
|
2018-07-15 15:51:35 +01:00
|
|
|
&format!("consider changing this to be a mutable {}", pointer_desc),
|
2018-07-15 15:11:29 +01:00
|
|
|
suggested_code,
|
2018-09-17 03:16:08 +03:00
|
|
|
Applicability::MachineApplicable,
|
2018-07-15 15:11:29 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if let Some(name) = local_decl.name {
|
|
|
|
|
err.span_label(
|
|
|
|
|
span,
|
|
|
|
|
format!(
|
2018-07-15 15:51:35 +01:00
|
|
|
"`{NAME}` is a `{SIGIL}` {DESC}, \
|
2018-07-15 15:11:29 +01:00
|
|
|
so the data it refers to cannot be {ACTED_ON}",
|
|
|
|
|
NAME = name,
|
2018-07-15 15:51:35 +01:00
|
|
|
SIGIL = pointer_sigil,
|
|
|
|
|
DESC = pointer_desc,
|
2018-07-15 15:11:29 +01:00
|
|
|
ACTED_ON = acted_on
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
} else {
|
|
|
|
|
err.span_label(
|
|
|
|
|
span,
|
2018-07-15 15:51:35 +01:00
|
|
|
format!(
|
|
|
|
|
"cannot {ACT} through `{SIGIL}` {DESC}",
|
|
|
|
|
ACT = act,
|
|
|
|
|
SIGIL = pointer_sigil,
|
|
|
|
|
DESC = pointer_desc
|
|
|
|
|
),
|
2018-07-15 15:11:29 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:51:35 +01:00
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base,
|
|
|
|
|
elem: ProjectionElem::Deref,
|
|
|
|
|
}) if *base == Place::Local(Local::new(1)) && !self.mir.upvar_decls.is_empty() =>
|
|
|
|
|
{
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
|
|
|
|
err.span_help(
|
|
|
|
|
self.mir.span,
|
|
|
|
|
"consider changing this to accept closures that implement `FnMut`"
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-30 19:46:27 +02:00
|
|
|
Place::Projection(box Projection {
|
|
|
|
|
base: Place::Local(local),
|
|
|
|
|
elem: ProjectionElem::Deref,
|
|
|
|
|
}) if error_access == AccessKind::MutableBorrow => {
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
|
|
|
|
|
|
|
|
|
let mpi = self.move_data.rev_lookup.find_local(*local);
|
|
|
|
|
for i in self.move_data.init_path_map[mpi].iter() {
|
|
|
|
|
if let InitLocation::Statement(location) = self.move_data.inits[*i].location {
|
|
|
|
|
if let Some(
|
|
|
|
|
Terminator {
|
|
|
|
|
kind: TerminatorKind::Call {
|
|
|
|
|
func: Operand::Constant(box Constant {
|
|
|
|
|
literal: Const {
|
|
|
|
|
ty: &TyS {
|
|
|
|
|
sty: TyKind::FnDef(id, substs),
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
..
|
|
|
|
|
}),
|
|
|
|
|
..
|
|
|
|
|
},
|
|
|
|
|
..
|
|
|
|
|
}
|
|
|
|
|
) = &self.mir.basic_blocks()[location.block].terminator {
|
2018-09-11 16:38:35 +02:00
|
|
|
let index_trait = self.infcx.tcx.lang_items().index_trait();
|
|
|
|
|
if self.infcx.tcx.parent(id) == index_trait {
|
2018-08-31 00:54:04 +02:00
|
|
|
let mut found = false;
|
2018-09-11 16:38:35 +02:00
|
|
|
self.infcx.tcx.for_each_relevant_impl(
|
|
|
|
|
self.infcx.tcx.lang_items().index_mut_trait().unwrap(),
|
2018-08-31 00:54:04 +02:00
|
|
|
substs.type_at(0),
|
|
|
|
|
|_relevant_impl| {
|
|
|
|
|
found = true;
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let extra = if found {
|
|
|
|
|
String::from("")
|
|
|
|
|
} else {
|
|
|
|
|
format!(", but it is not implemented for `{}`",
|
|
|
|
|
substs.type_at(0))
|
|
|
|
|
};
|
|
|
|
|
|
2018-08-30 19:46:27 +02:00
|
|
|
err.help(
|
|
|
|
|
&format!(
|
2018-08-31 00:54:04 +02:00
|
|
|
"trait `IndexMut` is required to modify indexed content{}",
|
|
|
|
|
extra,
|
2018-08-30 19:46:27 +02:00
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
_ => {
|
|
|
|
|
err.span_label(span, format!("cannot {ACT}", ACT = act));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-18 18:10:08 -03:00
|
|
|
err.buffer(&mut self.errors_buffer);
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-30 15:41:39 -07:00
|
|
|
fn suggest_ampmut_self<'cx, 'gcx, 'tcx>(
|
|
|
|
|
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
|
|
|
|
|
local_decl: &mir::LocalDecl<'tcx>,
|
|
|
|
|
) -> (Span, String) {
|
|
|
|
|
let sp = local_decl.source_info.span;
|
2018-08-18 12:14:09 +02:00
|
|
|
(sp, match tcx.sess.source_map().span_to_snippet(sp) {
|
2018-07-30 15:41:39 -07:00
|
|
|
Ok(snippet) => {
|
|
|
|
|
let lt_pos = snippet.find('\'');
|
|
|
|
|
if let Some(lt_pos) = lt_pos {
|
|
|
|
|
format!("&{}mut self", &snippet[lt_pos..snippet.len() - 4])
|
|
|
|
|
} else {
|
|
|
|
|
"&mut self".to_string()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => "&mut self".to_string()
|
|
|
|
|
})
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// When we want to suggest a user change a local variable to be a `&mut`, there
|
|
|
|
|
// are three potential "obvious" things to highlight:
|
|
|
|
|
//
|
|
|
|
|
// let ident [: Type] [= RightHandSideExpression];
|
|
|
|
|
// ^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
|
// (1.) (2.) (3.)
|
|
|
|
|
//
|
|
|
|
|
// We can always fallback on highlighting the first. But chances are good that
|
|
|
|
|
// the user experience will be better if we highlight one of the others if possible;
|
|
|
|
|
// for example, if the RHS is present and the Type is not, then the type is going to
|
|
|
|
|
// be inferred *from* the RHS, which means we should highlight that (and suggest
|
|
|
|
|
// that they borrow the RHS mutably).
|
|
|
|
|
//
|
|
|
|
|
// This implementation attempts to emulate AST-borrowck prioritization
|
|
|
|
|
// by trying (3.), then (2.) and finally falling back on (1.).
|
|
|
|
|
fn suggest_ampmut<'cx, 'gcx, 'tcx>(
|
|
|
|
|
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
|
|
|
|
|
mir: &Mir<'tcx>,
|
|
|
|
|
local: Local,
|
|
|
|
|
local_decl: &mir::LocalDecl<'tcx>,
|
|
|
|
|
opt_ty_info: Option<Span>,
|
|
|
|
|
) -> (Span, String) {
|
|
|
|
|
let locations = mir.find_assignments(local);
|
|
|
|
|
if locations.len() > 0 {
|
|
|
|
|
let assignment_rhs_span = mir.source_info(locations[0]).span;
|
2018-08-18 12:14:09 +02:00
|
|
|
if let Ok(src) = tcx.sess.source_map().span_to_snippet(assignment_rhs_span) {
|
2018-07-30 15:41:39 -07:00
|
|
|
if let (true, Some(ws_pos)) = (
|
|
|
|
|
src.starts_with("&'"),
|
|
|
|
|
src.find(|c: char| -> bool { c.is_whitespace() }),
|
|
|
|
|
) {
|
|
|
|
|
let lt_name = &src[1..ws_pos];
|
|
|
|
|
let ty = &src[ws_pos..];
|
|
|
|
|
return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
|
|
|
|
|
} else if src.starts_with('&') {
|
2018-07-15 15:11:29 +01:00
|
|
|
let borrowed_expr = src[1..].to_string();
|
2018-07-15 15:51:35 +01:00
|
|
|
return (assignment_rhs_span, format!("&mut {}", borrowed_expr));
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let highlight_span = match opt_ty_info {
|
|
|
|
|
// if this is a variable binding with an explicit type,
|
|
|
|
|
// try to highlight that for the suggestion.
|
|
|
|
|
Some(ty_span) => ty_span,
|
|
|
|
|
|
|
|
|
|
// otherwise, just highlight the span associated with
|
|
|
|
|
// the (MIR) LocalDecl.
|
|
|
|
|
None => local_decl.source_info.span,
|
|
|
|
|
};
|
|
|
|
|
|
2018-08-18 12:14:09 +02:00
|
|
|
if let Ok(src) = tcx.sess.source_map().span_to_snippet(highlight_span) {
|
2018-07-30 15:41:39 -07:00
|
|
|
if let (true, Some(ws_pos)) = (
|
|
|
|
|
src.starts_with("&'"),
|
|
|
|
|
src.find(|c: char| -> bool { c.is_whitespace() }),
|
|
|
|
|
) {
|
|
|
|
|
let lt_name = &src[1..ws_pos];
|
|
|
|
|
let ty = &src[ws_pos..];
|
|
|
|
|
return (highlight_span, format!("&{} mut{}", lt_name, ty));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-15 15:11:29 +01:00
|
|
|
let ty_mut = local_decl.ty.builtin_deref(true).unwrap();
|
|
|
|
|
assert_eq!(ty_mut.mutbl, hir::MutImmutable);
|
2018-07-30 15:41:39 -07:00
|
|
|
(highlight_span,
|
|
|
|
|
if local_decl.ty.is_region_ptr() {
|
|
|
|
|
format!("&mut {}", ty_mut.ty)
|
|
|
|
|
} else {
|
|
|
|
|
format!("*mut {}", ty_mut.ty)
|
|
|
|
|
})
|
2018-07-15 15:51:35 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn is_closure_or_generator(ty: ty::Ty) -> bool {
|
|
|
|
|
ty.is_closure() || ty.is_generator()
|
2018-07-15 15:11:29 +01:00
|
|
|
}
|