@@ -1179,10 +1179,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11791179 let mut only_extras_so_far = errors
11801180 . peek ( )
11811181 . is_some_and ( |first| matches ! ( first, Error :: Extra ( arg_idx) if arg_idx. index( ) == 0 ) ) ;
1182+ let mut only_missing_so_far = true ;
11821183 let mut prev_extra_idx = None ;
11831184 let mut suggestions = vec ! [ ] ;
11841185 while let Some ( error) = errors. next ( ) {
11851186 only_extras_so_far &= matches ! ( error, Error :: Extra ( _) ) ;
1187+ only_missing_so_far &= matches ! ( error, Error :: Missing ( _) ) ;
11861188
11871189 match error {
11881190 Error :: Invalid ( provided_idx, expected_idx, compatibility) => {
@@ -1580,80 +1582,146 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15801582 && !full_call_span. in_external_macro ( self . sess ( ) . source_map ( ) )
15811583 {
15821584 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 )
16091589 } 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+ } ;
16131595
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 } ,
16241601 }
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+ }
16351615 } ;
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+ } ;
16431689 }
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+ ) ;
16491724 }
1650- suggestion += ")" ;
1651- err. span_suggestion_verbose (
1652- suggestion_span,
1653- suggestion_text,
1654- suggestion,
1655- Applicability :: HasPlaceholders ,
1656- ) ;
16571725 }
16581726
16591727 err. emit ( )
0 commit comments