Skip to content

Commit 180e7ce

Browse files
Rollup merge of #144966 - scrabsha:push-rozroqqmurvu, r=jdonszelmann
Improve suggestion for "missing function argument" on multiline call `rustc` has a very neat suggestion when the argument count does not match, with a nice placeholder that shows where an argument may be missing. Unfortunately the suggestion is always single-line, even when the function call spans across multiple lines. With this PR, `rustc` tries to guess if the function call is multiline or not, and emits a multiline suggestion when required. r? `@jdonszelmann`
2 parents 5771665 + 1e271d6 commit 180e7ce

File tree

4 files changed

+91
-15
lines changed

4 files changed

+91
-15
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1589,26 +1589,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15891589
// e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`.
15901590
full_call_span.shrink_to_hi()
15911591
};
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 }
1609+
} else {
1610+
ArgumentsFormatting::SingleLine
1611+
}
1612+
};
1613+
15921614
let mut suggestion = "(".to_owned();
15931615
let mut needs_comma = false;
15941616
for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
15951617
if needs_comma {
1596-
suggestion += ", ";
1597-
} else {
1598-
needs_comma = true;
1618+
suggestion += ",";
1619+
}
1620+
match &arguments_formatting {
1621+
ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ",
1622+
ArgumentsFormatting::SingleLine => {}
1623+
ArgumentsFormatting::Multiline { .. } => suggestion += "\n",
15991624
}
1600-
let suggestion_text = if let Some(provided_idx) = provided_idx
1625+
needs_comma = true;
1626+
let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx
16011627
&& let (_, provided_span) = provided_arg_tys[*provided_idx]
16021628
&& let Ok(arg_text) = source_map.span_to_snippet(provided_span)
16031629
{
1604-
arg_text
1630+
(Some(provided_span), arg_text)
16051631
} else {
16061632
// Propose a placeholder of the correct type
16071633
let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
1608-
ty_to_snippet(expected_ty, expected_idx)
1634+
(None, ty_to_snippet(expected_ty, expected_idx))
16091635
};
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;
1643+
}
16101644
suggestion += &suggestion_text;
16111645
}
1646+
if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting {
1647+
suggestion += ",\n";
1648+
suggestion += &brace_indent;
1649+
}
16121650
suggestion += ")";
16131651
err.span_suggestion_verbose(
16141652
suggestion_span,

tests/ui/argument-suggestions/issue-100478.stderr

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,16 @@ LL | fn foo(p1: T1, p2: Arc<T2>, p3: T3, p4: Arc<T4>, p5: T5, p6: T6, p7: T7, p8
7575
| ^^^ -----------
7676
help: provide the argument
7777
|
78-
LL - foo(
79-
LL -
80-
LL - p1, //p2,
81-
LL - p3, p4, p5, p6, p7, p8,
82-
LL - );
83-
LL + foo(p1, /* Arc<T2> */, p3, p4, p5, p6, p7, p8);
78+
LL ~ foo(
79+
LL + p1,
80+
LL + /* Arc<T2> */,
81+
LL + p3,
82+
LL + p4,
83+
LL + p5,
84+
LL + p6,
85+
LL + p7,
86+
LL + p8,
87+
LL ~ );
8488
|
8589

8690
error: aborting due to 4 previous errors

tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,21 @@ impl Bar {
4646
}
4747
}
4848

49+
fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {}
50+
4951
fn main() {
5052
foo(1, 2, 3);
5153
//~^ ERROR function takes 4 arguments but 3
5254
bar(1, 2, 3);
5355
//~^ ERROR function takes 6 arguments but 3
56+
57+
let variable_name = 42;
58+
function_with_lots_of_arguments(
59+
variable_name,
60+
variable_name,
61+
variable_name,
62+
variable_name,
63+
variable_name,
64+
);
65+
//~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061]
5466
}

tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ LL | <$from>::$method(8, /* u8 */)
5252
| ++++++++++
5353

5454
error[E0061]: this function takes 4 arguments but 3 arguments were supplied
55-
--> $DIR/fn-arg-count-mismatch-diagnostics.rs:50:5
55+
--> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5
5656
|
5757
LL | foo(1, 2, 3);
5858
| ^^^--------- argument #4 of type `isize` is missing
@@ -68,7 +68,7 @@ LL | foo(1, 2, 3, /* isize */);
6868
| +++++++++++++
6969

7070
error[E0061]: this function takes 6 arguments but 3 arguments were supplied
71-
--> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5
71+
--> $DIR/fn-arg-count-mismatch-diagnostics.rs:54:5
7272
|
7373
LL | bar(1, 2, 3);
7474
| ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing
@@ -83,6 +83,28 @@ help: provide the arguments
8383
LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */);
8484
| +++++++++++++++++++++++++++++++++
8585

86-
error: aborting due to 5 previous errors
86+
error[E0061]: this function takes 6 arguments but 5 arguments were supplied
87+
--> $DIR/fn-arg-count-mismatch-diagnostics.rs:58:5
88+
|
89+
LL | function_with_lots_of_arguments(
90+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91+
LL | variable_name,
92+
LL | variable_name,
93+
| ------------- argument #2 of type `char` is missing
94+
|
95+
note: function defined here
96+
--> $DIR/fn-arg-count-mismatch-diagnostics.rs:49:4
97+
|
98+
LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {}
99+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -------
100+
help: provide the argument
101+
|
102+
LL | function_with_lots_of_arguments(
103+
LL | variable_name,
104+
LL ~ /* char */,
105+
LL ~ variable_name,
106+
|
107+
108+
error: aborting due to 6 previous errors
87109

88110
For more information about this error, try `rustc --explain E0061`.

0 commit comments

Comments
 (0)