@@ -7,6 +7,7 @@ use crate::errors::{self, CandidateTraitNote, NoAssociatedItem};
77use crate :: Expectation ;
88use crate :: FnCtxt ;
99use core:: ops:: ControlFlow ;
10+ use hir:: Expr ;
1011use rustc_ast:: ast:: Mutability ;
1112use rustc_attr:: parse_confusables;
1213use rustc_data_structures:: fx:: { FxIndexMap , FxIndexSet } ;
@@ -19,7 +20,6 @@ use rustc_hir as hir;
1920use rustc_hir:: def:: DefKind ;
2021use rustc_hir:: def_id:: DefId ;
2122use rustc_hir:: lang_items:: LangItem ;
22- use rustc_hir:: PatKind :: Binding ;
2323use rustc_hir:: PathSegment ;
2424use rustc_hir:: { ExprKind , Node , QPath } ;
2525use rustc_infer:: infer:: { self , RegionVariableOrigin } ;
@@ -46,7 +46,7 @@ use std::borrow::Cow;
4646
4747use super :: probe:: { AutorefOrPtrAdjustment , IsSuggestion , Mode , ProbeScope } ;
4848use super :: { CandidateSource , MethodError , NoMatchData } ;
49- use rustc_hir:: intravisit:: Visitor ;
49+ use rustc_hir:: intravisit:: { self , Visitor } ;
5050
5151impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
5252 fn is_fn_ty ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
@@ -188,6 +188,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
188188 pub fn report_method_error (
189189 & self ,
190190 span : Span ,
191+ rcvr_opt : Option < & ' tcx hir:: Expr < ' tcx > > ,
191192 rcvr_ty : Ty < ' tcx > ,
192193 item_name : Ident ,
193194 source : SelfSource < ' tcx > ,
@@ -212,6 +213,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
212213 MethodError :: NoMatch ( mut no_match_data) => {
213214 return self . report_no_match_method_error (
214215 span,
216+ rcvr_opt,
215217 rcvr_ty,
216218 item_name,
217219 source,
@@ -356,9 +358,197 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
356358 err
357359 }
358360
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+
359548 pub fn report_no_match_method_error (
360549 & self ,
361550 mut span : Span ,
551+ rcvr_opt : Option < & ' tcx hir:: Expr < ' tcx > > ,
362552 rcvr_ty : Ty < ' tcx > ,
363553 item_name : Ident ,
364554 source : SelfSource < ' tcx > ,
@@ -451,7 +641,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
451641 let mut err = if is_write && let SelfSource :: MethodCall ( rcvr_expr) = source {
452642 self . suggest_missing_writer ( rcvr_ty, rcvr_expr)
453643 } else {
454- tcx. dcx ( ) . create_err ( NoAssociatedItem {
644+ let mut err = tcx. dcx ( ) . create_err ( NoAssociatedItem {
455645 span,
456646 item_kind,
457647 item_name,
@@ -461,9 +651,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
461651 } else {
462652 rcvr_ty. prefix_string ( self . tcx )
463653 } ,
464- ty_str : ty_str_reported,
654+ ty_str : ty_str_reported. clone ( ) ,
465655 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
467668 } ;
468669 if tcx. sess . source_map ( ) . is_multiline ( sugg_span) {
469670 err. span_label ( sugg_span. with_hi ( span. lo ( ) ) , "" ) ;
@@ -2240,7 +2441,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22402441 type Result = ControlFlow < Option < & ' v hir:: Expr < ' v > > > ;
22412442 fn visit_stmt ( & mut self , ex : & ' v hir:: Stmt < ' v > ) -> Self :: Result {
22422443 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
22442445 && ident. name == self . ident_name
22452446 {
22462447 ControlFlow :: Break ( init)
0 commit comments