diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 3183f4101fdd3..3ae24d038e496 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -925,6 +925,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let mut err = fn_call_diag_ctxt.initial_final_diagnostic(); fn_call_diag_ctxt.suggest_confusable(&mut err); fn_call_diag_ctxt.detect_missing_turbofish(&mut err); + fn_call_diag_ctxt.suggest_missing_comma_before_closure(&mut err); // As we encounter issues, keep track of what we want to provide for the suggestion. @@ -3315,6 +3316,38 @@ impl<'a, 'tcx> ArgMatchingCtxt<'a, 'tcx> { } } + fn suggest_missing_comma_before_closure(&self, err: &mut Diag<'_>) { + if self.formal_and_expected_inputs.len() <= self.provided_args.len() { + return; + } + + for &arg in &self.provided_args { + let mut curr = arg; + let mut bitor_count = 0; + while let hir::ExprKind::Binary(op, lhs, _) = curr.kind + && op.node == hir::BinOpKind::BitOr + { + bitor_count += 1; + curr = lhs; + } + + // `t(() |c| c)` is greedily parsed as `((() | c) | c)`. Unit cannot BitOr, + // and closure params need two `|` delimiters. + if bitor_count >= 2 + && matches!(curr.kind, hir::ExprKind::Tup([])) + && curr.span.can_be_used_for_suggestions() + { + err.span_suggestion_verbose( + curr.span.shrink_to_hi(), + "add a comma", + ",", + Applicability::MaybeIncorrect, + ); + break; + } + } + } + /// A "softer" version of the `demand_compatible`, which checks types without persisting them, /// and treats error types differently /// This will allow us to "probe" for other argument orders that would likely have been correct diff --git a/tests/ui/argument-suggestions/missing-comma-before-closure.rs b/tests/ui/argument-suggestions/missing-comma-before-closure.rs new file mode 100644 index 0000000000000..6b01b41fbcbc7 --- /dev/null +++ b/tests/ui/argument-suggestions/missing-comma-before-closure.rs @@ -0,0 +1,20 @@ +fn t1(_: (), _: impl Fn(bool) -> bool) {} + +fn main() { + t1(() |c| c); + //~^ ERROR cannot find value `c` in this scope + //~| ERROR cannot find value `c` in this scope + //~| ERROR this function takes 2 arguments but 1 argument was supplied + t1((()) |c| c); + //~^ ERROR cannot find value `c` in this scope + //~| ERROR cannot find value `c` in this scope + //~| ERROR this function takes 2 arguments but 1 argument was supplied + let a = (); + t1(() |a| c); + //~^ ERROR no implementation for `() | ()` + //~| ERROR cannot find value `c` in this scope + //~| ERROR this function takes 2 arguments but 1 argument was supplied + t1(() | a); + //~^ ERROR no implementation for `() | ()` + //~| ERROR this function takes 2 arguments but 1 argument was supplied +} diff --git a/tests/ui/argument-suggestions/missing-comma-before-closure.stderr b/tests/ui/argument-suggestions/missing-comma-before-closure.stderr new file mode 100644 index 0000000000000..20d447b0a73bf --- /dev/null +++ b/tests/ui/argument-suggestions/missing-comma-before-closure.stderr @@ -0,0 +1,132 @@ +error[E0425]: cannot find value `c` in this scope + --> $DIR/missing-comma-before-closure.rs:4:12 + | +LL | t1(() |c| c); + | ^ not found in this scope + +error[E0425]: cannot find value `c` in this scope + --> $DIR/missing-comma-before-closure.rs:4:15 + | +LL | t1(() |c| c); + | ^ not found in this scope + +error[E0425]: cannot find value `c` in this scope + --> $DIR/missing-comma-before-closure.rs:8:14 + | +LL | t1((()) |c| c); + | ^ not found in this scope + +error[E0425]: cannot find value `c` in this scope + --> $DIR/missing-comma-before-closure.rs:8:17 + | +LL | t1((()) |c| c); + | ^ not found in this scope + +error[E0425]: cannot find value `c` in this scope + --> $DIR/missing-comma-before-closure.rs:13:15 + | +LL | t1(() |a| c); + | ^ not found in this scope + | +help: a local variable with a similar name exists + | +LL - t1(() |a| c); +LL + t1(() |a| a); + | + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/missing-comma-before-closure.rs:4:5 + | +LL | t1(() |c| c); + | ^^---------- argument #2 is missing + | +note: function defined here + --> $DIR/missing-comma-before-closure.rs:1:4 + | +LL | fn t1(_: (), _: impl Fn(bool) -> bool) {} + | ^^ ------------------------ +help: add a comma + | +LL | t1((), |c| c); + | + +help: provide the argument + | +LL | t1(() |c| c, /* _ */); + | +++++++++ + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/missing-comma-before-closure.rs:8:5 + | +LL | t1((()) |c| c); + | ^^------------ argument #2 is missing + | +note: function defined here + --> $DIR/missing-comma-before-closure.rs:1:4 + | +LL | fn t1(_: (), _: impl Fn(bool) -> bool) {} + | ^^ ------------------------ +help: add a comma + | +LL | t1((()), |c| c); + | + +help: provide the argument + | +LL | t1((()) |c| c, /* _ */); + | +++++++++ + +error[E0369]: no implementation for `() | ()` + --> $DIR/missing-comma-before-closure.rs:13:11 + | +LL | t1(() |a| c); + | -- ^- () + | | + | () + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/missing-comma-before-closure.rs:13:5 + | +LL | t1(() |a| c); + | ^^---------- argument #2 is missing + | +note: function defined here + --> $DIR/missing-comma-before-closure.rs:1:4 + | +LL | fn t1(_: (), _: impl Fn(bool) -> bool) {} + | ^^ ------------------------ +help: add a comma + | +LL | t1((), |a| c); + | + +help: provide the argument + | +LL | t1(() |a| c, /* _ */); + | +++++++++ + +error[E0369]: no implementation for `() | ()` + --> $DIR/missing-comma-before-closure.rs:17:11 + | +LL | t1(() | a); + | -- ^ - () + | | + | () + +error[E0061]: this function takes 2 arguments but 1 argument was supplied + --> $DIR/missing-comma-before-closure.rs:17:5 + | +LL | t1(() | a); + | ^^-------- argument #2 is missing + | +note: function defined here + --> $DIR/missing-comma-before-closure.rs:1:4 + | +LL | fn t1(_: (), _: impl Fn(bool) -> bool) {} + | ^^ ------------------------ +help: provide the argument + | +LL | t1(() | a, /* _ */); + | +++++++++ + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0061, E0369, E0425. +For more information about an error, try `rustc --explain E0061`.