Skip to content

Commit 73fe905

Browse files
committed
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.
1 parent 0c68443 commit 73fe905

7 files changed

Lines changed: 138 additions & 0 deletions

File tree

compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3178,6 +3178,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
31783178

31793179
self.suggest_tuple_wrapping(err, root_obligation, obligation);
31803180
}
3181+
self.suggest_shadowed_inherent_method(err, obligation, trait_predicate);
31813182
}
31823183

31833184
fn add_help_message_for_fn_trait(

compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4916,6 +4916,79 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
49164916
}
49174917
}
49184918

4919+
pub(super) fn suggest_shadowed_inherent_method(
4920+
&self,
4921+
err: &mut Diag<'_>,
4922+
obligation: &PredicateObligation<'tcx>,
4923+
trait_predicate: ty::PolyTraitPredicate<'tcx>,
4924+
) {
4925+
let ObligationCauseCode::FunctionArg { call_hir_id, .. } = obligation.cause.code() else {
4926+
return;
4927+
};
4928+
let Node::Expr(call) = self.tcx.hir_node(*call_hir_id) else { return };
4929+
let hir::ExprKind::MethodCall(segment, rcvr, args, ..) = call.kind else { return };
4930+
let Some(typeck) = &self.typeck_results else { return };
4931+
let Some(rcvr_ty) = typeck.expr_ty_adjusted_opt(rcvr) else { return };
4932+
let rcvr_ty = self.resolve_vars_if_possible(rcvr_ty);
4933+
let autoderef = (self.autoderef_steps)(rcvr_ty);
4934+
for (ty, def_id) in autoderef.iter().filter_map(|(ty, obligations)| {
4935+
if let ty::Adt(def, _) = ty.kind()
4936+
&& *ty != rcvr_ty.peel_refs()
4937+
&& obligations.iter().all(|obligation| self.predicate_may_hold(obligation))
4938+
{
4939+
Some((ty, def.did()))
4940+
} else {
4941+
None
4942+
}
4943+
}) {
4944+
for impl_def_id in self.tcx.inherent_impls(def_id) {
4945+
if *impl_def_id == trait_predicate.def_id() {
4946+
continue;
4947+
}
4948+
for m in self
4949+
.tcx
4950+
.provided_trait_methods(*impl_def_id)
4951+
.filter(|m| m.name() == segment.ident.name)
4952+
{
4953+
let fn_sig = self.tcx.fn_sig(m.def_id);
4954+
if fn_sig.skip_binder().inputs().skip_binder().len() != args.len() + 1 {
4955+
continue;
4956+
}
4957+
let rcvr_ty = fn_sig.skip_binder().input(0).skip_binder();
4958+
let (mutability, _ty) = match rcvr_ty.kind() {
4959+
ty::Ref(_, ty, hir::Mutability::Mut) => ("&mut ", ty),
4960+
ty::Ref(_, ty, _) => ("&", ty),
4961+
_ => ("", &rcvr_ty),
4962+
};
4963+
let path = self.tcx.def_path_str(def_id);
4964+
err.note(format!(
4965+
"there's an inherent method on `{ty}` of the same name, which can be \
4966+
auto-dereferenced from `{rcvr_ty}`"
4967+
));
4968+
err.multipart_suggestion(
4969+
format!(
4970+
"to access the inherent method on `{ty}`, use the fully-qualified path",
4971+
),
4972+
vec![
4973+
(
4974+
call.span.until(rcvr.span),
4975+
format!("{path}::{}({}", m.name(), mutability),
4976+
),
4977+
match &args {
4978+
[] => (
4979+
rcvr.span.shrink_to_hi().with_hi(call.span.hi()),
4980+
")".to_string(),
4981+
),
4982+
[first, ..] => (rcvr.span.between(first.span), ", ".to_string()),
4983+
},
4984+
],
4985+
Applicability::MaybeIncorrect,
4986+
);
4987+
}
4988+
}
4989+
}
4990+
}
4991+
49194992
pub(super) fn explain_hrtb_projection(
49204993
&self,
49214994
diag: &mut Diag<'_>,

tests/ui-fulldeps/session-diagnostic/diagnostic-derive-doc-comment-field.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Not
1313
LL | struct NotIntoDiagArg;
1414
| ^^^^^^^^^^^^^^^^^^^^^
1515
= help: normalized in stderr
16+
= note: there's an inherent method on `DiagInner` of the same name, which can be auto-dereferenced from `&mut DiagInner`
1617
note: required by a bound in `Diag::<'a, G>::arg`
1718
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
1819
::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC

tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,7 @@ help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Hel
589589
LL | struct Hello {}
590590
| ^^^^^^^^^^^^
591591
= help: normalized in stderr
592+
= note: there's an inherent method on `DiagInner` of the same name, which can be auto-dereferenced from `&mut DiagInner`
592593
note: required by a bound in `Diag::<'a, G>::arg`
593594
--> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
594595
::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ run-rustfix
2+
#![allow(unused_imports)]
3+
use std::rc::Rc;
4+
use std::cell::RefCell;
5+
use std::borrow::Borrow; // Without this import, the code would compile.
6+
7+
pub struct S {
8+
flag: bool,
9+
}
10+
11+
type SCell = Rc<RefCell<S>>;
12+
13+
fn main() {
14+
// Type annotations just for clarity
15+
let s : SCell = Rc::new(RefCell::new(S {flag: false}));
16+
let sb : &S = &RefCell::borrow(&s);
17+
//~^ ERROR: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied [E0277]
18+
//~| NOTE: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
19+
//~| NOTE: there's an inherent method on `RefCell<S>` of the same name
20+
println!("{:?}", sb.flag);
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//@ run-rustfix
2+
#![allow(unused_imports)]
3+
use std::rc::Rc;
4+
use std::cell::RefCell;
5+
use std::borrow::Borrow; // Without this import, the code would compile.
6+
7+
pub struct S {
8+
flag: bool,
9+
}
10+
11+
type SCell = Rc<RefCell<S>>;
12+
13+
fn main() {
14+
// Type annotations just for clarity
15+
let s : SCell = Rc::new(RefCell::new(S {flag: false}));
16+
let sb : &S = &s.borrow();
17+
//~^ ERROR: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied [E0277]
18+
//~| NOTE: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
19+
//~| NOTE: there's an inherent method on `RefCell<S>` of the same name
20+
println!("{:?}", sb.flag);
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error[E0277]: the trait bound `Rc<RefCell<S>>: Borrow<S>` is not satisfied
2+
--> $DIR/shadowed-intrinsic-method-deref.rs:16:22
3+
|
4+
LL | let sb : &S = &s.borrow();
5+
| ^^^^^^ the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
6+
|
7+
help: the trait `Borrow<S>` is not implemented for `Rc<RefCell<S>>`
8+
but trait `Borrow<RefCell<S>>` is implemented for it
9+
--> $SRC_DIR/alloc/src/rc.rs:LL:COL
10+
= help: for that trait implementation, expected `RefCell<S>`, found `S`
11+
= note: there's an inherent method on `RefCell<S>` of the same name, which can be auto-dereferenced from `&RefCell<T>`
12+
help: to access the inherent method on `RefCell<S>`, use the fully-qualified path
13+
|
14+
LL - let sb : &S = &s.borrow();
15+
LL + let sb : &S = &RefCell::borrow(&s);
16+
|
17+
18+
error: aborting due to 1 previous error
19+
20+
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)