// Errors
// Errors that can occur
-#[derive(PartialEq)]
+#[derive(Debug, PartialEq)]
pub enum bckerr_code<'tcx> {
err_mutbl,
/// superscope, subscope, loan cause
// Combination of an error code and the categorization of the expression
// that caused it
-#[derive(PartialEq)]
+#[derive(Debug, PartialEq)]
pub struct BckError<'tcx> {
span: Span,
cause: AliasableViolationKind,
_ => { }
}
- // General fallback.
- let span = err.span.clone();
- let mut db = self.struct_span_err(
- err.span,
- &self.bckerr_to_string(&err));
- self.note_and_explain_bckerr(&mut db, err, span);
+ let mut db = self.bckerr_to_diag(&err);
+ self.note_and_explain_bckerr(&mut db, err);
db.emit();
}
self.tcx.sess.span_err_with_code(s, msg, code);
}
- pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
- match err.code {
+ pub fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
+ let span = err.span.clone();
+ let mut immutable_field = None;
+
+ let msg = &match err.code {
err_mutbl => {
let descr = match err.cmt.note {
mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
format!("{} {}",
err.cmt.mutbl.to_user_str(),
self.cmt_to_string(&err.cmt))
+
}
Some(lp) => {
format!("{} {} `{}`",
BorrowViolation(euv::AutoUnsafe) |
BorrowViolation(euv::ForLoop) |
BorrowViolation(euv::MatchDiscriminant) => {
+ // Check for this field's definition to see if it is an immutable reference
+ // and suggest making it mutable if that is the case.
+ immutable_field = err.cmt.get_field_name()
+ .and_then(|name| err.cmt.get_field(name))
+ .and_then(|did| self.tcx.hir.as_local_node_id(did))
+ .and_then(|nid| {
+ if let hir_map::Node::NodeField(ref field) = self.tcx.hir.get(nid) {
+ return self.suggest_mut_for_immutable(&field.ty)
+ .map(|msg| (self.tcx.hir.span(nid), msg));
+ }
+ None
+ });
+
format!("cannot borrow {} as mutable", descr)
}
BorrowViolation(euv::ClosureInvocation) => {
its contents can be safely reborrowed",
descr)
}
+ };
+
+ let mut db = self.struct_span_err(span, msg);
+ if let Some((span, msg)) = immutable_field {
+ db.span_label(span, &msg);
}
+ db
}
pub fn report_aliasability_violation(&self,
span: Span,
kind: AliasableViolationKind,
- cause: mc::AliasableReason) {
+ cause: mc::AliasableReason,
+ cmt: mc::cmt<'tcx>) {
let mut is_closure = false;
let prefix = match kind {
MutabilityViolation => {
self.tcx.sess, span, E0389,
"{} in a `&` reference", prefix);
e.span_label(span, &"assignment into an immutable reference");
+ if let Some(nid) = cmt.get_arg_if_immutable(&self.tcx.hir) {
+ self.immutable_argument_should_be_mut(nid, &mut e);
+ }
e
}
};
err.emit();
}
+ /// Given a type, if it is an immutable reference, return a suggestion to make it mutable
+ fn suggest_mut_for_immutable(&self, pty: &hir::Ty) -> Option<String> {
+ // Check wether the argument is an immutable reference
+ if let hir::TyRptr(lifetime, hir::MutTy {
+ mutbl: hir::Mutability::MutImmutable,
+ ref ty
+ }) = pty.node {
+ // Account for existing lifetimes when generating the message
+ if !lifetime.is_elided() {
+ if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(ty.span) {
+ if let Ok(lifetime_snippet) = self.tcx.sess.codemap()
+ .span_to_snippet(lifetime.span) {
+ return Some(format!("use `&{} mut {}` here to make mutable",
+ lifetime_snippet,
+ snippet));
+ }
+ }
+ } else if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(pty.span) {
+ if snippet.starts_with("&") {
+ return Some(format!("use `{}` here to make mutable",
+ snippet.replace("&", "&mut ")));
+ }
+ } else {
+ bug!("couldn't find a snippet for span: {:?}", pty.span);
+ }
+ }
+ None
+ }
+
+ fn immutable_argument_should_be_mut(&self, nid: ast::NodeId, db: &mut DiagnosticBuilder) {
+ let parent = self.tcx.hir.get_parent_node(nid);
+ let parent_node = self.tcx.hir.get(parent);
+
+ // The parent node is like a fn
+ if let Some(fn_like) = FnLikeNode::from_node(parent_node) {
+ // `nid`'s parent's `Body`
+ let fn_body = self.tcx.hir.body(fn_like.body());
+ // Get the position of `nid` in the arguments list
+ let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.id == nid);
+ if let Some(i) = arg_pos {
+ // The argument's `Ty`
+ let arg_ty = &fn_like.decl().inputs[i];
+ if let Some(msg) = self.suggest_mut_for_immutable(&arg_ty) {
+ db.span_label(arg_ty.span, &msg);
+ }
+ }
+ }
+ }
+
fn report_out_of_scope_escaping_closure_capture(&self,
err: &BckError<'tcx>,
capture_span: Span)
}
}
- pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>,
- error_span: Span) {
+ pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
+ let error_span = err.span.clone();
match err.code {
err_mutbl => self.note_and_explain_mutbl_error(db, &err, &error_span),
err_out_of_scope(super_scope, sub_scope, cause) => {
}
}
_ => {
- if let Categorization::Deref(ref inner_cmt, ..) = err.cmt.cat {
- if let Categorization::Local(local_id) = inner_cmt.cat {
- let parent = self.tcx.hir.get_parent_node(local_id);
-
- if let Some(fn_like) = FnLikeNode::from_node(self.tcx.hir.get(parent)) {
- if let Some(i) = self.tcx.hir.body(fn_like.body()).arguments.iter()
- .position(|arg| arg.pat.id == local_id) {
- let arg_ty = &fn_like.decl().inputs[i];
- if let hir::TyRptr(
- opt_lifetime,
- hir::MutTy{mutbl: hir::Mutability::MutImmutable, ref ty}) =
- arg_ty.node {
- if let Some(lifetime) = opt_lifetime {
- if let Ok(snippet) = self.tcx.sess.codemap()
- .span_to_snippet(ty.span) {
- if let Ok(lifetime_snippet) = self.tcx.sess.codemap()
- .span_to_snippet(lifetime.span) {
- db.span_label(arg_ty.span,
- &format!("use `&{} mut {}` \
- here to make mutable",
- lifetime_snippet,
- snippet));
- }
- }
- }
- else if let Ok(snippet) = self.tcx.sess.codemap()
- .span_to_snippet(arg_ty.span) {
- if snippet.starts_with("&") {
- db.span_label(arg_ty.span,
- &format!("use `{}` here to make mutable",
- snippet.replace("&", "&mut ")));
- }
- }
- }
- }
+ if let Categorization::Deref(..) = err.cmt.cat {
+ db.span_label(*error_span, &"cannot borrow as mutable");
+ if let Some(local_id) = err.cmt.get_arg_if_immutable(&self.tcx.hir) {
+ self.immutable_argument_should_be_mut(local_id, db);
+ } else if let Categorization::Deref(ref inner_cmt, ..) = err.cmt.cat {
+ if let Categorization::Local(local_id) = inner_cmt.cat {
+ self.immutable_argument_should_be_mut(local_id, db);
}
}
} else if let Categorization::Local(local_id) = err.cmt.cat {