@@ -1154,7 +1154,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1154
1154
Reorder ,
1155
1155
DidYouMean ,
1156
1156
}
1157
- let mut suggestion_text = SuggestionText :: None ;
1157
+ let mut suggestion_kind = SuggestionText :: None ;
1158
1158
1159
1159
let ty_to_snippet = |ty : Ty < ' tcx > , expected_idx : ExpectedIdx | {
1160
1160
if ty. is_unit ( ) {
@@ -1179,10 +1179,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1179
1179
let mut only_extras_so_far = errors
1180
1180
. peek ( )
1181
1181
. is_some_and ( |first| matches ! ( first, Error :: Extra ( arg_idx) if arg_idx. index( ) == 0 ) ) ;
1182
+ let mut only_missing_so_far = true ;
1182
1183
let mut prev_extra_idx = None ;
1183
1184
let mut suggestions = vec ! [ ] ;
1184
1185
while let Some ( error) = errors. next ( ) {
1185
1186
only_extras_so_far &= matches ! ( error, Error :: Extra ( _) ) ;
1187
+ only_missing_so_far &= matches ! ( error, Error :: Missing ( _) ) ;
1186
1188
1187
1189
match error {
1188
1190
Error :: Invalid ( provided_idx, expected_idx, compatibility) => {
@@ -1290,7 +1292,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1290
1292
1291
1293
suggestions. push ( ( span, String :: new ( ) ) ) ;
1292
1294
1293
- suggestion_text = match suggestion_text {
1295
+ suggestion_kind = match suggestion_kind {
1294
1296
SuggestionText :: None => SuggestionText :: Remove ( false ) ,
1295
1297
SuggestionText :: Remove ( _) => SuggestionText :: Remove ( true ) ,
1296
1298
_ => SuggestionText :: DidYouMean ,
@@ -1343,7 +1345,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1343
1345
) ,
1344
1346
) ) ;
1345
1347
1346
- suggestion_text = match suggestion_text {
1348
+ suggestion_kind = match suggestion_kind {
1347
1349
SuggestionText :: None => SuggestionText :: Provide ( false ) ,
1348
1350
SuggestionText :: Provide ( _) => SuggestionText :: Provide ( true ) ,
1349
1351
_ => SuggestionText :: DidYouMean ,
@@ -1369,7 +1371,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1369
1371
"" . to_string ( )
1370
1372
} ;
1371
1373
labels. push ( ( span, format ! ( "two arguments{rendered} are missing" ) ) ) ;
1372
- suggestion_text = match suggestion_text {
1374
+ suggestion_kind = match suggestion_kind {
1373
1375
SuggestionText :: None | SuggestionText :: Provide ( _) => {
1374
1376
SuggestionText :: Provide ( true )
1375
1377
}
@@ -1400,7 +1402,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1400
1402
"" . to_string ( )
1401
1403
} ;
1402
1404
labels. push ( ( span, format ! ( "three arguments{rendered} are missing" ) ) ) ;
1403
- suggestion_text = match suggestion_text {
1405
+ suggestion_kind = match suggestion_kind {
1404
1406
SuggestionText :: None | SuggestionText :: Provide ( _) => {
1405
1407
SuggestionText :: Provide ( true )
1406
1408
}
@@ -1422,7 +1424,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1422
1424
args_span
1423
1425
} ;
1424
1426
labels. push ( ( span, "multiple arguments are missing" . to_string ( ) ) ) ;
1425
- suggestion_text = match suggestion_text {
1427
+ suggestion_kind = match suggestion_kind {
1426
1428
SuggestionText :: None | SuggestionText :: Provide ( _) => {
1427
1429
SuggestionText :: Provide ( true )
1428
1430
}
@@ -1461,7 +1463,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1461
1463
format ! ( "expected `{second_expected_ty}`{second_provided_ty_name}" ) ,
1462
1464
) ) ;
1463
1465
1464
- suggestion_text = match suggestion_text {
1466
+ suggestion_kind = match suggestion_kind {
1465
1467
SuggestionText :: None => SuggestionText :: Swap ,
1466
1468
_ => SuggestionText :: DidYouMean ,
1467
1469
} ;
@@ -1481,7 +1483,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1481
1483
) ) ;
1482
1484
}
1483
1485
1484
- suggestion_text = match suggestion_text {
1486
+ suggestion_kind = match suggestion_kind {
1485
1487
SuggestionText :: None => SuggestionText :: Reorder ,
1486
1488
_ => SuggestionText :: DidYouMean ,
1487
1489
} ;
@@ -1559,7 +1561,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1559
1561
) ;
1560
1562
1561
1563
// And add a suggestion block for all of the parameters
1562
- let suggestion_text = match suggestion_text {
1564
+ let suggestion_text = match suggestion_kind {
1563
1565
SuggestionText :: None => None ,
1564
1566
SuggestionText :: Provide ( plural) => {
1565
1567
Some ( format ! ( "provide the argument{}" , if plural { "s" } else { "" } ) )
@@ -1580,80 +1582,149 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1580
1582
&& !full_call_span. in_external_macro ( self . sess ( ) . source_map ( ) )
1581
1583
{
1582
1584
let source_map = self . sess ( ) . source_map ( ) ;
1583
- let suggestion_span = if let Some ( args_span) = error_span. trim_start ( full_call_span) {
1584
- // Span of the braces, e.g. `(a, b, c)`.
1585
- args_span
1586
- } else {
1587
- // The arg span of a function call that wasn't even given braces
1588
- // like what might happen with delegation reuse.
1589
- // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`.
1590
- full_call_span. shrink_to_hi ( )
1591
- } ;
1592
-
1593
- // Controls how the arguments should be listed in the suggestion.
1594
- enum ArgumentsFormatting {
1595
- SingleLine ,
1596
- Multiline { fallback_indent : String , brace_indent : String } ,
1597
- }
1598
- let arguments_formatting = {
1599
- let mut provided_inputs = matched_inputs. iter ( ) . filter_map ( |a| * a) ;
1600
- if let Some ( brace_indent) = source_map. indentation_before ( suggestion_span)
1601
- && let Some ( first_idx) = provided_inputs. by_ref ( ) . next ( )
1602
- && let Some ( last_idx) = provided_inputs. by_ref ( ) . next ( )
1603
- && let ( _, first_span) = provided_arg_tys[ first_idx]
1604
- && let ( _, last_span) = provided_arg_tys[ last_idx]
1605
- && source_map. is_multiline ( first_span. to ( last_span) )
1606
- && let Some ( fallback_indent) = source_map. indentation_before ( first_span)
1607
- {
1608
- ArgumentsFormatting :: Multiline { fallback_indent, brace_indent }
1585
+ let ( suggestion_span, has_paren) =
1586
+ if let Some ( args_span) = error_span. trim_start ( full_call_span) {
1587
+ // Span of the braces, e.g. `(a, b, c)`.
1588
+ ( args_span, true )
1609
1589
} else {
1610
- ArgumentsFormatting :: SingleLine
1611
- }
1612
- } ;
1590
+ // The arg span of a function call that wasn't even given braces
1591
+ // like what might happen with delegation reuse.
1592
+ // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`.
1593
+ ( full_call_span. shrink_to_hi ( ) , false )
1594
+ } ;
1613
1595
1614
- let mut suggestion = "(" . to_owned ( ) ;
1615
- let mut needs_comma = false ;
1616
- for ( expected_idx, provided_idx) in matched_inputs. iter_enumerated ( ) {
1617
- if needs_comma {
1618
- suggestion += "," ;
1619
- }
1620
- match & arguments_formatting {
1621
- ArgumentsFormatting :: SingleLine if needs_comma => suggestion += " " ,
1622
- ArgumentsFormatting :: SingleLine => { }
1623
- ArgumentsFormatting :: Multiline { .. } => suggestion += "\n " ,
1596
+ if matches ! ( suggestion_kind, SuggestionText :: Provide ( _) )
1597
+ && has_paren
1598
+ && only_missing_so_far
1599
+ {
1600
+ // Controls how the arguments should be listed in the suggestion.
1601
+ enum ArgumentsFormatting {
1602
+ SingleLine ,
1603
+ Multiline { fallback_indent : String } ,
1624
1604
}
1625
- needs_comma = true ;
1626
- let ( suggestion_span, suggestion_text) = if let Some ( provided_idx) = provided_idx
1627
- && let ( _, provided_span) = provided_arg_tys[ * provided_idx]
1628
- && let Ok ( arg_text) = source_map. span_to_snippet ( provided_span)
1629
- {
1630
- ( Some ( provided_span) , arg_text)
1631
- } else {
1632
- // Propose a placeholder of the correct type
1633
- let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1634
- ( None , ty_to_snippet ( expected_ty, expected_idx) )
1605
+ let arguments_formatting = {
1606
+ let mut provided_inputs = matched_inputs. iter ( ) . filter_map ( |a| * a) ;
1607
+ if let Some ( first_idx) = provided_inputs. by_ref ( ) . next ( )
1608
+ && let Some ( last_idx) = provided_inputs. by_ref ( ) . next ( )
1609
+ && let ( _, first_span) = provided_arg_tys[ first_idx]
1610
+ && let ( _, last_span) = provided_arg_tys[ last_idx]
1611
+ && source_map. is_multiline ( first_span. to ( last_span) )
1612
+ && let Some ( fallback_indent) = source_map. indentation_before ( first_span)
1613
+ {
1614
+ ArgumentsFormatting :: Multiline { fallback_indent }
1615
+ } else {
1616
+ ArgumentsFormatting :: SingleLine
1617
+ }
1635
1618
} ;
1636
- if let ArgumentsFormatting :: Multiline { fallback_indent, .. } =
1637
- & arguments_formatting
1638
- {
1639
- let indent = suggestion_span
1640
- . and_then ( |span| source_map. indentation_before ( span) )
1641
- . unwrap_or_else ( || fallback_indent. clone ( ) ) ;
1642
- suggestion += & indent;
1619
+
1620
+ let mut suggestions: Vec < ( Span , String ) > = Vec :: new ( ) ;
1621
+ let mut previous_span = source_map. start_point ( suggestion_span) . shrink_to_hi ( ) ;
1622
+ let mut first = true ;
1623
+ for ( expected_idx, provided_idx) in matched_inputs. iter_enumerated ( ) {
1624
+ if let Some ( provided_idx) = provided_idx {
1625
+ let ( _, provided_span) = provided_arg_tys[ * provided_idx] ;
1626
+ previous_span = provided_span;
1627
+ } else {
1628
+ // Propose a placeholder of the correct type
1629
+ let ( insertion_span, last_arg) = if first {
1630
+ ( previous_span, matched_inputs. len ( ) - 1 == expected_idx. as_usize ( ) )
1631
+ } else {
1632
+ let mut comma_hit = false ;
1633
+ let mut closing_paren_hit = false ;
1634
+ let after_arg = previous_span. shrink_to_hi ( ) ;
1635
+ let after_previous_comma = source_map
1636
+ . span_extend_while ( after_arg, |c| {
1637
+ closing_paren_hit = c == ')' ;
1638
+ if comma_hit || closing_paren_hit {
1639
+ false
1640
+ } else {
1641
+ comma_hit = c == ',' ;
1642
+ true
1643
+ }
1644
+ } )
1645
+ . unwrap ( )
1646
+ . shrink_to_hi ( ) ;
1647
+ let span = if closing_paren_hit {
1648
+ after_previous_comma
1649
+ } else {
1650
+ source_map. next_point ( after_previous_comma) . shrink_to_hi ( )
1651
+ } ;
1652
+ ( span, closing_paren_hit)
1653
+ } ;
1654
+ let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1655
+ let expected_ty = ty_to_snippet ( expected_ty, expected_idx) ;
1656
+ let indent = match arguments_formatting {
1657
+ ArgumentsFormatting :: SingleLine => "" ,
1658
+ ArgumentsFormatting :: Multiline { ref fallback_indent, .. } => {
1659
+ fallback_indent
1660
+ }
1661
+ } ;
1662
+ let prefix = if last_arg && !first {
1663
+ // `call(a)` -> `call(a, b)` requires adding a comma.
1664
+ ", "
1665
+ } else {
1666
+ ""
1667
+ } ;
1668
+ let suffix = match arguments_formatting {
1669
+ ArgumentsFormatting :: SingleLine if !last_arg => ", " ,
1670
+ ArgumentsFormatting :: SingleLine => "" ,
1671
+ ArgumentsFormatting :: Multiline { .. } => "\n " ,
1672
+ } ;
1673
+ let suggestion = format ! ( "{indent}{prefix}{expected_ty}{suffix}" ) ;
1674
+ if let Some ( ( last_sugg_span, last_sugg_msg) ) = suggestions. last_mut ( )
1675
+ && * last_sugg_span == insertion_span
1676
+ {
1677
+ // More than one suggestion to insert at a given span.
1678
+ // Merge them into one.
1679
+ if last_sugg_msg. ends_with ( ", " ) && prefix == ", " {
1680
+ // TODO: explain what this condition is and why
1681
+ // it is needed.
1682
+ // TODO: find a better way to express this
1683
+ // condition.
1684
+ last_sugg_msg. truncate ( last_sugg_msg. len ( ) - 2 ) ;
1685
+ }
1686
+ last_sugg_msg. push_str ( & suggestion) ;
1687
+ } else {
1688
+ suggestions. push ( ( insertion_span, suggestion) ) ;
1689
+ } ;
1690
+ } ;
1691
+ first = false ;
1643
1692
}
1644
- suggestion += & suggestion_text;
1645
- }
1646
- if let ArgumentsFormatting :: Multiline { brace_indent, .. } = arguments_formatting {
1647
- suggestion += ",\n " ;
1648
- suggestion += & brace_indent;
1693
+ err. multipart_suggestion_verbose (
1694
+ suggestion_text,
1695
+ suggestions,
1696
+ Applicability :: HasPlaceholders ,
1697
+ ) ;
1698
+ } else {
1699
+ // FIXME: make this multiline-aware.
1700
+ let mut suggestion = "(" . to_owned ( ) ;
1701
+ let mut needs_comma = false ;
1702
+ for ( expected_idx, provided_idx) in matched_inputs. iter_enumerated ( ) {
1703
+ if needs_comma {
1704
+ suggestion += ", " ;
1705
+ } else {
1706
+ needs_comma = true ;
1707
+ }
1708
+ let suggestion_text = if let Some ( provided_idx) = provided_idx
1709
+ && let ( _, provided_span) = provided_arg_tys[ * provided_idx]
1710
+ && let Ok ( arg_text) = source_map. span_to_snippet ( provided_span)
1711
+ {
1712
+ arg_text
1713
+ } else {
1714
+ // Propose a placeholder of the correct type
1715
+ let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1716
+ ty_to_snippet ( expected_ty, expected_idx)
1717
+ } ;
1718
+ suggestion += & suggestion_text;
1719
+ }
1720
+ suggestion += ")" ;
1721
+ err. span_suggestion_verbose (
1722
+ suggestion_span,
1723
+ suggestion_text,
1724
+ suggestion,
1725
+ Applicability :: HasPlaceholders ,
1726
+ ) ;
1649
1727
}
1650
- suggestion += ")" ;
1651
- err. span_suggestion_verbose (
1652
- suggestion_span,
1653
- suggestion_text,
1654
- suggestion,
1655
- Applicability :: HasPlaceholders ,
1656
- ) ;
1657
1728
}
1658
1729
1659
1730
err. emit ( )
0 commit comments