about summary refs log tree commit diff
path: root/src/librustc_borrowck
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2016-11-29 17:27:00 -0600
committerGitHub <noreply@github.com>2016-11-29 17:27:00 -0600
commitfa0005f2d5a9081597867b383c9b2d0b21569b88 (patch)
tree7b15f4a632de3064f87803ce6244b1ffaa15c2de /src/librustc_borrowck
parentb30022a1d32ebbd41f6623ab6fe7c62d489548e1 (diff)
parent67a24c2e184b8c7f515d52225b8f67f94abe6ff2 (diff)
downloadrust-fa0005f2d5a9081597867b383c9b2d0b21569b88.tar.gz
rust-fa0005f2d5a9081597867b383c9b2d0b21569b88.zip
Auto merge of #37863 - mikhail-m1:mut_error, r=nikomatsakis
add hint to fix error for immutable ref in arg

fix  #36412 part of #35233
r? @jonathandturner
Diffstat (limited to 'src/librustc_borrowck')
-rw-r--r--src/librustc_borrowck/borrowck/mod.rs129
1 files changed, 83 insertions, 46 deletions
diff --git a/src/librustc_borrowck/borrowck/mod.rs b/src/librustc_borrowck/borrowck/mod.rs
index 34e91e60074..ecf5c3ef176 100644
--- a/src/librustc_borrowck/borrowck/mod.rs
+++ b/src/librustc_borrowck/borrowck/mod.rs
@@ -24,7 +24,7 @@ use self::InteriorKind::*;
 
 use rustc::dep_graph::DepNode;
 use rustc::hir::map as hir_map;
-use rustc::hir::map::blocks::FnParts;
+use rustc::hir::map::blocks::{FnParts, FnLikeNode};
 use rustc::cfg;
 use rustc::middle::dataflow::DataFlowContext;
 use rustc::middle::dataflow::BitwiseOperator;
@@ -978,51 +978,8 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
 
     pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>,
         error_span: Span) {
-        let code = err.code;
-        match code {
-            err_mutbl => {
-                match err.cmt.note {
-                    mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
-                        // If this is an `Fn` closure, it simply can't mutate upvars.
-                        // If it's an `FnMut` closure, the original variable was declared immutable.
-                        // We need to determine which is the case here.
-                        let kind = match err.cmt.upvar().unwrap().cat {
-                            Categorization::Upvar(mc::Upvar { kind, .. }) => kind,
-                            _ => bug!()
-                        };
-                        if kind == ty::ClosureKind::Fn {
-                            db.span_help(
-                                self.tcx.map.span(upvar_id.closure_expr_id),
-                                "consider changing this closure to take \
-                                 self by mutable reference");
-                        }
-                    }
-                    _ => {
-                        if let Categorization::Local(local_id) = err.cmt.cat {
-                            let span = self.tcx.map.span(local_id);
-                            if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
-                                if snippet.starts_with("ref mut ") || snippet.starts_with("&mut ") {
-                                    db.span_label(error_span, &format!("cannot reborrow mutably"));
-                                    db.span_label(error_span, &format!("try removing `&mut` here"));
-                                } else {
-                                    if snippet.starts_with("ref ") {
-                                        db.span_label(span,
-                                            &format!("use `{}` here to make mutable",
-                                                snippet.replace("ref ", "ref mut ")));
-                                    } else if snippet != "self" {
-                                        db.span_label(span,
-                                            &format!("use `mut {}` here to make mutable", snippet));
-                                    }
-                                    db.span_label(error_span, &format!("cannot borrow mutably"));
-                                }
-                            } else {
-                                db.span_label(error_span, &format!("cannot borrow mutably"));
-                            }
-                        }
-                    }
-                }
-            }
-
+        match err.code {
+            err_mutbl => self.note_and_explain_mutbl_error(db, &err, &error_span),
             err_out_of_scope(super_scope, sub_scope, cause) => {
                 let (value_kind, value_msg) = match err.cmt.cat {
                     mc::Categorization::Rvalue(_) =>
@@ -1143,6 +1100,86 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
         }
     }
 
+    fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder, err: &BckError<'tcx>,
+                                    error_span: &Span) {
+        match err.cmt.note {
+            mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
+                // If this is an `Fn` closure, it simply can't mutate upvars.
+                // If it's an `FnMut` closure, the original variable was declared immutable.
+                // We need to determine which is the case here.
+                let kind = match err.cmt.upvar().unwrap().cat {
+                    Categorization::Upvar(mc::Upvar { kind, .. }) => kind,
+                    _ => bug!()
+                };
+                if kind == ty::ClosureKind::Fn {
+                    db.span_help(self.tcx.map.span(upvar_id.closure_expr_id),
+                                 "consider changing this closure to take \
+                                 self by mutable reference");
+                }
+            }
+            _ => {
+                if let Categorization::Deref(ref inner_cmt, ..) = err.cmt.cat {
+                    if let Categorization::Local(local_id) = inner_cmt.cat {
+                        let parent = self.tcx.map.get_parent_node(local_id);
+                        let opt_fn_decl = FnLikeNode::from_node(self.tcx.map.get(parent))
+                            .map(|fn_like| fn_like.decl());
+
+                        if let Some(fn_decl) = opt_fn_decl {
+                            if let Some(ref arg) = fn_decl.inputs.iter()
+                                .find(|ref arg| arg.pat.id == local_id) {
+                                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 ")));
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                } else if let Categorization::Local(local_id) = err.cmt.cat {
+                    let span = self.tcx.map.span(local_id);
+                    if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
+                        if snippet.starts_with("ref mut ") || snippet.starts_with("&mut ") {
+                            db.span_label(*error_span, &format!("cannot reborrow mutably"));
+                            db.span_label(*error_span, &format!("try removing `&mut` here"));
+                        } else {
+                            if snippet.starts_with("ref ") {
+                                db.span_label(span, &format!("use `{}` here to make mutable",
+                                                             snippet.replace("ref ", "ref mut ")));
+                            } else if snippet != "self" {
+                                db.span_label(span,
+                                              &format!("use `mut {}` here to make mutable",
+                                                       snippet));
+                            }
+                            db.span_label(*error_span, &format!("cannot borrow mutably"));
+                        }
+                    } else {
+                        db.span_label(*error_span, &format!("cannot borrow mutably"));
+                    }
+                }
+            }
+        }
+    }
     pub fn append_loan_path_to_string(&self,
                                       loan_path: &LoanPath<'tcx>,
                                       out: &mut String) {