Detect inherent method behind deref being shadowed by trait method

```
error[E0277]: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied
  --> $DIR/shadowed-intrinsic-method-deref.rs:16:22
   |
LL |     let sb : &S = &s.borrow();
   |                      ^^^^^^ the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
   |
help: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
      but trait `Borrow<RefCell<S>>` is implemented for it
  --> $SRC_DIR/alloc/src/rc.rs:LL:COL
   = help: for that trait implementation, expected `RefCell<S>`, found `S`
   = note: there's an inherent method on `RefCell<S>` of the same name, which can be auto-dereferenced from `&RefCell<T>`
help: to access the inherent method on `RefCell<S>`, use the fully-qualified path
   |
LL -     let sb : &S = &s.borrow();
LL +     let sb : &S = &RefCell::borrow(&s);
   |
```

In the example above, method `borrow` is available both on `<RefCell<S> as Borrow<S>>` *and* on `RefCell<S>`. Adding the import `use std::borrow::Borrow;` causes `s.borrow()` to find the former instead of the latter. We now point out that the other exists, and provide a suggestion on how to call it.
This commit is contained in:
Esteban Küber
2026-03-10 21:53:03 +00:00
parent 0c68443b0a
commit 73fe905f3c
7 changed files with 138 additions and 0 deletions
@@ -3178,6 +3178,7 @@ fn try_to_add_help_message(
self.suggest_tuple_wrapping(err, root_obligation, obligation);
}
self.suggest_shadowed_inherent_method(err, obligation, trait_predicate);
}
fn add_help_message_for_fn_trait(
@@ -4916,6 +4916,79 @@ pub(super) fn suggest_tuple_wrapping(
}
}
pub(super) fn suggest_shadowed_inherent_method(
&self,
err: &mut Diag<'_>,
obligation: &PredicateObligation<'tcx>,
trait_predicate: ty::PolyTraitPredicate<'tcx>,
) {
let ObligationCauseCode::FunctionArg { call_hir_id, .. } = obligation.cause.code() else {
return;
};
let Node::Expr(call) = self.tcx.hir_node(*call_hir_id) else { return };
let hir::ExprKind::MethodCall(segment, rcvr, args, ..) = call.kind else { return };
let Some(typeck) = &self.typeck_results else { return };
let Some(rcvr_ty) = typeck.expr_ty_adjusted_opt(rcvr) else { return };
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
let autoderef = (self.autoderef_steps)(rcvr_ty);
for (ty, def_id) in autoderef.iter().filter_map(|(ty, obligations)| {
if let ty::Adt(def, _) = ty.kind()
&& *ty != rcvr_ty.peel_refs()
&& obligations.iter().all(|obligation| self.predicate_may_hold(obligation))
{
Some((ty, def.did()))
} else {
None
}
}) {
for impl_def_id in self.tcx.inherent_impls(def_id) {
if *impl_def_id == trait_predicate.def_id() {
continue;
}
for m in self
.tcx
.provided_trait_methods(*impl_def_id)
.filter(|m| m.name() == segment.ident.name)
{
let fn_sig = self.tcx.fn_sig(m.def_id);
if fn_sig.skip_binder().inputs().skip_binder().len() != args.len() + 1 {
continue;
}
let rcvr_ty = fn_sig.skip_binder().input(0).skip_binder();
let (mutability, _ty) = match rcvr_ty.kind() {
ty::Ref(_, ty, hir::Mutability::Mut) => ("&mut ", ty),
ty::Ref(_, ty, _) => ("&", ty),
_ => ("", &rcvr_ty),
};
let path = self.tcx.def_path_str(def_id);
err.note(format!(
"there's an inherent method on `{ty}` of the same name, which can be \
auto-dereferenced from `{rcvr_ty}`"
));
err.multipart_suggestion(
format!(
"to access the inherent method on `{ty}`, use the fully-qualified path",
),
vec![
(
call.span.until(rcvr.span),
format!("{path}::{}({}", m.name(), mutability),
),
match &args {
[] => (
rcvr.span.shrink_to_hi().with_hi(call.span.hi()),
")".to_string(),
),
[first, ..] => (rcvr.span.between(first.span), ", ".to_string()),
},
],
Applicability::MaybeIncorrect,
);
}
}
}
}
pub(super) fn explain_hrtb_projection(
&self,
diag: &mut Diag<'_>,
@@ -13,6 +13,7 @@ help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Not
LL | struct NotIntoDiagArg;
| ^^^^^^^^^^^^^^^^^^^^^
= help: normalized in stderr
= note: there's an inherent method on `DiagInner` of the same name, which can be auto-dereferenced from `&mut DiagInner`
note: required by a bound in `Diag::<'a, G>::arg`
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
@@ -589,6 +589,7 @@ help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Hel
LL | struct Hello {}
| ^^^^^^^^^^^^
= help: normalized in stderr
= note: there's an inherent method on `DiagInner` of the same name, which can be auto-dereferenced from `&mut DiagInner`
note: required by a bound in `Diag::<'a, G>::arg`
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
@@ -0,0 +1,21 @@
//@ run-rustfix
#![allow(unused_imports)]
use std::rc::Rc;
use std::cell::RefCell;
use std::borrow::Borrow; // Without this import, the code would compile.
pub struct S {
flag: bool,
}
type SCell = Rc<RefCell<S>>;
fn main() {
// Type annotations just for clarity
let s : SCell = Rc::new(RefCell::new(S {flag: false}));
let sb : &S = &RefCell::borrow(&s);
//~^ ERROR: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied [E0277]
//~| NOTE: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
//~| NOTE: there's an inherent method on `RefCell<S>` of the same name
println!("{:?}", sb.flag);
}
@@ -0,0 +1,21 @@
//@ run-rustfix
#![allow(unused_imports)]
use std::rc::Rc;
use std::cell::RefCell;
use std::borrow::Borrow; // Without this import, the code would compile.
pub struct S {
flag: bool,
}
type SCell = Rc<RefCell<S>>;
fn main() {
// Type annotations just for clarity
let s : SCell = Rc::new(RefCell::new(S {flag: false}));
let sb : &S = &s.borrow();
//~^ ERROR: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied [E0277]
//~| NOTE: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
//~| NOTE: there's an inherent method on `RefCell<S>` of the same name
println!("{:?}", sb.flag);
}
@@ -0,0 +1,20 @@
error[E0277]: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied
--> $DIR/shadowed-intrinsic-method-deref.rs:16:22
|
LL | let sb : &S = &s.borrow();
| ^^^^^^ the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
|
help: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
but trait `Borrow<RefCell<S>>` is implemented for it
--> $SRC_DIR/alloc/src/rc.rs:LL:COL
= help: for that trait implementation, expected `RefCell<S>`, found `S`
= note: there's an inherent method on `RefCell<S>` of the same name, which can be auto-dereferenced from `&RefCell<T>`
help: to access the inherent method on `RefCell<S>`, use the fully-qualified path
|
LL - let sb : &S = &s.borrow();
LL + let sb : &S = &RefCell::borrow(&s);
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0277`.