From 94c300a4522f3386546ba77a21865bb78fe9bba8 Mon Sep 17 00:00:00 2001
From: Rob Pilling <robpilling@gmail.com>
Date: Sun, 7 Nov 2021 22:41:35 +0000
Subject: [PATCH 1/5] Suggest tuple-parentheses when passing N arguments to an
 N-tuple argument

---
 .../rustc_typeck/src/check/fn_ctxt/checks.rs  | 73 +++++++++++++++++--
 1 file changed, 67 insertions(+), 6 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index c39199f84b527..5f3507846ba9b 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -28,6 +28,11 @@ use crate::structured_errors::StructuredDiagnostic;
 use std::iter;
 use std::slice;
 
+enum FnArgsAsTuple<'hir> {
+    Single(&'hir hir::Expr<'hir>),
+    Multi { first: &'hir hir::Expr<'hir>, last: &'hir hir::Expr<'hir> },
+}
+
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
     pub(in super::super) fn check_casts(&self) {
         let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut();
@@ -127,8 +132,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         let expected_arg_count = formal_input_tys.len();
 
-        // expected_count, arg_count, error_code, sugg_unit
-        let mut error: Option<(usize, usize, &str, bool)> = None;
+        // expected_count, arg_count, error_code, sugg_unit, sugg_tuple_wrap_args
+        let mut error: Option<(usize, usize, &str, bool, Option<FnArgsAsTuple<'_>>)> = None;
 
         // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here
         let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments {
@@ -138,7 +143,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ty::Tuple(arg_types) => {
                     // Argument length differs
                     if arg_types.len() != provided_args.len() {
-                        error = Some((arg_types.len(), provided_args.len(), "E0057", false));
+                        error = Some((arg_types.len(), provided_args.len(), "E0057", false, None));
                     }
                     let expected_input_tys = match expected_input_tys.get(0) {
                         Some(&ty) => match ty.kind() {
@@ -169,7 +174,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             if supplied_arg_count >= expected_arg_count {
                 (formal_input_tys.to_vec(), expected_input_tys)
             } else {
-                error = Some((expected_arg_count, supplied_arg_count, "E0060", false));
+                error = Some((expected_arg_count, supplied_arg_count, "E0060", false, None));
                 (self.err_args(supplied_arg_count), vec![])
             }
         } else {
@@ -181,7 +186,43 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             } else {
                 false
             };
-            error = Some((expected_arg_count, supplied_arg_count, "E0061", sugg_unit));
+
+            // are we passing elements of a tuple without the tuple parentheses?
+            let chosen_arg_tys = if expected_input_tys.is_empty() {
+                // In most cases we can use expected_arg_tys, but some callers won't have the type
+                // information, in which case we fall back to the types from the input expressions.
+                formal_input_tys
+            } else {
+                &*expected_input_tys
+            };
+
+            let sugg_tuple_wrap_args = chosen_arg_tys
+                .get(0)
+                .cloned()
+                .map(|arg_ty| self.resolve_vars_if_possible(arg_ty))
+                .and_then(|arg_ty| match arg_ty.kind() {
+                    ty::Tuple(tup_elems) => Some(tup_elems),
+                    _ => None,
+                })
+                .and_then(|tup_elems| {
+                    if tup_elems.len() == supplied_arg_count && chosen_arg_tys.len() == 1 {
+                        match provided_args {
+                            [] => None,
+                            [single] => Some(FnArgsAsTuple::Single(single)),
+                            [first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
+                        }
+                    } else {
+                        None
+                    }
+                });
+
+            error = Some((
+                expected_arg_count,
+                supplied_arg_count,
+                "E0061",
+                sugg_unit,
+                sugg_tuple_wrap_args,
+            ));
             (self.err_args(supplied_arg_count), vec![])
         };
 
@@ -305,7 +346,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
 
         // If there was an error in parameter count, emit that here
-        if let Some((expected_count, arg_count, err_code, sugg_unit)) = error {
+        if let Some((expected_count, arg_count, err_code, sugg_unit, sugg_tuple_wrap_args)) = error
+        {
             let (span, start_span, args, ctor_of) = match &call_expr.kind {
                 hir::ExprKind::Call(
                     hir::Expr {
@@ -408,6 +450,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     String::from("()"),
                     Applicability::MachineApplicable,
                 );
+            } else if let Some(tuple_fn_arg) = sugg_tuple_wrap_args {
+                use FnArgsAsTuple::*;
+
+                let spans = match tuple_fn_arg {
+                    Multi { first, last } => vec![
+                        (first.span.shrink_to_lo(), '('.to_string()),
+                        (last.span.shrink_to_hi(), ')'.to_string()),
+                    ],
+                    Single(single) => vec![
+                        (single.span.shrink_to_lo(), '('.to_string()),
+                        (single.span.shrink_to_hi(), ",)".to_string()),
+                    ],
+                };
+
+                err.multipart_suggestion(
+                    "use parentheses to construct a tuple",
+                    spans,
+                    Applicability::MachineApplicable,
+                );
             } else {
                 err.span_label(
                     span,

From 80059f99424c64d10f073ab5c502856d46b1c26d Mon Sep 17 00:00:00 2001
From: Rob Pilling <robpilling@gmail.com>
Date: Tue, 9 Nov 2021 23:20:26 +0000
Subject: [PATCH 2/5] Test tuple suggestions, including
 tuple-as-function-argument

---
 .../suggestions/args-instead-of-tuple.fixed   | 18 +++++++
 .../ui/suggestions/args-instead-of-tuple.rs   | 18 +++++++
 .../suggestions/args-instead-of-tuple.stderr  | 52 +++++++++++++++++++
 3 files changed, 88 insertions(+)
 create mode 100644 src/test/ui/suggestions/args-instead-of-tuple.fixed
 create mode 100644 src/test/ui/suggestions/args-instead-of-tuple.rs
 create mode 100644 src/test/ui/suggestions/args-instead-of-tuple.stderr

diff --git a/src/test/ui/suggestions/args-instead-of-tuple.fixed b/src/test/ui/suggestions/args-instead-of-tuple.fixed
new file mode 100644
index 0000000000000..adb832b8a7b74
--- /dev/null
+++ b/src/test/ui/suggestions/args-instead-of-tuple.fixed
@@ -0,0 +1,18 @@
+// Test suggesting tuples where bare arguments may have been passed
+// See issue #86481 for details.
+
+// run-rustfix
+
+fn main() {
+    let _: Result<(i32, i8), ()> = Ok((1, 2));
+    //~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
+    let _: Option<(i32, i8, &'static str)> = Some((1, 2, "hi"));
+    //~^ ERROR this enum variant takes 1 argument but 3 arguments were supplied
+    let _: Option<()> = Some(());
+    //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
+
+    f((1, 2)); //~ ERROR this function takes 1 argument
+}
+
+fn f(_: (i32, i32)) {
+}
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.rs b/src/test/ui/suggestions/args-instead-of-tuple.rs
new file mode 100644
index 0000000000000..8dbc58daeb1f5
--- /dev/null
+++ b/src/test/ui/suggestions/args-instead-of-tuple.rs
@@ -0,0 +1,18 @@
+// Test suggesting tuples where bare arguments may have been passed
+// See issue #86481 for details.
+
+// run-rustfix
+
+fn main() {
+    let _: Result<(i32, i8), ()> = Ok(1, 2);
+    //~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
+    let _: Option<(i32, i8, &'static str)> = Some(1, 2, "hi");
+    //~^ ERROR this enum variant takes 1 argument but 3 arguments were supplied
+    let _: Option<()> = Some();
+    //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
+
+    f(1, 2); //~ ERROR this function takes 1 argument
+}
+
+fn f(_: (i32, i32)) {
+}
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.stderr b/src/test/ui/suggestions/args-instead-of-tuple.stderr
new file mode 100644
index 0000000000000..95bbbdb274964
--- /dev/null
+++ b/src/test/ui/suggestions/args-instead-of-tuple.stderr
@@ -0,0 +1,52 @@
+error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
+  --> $DIR/args-instead-of-tuple.rs:7:36
+   |
+LL |     let _: Result<(i32, i8), ()> = Ok(1, 2);
+   |                                    ^^ -  - supplied 2 arguments
+   |
+help: use parentheses to construct a tuple
+   |
+LL |     let _: Result<(i32, i8), ()> = Ok((1, 2));
+   |                                       +    +
+
+error[E0061]: this enum variant takes 1 argument but 3 arguments were supplied
+  --> $DIR/args-instead-of-tuple.rs:9:46
+   |
+LL |     let _: Option<(i32, i8, &'static str)> = Some(1, 2, "hi");
+   |                                              ^^^^ -  -  ---- supplied 3 arguments
+   |
+help: use parentheses to construct a tuple
+   |
+LL |     let _: Option<(i32, i8, &'static str)> = Some((1, 2, "hi"));
+   |                                                   +          +
+
+error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied
+  --> $DIR/args-instead-of-tuple.rs:11:25
+   |
+LL |     let _: Option<()> = Some();
+   |                         ^^^^-- supplied 0 arguments
+   |
+help: expected the unit value `()`; create it with empty parentheses
+   |
+LL |     let _: Option<()> = Some(());
+   |                              ++
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/args-instead-of-tuple.rs:14:5
+   |
+LL |     f(1, 2);
+   |     ^ -  - supplied 2 arguments
+   |
+note: function defined here
+  --> $DIR/args-instead-of-tuple.rs:17:4
+   |
+LL | fn f(_: (i32, i32)) {
+   |    ^ -------------
+help: use parentheses to construct a tuple
+   |
+LL |     f((1, 2));
+   |       +    +
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0061`.

From 54d2d30662c2832554bb127f017f7a311bb62b4e Mon Sep 17 00:00:00 2001
From: Rob Pilling <robpilling@gmail.com>
Date: Sun, 5 Dec 2021 21:41:33 +0000
Subject: [PATCH 3/5] Compare tuple element & arg types before suggesting a
 tuple

---
 .../rustc_typeck/src/check/fn_ctxt/checks.rs  | 53 +++++++++++--------
 .../args-instead-of-tuple-errors.rs           | 13 +++++
 .../args-instead-of-tuple-errors.stderr       | 25 +++++++++
 .../suggestions/args-instead-of-tuple.fixed   |  4 +-
 .../ui/suggestions/args-instead-of-tuple.rs   |  4 +-
 .../suggestions/args-instead-of-tuple.stderr  | 12 ++---
 6 files changed, 79 insertions(+), 32 deletions(-)
 create mode 100644 src/test/ui/suggestions/args-instead-of-tuple-errors.rs
 create mode 100644 src/test/ui/suggestions/args-instead-of-tuple-errors.stderr

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index 5f3507846ba9b..a94e6b480d643 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -18,7 +18,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, ParamEnv, Ty};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::{self, MultiSpan, Span};
@@ -188,33 +188,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             };
 
             // are we passing elements of a tuple without the tuple parentheses?
-            let chosen_arg_tys = if expected_input_tys.is_empty() {
-                // In most cases we can use expected_arg_tys, but some callers won't have the type
+            let expected_input_tys = if expected_input_tys.is_empty() {
+                // In most cases we can use expected_input_tys, but some callers won't have the type
                 // information, in which case we fall back to the types from the input expressions.
                 formal_input_tys
             } else {
                 &*expected_input_tys
             };
 
-            let sugg_tuple_wrap_args = chosen_arg_tys
-                .get(0)
-                .cloned()
-                .map(|arg_ty| self.resolve_vars_if_possible(arg_ty))
-                .and_then(|arg_ty| match arg_ty.kind() {
-                    ty::Tuple(tup_elems) => Some(tup_elems),
-                    _ => None,
-                })
-                .and_then(|tup_elems| {
-                    if tup_elems.len() == supplied_arg_count && chosen_arg_tys.len() == 1 {
-                        match provided_args {
-                            [] => None,
-                            [single] => Some(FnArgsAsTuple::Single(single)),
-                            [first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
-                        }
-                    } else {
-                        None
-                    }
-                });
+            let sugg_tuple_wrap_args = self.suggested_tuple_wrap(expected_input_tys, provided_args);
 
             error = Some((
                 expected_arg_count,
@@ -518,6 +500,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 
+    fn suggested_tuple_wrap(
+        &self,
+        expected_input_tys: &[Ty<'tcx>],
+        provided_args: &'tcx [hir::Expr<'tcx>],
+    ) -> Option<FnArgsAsTuple<'_>> {
+        let [expected_arg_type] = &expected_input_tys[..] else { return None };
+
+        let ty::Tuple(expected_elems) = self.resolve_vars_if_possible(*expected_arg_type).kind()
+            else { return None };
+
+        let expected_types: Vec<_> = expected_elems.iter().map(|k| k.expect_ty()).collect();
+        let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect();
+
+        let all_match = iter::zip(expected_types, supplied_types)
+            .all(|(expected, supplied)| self.can_eq(ParamEnv::empty(), expected, supplied).is_ok());
+
+        if all_match {
+            match provided_args {
+                [] => None,
+                [single] => Some(FnArgsAsTuple::Single(single)),
+                [first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
+            }
+        } else {
+            None
+        }
+    }
+
     // AST fragment checking
     pub(in super::super) fn check_lit(
         &self,
diff --git a/src/test/ui/suggestions/args-instead-of-tuple-errors.rs b/src/test/ui/suggestions/args-instead-of-tuple-errors.rs
new file mode 100644
index 0000000000000..c4e9c68e219e6
--- /dev/null
+++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.rs
@@ -0,0 +1,13 @@
+// Ensure we don't suggest tuple-wrapping when we'd end up with a type error
+
+fn main() {
+    // we shouldn't suggest to fix these - `2` isn't a `bool`
+
+    let _: Option<(i32, bool)> = Some(1, 2);
+    //~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
+    int_bool(1, 2);
+    //~^ ERROR this function takes 1 argument but 2 arguments were supplied
+}
+
+fn int_bool(_: (i32, bool)) {
+}
diff --git a/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
new file mode 100644
index 0000000000000..c53c8bbdcc9df
--- /dev/null
+++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
@@ -0,0 +1,25 @@
+error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
+  --> $DIR/args-instead-of-tuple-errors.rs:6:34
+   |
+LL |     let _: Option<(i32, bool)> = Some(1, 2);
+   |                                  ^^^^ -  - supplied 2 arguments
+   |                                  |
+   |                                  expected 1 argument
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/args-instead-of-tuple-errors.rs:8:5
+   |
+LL |     int_bool(1, 2);
+   |     ^^^^^^^^ -  - supplied 2 arguments
+   |     |
+   |     expected 1 argument
+   |
+note: function defined here
+  --> $DIR/args-instead-of-tuple-errors.rs:12:4
+   |
+LL | fn int_bool(_: (i32, bool)) {
+   |    ^^^^^^^^ --------------
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0061`.
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.fixed b/src/test/ui/suggestions/args-instead-of-tuple.fixed
index adb832b8a7b74..095be95f18586 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple.fixed
+++ b/src/test/ui/suggestions/args-instead-of-tuple.fixed
@@ -11,8 +11,8 @@ fn main() {
     let _: Option<()> = Some(());
     //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
 
-    f((1, 2)); //~ ERROR this function takes 1 argument
+    two_ints((1, 2)); //~ ERROR this function takes 1 argument
 }
 
-fn f(_: (i32, i32)) {
+fn two_ints(_: (i32, i32)) {
 }
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.rs b/src/test/ui/suggestions/args-instead-of-tuple.rs
index 8dbc58daeb1f5..3466a46df848e 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple.rs
+++ b/src/test/ui/suggestions/args-instead-of-tuple.rs
@@ -11,8 +11,8 @@ fn main() {
     let _: Option<()> = Some();
     //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
 
-    f(1, 2); //~ ERROR this function takes 1 argument
+    two_ints(1, 2); //~ ERROR this function takes 1 argument
 }
 
-fn f(_: (i32, i32)) {
+fn two_ints(_: (i32, i32)) {
 }
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.stderr b/src/test/ui/suggestions/args-instead-of-tuple.stderr
index 95bbbdb274964..1bf7e7a8d171b 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple.stderr
+++ b/src/test/ui/suggestions/args-instead-of-tuple.stderr
@@ -34,18 +34,18 @@ LL |     let _: Option<()> = Some(());
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/args-instead-of-tuple.rs:14:5
    |
-LL |     f(1, 2);
-   |     ^ -  - supplied 2 arguments
+LL |     two_ints(1, 2);
+   |     ^^^^^^^^ -  - supplied 2 arguments
    |
 note: function defined here
   --> $DIR/args-instead-of-tuple.rs:17:4
    |
-LL | fn f(_: (i32, i32)) {
-   |    ^ -------------
+LL | fn two_ints(_: (i32, i32)) {
+   |    ^^^^^^^^ -------------
 help: use parentheses to construct a tuple
    |
-LL |     f((1, 2));
-   |       +    +
+LL |     two_ints((1, 2));
+   |              +    +
 
 error: aborting due to 4 previous errors
 

From a129a85144efb67bfd8f380a758ed6be41d3e29b Mon Sep 17 00:00:00 2001
From: Rob Pilling <robpilling@gmail.com>
Date: Sun, 16 Jan 2022 21:47:44 +0000
Subject: [PATCH 4/5] Handle generics with ParamEnv

---
 .../rustc_typeck/src/check/fn_ctxt/checks.rs  |  4 +--
 .../suggestions/args-instead-of-tuple.fixed   |  9 +++++
 .../ui/suggestions/args-instead-of-tuple.rs   |  9 +++++
 .../suggestions/args-instead-of-tuple.stderr  | 36 +++++++++++++++++--
 4 files changed, 54 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index a94e6b480d643..af0c9e5e5090e 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -18,7 +18,7 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{ExprKind, Node, QPath};
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::{self, ParamEnv, Ty};
+use rustc_middle::ty::{self, Ty};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::{self, MultiSpan, Span};
@@ -514,7 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let supplied_types: Vec<_> = provided_args.iter().map(|arg| self.check_expr(arg)).collect();
 
         let all_match = iter::zip(expected_types, supplied_types)
-            .all(|(expected, supplied)| self.can_eq(ParamEnv::empty(), expected, supplied).is_ok());
+            .all(|(expected, supplied)| self.can_eq(self.param_env, expected, supplied).is_ok());
 
         if all_match {
             match provided_args {
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.fixed b/src/test/ui/suggestions/args-instead-of-tuple.fixed
index 095be95f18586..c9b8a41d469b9 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple.fixed
+++ b/src/test/ui/suggestions/args-instead-of-tuple.fixed
@@ -12,7 +12,16 @@ fn main() {
     //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
 
     two_ints((1, 2)); //~ ERROR this function takes 1 argument
+
+    with_generic((3, 4)); //~ ERROR this function takes 1 argument
 }
 
 fn two_ints(_: (i32, i32)) {
 }
+
+fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
+    if false {
+        // test generics/bound handling
+        with_generic((a, b)); //~ ERROR this function takes 1 argument
+    }
+}
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.rs b/src/test/ui/suggestions/args-instead-of-tuple.rs
index 3466a46df848e..d4cc3024dd0d2 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple.rs
+++ b/src/test/ui/suggestions/args-instead-of-tuple.rs
@@ -12,7 +12,16 @@ fn main() {
     //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
 
     two_ints(1, 2); //~ ERROR this function takes 1 argument
+
+    with_generic(3, 4); //~ ERROR this function takes 1 argument
 }
 
 fn two_ints(_: (i32, i32)) {
 }
+
+fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
+    if false {
+        // test generics/bound handling
+        with_generic(a, b); //~ ERROR this function takes 1 argument
+    }
+}
diff --git a/src/test/ui/suggestions/args-instead-of-tuple.stderr b/src/test/ui/suggestions/args-instead-of-tuple.stderr
index 1bf7e7a8d171b..172db7ee3df38 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple.stderr
+++ b/src/test/ui/suggestions/args-instead-of-tuple.stderr
@@ -38,7 +38,7 @@ LL |     two_ints(1, 2);
    |     ^^^^^^^^ -  - supplied 2 arguments
    |
 note: function defined here
-  --> $DIR/args-instead-of-tuple.rs:17:4
+  --> $DIR/args-instead-of-tuple.rs:19:4
    |
 LL | fn two_ints(_: (i32, i32)) {
    |    ^^^^^^^^ -------------
@@ -47,6 +47,38 @@ help: use parentheses to construct a tuple
 LL |     two_ints((1, 2));
    |              +    +
 
-error: aborting due to 4 previous errors
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/args-instead-of-tuple.rs:16:5
+   |
+LL |     with_generic(3, 4);
+   |     ^^^^^^^^^^^^ -  - supplied 2 arguments
+   |
+note: function defined here
+  --> $DIR/args-instead-of-tuple.rs:22:4
+   |
+LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
+   |    ^^^^^^^^^^^^                 ----------------
+help: use parentheses to construct a tuple
+   |
+LL |     with_generic((3, 4));
+   |                  +    +
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/args-instead-of-tuple.rs:25:9
+   |
+LL |         with_generic(a, b);
+   |         ^^^^^^^^^^^^ -  - supplied 2 arguments
+   |
+note: function defined here
+  --> $DIR/args-instead-of-tuple.rs:22:4
+   |
+LL | fn with_generic<T: Copy + Send>((a, b): (i32, T)) {
+   |    ^^^^^^^^^^^^                 ----------------
+help: use parentheses to construct a tuple
+   |
+LL |         with_generic((a, b));
+   |                      +    +
+
+error: aborting due to 6 previous errors
 
 For more information about this error, try `rustc --explain E0061`.

From a8bac9879a36d01c1fc325ed85d6a992deab88fa Mon Sep 17 00:00:00 2001
From: Rob Pilling <robpilling@gmail.com>
Date: Sun, 16 Jan 2022 22:47:33 +0000
Subject: [PATCH 5/5] Remove 1-tuple unreachable case

---
 .../rustc_typeck/src/check/fn_ctxt/checks.rs  | 30 +++++++------------
 .../args-instead-of-tuple-errors.rs           |  3 ++
 .../args-instead-of-tuple-errors.stderr       | 12 ++++++--
 3 files changed, 24 insertions(+), 21 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index af0c9e5e5090e..d7022c27d3984 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -28,9 +28,9 @@ use crate::structured_errors::StructuredDiagnostic;
 use std::iter;
 use std::slice;
 
-enum FnArgsAsTuple<'hir> {
-    Single(&'hir hir::Expr<'hir>),
-    Multi { first: &'hir hir::Expr<'hir>, last: &'hir hir::Expr<'hir> },
+struct FnArgsAsTuple<'hir> {
+    first: &'hir hir::Expr<'hir>,
+    last: &'hir hir::Expr<'hir>,
 }
 
 impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -432,23 +432,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     String::from("()"),
                     Applicability::MachineApplicable,
                 );
-            } else if let Some(tuple_fn_arg) = sugg_tuple_wrap_args {
-                use FnArgsAsTuple::*;
-
-                let spans = match tuple_fn_arg {
-                    Multi { first, last } => vec![
+            } else if let Some(FnArgsAsTuple { first, last }) = sugg_tuple_wrap_args {
+                err.multipart_suggestion(
+                    "use parentheses to construct a tuple",
+                    vec![
                         (first.span.shrink_to_lo(), '('.to_string()),
                         (last.span.shrink_to_hi(), ')'.to_string()),
                     ],
-                    Single(single) => vec![
-                        (single.span.shrink_to_lo(), '('.to_string()),
-                        (single.span.shrink_to_hi(), ",)".to_string()),
-                    ],
-                };
-
-                err.multipart_suggestion(
-                    "use parentheses to construct a tuple",
-                    spans,
                     Applicability::MachineApplicable,
                 );
             } else {
@@ -519,8 +509,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if all_match {
             match provided_args {
                 [] => None,
-                [single] => Some(FnArgsAsTuple::Single(single)),
-                [first, .., last] => Some(FnArgsAsTuple::Multi { first, last }),
+                [_] => unreachable!(
+                    "shouldn't reach here - need count mismatch between 1-tuple and 1-argument"
+                ),
+                [first, .., last] => Some(FnArgsAsTuple { first, last }),
             }
         } else {
             None
diff --git a/src/test/ui/suggestions/args-instead-of-tuple-errors.rs b/src/test/ui/suggestions/args-instead-of-tuple-errors.rs
index c4e9c68e219e6..2c3ee5fcb8039 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple-errors.rs
+++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.rs
@@ -7,6 +7,9 @@ fn main() {
     //~^ ERROR this enum variant takes 1 argument but 2 arguments were supplied
     int_bool(1, 2);
     //~^ ERROR this function takes 1 argument but 2 arguments were supplied
+
+    let _: Option<(i8,)> = Some();
+    //~^ ERROR this enum variant takes 1 argument but 0 arguments were supplied
 }
 
 fn int_bool(_: (i32, bool)) {
diff --git a/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
index c53c8bbdcc9df..a2ad602dbd47a 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
+++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
@@ -15,11 +15,19 @@ LL |     int_bool(1, 2);
    |     expected 1 argument
    |
 note: function defined here
-  --> $DIR/args-instead-of-tuple-errors.rs:12:4
+  --> $DIR/args-instead-of-tuple-errors.rs:15:4
    |
 LL | fn int_bool(_: (i32, bool)) {
    |    ^^^^^^^^ --------------
 
-error: aborting due to 2 previous errors
+error[E0061]: this enum variant takes 1 argument but 0 arguments were supplied
+  --> $DIR/args-instead-of-tuple-errors.rs:11:28
+   |
+LL |     let _: Option<(i8,)> = Some();
+   |                            ^^^^-- supplied 0 arguments
+   |                            |
+   |                            expected 1 argument
+
+error: aborting due to 3 previous errors
 
 For more information about this error, try `rustc --explain E0061`.