]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
New upstream version 1.54.0+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / diagnostics / mutability_errors.rs
index d1fb999e518ca59b46dda2ce97d4193e2a51f0a8..bf5f2c0eec23edc8382fc0819410dd4811600948 100644 (file)
@@ -1,12 +1,11 @@
 use rustc_hir as hir;
 use rustc_hir::Node;
-use rustc_index::vec::Idx;
 use rustc_middle::hir::map::Map;
 use rustc_middle::mir::{Mutability, Place, PlaceRef, ProjectionElem};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_middle::{
     hir::place::PlaceBase,
-    mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, Location},
+    mir::{self, ClearCrossCrate, Local, LocalDecl, LocalInfo, LocalKind, Location},
 };
 use rustc_span::source_map::DesugaringKind;
 use rustc_span::symbol::{kw, Symbol};
@@ -85,7 +84,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                     if self.is_upvar_field_projection(access_place.as_ref()).is_some() {
                         reason = ", as it is not declared as mutable".to_string();
                     } else {
-                        let name = self.upvars[upvar_index.index()].name;
+                        let name = self.upvars[upvar_index.index()].place.to_string(self.infcx.tcx);
                         reason = format!(", as `{}` is not declared as mutable", name);
                     }
                 }
@@ -115,12 +114,14 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
             PlaceRef { local: _, projection: [proj_base @ .., ProjectionElem::Deref] } => {
-                if the_place_err.local == Local::new(1)
+                if the_place_err.local == ty::CAPTURE_STRUCT_LOCAL
                     && proj_base.is_empty()
                     && !self.upvars.is_empty()
                 {
                     item_msg = format!("`{}`", access_place_desc.unwrap());
-                    debug_assert!(self.body.local_decls[Local::new(1)].ty.is_region_ptr());
+                    debug_assert!(
+                        self.body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty.is_region_ptr()
+                    );
                     debug_assert!(is_closure_or_generator(
                         Place::ty_from(
                             the_place_err.local,
@@ -195,6 +196,7 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                         "mutable borrow occurs due to use of {} in closure",
                         self.describe_any_place(access_place.as_ref()),
                     ),
+                    "mutable",
                 );
                 borrow_span
             }
@@ -423,15 +425,28 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
 
                         match label {
                             Some((true, err_help_span, suggested_code)) => {
-                                err.span_suggestion(
-                                    err_help_span,
-                                    &format!(
-                                        "consider changing this to be a mutable {}",
-                                        pointer_desc
-                                    ),
-                                    suggested_code,
-                                    Applicability::MachineApplicable,
-                                );
+                                let (is_trait_sig, local_trait) = self.is_error_in_trait(local);
+                                if !is_trait_sig {
+                                    err.span_suggestion(
+                                        err_help_span,
+                                        &format!(
+                                            "consider changing this to be a mutable {}",
+                                            pointer_desc
+                                        ),
+                                        suggested_code,
+                                        Applicability::MachineApplicable,
+                                    );
+                                } else if let Some(x) = local_trait {
+                                    err.span_suggestion(
+                                        x,
+                                        &format!(
+                                            "consider changing that to be a mutable {}",
+                                            pointer_desc
+                                        ),
+                                        suggested_code,
+                                        Applicability::MachineApplicable,
+                                    );
+                                }
                             }
                             Some((false, err_label_span, message)) => {
                                 err.span_label(err_label_span, &message);
@@ -464,11 +479,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                 }
             }
 
-            PlaceRef {
-                local,
-                projection: [ProjectionElem::Deref],
-                // FIXME document what is this 1 magic number about
-            } if local == Local::new(1) && !self.upvars.is_empty() => {
+            PlaceRef { local, projection: [ProjectionElem::Deref] }
+                if local == ty::CAPTURE_STRUCT_LOCAL && !self.upvars.is_empty() =>
+            {
                 self.expected_fn_found_fn_mut_call(&mut err, span, act);
             }
 
@@ -502,6 +515,65 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         err.buffer(&mut self.errors_buffer);
     }
 
+    /// User cannot make signature of a trait mutable without changing the
+    /// trait. So we find if this error belongs to a trait and if so we move
+    /// suggestion to the trait or disable it if it is out of scope of this crate
+    fn is_error_in_trait(&self, local: Local) -> (bool, Option<Span>) {
+        if self.body.local_kind(local) != LocalKind::Arg {
+            return (false, None);
+        }
+        let hir_map = self.infcx.tcx.hir();
+        let my_def = self.body.source.def_id();
+        let my_hir = hir_map.local_def_id_to_hir_id(my_def.as_local().unwrap());
+        let td = if let Some(a) =
+            self.infcx.tcx.impl_of_method(my_def).and_then(|x| self.infcx.tcx.trait_id_of_impl(x))
+        {
+            a
+        } else {
+            return (false, None);
+        };
+        (
+            true,
+            td.as_local().and_then(|tld| {
+                let h = hir_map.local_def_id_to_hir_id(tld);
+                match hir_map.find(h) {
+                    Some(Node::Item(hir::Item {
+                        kind: hir::ItemKind::Trait(_, _, _, _, items),
+                        ..
+                    })) => {
+                        let mut f_in_trait_opt = None;
+                        for hir::TraitItemRef { id: fi, kind: k, .. } in *items {
+                            let hi = fi.hir_id();
+                            if !matches!(k, hir::AssocItemKind::Fn { .. }) {
+                                continue;
+                            }
+                            if hir_map.name(hi) != hir_map.name(my_hir) {
+                                continue;
+                            }
+                            f_in_trait_opt = Some(hi);
+                            break;
+                        }
+                        f_in_trait_opt.and_then(|f_in_trait| match hir_map.find(f_in_trait) {
+                            Some(Node::TraitItem(hir::TraitItem {
+                                kind:
+                                    hir::TraitItemKind::Fn(
+                                        hir::FnSig { decl: hir::FnDecl { inputs, .. }, .. },
+                                        _,
+                                    ),
+                                ..
+                            })) => {
+                                let hir::Ty { span, .. } = inputs[local.index() - 1];
+                                Some(span)
+                            }
+                            _ => None,
+                        })
+                    }
+                    _ => None,
+                }
+            }),
+        )
+    }
+
     // point to span of upvar making closure call require mutable borrow
     fn show_mutating_upvar(
         &self,
@@ -830,9 +902,13 @@ fn suggest_ampmut<'tcx>(
             {
                 let lt_name = &src[1..ws_pos];
                 let ty = &src[ws_pos..];
-                return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
+                if !ty.trim_start().starts_with("mut") {
+                    return (assignment_rhs_span, format!("&{} mut {}", lt_name, ty));
+                }
             } else if let Some(stripped) = src.strip_prefix('&') {
-                return (assignment_rhs_span, format!("&mut {}", stripped));
+                if !stripped.trim_start().starts_with("mut") {
+                    return (assignment_rhs_span, format!("&mut {}", stripped));
+                }
             }
         }
     }