@@ -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) => {
@@ -1580,80 +1582,146 @@ 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 has_paren && only_missing_so_far {
1597
+ // Controls how the arguments should be listed in the suggestion.
1598
+ enum ArgumentsFormatting {
1599
+ SingleLine ,
1600
+ Multiline { fallback_indent : String } ,
1624
1601
}
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) )
1602
+ let arguments_formatting = {
1603
+ let mut provided_inputs = matched_inputs. iter ( ) . filter_map ( |a| * a) ;
1604
+ if let Some ( first_idx) = provided_inputs. by_ref ( ) . next ( )
1605
+ && let Some ( last_idx) = provided_inputs. by_ref ( ) . next ( )
1606
+ && let ( _, first_span) = provided_arg_tys[ first_idx]
1607
+ && let ( _, last_span) = provided_arg_tys[ last_idx]
1608
+ && source_map. is_multiline ( first_span. to ( last_span) )
1609
+ && let Some ( fallback_indent) = source_map. indentation_before ( first_span)
1610
+ {
1611
+ ArgumentsFormatting :: Multiline { fallback_indent }
1612
+ } else {
1613
+ ArgumentsFormatting :: SingleLine
1614
+ }
1635
1615
} ;
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;
1616
+
1617
+ let mut suggestions: Vec < ( Span , String ) > = Vec :: new ( ) ;
1618
+ let mut previous_span = source_map. start_point ( suggestion_span) . shrink_to_hi ( ) ;
1619
+ let mut no_provided_arg_yet = true ;
1620
+ for ( expected_idx, provided_idx) in matched_inputs. iter_enumerated ( ) {
1621
+ if let Some ( provided_idx) = provided_idx {
1622
+ let ( _, provided_span) = provided_arg_tys[ * provided_idx] ;
1623
+ previous_span = provided_span;
1624
+ no_provided_arg_yet = false ;
1625
+ } else {
1626
+ // Propose a placeholder of the correct type
1627
+ let ( insertion_span, last_arg) = if no_provided_arg_yet {
1628
+ ( previous_span, matched_inputs. len ( ) - 1 == expected_idx. as_usize ( ) )
1629
+ } else {
1630
+ let mut comma_hit = false ;
1631
+ let mut closing_paren_hit = false ;
1632
+ let after_arg = previous_span. shrink_to_hi ( ) ;
1633
+ let after_previous_comma = source_map
1634
+ . span_extend_while ( after_arg, |c| {
1635
+ closing_paren_hit = c == ')' ;
1636
+ if comma_hit || closing_paren_hit {
1637
+ false
1638
+ } else {
1639
+ comma_hit = c == ',' ;
1640
+ true
1641
+ }
1642
+ } )
1643
+ . unwrap ( )
1644
+ . shrink_to_hi ( ) ;
1645
+ let span = if closing_paren_hit {
1646
+ after_previous_comma
1647
+ } else {
1648
+ source_map. next_point ( after_previous_comma) . shrink_to_hi ( )
1649
+ } ;
1650
+ ( span, closing_paren_hit)
1651
+ } ;
1652
+ let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1653
+ let expected_ty = ty_to_snippet ( expected_ty, expected_idx) ;
1654
+ let indent = match arguments_formatting {
1655
+ ArgumentsFormatting :: SingleLine => "" ,
1656
+ ArgumentsFormatting :: Multiline { ref fallback_indent, .. } => {
1657
+ fallback_indent
1658
+ }
1659
+ } ;
1660
+ let prefix = if last_arg && !no_provided_arg_yet {
1661
+ // `call(a)` -> `call(a, b)` requires adding a comma.
1662
+ ", "
1663
+ } else {
1664
+ ""
1665
+ } ;
1666
+ let suffix = match arguments_formatting {
1667
+ ArgumentsFormatting :: SingleLine if !last_arg => ", " ,
1668
+ ArgumentsFormatting :: SingleLine => "" ,
1669
+ ArgumentsFormatting :: Multiline { .. } => "\n " ,
1670
+ } ;
1671
+ let suggestion = format ! ( "{indent}{prefix}{expected_ty}{suffix}" ) ;
1672
+ if let Some ( ( last_sugg_span, last_sugg_msg) ) = suggestions. last_mut ( )
1673
+ && * last_sugg_span == insertion_span
1674
+ {
1675
+ // More than one suggestion to insert at a given span.
1676
+ // Merge them into one.
1677
+ if last_sugg_msg. ends_with ( ", " ) && prefix == ", " {
1678
+ // TODO: explain what this condition is and why
1679
+ // it is needed.
1680
+ // TODO: find a better way to express this
1681
+ // condition.
1682
+ last_sugg_msg. truncate ( last_sugg_msg. len ( ) - 2 ) ;
1683
+ }
1684
+ last_sugg_msg. push_str ( & suggestion) ;
1685
+ } else {
1686
+ suggestions. push ( ( insertion_span, suggestion) ) ;
1687
+ } ;
1688
+ } ;
1643
1689
}
1644
- suggestion += & suggestion_text;
1645
- }
1646
- if let ArgumentsFormatting :: Multiline { brace_indent, .. } = arguments_formatting {
1647
- suggestion += ",\n " ;
1648
- suggestion += & brace_indent;
1690
+ err. multipart_suggestion_verbose (
1691
+ suggestion_text,
1692
+ suggestions,
1693
+ Applicability :: HasPlaceholders ,
1694
+ ) ;
1695
+ } else {
1696
+ // FIXME: make this multiline-aware.
1697
+ let mut suggestion = "(" . to_owned ( ) ;
1698
+ let mut needs_comma = false ;
1699
+ for ( expected_idx, provided_idx) in matched_inputs. iter_enumerated ( ) {
1700
+ if needs_comma {
1701
+ suggestion += ", " ;
1702
+ } else {
1703
+ needs_comma = true ;
1704
+ }
1705
+ let suggestion_text = if let Some ( provided_idx) = provided_idx
1706
+ && let ( _, provided_span) = provided_arg_tys[ * provided_idx]
1707
+ && let Ok ( arg_text) = source_map. span_to_snippet ( provided_span)
1708
+ {
1709
+ arg_text
1710
+ } else {
1711
+ // Propose a placeholder of the correct type
1712
+ let ( _, expected_ty) = formal_and_expected_inputs[ expected_idx] ;
1713
+ ty_to_snippet ( expected_ty, expected_idx)
1714
+ } ;
1715
+ suggestion += & suggestion_text;
1716
+ }
1717
+ suggestion += ")" ;
1718
+ err. span_suggestion_verbose (
1719
+ suggestion_span,
1720
+ suggestion_text,
1721
+ suggestion,
1722
+ Applicability :: HasPlaceholders ,
1723
+ ) ;
1649
1724
}
1650
- suggestion += ")" ;
1651
- err. span_suggestion_verbose (
1652
- suggestion_span,
1653
- suggestion_text,
1654
- suggestion,
1655
- Applicability :: HasPlaceholders ,
1656
- ) ;
1657
1725
}
1658
1726
1659
1727
err. emit ( )
0 commit comments