@@ -5,6 +5,7 @@ use crate::errors;
55use crate :: FnCtxt ;
66use rustc_ast:: ast:: Mutability ;
77use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
8+ use rustc_errors:: StashKey ;
89use rustc_errors:: {
910 pluralize, struct_span_err, Applicability , Diagnostic , DiagnosticBuilder , ErrorGuaranteed ,
1011 MultiSpan ,
@@ -13,6 +14,8 @@ use rustc_hir as hir;
1314use rustc_hir:: def:: DefKind ;
1415use rustc_hir:: def_id:: DefId ;
1516use rustc_hir:: lang_items:: LangItem ;
17+ use rustc_hir:: PatKind :: Binding ;
18+ use rustc_hir:: PathSegment ;
1619use rustc_hir:: { ExprKind , Node , QPath } ;
1720use rustc_infer:: infer:: {
1821 type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ,
@@ -35,11 +38,11 @@ use rustc_trait_selection::traits::{
3538 FulfillmentError , Obligation , ObligationCause , ObligationCauseCode ,
3639} ;
3740
38- use std:: cmp:: Ordering ;
39- use std:: iter;
40-
4141use super :: probe:: { AutorefOrPtrAdjustment , IsSuggestion , Mode , ProbeScope } ;
4242use super :: { CandidateSource , MethodError , NoMatchData } ;
43+ use rustc_hir:: intravisit:: Visitor ;
44+ use std:: cmp:: Ordering ;
45+ use std:: iter;
4346
4447impl < ' a , ' tcx > FnCtxt < ' a , ' tcx > {
4548 fn is_fn_ty ( & self , ty : Ty < ' tcx > , span : Span ) -> bool {
@@ -1470,6 +1473,61 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14701473 false
14711474 }
14721475
1476+ /// For code `rect::area(...)`,
1477+ /// if `rect` is a local variable and `area` is a valid assoc method for it,
1478+ /// we try to suggest `rect.area()`
1479+ pub ( crate ) fn suggest_assoc_method_call ( & self , segs : & [ PathSegment < ' _ > ] ) {
1480+ debug ! ( "suggest_assoc_method_call segs: {:?}" , segs) ;
1481+ let [ seg1, seg2] = segs else { return ; } ;
1482+ let Some ( mut diag) =
1483+ self . tcx . sess . diagnostic ( ) . steal_diagnostic ( seg1. ident . span , StashKey :: CallAssocMethod )
1484+ else { return } ;
1485+
1486+ let map = self . infcx . tcx . hir ( ) ;
1487+ let body = map. body ( rustc_hir:: BodyId { hir_id : self . body_id } ) ;
1488+ struct LetVisitor < ' a > {
1489+ result : Option < & ' a hir:: Expr < ' a > > ,
1490+ ident_name : Symbol ,
1491+ }
1492+
1493+ impl < ' v > Visitor < ' v > for LetVisitor < ' v > {
1494+ fn visit_stmt ( & mut self , ex : & ' v hir:: Stmt < ' v > ) {
1495+ if let hir:: StmtKind :: Local ( hir:: Local { pat, init, .. } ) = & ex. kind {
1496+ if let Binding ( _, _, ident, ..) = pat. kind &&
1497+ ident. name == self . ident_name {
1498+ self . result = * init;
1499+ }
1500+ }
1501+ hir:: intravisit:: walk_stmt ( self , ex) ;
1502+ }
1503+ }
1504+
1505+ let mut visitor = LetVisitor { result : None , ident_name : seg1. ident . name } ;
1506+ visitor. visit_body ( & body) ;
1507+
1508+ let parent = self . tcx . hir ( ) . get_parent_node ( seg1. hir_id ) ;
1509+ if let Some ( Node :: Expr ( call_expr) ) = self . tcx . hir ( ) . find ( parent) &&
1510+ let Some ( expr) = visitor. result {
1511+ let self_ty = self . node_ty ( expr. hir_id ) ;
1512+ let probe = self . lookup_probe (
1513+ seg2. ident ,
1514+ self_ty,
1515+ call_expr,
1516+ ProbeScope :: TraitsInScope ,
1517+ ) ;
1518+ if probe. is_ok ( ) {
1519+ let sm = self . infcx . tcx . sess . source_map ( ) ;
1520+ diag. span_suggestion_verbose (
1521+ sm. span_extend_while ( seg1. ident . span . shrink_to_hi ( ) , |c| c == ':' ) . unwrap ( ) ,
1522+ "you may have meant to call an instance method" ,
1523+ "." . to_string ( ) ,
1524+ Applicability :: MaybeIncorrect
1525+ ) ;
1526+ }
1527+ }
1528+ diag. emit ( ) ;
1529+ }
1530+
14731531 /// Suggest calling a method on a field i.e. `a.field.bar()` instead of `a.bar()`
14741532 fn suggest_calling_method_on_field (
14751533 & self ,
0 commit comments