Skip to content

Commit 15a6589

Browse files
committed
Note for E0599 if shadowed bindings has the method.
implement #123558
1 parent 9f432d7 commit 15a6589

7 files changed

+242
-6
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13471347
if segment.ident.name != kw::Empty {
13481348
if let Some(err) = self.report_method_error(
13491349
span,
1350+
Some(rcvr),
13501351
rcvr_t,
13511352
segment.ident,
13521353
SelfSource::MethodCall(rcvr),

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

+1
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
882882
if item_name.name != kw::Empty {
883883
if let Some(e) = self.report_method_error(
884884
span,
885+
None,
885886
ty.normalized,
886887
item_name,
887888
SelfSource::QPath(qself),

compiler/rustc_hir_typeck/src/method/suggest.rs

+200-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
77
use crate::Expectation;
88
use crate::FnCtxt;
99
use core::ops::ControlFlow;
10+
use hir::Expr;
1011
use rustc_ast::ast::Mutability;
1112
use rustc_attr::parse_confusables;
1213
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
@@ -19,7 +20,6 @@ use rustc_hir as hir;
1920
use rustc_hir::def::DefKind;
2021
use rustc_hir::def_id::DefId;
2122
use rustc_hir::lang_items::LangItem;
22-
use rustc_hir::PatKind::Binding;
2323
use rustc_hir::PathSegment;
2424
use rustc_hir::{ExprKind, Node, QPath};
2525
use rustc_infer::infer::{self, type_variable::TypeVariableOrigin, RegionVariableOrigin};
@@ -44,7 +44,7 @@ use std::borrow::Cow;
4444

4545
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
4646
use super::{CandidateSource, MethodError, NoMatchData};
47-
use rustc_hir::intravisit::Visitor;
47+
use rustc_hir::intravisit::{self, Visitor};
4848
use std::iter;
4949

5050
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -190,6 +190,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
190190
pub fn report_method_error(
191191
&self,
192192
span: Span,
193+
rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
193194
rcvr_ty: Ty<'tcx>,
194195
item_name: Ident,
195196
source: SelfSource<'tcx>,
@@ -214,6 +215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
214215
MethodError::NoMatch(mut no_match_data) => {
215216
return self.report_no_match_method_error(
216217
span,
218+
rcvr_opt,
217219
rcvr_ty,
218220
item_name,
219221
source,
@@ -358,9 +360,190 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
358360
err
359361
}
360362

363+
pub fn suggest_use_shadowed_binding_with_method(
364+
&self,
365+
rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
366+
method_name: Ident,
367+
ty_str_reported: &str,
368+
err: &mut Diag<'_>,
369+
) {
370+
#[derive(Debug)]
371+
struct LetStmt {
372+
ty_hir_id_opt: Option<hir::HirId>,
373+
binding_id: hir::HirId,
374+
span: Span,
375+
init_hir_id: hir::HirId,
376+
}
377+
378+
// Used for finding suggest binding.
379+
// ```rust
380+
// earlier binding for suggesting:
381+
// let y = vec![1, 2];
382+
// now binding:
383+
// if let Some(y) = x {
384+
// y.push(y);
385+
// }
386+
// ```
387+
struct LetVisitor<'a, 'tcx> {
388+
// Error binding which don't have `method_name`.
389+
binding_name: Symbol,
390+
binding_id: hir::HirId,
391+
// Used for check if the suggest binding has `method_name`.
392+
fcx: &'a FnCtxt<'a, 'tcx>,
393+
call_expr: &'tcx Expr<'tcx>,
394+
method_name: Ident,
395+
// Suggest the binding which is shallowed.
396+
sugg_let: Option<LetStmt>,
397+
}
398+
399+
impl<'a, 'tcx> LetVisitor<'a, 'tcx> {
400+
// Check scope of binding.
401+
fn is_sub_scope(&self, sub_id: hir::ItemLocalId, super_id: hir::ItemLocalId) -> bool {
402+
let scope_tree = self.fcx.tcx.region_scope_tree(self.fcx.body_id);
403+
if let Some(sub_var_scope) = scope_tree.var_scope(sub_id)
404+
&& let Some(super_var_scope) = scope_tree.var_scope(super_id)
405+
&& scope_tree.is_subscope_of(sub_var_scope, super_var_scope)
406+
{
407+
return true;
408+
}
409+
false
410+
}
411+
412+
fn check_and_add_sugg_binding(&mut self, binding: LetStmt) -> bool {
413+
if !self.is_sub_scope(self.binding_id.local_id, binding.binding_id.local_id) {
414+
return false;
415+
}
416+
417+
if let Some(ty_hir_id) = binding.ty_hir_id_opt
418+
&& let Some(tyck_ty) = self.fcx.node_ty_opt(ty_hir_id)
419+
{
420+
if self
421+
.fcx
422+
.lookup_probe_for_diagnostic(
423+
self.method_name,
424+
tyck_ty,
425+
self.call_expr,
426+
ProbeScope::TraitsInScope,
427+
None,
428+
)
429+
.is_ok()
430+
{
431+
self.sugg_let = Some(binding);
432+
return true;
433+
} else {
434+
return false;
435+
}
436+
}
437+
438+
if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id)
439+
&& self
440+
.fcx
441+
.lookup_probe_for_diagnostic(
442+
self.method_name,
443+
self_ty,
444+
self.call_expr,
445+
ProbeScope::TraitsInScope,
446+
None,
447+
)
448+
.is_ok()
449+
{
450+
self.sugg_let = Some(binding);
451+
return true;
452+
}
453+
return false;
454+
}
455+
}
456+
457+
impl<'v> Visitor<'v> for LetVisitor<'_, '_> {
458+
type Result = ControlFlow<()>;
459+
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
460+
if let hir::StmtKind::Let(&hir::LetStmt { pat, ty, init, .. }) = ex.kind
461+
&& let hir::PatKind::Binding(_, binding_id, binding_name, ..) = pat.kind
462+
&& let Some(init) = init
463+
&& binding_name.name == self.binding_name
464+
&& binding_id != self.binding_id
465+
{
466+
if self.check_and_add_sugg_binding(LetStmt {
467+
ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None },
468+
binding_id: binding_id,
469+
span: pat.span,
470+
init_hir_id: init.hir_id,
471+
}) {
472+
return ControlFlow::Break(());
473+
}
474+
ControlFlow::Continue(())
475+
} else {
476+
hir::intravisit::walk_stmt(self, ex)
477+
}
478+
}
479+
480+
// Used for find the error binding.
481+
// When the visitor reaches this point, all the shadowed bindings
482+
// have been found, so the visitor ends.
483+
fn visit_pat(&mut self, p: &'v hir::Pat<'v>) -> Self::Result {
484+
match p.kind {
485+
hir::PatKind::Binding(_, binding_id, binding_name, _) => {
486+
if binding_name.name == self.binding_name && binding_id == self.binding_id {
487+
return ControlFlow::Break(());
488+
}
489+
}
490+
_ => {
491+
intravisit::walk_pat(self, p);
492+
}
493+
}
494+
ControlFlow::Continue(())
495+
}
496+
}
497+
498+
if let Some(rcvr) = rcvr_opt
499+
&& let hir::ExprKind::Path(QPath::Resolved(_, path)) = rcvr.kind
500+
&& let hir::def::Res::Local(recv_id) = path.res
501+
&& let Some(segment) = path.segments.first()
502+
{
503+
let map = self.infcx.tcx.hir();
504+
let body_id = self.tcx.hir().body_owned_by(self.body_id);
505+
let body = map.body(body_id);
506+
507+
if let Node::Expr(call_expr) = self.tcx.parent_hir_node(rcvr.hir_id) {
508+
let mut let_visitor = LetVisitor {
509+
fcx: self,
510+
call_expr,
511+
binding_name: segment.ident.name,
512+
binding_id: recv_id,
513+
method_name,
514+
sugg_let: None,
515+
};
516+
let_visitor.visit_body(body);
517+
if let Some(sugg_let) = let_visitor.sugg_let
518+
&& let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id)
519+
{
520+
let _sm = self.infcx.tcx.sess.source_map();
521+
let rcvr_name = segment.ident.name;
522+
let mut span = MultiSpan::from_span(sugg_let.span);
523+
span.push_span_label(sugg_let.span,
524+
format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here"));
525+
span.push_span_label(
526+
self.tcx.hir().span(recv_id),
527+
format!(
528+
"earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`"
529+
),
530+
);
531+
err.span_note(
532+
span,
533+
format!(
534+
"there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \
535+
that *has* method `{method_name}` available"
536+
),
537+
);
538+
}
539+
}
540+
}
541+
}
542+
361543
pub fn report_no_match_method_error(
362544
&self,
363545
mut span: Span,
546+
rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
364547
rcvr_ty: Ty<'tcx>,
365548
item_name: Ident,
366549
source: SelfSource<'tcx>,
@@ -453,7 +636,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
453636
let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source {
454637
self.suggest_missing_writer(rcvr_ty, rcvr_expr)
455638
} else {
456-
tcx.dcx().create_err(NoAssociatedItem {
639+
let mut err = tcx.dcx().create_err(NoAssociatedItem {
457640
span,
458641
item_kind,
459642
item_name,
@@ -463,9 +646,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
463646
} else {
464647
rcvr_ty.prefix_string(self.tcx)
465648
},
466-
ty_str: ty_str_reported,
649+
ty_str: ty_str_reported.clone(),
467650
trait_missing_method,
468-
})
651+
});
652+
653+
if is_method {
654+
self.suggest_use_shadowed_binding_with_method(
655+
rcvr_opt,
656+
item_name,
657+
&ty_str_reported,
658+
&mut err,
659+
);
660+
}
661+
662+
err
469663
};
470664
if tcx.sess.source_map().is_multiline(sugg_span) {
471665
err.span_label(sugg_span.with_hi(span.lo()), "");
@@ -2234,7 +2428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22342428
type Result = ControlFlow<Option<&'v hir::Expr<'v>>>;
22352429
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
22362430
if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind
2237-
&& let Binding(_, _, ident, ..) = pat.kind
2431+
&& let hir::PatKind::Binding(_, _, ident, ..) = pat.kind
22382432
&& ident.name == self.ident_name
22392433
{
22402434
ControlFlow::Break(init)

tests/ui/attributes/rustc_confusables_std_cases.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ error[E0599]: no method named `push` found for struct `VecDeque` in the current
2626
LL | x.push(1);
2727
| ^^^^ method not found in `VecDeque<_>`
2828
|
29+
note: there's an earlier shadowed binding `x` of type `Vec<_>` that *has* method `push` available
30+
--> $DIR/rustc_confusables_std_cases.rs:8:9
31+
|
32+
LL | let mut x = Vec::new();
33+
| ^^^^^ `x` of type `Vec<_>` that has method `push` defined earlier here
34+
...
35+
LL | let mut x = VecDeque::new();
36+
| ----- earlier `x` shadowed here with type `VecDeque`
2937
help: you might have meant to use `push_back`
3038
|
3139
LL | x.push_back(1);

tests/ui/derives/deriving-with-repr-packed-2.stderr

+8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,14 @@ LL | struct NonCopy;
1010
LL | _ = x.clone();
1111
| ^^^^^ method cannot be called on `Foo<NonCopy>` due to unsatisfied trait bounds
1212
|
13+
note: there's an earlier shadowed binding `x` of type `Foo<u32>` that *has* method `clone` available
14+
--> $DIR/deriving-with-repr-packed-2.rs:13:9
15+
|
16+
LL | let x: Foo<u32> = Foo(1, 2, 3);
17+
| ^ `x` of type `Foo<u32>` that has method `clone` defined earlier here
18+
...
19+
LL | let x: Foo<NonCopy> = Foo(NonCopy, NonCopy, NonCopy);
20+
| - earlier `x` shadowed here with type `Foo<NonCopy>`
1321
note: the following trait bounds were not satisfied:
1422
`NonCopy: Clone`
1523
`NonCopy: Copy`
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn main() {
2+
let x = Some(3);
3+
let y = vec![1, 2];
4+
if let Some(y) = x {
5+
y.push(y); //~ ERROR E0599
6+
}
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0599]: no method named `push` found for type `{integer}` in the current scope
2+
--> $DIR/account-for-shadowed-bindings-issue-123558.rs:5:11
3+
|
4+
LL | y.push(y);
5+
| ^^^^ method not found in `{integer}`
6+
|
7+
note: there's an earlier shadowed binding `y` of type `Vec<{integer}>` that *has* method `push` available
8+
--> $DIR/account-for-shadowed-bindings-issue-123558.rs:3:9
9+
|
10+
LL | let y = vec![1, 2];
11+
| ^ `y` of type `Vec<{integer}>` that has method `push` defined earlier here
12+
LL | if let Some(y) = x {
13+
| - earlier `y` shadowed here with type `{integer}`
14+
15+
error: aborting due to 1 previous error
16+
17+
For more information about this error, try `rustc --explain E0599`.

0 commit comments

Comments
 (0)