]> git.proxmox.com Git - rustc.git/blobdiff - compiler/rustc_mir/src/borrow_check/diagnostics/mutability_errors.rs
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / compiler / rustc_mir / src / borrow_check / diagnostics / mutability_errors.rs
index 333ac0738d45be1fba045d2aba44fef7007abb58..28f6508cab2dadf449e32e0a5f3380b07a977a2c 100644 (file)
@@ -1,6 +1,7 @@
 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::{
@@ -376,15 +377,18 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
                                     opt_assignment_rhs_span.and_then(|span| span.desugaring_kind());
                                 match opt_desugaring_kind {
                                     // on for loops, RHS points to the iterator part
-                                    Some(DesugaringKind::ForLoop(_)) => Some((
-                                        false,
-                                        opt_assignment_rhs_span.unwrap(),
-                                        format!(
-                                            "this iterator yields `{SIGIL}` {DESC}s",
-                                            SIGIL = pointer_sigil,
-                                            DESC = pointer_desc
-                                        ),
-                                    )),
+                                    Some(DesugaringKind::ForLoop(_)) => {
+                                        self.suggest_similar_mut_method_for_for_loop(&mut err);
+                                        Some((
+                                            false,
+                                            opt_assignment_rhs_span.unwrap(),
+                                            format!(
+                                                "this iterator yields `{SIGIL}` {DESC}s",
+                                                SIGIL = pointer_sigil,
+                                                DESC = pointer_desc
+                                            ),
+                                        ))
+                                    }
                                     // don't create labels for compiler-generated spans
                                     Some(_) => None,
                                     None => {
@@ -506,35 +510,150 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
         the_place_err: PlaceRef<'tcx>,
         err: &mut DiagnosticBuilder<'_>,
     ) {
-        let id = id.expect_local();
-        let tables = tcx.typeck(id);
-        let hir_id = tcx.hir().local_def_id_to_hir_id(id);
-        let (span, place) = &tables.closure_kind_origins()[hir_id];
-        let reason = if let PlaceBase::Upvar(upvar_id) = place.base {
-            let upvar = ty::place_to_string_for_capture(tcx, place);
-            match tables.upvar_capture(upvar_id) {
-                ty::UpvarCapture::ByRef(ty::UpvarBorrow {
-                    kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
-                    ..
-                }) => {
-                    format!("mutable borrow of `{}`", upvar)
+        let closure_local_def_id = id.expect_local();
+        let tables = tcx.typeck(closure_local_def_id);
+        let closure_hir_id = tcx.hir().local_def_id_to_hir_id(closure_local_def_id);
+        if let Some((span, closure_kind_origin)) =
+            &tables.closure_kind_origins().get(closure_hir_id)
+        {
+            let reason = if let PlaceBase::Upvar(upvar_id) = closure_kind_origin.base {
+                let upvar = ty::place_to_string_for_capture(tcx, closure_kind_origin);
+                let root_hir_id = upvar_id.var_path.hir_id;
+                // we have a origin for this closure kind starting at this root variable so it's safe to unwrap here
+                let captured_places = tables.closure_min_captures[id].get(&root_hir_id).unwrap();
+
+                let origin_projection = closure_kind_origin
+                    .projections
+                    .iter()
+                    .map(|proj| proj.kind)
+                    .collect::<Vec<_>>();
+                let mut capture_reason = String::new();
+                for captured_place in captured_places {
+                    let captured_place_kinds = captured_place
+                        .place
+                        .projections
+                        .iter()
+                        .map(|proj| proj.kind)
+                        .collect::<Vec<_>>();
+                    if rustc_middle::ty::is_ancestor_or_same_capture(
+                        &captured_place_kinds,
+                        &origin_projection,
+                    ) {
+                        match captured_place.info.capture_kind {
+                            ty::UpvarCapture::ByRef(ty::UpvarBorrow {
+                                kind: ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow,
+                                ..
+                            }) => {
+                                capture_reason = format!("mutable borrow of `{}`", upvar);
+                            }
+                            ty::UpvarCapture::ByValue(_) => {
+                                capture_reason = format!("possible mutation of `{}`", upvar);
+                            }
+                            _ => bug!("upvar `{}` borrowed, but not mutably", upvar),
+                        }
+                        break;
+                    }
+                }
+                if capture_reason.is_empty() {
+                    bug!("upvar `{}` borrowed, but cannot find reason", upvar);
                 }
-                ty::UpvarCapture::ByValue(_) => {
-                    format!("possible mutation of `{}`", upvar)
+                capture_reason
+            } else {
+                bug!("not an upvar")
+            };
+            err.span_label(
+                *span,
+                format!(
+                    "calling `{}` requires mutable binding due to {}",
+                    self.describe_place(the_place_err).unwrap(),
+                    reason
+                ),
+            );
+        }
+    }
+
+    // Attempt to search similar mutable associated items for suggestion.
+    // In the future, attempt in all path but initially for RHS of for_loop
+    fn suggest_similar_mut_method_for_for_loop(&self, err: &mut DiagnosticBuilder<'_>) {
+        use hir::{
+            BodyId, Expr,
+            ExprKind::{Block, Call, DropTemps, Match, MethodCall},
+            HirId, ImplItem, ImplItemKind, Item, ItemKind,
+        };
+
+        fn maybe_body_id_of_fn(hir_map: &Map<'tcx>, id: HirId) -> Option<BodyId> {
+            match hir_map.find(id) {
+                Some(Node::Item(Item { kind: ItemKind::Fn(_, _, body_id), .. }))
+                | Some(Node::ImplItem(ImplItem { kind: ImplItemKind::Fn(_, body_id), .. })) => {
+                    Some(*body_id)
+                }
+                _ => None,
+            }
+        }
+        let hir_map = self.infcx.tcx.hir();
+        let mir_body_hir_id = self.mir_hir_id();
+        if let Some(fn_body_id) = maybe_body_id_of_fn(&hir_map, mir_body_hir_id) {
+            if let Block(
+                hir::Block {
+                    expr:
+                        Some(Expr {
+                            kind:
+                                DropTemps(Expr {
+                                    kind:
+                                        Match(
+                                            Expr {
+                                                kind:
+                                                    Call(
+                                                        _,
+                                                        [Expr {
+                                                            kind: MethodCall(path_segment, ..),
+                                                            hir_id,
+                                                            ..
+                                                        }, ..],
+                                                    ),
+                                                ..
+                                            },
+                                            ..,
+                                        ),
+                                    ..
+                                }),
+                            ..
+                        }),
+                    ..
+                },
+                _,
+            ) = hir_map.body(fn_body_id).value.kind
+            {
+                let opt_suggestions = path_segment
+                    .hir_id
+                    .map(|path_hir_id| self.infcx.tcx.typeck(path_hir_id.owner))
+                    .and_then(|typeck| typeck.type_dependent_def_id(*hir_id))
+                    .and_then(|def_id| self.infcx.tcx.impl_of_method(def_id))
+                    .map(|def_id| self.infcx.tcx.associated_items(def_id))
+                    .map(|assoc_items| {
+                        assoc_items
+                            .in_definition_order()
+                            .map(|assoc_item_def| assoc_item_def.ident)
+                            .filter(|&ident| {
+                                let original_method_ident = path_segment.ident;
+                                original_method_ident != ident
+                                    && ident
+                                        .as_str()
+                                        .starts_with(&original_method_ident.name.to_string())
+                            })
+                            .map(|ident| format!("{}()", ident))
+                    });
+
+                if let Some(suggestions) = opt_suggestions {
+                    err.span_suggestions(
+                        path_segment.ident.span,
+                        &format!("use mutable method"),
+                        suggestions,
+                        Applicability::MaybeIncorrect,
+                    );
                 }
-                val => bug!("upvar `{}` borrowed, but not mutably: {:?}", upvar, val),
             }
-        } else {
-            bug!("not an upvar")
         };
-        err.span_label(
-            *span,
-            format!(
-                "calling `{}` requires mutable binding due to {}",
-                self.describe_place(the_place_err).unwrap(),
-                reason
-            ),
-        );
     }
 
     /// Targeted error when encountering an `FnMut` closure where an `Fn` closure was expected.