@@ -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 , type_variable:: TypeVariableOrigin , RegionVariableOrigin } ;
@@ -44,7 +44,7 @@ use std::borrow::Cow;
44
44
45
45
use super :: probe:: { AutorefOrPtrAdjustment , IsSuggestion , Mode , ProbeScope } ;
46
46
use super :: { CandidateSource , MethodError , NoMatchData } ;
47
- use rustc_hir:: intravisit:: Visitor ;
47
+ use rustc_hir:: intravisit:: { self , Visitor } ;
48
48
use std:: iter;
49
49
50
50
impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
@@ -190,6 +190,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
190
190
pub fn report_method_error (
191
191
& self ,
192
192
span : Span ,
193
+ rcvr_opt : Option < & ' tcx hir:: Expr < ' tcx > > ,
193
194
rcvr_ty : Ty < ' tcx > ,
194
195
item_name : Ident ,
195
196
source : SelfSource < ' tcx > ,
@@ -214,6 +215,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
214
215
MethodError :: NoMatch ( mut no_match_data) => {
215
216
return self . report_no_match_method_error (
216
217
span,
218
+ rcvr_opt,
217
219
rcvr_ty,
218
220
item_name,
219
221
source,
@@ -358,9 +360,190 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
358
360
err
359
361
}
360
362
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
+
361
543
pub fn report_no_match_method_error (
362
544
& self ,
363
545
mut span : Span ,
546
+ rcvr_opt : Option < & ' tcx hir:: Expr < ' tcx > > ,
364
547
rcvr_ty : Ty < ' tcx > ,
365
548
item_name : Ident ,
366
549
source : SelfSource < ' tcx > ,
@@ -453,7 +636,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
453
636
let mut err = if is_write && let SelfSource :: MethodCall ( rcvr_expr) = source {
454
637
self . suggest_missing_writer ( rcvr_ty, rcvr_expr)
455
638
} else {
456
- tcx. dcx ( ) . create_err ( NoAssociatedItem {
639
+ let mut err = tcx. dcx ( ) . create_err ( NoAssociatedItem {
457
640
span,
458
641
item_kind,
459
642
item_name,
@@ -463,9 +646,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
463
646
} else {
464
647
rcvr_ty. prefix_string ( self . tcx )
465
648
} ,
466
- ty_str : ty_str_reported,
649
+ ty_str : ty_str_reported. clone ( ) ,
467
650
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
469
663
} ;
470
664
if tcx. sess . source_map ( ) . is_multiline ( sugg_span) {
471
665
err. span_label ( sugg_span. with_hi ( span. lo ( ) ) , "" ) ;
@@ -2234,7 +2428,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
2234
2428
type Result = ControlFlow < Option < & ' v hir:: Expr < ' v > > > ;
2235
2429
fn visit_stmt ( & mut self , ex : & ' v hir:: Stmt < ' v > ) -> Self :: Result {
2236
2430
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
2238
2432
&& ident. name == self . ident_name
2239
2433
{
2240
2434
ControlFlow :: Break ( init)
0 commit comments