Skip to content

Commit 34b9594

Browse files
committed
Detect when method call on LHS might be shadowed
Address rust-lang#39232.
1 parent caa64e5 commit 34b9594

File tree

5 files changed

+167
-0
lines changed

5 files changed

+167
-0
lines changed

compiler/rustc_hir_typeck/src/demand.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::FnCtxt;
22
use rustc_ast::util::parser::PREC_POSTFIX;
3+
use rustc_errors::MultiSpan;
34
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
45
use rustc_hir as hir;
56
use rustc_hir::def::CtorKind;
@@ -36,6 +37,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3637
return;
3738
}
3839

40+
self.annotate_alternative_method_deref(err, expr, error);
41+
3942
// Use `||` to give these suggestions a precedence
4043
let _ = self.suggest_missing_parentheses(err, expr)
4144
|| self.suggest_remove_last_method_call(err, expr, expected)
@@ -316,6 +319,95 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
316319
}
317320
}
318321

322+
fn annotate_alternative_method_deref(
323+
&self,
324+
err: &mut Diagnostic,
325+
expr: &hir::Expr<'_>,
326+
error: Option<TypeError<'tcx>>,
327+
) {
328+
let parent = self.tcx.hir().get_parent_node(expr.hir_id);
329+
let Some(TypeError::Sorts(ExpectedFound { expected, .. })) = error else {return;};
330+
let Some(hir::Node::Expr(hir::Expr {
331+
kind: hir::ExprKind::Assign(lhs, rhs, _), ..
332+
})) = self.tcx.hir().find(parent) else {return; };
333+
if rhs.hir_id != expr.hir_id || expected.is_closure() {
334+
return;
335+
}
336+
let hir::ExprKind::Unary(hir::UnOp::Deref, deref) = lhs.kind else { return; };
337+
let hir::ExprKind::MethodCall(path, base, args, _) = deref.kind else { return; };
338+
let self_ty = self.typeck_results.borrow().expr_ty_adjusted_opt(base).unwrap();
339+
let pick = self
340+
.probe_for_name(
341+
probe::Mode::MethodCall,
342+
path.ident,
343+
probe::IsSuggestion(true),
344+
self_ty,
345+
deref.hir_id,
346+
probe::ProbeScope::TraitsInScope,
347+
)
348+
.unwrap();
349+
let methods = self.probe_for_name_many(
350+
probe::Mode::MethodCall,
351+
path.ident,
352+
probe::IsSuggestion(true),
353+
self_ty,
354+
deref.hir_id,
355+
probe::ProbeScope::AllTraits,
356+
);
357+
let suggestions: Vec<_> = methods
358+
.into_iter()
359+
.filter(|m| m.def_id != pick.item.def_id)
360+
.map(|m| {
361+
let substs = ty::InternalSubsts::for_item(self.tcx, m.def_id, |param, _| {
362+
self.var_for_def(deref.span, param)
363+
});
364+
vec![
365+
(
366+
deref.span.until(base.span),
367+
format!(
368+
"{}({}",
369+
with_no_trimmed_paths!(
370+
self.tcx.def_path_str_with_substs(m.def_id, substs,)
371+
),
372+
match self.tcx.fn_sig(m.def_id).input(0).skip_binder().kind() {
373+
ty::Ref(_, _, hir::Mutability::Mut) => "&mut ",
374+
ty::Ref(_, _, _) => "&",
375+
_ => "",
376+
},
377+
),
378+
),
379+
match &args[..] {
380+
[] => (base.span.shrink_to_hi().with_hi(deref.span.hi()), ")".to_string()),
381+
[first, ..] => (base.span.until(first.span), String::new()),
382+
},
383+
]
384+
})
385+
.collect();
386+
if suggestions.is_empty() {
387+
return;
388+
}
389+
let mut path_span: MultiSpan = path.ident.span.into();
390+
path_span.push_span_label(
391+
path.ident.span,
392+
format!(
393+
"refers to `{}`",
394+
with_no_trimmed_paths!(self.tcx.def_path_str(pick.item.def_id)),
395+
),
396+
);
397+
err.span_note(
398+
path_span,
399+
&format!(
400+
"there are multiple methods with the same name, `{}` refers to `{}` in the method call",
401+
path.ident,
402+
with_no_trimmed_paths!(self.tcx.def_path_str(pick.item.def_id)),
403+
));
404+
err.multipart_suggestions(
405+
"you might have meant to invoke a different method, you can use the fully-qualified path",
406+
suggestions,
407+
Applicability::MaybeIncorrect,
408+
);
409+
}
410+
319411
/// If the expected type is an enum (Issue #55250) with any variants whose
320412
/// sole field is of the found type, suggest such variants. (Issue #42764)
321413
fn suggest_compatible_variants(

compiler/rustc_hir_typeck/src/method/probe.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
322322
)
323323
}
324324

325+
#[instrument(level = "debug", skip(self))]
326+
pub fn probe_for_name_many(
327+
&self,
328+
mode: Mode,
329+
item_name: Ident,
330+
is_suggestion: IsSuggestion,
331+
self_ty: Ty<'tcx>,
332+
scope_expr_id: hir::HirId,
333+
scope: ProbeScope,
334+
) -> Vec<ty::AssocItem> {
335+
self.probe_op(
336+
item_name.span,
337+
mode,
338+
Some(item_name),
339+
None,
340+
is_suggestion,
341+
self_ty,
342+
scope_expr_id,
343+
scope,
344+
|probe_cx| {
345+
Ok(probe_cx
346+
.inherent_candidates
347+
.iter()
348+
.chain(&probe_cx.extension_candidates)
349+
// .filter(|candidate| candidate_filter(&candidate.item))
350+
.map(|candidate| candidate.item)
351+
.collect())
352+
},
353+
)
354+
.unwrap()
355+
}
356+
325357
fn probe_op<OP, R>(
326358
&'a self,
327359
span: Span,
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-rustfix
2+
#![allow(unused_imports)]
3+
use std::borrow::BorrowMut;
4+
use std::cell::RefCell;
5+
use std::rc::Rc;
6+
7+
fn main() {
8+
let rc = Rc::new(RefCell::new(true));
9+
*std::cell::RefCell::<_>::borrow_mut(&rc) = false; //~ ERROR E0308
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// run-rustfix
2+
#![allow(unused_imports)]
3+
use std::borrow::BorrowMut;
4+
use std::cell::RefCell;
5+
use std::rc::Rc;
6+
7+
fn main() {
8+
let rc = Rc::new(RefCell::new(true));
9+
*rc.borrow_mut() = false; //~ ERROR E0308
10+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/shadowed-lplace-method.rs:9:24
3+
|
4+
LL | *rc.borrow_mut() = false;
5+
| ---------------- ^^^^^ expected struct `Rc`, found `bool`
6+
| |
7+
| expected due to the type of this binding
8+
|
9+
= note: expected struct `Rc<RefCell<bool>>`
10+
found type `bool`
11+
note: there are multiple methods with the same name, `borrow_mut` refers to `std::borrow::BorrowMut::borrow_mut` in the method call
12+
--> $DIR/shadowed-lplace-method.rs:9:9
13+
|
14+
LL | *rc.borrow_mut() = false;
15+
| ^^^^^^^^^^ refers to `std::borrow::BorrowMut::borrow_mut`
16+
help: you might have meant to invoke a different method, you can use the fully-qualified path
17+
|
18+
LL | *std::cell::RefCell::<_>::borrow_mut(&rc) = false;
19+
| +++++++++++++++++++++++++++++++++++++ ~
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)