@@ -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