@@ -71,6 +71,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7171 self . note_type_is_not_clone ( err, expected, expr_ty, expr) ;
7272 self . note_need_for_fn_pointer ( err, expected, expr_ty) ;
7373 self . note_internal_mutation_in_method ( err, expr, expected, expr_ty) ;
74+ self . check_for_range_as_method_call ( err, expr, expr_ty, expected) ;
7475 }
7576
7677 /// Requires that the two types unify, and prints an error message if
@@ -1449,14 +1450,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14491450 }
14501451 }
14511452
1453+ /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
14521454 pub fn check_for_range_as_method_call (
14531455 & self ,
14541456 err : & mut Diagnostic ,
14551457 expr : & hir:: Expr < ' _ > ,
14561458 checked_ty : Ty < ' tcx > ,
1457- // FIXME: We should do analysis to see if we can synthesize an expresion that produces
1458- // this type for always accurate suggestions, or at least marking the suggestion as
1459- // machine applicable.
14601459 expected_ty : Ty < ' tcx > ,
14611460 ) {
14621461 if !hir:: is_range_literal ( expr) {
@@ -1467,13 +1466,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14671466 [ start, end] ,
14681467 _,
14691468 ) = expr. kind else { return ; } ;
1469+ let parent = self . tcx . hir ( ) . get_parent_node ( expr. hir_id ) ;
1470+ if let Some ( hir:: Node :: ExprField ( _) ) = self . tcx . hir ( ) . find ( parent) {
1471+ // Ignore `Foo { field: a..Default::default() }`
1472+ return ;
1473+ }
14701474 let mut expr = end. expr ;
14711475 while let hir:: ExprKind :: MethodCall ( _, rcvr, ..) = expr. kind {
14721476 // Getting to the root receiver and asserting it is a fn call let's us ignore cases in
14731477 // `src/test/ui/methods/issues/issue-90315.stderr`.
14741478 expr = rcvr;
14751479 }
1476- let hir:: ExprKind :: Call ( .. ) = expr. kind else { return ; } ;
1480+ let hir:: ExprKind :: Call ( method_name , _ ) = expr. kind else { return ; } ;
14771481 let ty:: Adt ( adt, _) = checked_ty. kind ( ) else { return ; } ;
14781482 if self . tcx . lang_items ( ) . range_struct ( ) != Some ( adt. did ( ) ) {
14791483 return ;
@@ -1483,11 +1487,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14831487 {
14841488 return ;
14851489 }
1490+ // Check if start has method named end.
1491+ let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , p) ) = method_name. kind else { return ; } ;
1492+ let [ hir:: PathSegment { ident, .. } ] = p. segments else { return ; } ;
1493+ let self_ty = self . typeck_results . borrow ( ) . expr_ty ( start. expr ) ;
1494+ let Ok ( _pick) = self . probe_for_name (
1495+ probe:: Mode :: MethodCall ,
1496+ * ident,
1497+ probe:: IsSuggestion ( true ) ,
1498+ self_ty,
1499+ expr. hir_id ,
1500+ probe:: ProbeScope :: AllTraits ,
1501+ ) else { return ; } ;
1502+ let mut sugg = "." ;
1503+ let mut span = start. expr . span . between ( end. expr . span ) ;
1504+ if span. lo ( ) + BytePos ( 2 ) == span. hi ( ) {
1505+ // There's no space between the start, the range op and the end, suggest removal which
1506+ // will be more noticeable than the replacement of `..` with `.`.
1507+ span = span. with_lo ( span. lo ( ) + BytePos ( 1 ) ) ;
1508+ sugg = "" ;
1509+ }
14861510 err. span_suggestion_verbose (
1487- start . expr . span . between ( end . expr . span ) ,
1488- "you might have meant to write a method call instead of a range" ,
1489- "." . to_string ( ) ,
1490- Applicability :: MaybeIncorrect ,
1511+ span,
1512+ "you likely meant to write a method call instead of a range" ,
1513+ sugg ,
1514+ Applicability :: MachineApplicable ,
14911515 ) ;
14921516 }
14931517}
0 commit comments