From bdaabc17b664952ce0b9666b9b57188a6126935b Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Mon, 4 Aug 2025 20:47:43 +0200 Subject: [PATCH 1/2] Add test for "missing function argument" on multiline call --- .../fn/fn-arg-count-mismatch-diagnostics.rs | 12 +++++++ .../fn-arg-count-mismatch-diagnostics.stderr | 32 +++++++++++++++++-- 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs index b2f80ba1bf62f..3b12ea1a73681 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.rs @@ -46,9 +46,21 @@ impl Bar { } } +fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + fn main() { foo(1, 2, 3); //~^ ERROR function takes 4 arguments but 3 bar(1, 2, 3); //~^ ERROR function takes 6 arguments but 3 + + let variable_name = 42; + function_with_lots_of_arguments( + variable_name, + variable_name, + variable_name, + variable_name, + variable_name, + ); + //~^^^^^^^ ERROR this function takes 6 arguments but 5 arguments were supplied [E0061] } diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 6af7671af03bf..494d9bf60a652 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -52,7 +52,7 @@ LL | <$from>::$method(8, /* u8 */) | ++++++++++ error[E0061]: this function takes 4 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:50:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 | LL | foo(1, 2, 3); | ^^^--------- argument #4 of type `isize` is missing @@ -68,7 +68,7 @@ LL | foo(1, 2, 3, /* isize */); | +++++++++++++ error[E0061]: this function takes 6 arguments but 3 arguments were supplied - --> $DIR/fn-arg-count-mismatch-diagnostics.rs:52:5 + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:54:5 | LL | bar(1, 2, 3); | ^^^--------- three arguments of type `i32`, `i32`, and `i32` are missing @@ -83,6 +83,32 @@ help: provide the arguments LL | bar(1, 2, 3, /* i32 */, /* i32 */, /* i32 */); | +++++++++++++++++++++++++++++++++ -error: aborting due to 5 previous errors +error[E0061]: this function takes 6 arguments but 5 arguments were supplied + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:58:5 + | +LL | function_with_lots_of_arguments( + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | variable_name, +LL | variable_name, + | ------------- argument #2 of type `char` is missing + | +note: function defined here + --> $DIR/fn-arg-count-mismatch-diagnostics.rs:49:4 + | +LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, f: i32) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- +help: provide the argument + | +LL - function_with_lots_of_arguments( +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - variable_name, +LL - ); +LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); + | + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0061`. From 1e271d6ed13756db08160daaa327b99b7fe6099e Mon Sep 17 00:00:00 2001 From: Sasha Pourcelot Date: Tue, 5 Aug 2025 00:11:01 +0200 Subject: [PATCH 2/2] Allow function argument mismatch suggestion to be multiline --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 50 ++++++++++++++++--- .../argument-suggestions/issue-100478.stderr | 16 +++--- .../fn-arg-count-mismatch-diagnostics.stderr | 12 ++--- 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 36abd7c85550f..b80a2af31007e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -1589,26 +1589,64 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // e.g. `reuse HasSelf::method;` should suggest `reuse HasSelf::method($args);`. full_call_span.shrink_to_hi() }; + + // Controls how the arguments should be listed in the suggestion. + enum ArgumentsFormatting { + SingleLine, + Multiline { fallback_indent: String, brace_indent: String }, + } + let arguments_formatting = { + let mut provided_inputs = matched_inputs.iter().filter_map(|a| *a); + if let Some(brace_indent) = source_map.indentation_before(suggestion_span) + && let Some(first_idx) = provided_inputs.by_ref().next() + && let Some(last_idx) = provided_inputs.by_ref().next() + && let (_, first_span) = provided_arg_tys[first_idx] + && let (_, last_span) = provided_arg_tys[last_idx] + && source_map.is_multiline(first_span.to(last_span)) + && let Some(fallback_indent) = source_map.indentation_before(first_span) + { + ArgumentsFormatting::Multiline { fallback_indent, brace_indent } + } else { + ArgumentsFormatting::SingleLine + } + }; + let mut suggestion = "(".to_owned(); let mut needs_comma = false; for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { if needs_comma { - suggestion += ", "; - } else { - needs_comma = true; + suggestion += ","; + } + match &arguments_formatting { + ArgumentsFormatting::SingleLine if needs_comma => suggestion += " ", + ArgumentsFormatting::SingleLine => {} + ArgumentsFormatting::Multiline { .. } => suggestion += "\n", } - let suggestion_text = if let Some(provided_idx) = provided_idx + needs_comma = true; + let (suggestion_span, suggestion_text) = if let Some(provided_idx) = provided_idx && let (_, provided_span) = provided_arg_tys[*provided_idx] && let Ok(arg_text) = source_map.span_to_snippet(provided_span) { - arg_text + (Some(provided_span), arg_text) } else { // Propose a placeholder of the correct type let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - ty_to_snippet(expected_ty, expected_idx) + (None, ty_to_snippet(expected_ty, expected_idx)) }; + if let ArgumentsFormatting::Multiline { fallback_indent, .. } = + &arguments_formatting + { + let indent = suggestion_span + .and_then(|span| source_map.indentation_before(span)) + .unwrap_or_else(|| fallback_indent.clone()); + suggestion += &indent; + } suggestion += &suggestion_text; } + if let ArgumentsFormatting::Multiline { brace_indent, .. } = arguments_formatting { + suggestion += ",\n"; + suggestion += &brace_indent; + } suggestion += ")"; err.span_suggestion_verbose( suggestion_span, diff --git a/tests/ui/argument-suggestions/issue-100478.stderr b/tests/ui/argument-suggestions/issue-100478.stderr index 8889a0ab5df0b..81dbff2f00089 100644 --- a/tests/ui/argument-suggestions/issue-100478.stderr +++ b/tests/ui/argument-suggestions/issue-100478.stderr @@ -75,12 +75,16 @@ LL | fn foo(p1: T1, p2: Arc, p3: T3, p4: Arc, p5: T5, p6: T6, p7: T7, p8 | ^^^ ----------- help: provide the argument | -LL - foo( -LL - -LL - p1, //p2, -LL - p3, p4, p5, p6, p7, p8, -LL - ); -LL + foo(p1, /* Arc */, p3, p4, p5, p6, p7, p8); +LL ~ foo( +LL + p1, +LL + /* Arc */, +LL + p3, +LL + p4, +LL + p5, +LL + p6, +LL + p7, +LL + p8, +LL ~ ); | error: aborting due to 4 previous errors diff --git a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr index 494d9bf60a652..dda9b398a833c 100644 --- a/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr +++ b/tests/ui/fn/fn-arg-count-mismatch-diagnostics.stderr @@ -99,14 +99,10 @@ LL | fn function_with_lots_of_arguments(a: i32, b: char, c: i32, d: i32, e: i32, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------- help: provide the argument | -LL - function_with_lots_of_arguments( -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - variable_name, -LL - ); -LL + function_with_lots_of_arguments(variable_name, /* char */, variable_name, variable_name, variable_name, variable_name); +LL | function_with_lots_of_arguments( +LL | variable_name, +LL ~ /* char */, +LL ~ variable_name, | error: aborting due to 6 previous errors