about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs92
-rw-r--r--compiler/rustc_hir_typeck/src/method/probe.rs32
-rw-r--r--src/test/ui/suggestions/shadowed-lplace-method.fixed10
-rw-r--r--src/test/ui/suggestions/shadowed-lplace-method.rs10
-rw-r--r--src/test/ui/suggestions/shadowed-lplace-method.stderr23
5 files changed, 167 insertions, 0 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index 042ff0b46a5..f7f492863ab 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,5 +1,6 @@
 use crate::FnCtxt;
 use rustc_ast::util::parser::PREC_POSTFIX;
+use rustc_errors::MultiSpan;
 use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
@@ -36,6 +37,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             return;
         }
 
+        self.annotate_alternative_method_deref(err, expr, error);
+
         // Use `||` to give these suggestions a precedence
         let _ = self.suggest_missing_parentheses(err, expr)
             || self.suggest_remove_last_method_call(err, expr, expected)
@@ -316,6 +319,95 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn annotate_alternative_method_deref(
+        &self,
+        err: &mut Diagnostic,
+        expr: &hir::Expr<'_>,
+        error: Option<TypeError<'tcx>>,
+    ) {
+        let parent = self.tcx.hir().get_parent_node(expr.hir_id);
+        let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else {return;};
+        let Some(hir::Node::Expr(hir::Expr {
+                    kind: hir::ExprKind::Assign(lhs, rhs, _), ..
+                })) = self.tcx.hir().find(parent) else {return; };
+        if rhs.hir_id != expr.hir_id || expected.is_closure() {
+            return;
+        }
+        let hir::ExprKind::Unary(hir::UnOp::Deref, deref) = lhs.kind else { return; };
+        let hir::ExprKind::MethodCall(path, base, args, _) = deref.kind else { return; };
+        let self_ty = self.typeck_results.borrow().expr_ty_adjusted_opt(base).unwrap();
+        let pick = self
+            .probe_for_name(
+                probe::Mode::MethodCall,
+                path.ident,
+                probe::IsSuggestion(true),
+                self_ty,
+                deref.hir_id,
+                probe::ProbeScope::TraitsInScope,
+            )
+            .unwrap();
+        let methods = self.probe_for_name_many(
+            probe::Mode::MethodCall,
+            path.ident,
+            probe::IsSuggestion(true),
+            self_ty,
+            deref.hir_id,
+            probe::ProbeScope::AllTraits,
+        );
+        let suggestions: Vec<_> = methods
+            .into_iter()
+            .filter(|m| m.def_id != pick.item.def_id)
+            .map(|m| {
+                let substs = ty::InternalSubsts::for_item(self.tcx, m.def_id, |param, _| {
+                    self.var_for_def(deref.span, param)
+                });
+                vec![
+                    (
+                        deref.span.until(base.span),
+                        format!(
+                            "{}({}",
+                            with_no_trimmed_paths!(
+                                self.tcx.def_path_str_with_substs(m.def_id, substs,)
+                            ),
+                            match self.tcx.fn_sig(m.def_id).input(0).skip_binder().kind() {
+                                ty::Ref(_, _, hir::Mutability::Mut) => "&mut ",
+                                ty::Ref(_, _, _) => "&",
+                                _ => "",
+                            },
+                        ),
+                    ),
+                    match &args[..] {
+                        [] => (base.span.shrink_to_hi().with_hi(deref.span.hi()), ")".to_string()),
+                        [first, ..] => (base.span.until(first.span), String::new()),
+                    },
+                ]
+            })
+            .collect();
+        if suggestions.is_empty() {
+            return;
+        }
+        let mut path_span: MultiSpan = path.ident.span.into();
+        path_span.push_span_label(
+            path.ident.span,
+            format!(
+                "refers to `{}`",
+                with_no_trimmed_paths!(self.tcx.def_path_str(pick.item.def_id)),
+            ),
+        );
+        err.span_note(
+            path_span,
+            &format!(
+            "there are multiple methods with the same name, `{}` refers to `{}` in the method call",
+            path.ident,
+            with_no_trimmed_paths!(self.tcx.def_path_str(pick.item.def_id)),
+        ));
+        err.multipart_suggestions(
+            "you might have meant to invoke a different method, you can use the fully-qualified path",
+        suggestions,
+            Applicability::MaybeIncorrect,
+        );
+    }
+
     /// If the expected type is an enum (Issue #55250) with any variants whose
     /// sole field is of the found type, suggest such variants. (Issue #42764)
     fn suggest_compatible_variants(
diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs
index b9e7830bf07..a7574d4e1af 100644
--- a/compiler/rustc_hir_typeck/src/method/probe.rs
+++ b/compiler/rustc_hir_typeck/src/method/probe.rs
@@ -322,6 +322,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         )
     }
 
+    #[instrument(level = "debug", skip(self))]
+    pub fn probe_for_name_many(
+        &self,
+        mode: Mode,
+        item_name: Ident,
+        is_suggestion: IsSuggestion,
+        self_ty: Ty<'tcx>,
+        scope_expr_id: hir::HirId,
+        scope: ProbeScope,
+    ) -> Vec<ty::AssocItem> {
+        self.probe_op(
+            item_name.span,
+            mode,
+            Some(item_name),
+            None,
+            is_suggestion,
+            self_ty,
+            scope_expr_id,
+            scope,
+            |probe_cx| {
+                Ok(probe_cx
+                    .inherent_candidates
+                    .iter()
+                    .chain(&probe_cx.extension_candidates)
+                    // .filter(|candidate| candidate_filter(&candidate.item))
+                    .map(|candidate| candidate.item)
+                    .collect())
+            },
+        )
+        .unwrap()
+    }
+
     fn probe_op<OP, R>(
         &'a self,
         span: Span,
diff --git a/src/test/ui/suggestions/shadowed-lplace-method.fixed b/src/test/ui/suggestions/shadowed-lplace-method.fixed
new file mode 100644
index 00000000000..740ac77ee0c
--- /dev/null
+++ b/src/test/ui/suggestions/shadowed-lplace-method.fixed
@@ -0,0 +1,10 @@
+// run-rustfix
+#![allow(unused_imports)]
+use std::borrow::BorrowMut;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+fn main() {
+    let rc = Rc::new(RefCell::new(true));
+    *std::cell::RefCell::<_>::borrow_mut(&rc) = false; //~ ERROR E0308
+}
diff --git a/src/test/ui/suggestions/shadowed-lplace-method.rs b/src/test/ui/suggestions/shadowed-lplace-method.rs
new file mode 100644
index 00000000000..6bf12879e6f
--- /dev/null
+++ b/src/test/ui/suggestions/shadowed-lplace-method.rs
@@ -0,0 +1,10 @@
+// run-rustfix
+#![allow(unused_imports)]
+use std::borrow::BorrowMut;
+use std::cell::RefCell;
+use std::rc::Rc;
+
+fn main() {
+    let rc = Rc::new(RefCell::new(true));
+    *rc.borrow_mut() = false; //~ ERROR E0308
+}
diff --git a/src/test/ui/suggestions/shadowed-lplace-method.stderr b/src/test/ui/suggestions/shadowed-lplace-method.stderr
new file mode 100644
index 00000000000..080600128a3
--- /dev/null
+++ b/src/test/ui/suggestions/shadowed-lplace-method.stderr
@@ -0,0 +1,23 @@
+error[E0308]: mismatched types
+  --> $DIR/shadowed-lplace-method.rs:9:24
+   |
+LL |     *rc.borrow_mut() = false;
+   |     ----------------   ^^^^^ expected struct `Rc`, found `bool`
+   |     |
+   |     expected due to the type of this binding
+   |
+   = note: expected struct `Rc<RefCell<bool>>`
+                found type `bool`
+note: there are multiple methods with the same name, `borrow_mut` refers to `std::borrow::BorrowMut::borrow_mut` in the method call
+  --> $DIR/shadowed-lplace-method.rs:9:9
+   |
+LL |     *rc.borrow_mut() = false;
+   |         ^^^^^^^^^^ refers to `std::borrow::BorrowMut::borrow_mut`
+help: you might have meant to invoke a different method, you can use the fully-qualified path
+   |
+LL |     *std::cell::RefCell::<_>::borrow_mut(&rc) = false;
+   |      +++++++++++++++++++++++++++++++++++++  ~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.