@@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
7
7
use crate::Expectation;
8
8
use crate::FnCtxt;
9
9
use core::ops::ControlFlow;
10
+ use hir::Expr;
10
11
use rustc_ast::ast::Mutability;
11
12
use rustc_attr::parse_confusables;
12
13
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
@@ -19,7 +20,6 @@ use rustc_hir as hir;
19
20
use rustc_hir::def::DefKind;
20
21
use rustc_hir::def_id::DefId;
21
22
use rustc_hir::lang_items::LangItem;
22
- use rustc_hir::PatKind::Binding;
23
23
use rustc_hir::PathSegment;
24
24
use rustc_hir::{ExprKind, Node, QPath};
25
25
use rustc_infer::infer::{self, RegionVariableOrigin};
@@ -46,7 +46,7 @@ use std::borrow::Cow;
46
46
47
47
use super::probe::{AutorefOrPtrAdjustment, IsSuggestion, Mode, ProbeScope};
48
48
use super::{CandidateSource, MethodError, NoMatchData};
49
- use rustc_hir::intravisit::Visitor;
49
+ use rustc_hir::intravisit::{self, Visitor} ;
50
50
51
51
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
52
52
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
@@ -188,6 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
188
188
pub fn report_method_error(
189
189
&self,
190
190
span: Span,
191
+ rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
191
192
rcvr_ty: Ty<'tcx>,
192
193
item_name: Ident,
193
194
source: SelfSource<'tcx>,
@@ -212,6 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
212
213
MethodError::NoMatch(mut no_match_data) => {
213
214
return self.report_no_match_method_error(
214
215
span,
216
+ rcvr_opt,
215
217
rcvr_ty,
216
218
item_name,
217
219
source,
@@ -356,9 +358,197 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
356
358
err
357
359
}
358
360
361
+ pub fn suggest_use_shadowed_binding_with_method(
362
+ &self,
363
+ rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
364
+ method_name: Ident,
365
+ ty_str_reported: &str,
366
+ err: &mut Diag<'_>,
367
+ ) {
368
+ #[derive(Debug)]
369
+ struct LetStmt {
370
+ ty_hir_id_opt: Option<hir::HirId>,
371
+ binding_id: hir::HirId,
372
+ span: Span,
373
+ init_hir_id: hir::HirId,
374
+ }
375
+
376
+ // Used for finding suggest binding.
377
+ // ```rust
378
+ // earlier binding for suggesting:
379
+ // let y = vec![1, 2];
380
+ // now binding:
381
+ // if let Some(y) = x {
382
+ // y.push(y);
383
+ // }
384
+ // ```
385
+ struct LetVisitor<'a, 'tcx> {
386
+ // Error binding which don't have `method_name`.
387
+ binding_name: Symbol,
388
+ binding_id: hir::HirId,
389
+ // Used for check if the suggest binding has `method_name`.
390
+ fcx: &'a FnCtxt<'a, 'tcx>,
391
+ call_expr: &'tcx Expr<'tcx>,
392
+ method_name: Ident,
393
+ // Suggest the binding which is shallowed.
394
+ sugg_let: Option<LetStmt>,
395
+ }
396
+
397
+ impl<'a, 'tcx> LetVisitor<'a, 'tcx> {
398
+ // Check scope of binding.
399
+ fn is_sub_scope(&self, sub_id: hir::ItemLocalId, super_id: hir::ItemLocalId) -> bool {
400
+ let scope_tree = self.fcx.tcx.region_scope_tree(self.fcx.body_id);
401
+ if let Some(sub_var_scope) = scope_tree.var_scope(sub_id)
402
+ && let Some(super_var_scope) = scope_tree.var_scope(super_id)
403
+ && scope_tree.is_subscope_of(sub_var_scope, super_var_scope)
404
+ {
405
+ return true;
406
+ }
407
+ false
408
+ }
409
+
410
+ // Check if an earlier shadowed binding make `the receiver` of a MethodCall has the method.
411
+ // If it does, record the earlier binding for subsequent notes.
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
+ // Get the earlier shadowed binding'ty and use it to check the method.
418
+ if let Some(ty_hir_id) = binding.ty_hir_id_opt
419
+ && let Some(tyck_ty) = self.fcx.node_ty_opt(ty_hir_id)
420
+ {
421
+ if self
422
+ .fcx
423
+ .lookup_probe_for_diagnostic(
424
+ self.method_name,
425
+ tyck_ty,
426
+ self.call_expr,
427
+ ProbeScope::TraitsInScope,
428
+ None,
429
+ )
430
+ .is_ok()
431
+ {
432
+ self.sugg_let = Some(binding);
433
+ return true;
434
+ } else {
435
+ return false;
436
+ }
437
+ }
438
+
439
+ // If the shadowed binding has an an itializer expression,
440
+ // use the initializer expression'ty to try to find the method again.
441
+ // For example like: `let mut x = Vec::new();`,
442
+ // `Vec::new()` is the itializer expression.
443
+ if let Some(self_ty) = self.fcx.node_ty_opt(binding.init_hir_id)
444
+ && self
445
+ .fcx
446
+ .lookup_probe_for_diagnostic(
447
+ self.method_name,
448
+ self_ty,
449
+ self.call_expr,
450
+ ProbeScope::TraitsInScope,
451
+ None,
452
+ )
453
+ .is_ok()
454
+ {
455
+ self.sugg_let = Some(binding);
456
+ return true;
457
+ }
458
+ return false;
459
+ }
460
+ }
461
+
462
+ impl<'v> Visitor<'v> for LetVisitor<'_, '_> {
463
+ type Result = ControlFlow<()>;
464
+ fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
465
+ if let hir::StmtKind::Let(&hir::LetStmt { pat, ty, init, .. }) = ex.kind
466
+ && let hir::PatKind::Binding(_, binding_id, binding_name, ..) = pat.kind
467
+ && let Some(init) = init
468
+ && binding_name.name == self.binding_name
469
+ && binding_id != self.binding_id
470
+ {
471
+ if self.check_and_add_sugg_binding(LetStmt {
472
+ ty_hir_id_opt: if let Some(ty) = ty { Some(ty.hir_id) } else { None },
473
+ binding_id: binding_id,
474
+ span: pat.span,
475
+ init_hir_id: init.hir_id,
476
+ }) {
477
+ return ControlFlow::Break(());
478
+ }
479
+ ControlFlow::Continue(())
480
+ } else {
481
+ hir::intravisit::walk_stmt(self, ex)
482
+ }
483
+ }
484
+
485
+ // Used for find the error binding.
486
+ // When the visitor reaches this point, all the shadowed bindings
487
+ // have been found, so the visitor ends.
488
+ fn visit_pat(&mut self, p: &'v hir::Pat<'v>) -> Self::Result {
489
+ match p.kind {
490
+ hir::PatKind::Binding(_, binding_id, binding_name, _) => {
491
+ if binding_name.name == self.binding_name && binding_id == self.binding_id {
492
+ return ControlFlow::Break(());
493
+ }
494
+ }
495
+ _ => {
496
+ intravisit::walk_pat(self, p);
497
+ }
498
+ }
499
+ ControlFlow::Continue(())
500
+ }
501
+ }
502
+
503
+ if let Some(rcvr) = rcvr_opt
504
+ && let hir::ExprKind::Path(QPath::Resolved(_, path)) = rcvr.kind
505
+ && let hir::def::Res::Local(recv_id) = path.res
506
+ && let Some(segment) = path.segments.first()
507
+ {
508
+ let map = self.infcx.tcx.hir();
509
+ let body_id = self.tcx.hir().body_owned_by(self.body_id);
510
+ let body = map.body(body_id);
511
+
512
+ if let Node::Expr(call_expr) = self.tcx.parent_hir_node(rcvr.hir_id) {
513
+ let mut let_visitor = LetVisitor {
514
+ fcx: self,
515
+ call_expr,
516
+ binding_name: segment.ident.name,
517
+ binding_id: recv_id,
518
+ method_name,
519
+ sugg_let: None,
520
+ };
521
+ let_visitor.visit_body(body);
522
+ if let Some(sugg_let) = let_visitor.sugg_let
523
+ && let Some(self_ty) = self.node_ty_opt(sugg_let.init_hir_id)
524
+ {
525
+ let _sm = self.infcx.tcx.sess.source_map();
526
+ let rcvr_name = segment.ident.name;
527
+ let mut span = MultiSpan::from_span(sugg_let.span);
528
+ span.push_span_label(sugg_let.span,
529
+ format!("`{rcvr_name}` of type `{self_ty}` that has method `{method_name}` defined earlier here"));
530
+ span.push_span_label(
531
+ self.tcx.hir().span(recv_id),
532
+ format!(
533
+ "earlier `{rcvr_name}` shadowed here with type `{ty_str_reported}`"
534
+ ),
535
+ );
536
+ err.span_note(
537
+ span,
538
+ format!(
539
+ "there's an earlier shadowed binding `{rcvr_name}` of type `{self_ty}` \
540
+ that has method `{method_name}` available"
541
+ ),
542
+ );
543
+ }
544
+ }
545
+ }
546
+ }
547
+
359
548
pub fn report_no_match_method_error(
360
549
&self,
361
550
mut span: Span,
551
+ rcvr_opt: Option<&'tcx hir::Expr<'tcx>>,
362
552
rcvr_ty: Ty<'tcx>,
363
553
item_name: Ident,
364
554
source: SelfSource<'tcx>,
@@ -451,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
451
641
let mut err = if is_write && let SelfSource::MethodCall(rcvr_expr) = source {
452
642
self.suggest_missing_writer(rcvr_ty, rcvr_expr)
453
643
} else {
454
- tcx.dcx().create_err(NoAssociatedItem {
644
+ let mut err = tcx.dcx().create_err(NoAssociatedItem {
455
645
span,
456
646
item_kind,
457
647
item_name,
@@ -461,9 +651,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
461
651
} else {
462
652
rcvr_ty.prefix_string(self.tcx)
463
653
},
464
- ty_str: ty_str_reported,
654
+ ty_str: ty_str_reported.clone() ,
465
655
trait_missing_method,
466
- })
656
+ });
657
+
658
+ if is_method {
659
+ self.suggest_use_shadowed_binding_with_method(
660
+ rcvr_opt,
661
+ item_name,
662
+ &ty_str_reported,
663
+ &mut err,
664
+ );
665
+ }
666
+
667
+ err
467
668
};
468
669
if tcx.sess.source_map().is_multiline(sugg_span) {
469
670
err.span_label(sugg_span.with_hi(span.lo()), "");
@@ -2240,7 +2441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2240
2441
type Result = ControlFlow<Option<&'v hir::Expr<'v>>>;
2241
2442
fn visit_stmt(&mut self, ex: &'v hir::Stmt<'v>) -> Self::Result {
2242
2443
if let hir::StmtKind::Let(&hir::LetStmt { pat, init, .. }) = ex.kind
2243
- && let Binding(_, _, ident, ..) = pat.kind
2444
+ && let hir::PatKind:: Binding(_, _, ident, ..) = pat.kind
2244
2445
&& ident.name == self.ident_name
2245
2446
{
2246
2447
ControlFlow::Break(init)
0 commit comments