@@ -71,6 +71,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
71
71
self . note_type_is_not_clone ( err, expected, expr_ty, expr) ;
72
72
self . note_need_for_fn_pointer ( err, expected, expr_ty) ;
73
73
self . note_internal_mutation_in_method ( err, expr, expected, expr_ty) ;
74
+ self . check_for_range_as_method_call ( err, expr, expr_ty, expected) ;
74
75
}
75
76
76
77
/// Requires that the two types unify, and prints an error message if
@@ -1449,14 +1450,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1449
1450
}
1450
1451
}
1451
1452
1453
+ /// Identify when the user has written `foo..bar()` instead of `foo.bar()`.
1452
1454
pub fn check_for_range_as_method_call (
1453
1455
& self ,
1454
1456
err : & mut Diagnostic ,
1455
1457
expr : & hir:: Expr < ' _ > ,
1456
1458
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.
1460
1459
expected_ty : Ty < ' tcx > ,
1461
1460
) {
1462
1461
if !hir:: is_range_literal ( expr) {
@@ -1467,13 +1466,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1467
1466
[ start, end] ,
1468
1467
_,
1469
1468
) = 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
+ }
1470
1474
let mut expr = end. expr ;
1471
1475
while let hir:: ExprKind :: MethodCall ( _, rcvr, ..) = expr. kind {
1472
1476
// Getting to the root receiver and asserting it is a fn call let's us ignore cases in
1473
1477
// `src/test/ui/methods/issues/issue-90315.stderr`.
1474
1478
expr = rcvr;
1475
1479
}
1476
- let hir:: ExprKind :: Call ( .. ) = expr. kind else { return ; } ;
1480
+ let hir:: ExprKind :: Call ( method_name , _ ) = expr. kind else { return ; } ;
1477
1481
let ty:: Adt ( adt, _) = checked_ty. kind ( ) else { return ; } ;
1478
1482
if self . tcx . lang_items ( ) . range_struct ( ) != Some ( adt. did ( ) ) {
1479
1483
return ;
@@ -1483,11 +1487,31 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1483
1487
{
1484
1488
return ;
1485
1489
}
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
+ }
1486
1510
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 ,
1491
1515
) ;
1492
1516
}
1493
1517
}
0 commit comments