diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs
index b7ba9d9787846..7602f2550e85b 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs
@@ -1,7 +1,26 @@
 use std::cmp;
 
+use rustc_index::vec::IndexVec;
 use rustc_middle::ty::error::TypeError;
 
+rustc_index::newtype_index! {
+    pub(crate) struct ExpectedIdx {
+        DEBUG_FORMAT = "ExpectedIdx({})",
+    }
+}
+
+rustc_index::newtype_index! {
+    pub(crate) struct ProvidedIdx {
+        DEBUG_FORMAT = "ProvidedIdx({})",
+    }
+}
+
+impl ExpectedIdx {
+    pub fn to_provided_idx(self) -> ProvidedIdx {
+        ProvidedIdx::from_usize(self.as_usize())
+    }
+}
+
 // An issue that might be found in the compatibility matrix
 #[derive(Debug)]
 enum Issue {
@@ -27,24 +46,24 @@ pub(crate) enum Compatibility<'tcx> {
 #[derive(Debug)]
 pub(crate) enum Error<'tcx> {
     /// The provided argument is the invalid type for the expected input
-    Invalid(usize, usize, Compatibility<'tcx>), // provided, expected
+    Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>),
     /// There is a missing input
-    Missing(usize),
+    Missing(ExpectedIdx),
     /// There's a superfluous argument
-    Extra(usize),
+    Extra(ProvidedIdx),
     /// Two arguments should be swapped
-    Swap(usize, usize, usize, usize),
+    Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx),
     /// Several arguments should be reordered
-    Permutation(Vec<(usize, usize)>), // dest_arg, dest_input
+    Permutation(Vec<(ExpectedIdx, ProvidedIdx)>),
 }
 
 pub(crate) struct ArgMatrix<'tcx> {
     /// Maps the indices in the `compatibility_matrix` rows to the indices of
     /// the *user provided* inputs
-    input_indexes: Vec<usize>,
+    provided_indices: Vec<ProvidedIdx>,
     /// Maps the indices in the `compatibility_matrix` columns to the indices
     /// of the *expected* args
-    arg_indexes: Vec<usize>,
+    expected_indices: Vec<ExpectedIdx>,
     /// The first dimension (rows) are the remaining user provided inputs to
     /// match and the second dimension (cols) are the remaining expected args
     /// to match
@@ -52,62 +71,64 @@ pub(crate) struct ArgMatrix<'tcx> {
 }
 
 impl<'tcx> ArgMatrix<'tcx> {
-    pub(crate) fn new<F: FnMut(usize, usize) -> Compatibility<'tcx>>(
-        minimum_input_count: usize,
-        provided_arg_count: usize,
+    pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>(
+        provided_count: usize,
+        expected_input_count: usize,
         mut is_compatible: F,
     ) -> Self {
-        let compatibility_matrix = (0..provided_arg_count)
-            .map(|i| (0..minimum_input_count).map(|j| is_compatible(i, j)).collect())
+        let compatibility_matrix = (0..provided_count)
+            .map(|i| {
+                (0..expected_input_count)
+                    .map(|j| is_compatible(ProvidedIdx::from_usize(i), ExpectedIdx::from_usize(j)))
+                    .collect()
+            })
             .collect();
         ArgMatrix {
-            input_indexes: (0..provided_arg_count).collect(),
-            arg_indexes: (0..minimum_input_count).collect(),
+            provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(),
+            expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(),
             compatibility_matrix,
         }
     }
 
     /// Remove a given input from consideration
-    fn eliminate_input(&mut self, idx: usize) {
-        self.input_indexes.remove(idx);
+    fn eliminate_provided(&mut self, idx: usize) {
+        self.provided_indices.remove(idx);
         self.compatibility_matrix.remove(idx);
     }
 
     /// Remove a given argument from consideration
-    fn eliminate_arg(&mut self, idx: usize) {
-        self.arg_indexes.remove(idx);
+    fn eliminate_expected(&mut self, idx: usize) {
+        self.expected_indices.remove(idx);
         for row in &mut self.compatibility_matrix {
             row.remove(idx);
         }
     }
 
     /// "satisfy" an input with a given arg, removing both from consideration
-    fn satisfy_input(&mut self, input_idx: usize, arg_idx: usize) {
-        self.eliminate_input(input_idx);
-        self.eliminate_arg(arg_idx);
+    fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) {
+        self.eliminate_provided(provided_idx);
+        self.eliminate_expected(expected_idx);
     }
 
     // Returns a `Vec` of (user input, expected arg) of matched arguments. These
     // are inputs on the remaining diagonal that match.
-    fn eliminate_satisfied(&mut self) -> Vec<(usize, usize)> {
-        let mut i = cmp::min(self.input_indexes.len(), self.arg_indexes.len());
+    fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> {
+        let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len());
         let mut eliminated = vec![];
-        while i > 0 {
-            let idx = i - 1;
-            if matches!(self.compatibility_matrix[idx][idx], Compatibility::Compatible) {
-                eliminated.push((self.input_indexes[idx], self.arg_indexes[idx]));
-                self.satisfy_input(idx, idx);
+        for i in (0..num_args).rev() {
+            if matches!(self.compatibility_matrix[i][i], Compatibility::Compatible) {
+                eliminated.push((self.provided_indices[i], self.expected_indices[i]));
+                self.satisfy_input(i, i);
             }
-            i -= 1;
         }
-        return eliminated;
+        eliminated
     }
 
     // Find some issue in the compatibility matrix
     fn find_issue(&self) -> Option<Issue> {
         let mat = &self.compatibility_matrix;
-        let ai = &self.arg_indexes;
-        let ii = &self.input_indexes;
+        let ai = &self.expected_indices;
+        let ii = &self.provided_indices;
 
         for i in 0..cmp::max(ai.len(), ii.len()) {
             // If we eliminate the last row, any left-over inputs are considered missing
@@ -264,12 +285,15 @@ impl<'tcx> ArgMatrix<'tcx> {
     //
     // We'll want to know which arguments and inputs these rows and columns correspond to
     // even after we delete them.
-    pub(crate) fn find_errors(mut self) -> (Vec<Error<'tcx>>, Vec<Option<usize>>) {
-        let provided_arg_count = self.input_indexes.len();
+    pub(crate) fn find_errors(
+        mut self,
+    ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) {
+        let provided_arg_count = self.provided_indices.len();
 
         let mut errors: Vec<Error<'tcx>> = vec![];
         // For each expected argument, the matched *actual* input
-        let mut matched_inputs: Vec<Option<usize>> = vec![None; self.arg_indexes.len()];
+        let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> =
+            IndexVec::from_elem_n(None, self.expected_indices.len());
 
         // Before we start looking for issues, eliminate any arguments that are already satisfied,
         // so that an argument which is already spoken for by the input it's in doesn't
@@ -280,34 +304,34 @@ impl<'tcx> ArgMatrix<'tcx> {
         // Without this elimination, the first argument causes the second argument
         // to show up as both a missing input and extra argument, rather than
         // just an invalid type.
-        for (inp, arg) in self.eliminate_satisfied() {
-            matched_inputs[arg] = Some(inp);
+        for (provided, expected) in self.eliminate_satisfied() {
+            matched_inputs[expected] = Some(provided);
         }
 
-        while self.input_indexes.len() > 0 || self.arg_indexes.len() > 0 {
+        while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() {
             match self.find_issue() {
                 Some(Issue::Invalid(idx)) => {
                     let compatibility = self.compatibility_matrix[idx][idx].clone();
-                    let input_idx = self.input_indexes[idx];
-                    let arg_idx = self.arg_indexes[idx];
+                    let input_idx = self.provided_indices[idx];
+                    let arg_idx = self.expected_indices[idx];
                     self.satisfy_input(idx, idx);
                     errors.push(Error::Invalid(input_idx, arg_idx, compatibility));
                 }
                 Some(Issue::Extra(idx)) => {
-                    let input_idx = self.input_indexes[idx];
-                    self.eliminate_input(idx);
+                    let input_idx = self.provided_indices[idx];
+                    self.eliminate_provided(idx);
                     errors.push(Error::Extra(input_idx));
                 }
                 Some(Issue::Missing(idx)) => {
-                    let arg_idx = self.arg_indexes[idx];
-                    self.eliminate_arg(idx);
+                    let arg_idx = self.expected_indices[idx];
+                    self.eliminate_expected(idx);
                     errors.push(Error::Missing(arg_idx));
                 }
                 Some(Issue::Swap(idx, other)) => {
-                    let input_idx = self.input_indexes[idx];
-                    let other_input_idx = self.input_indexes[other];
-                    let arg_idx = self.arg_indexes[idx];
-                    let other_arg_idx = self.arg_indexes[other];
+                    let input_idx = self.provided_indices[idx];
+                    let other_input_idx = self.provided_indices[other];
+                    let arg_idx = self.expected_indices[idx];
+                    let other_arg_idx = self.expected_indices[other];
                     let (min, max) = (cmp::min(idx, other), cmp::max(idx, other));
                     self.satisfy_input(min, max);
                     // Subtract 1 because we already removed the "min" row
@@ -319,13 +343,14 @@ impl<'tcx> ArgMatrix<'tcx> {
                 Some(Issue::Permutation(args)) => {
                     let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect();
 
-                    let mut real_idxs = vec![None; provided_arg_count];
+                    let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> =
+                        IndexVec::from_elem_n(None, provided_arg_count);
                     for (src, dst) in
                         args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst)))
                     {
-                        let src_input_idx = self.input_indexes[src];
-                        let dst_input_idx = self.input_indexes[dst];
-                        let dest_arg_idx = self.arg_indexes[dst];
+                        let src_input_idx = self.provided_indices[src];
+                        let dst_input_idx = self.provided_indices[dst];
+                        let dest_arg_idx = self.expected_indices[dst];
                         real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx));
                         matched_inputs[dest_arg_idx] = Some(src_input_idx);
                     }
diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index 2326c4069e483..08df01c0c1a1a 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -1,6 +1,8 @@
 use crate::astconv::AstConv;
 use crate::check::coercion::CoerceMany;
-use crate::check::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error};
+use crate::check::fn_ctxt::arg_matrix::{
+    ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx,
+};
 use crate::check::gather_locals::Declaration;
 use crate::check::method::MethodCallee;
 use crate::check::Expectation::*;
@@ -17,13 +19,14 @@ use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{ExprKind, Node, QPath};
+use rustc_index::vec::IndexVec;
 use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt};
 use rustc_infer::infer::InferOk;
 use rustc_infer::infer::TypeTrace;
 use rustc_middle::ty::adjustment::AllowTwoPhase;
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::fold::TypeFoldable;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
 use rustc_session::Session;
 use rustc_span::symbol::Ident;
 use rustc_span::{self, Span};
@@ -214,6 +217,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         let provided_arg_count = provided_args.len();
 
         // We'll also want to keep track of the fully coerced argument types, for an awkward hack near the end
+        // FIXME(compiler-errors): Get rid of this, actually.
         let mut final_arg_types: Vec<Option<(Ty<'_>, Ty<'_>)>> = vec![None; provided_arg_count];
 
         // We introduce a helper function to demand that a given argument satisfy a given input
@@ -287,54 +291,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         };
 
-        // A "softer" version of the helper above, 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
-        let check_compatible = |input_idx, arg_idx| {
-            let formal_input_ty: Ty<'tcx> = formal_input_tys[arg_idx];
-            let expected_input_ty: Ty<'tcx> = expected_input_tys[arg_idx];
-
-            // If either is an error type, we defy the usual convention and consider them to *not* be
-            // coercible.  This prevents our error message heuristic from trying to pass errors into
-            // every argument.
-            if formal_input_ty.references_error() || expected_input_ty.references_error() {
-                return Compatibility::Incompatible(None);
-            }
-
-            let provided_arg: &hir::Expr<'tcx> = &provided_args[input_idx];
-            let expectation = Expectation::rvalue_hint(self, expected_input_ty);
-            // FIXME: check that this is safe; I don't believe this commits any of the obligations, but I can't be sure.
-            //
-            //   I had another method of "soft" type checking before,
-            //   but it was failing to find the type of some expressions (like "")
-            //   so I prodded this method and made it pub(super) so I could call it, and it seems to work well.
-            let checked_ty = self.check_expr_kind(provided_arg, expectation);
-
-            let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
-            let can_coerce = self.can_coerce(checked_ty, coerced_ty);
-
-            if !can_coerce {
-                return Compatibility::Incompatible(None);
-            }
-
-            let subtyping_result = self
-                .at(&self.misc(provided_arg.span), self.param_env)
-                .sup(formal_input_ty, coerced_ty);
-
-            // Same as above: if either the coerce type or the checked type is an error type,
-            // consider them *not* compatible.
-            let coercible =
-                !coerced_ty.references_error() && !checked_ty.references_error() && can_coerce;
-
-            match (coercible, &subtyping_result) {
-                (true, Ok(_)) => Compatibility::Compatible,
-                _ => Compatibility::Incompatible(subtyping_result.err()),
-            }
-        };
-
         // To start, we only care "along the diagonal", where we expect every
         // provided arg to be in the right spot
-        let mut compatibility = vec![Compatibility::Incompatible(None); provided_args.len()];
+        let mut compatibility_diagonal =
+            vec![Compatibility::Incompatible(None); provided_args.len()];
 
         // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path
         // if the wrong number of arguments were supplied, we CAN'T be satisfied,
@@ -394,7 +354,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                 let compatible = demand_compatible(idx, &mut final_arg_types);
                 let is_compatible = matches!(compatible, Compatibility::Compatible);
-                compatibility[idx] = compatible;
+                compatibility_diagonal[idx] = compatible;
 
                 if !is_compatible {
                     call_appears_satisfied = false;
@@ -402,70 +362,54 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        // Logic here is a bit hairy
-        'errors: {
-            // If something above didn't typecheck, we've fallen off the happy path
-            // and we should make some effort to provide better error messages
-            if call_appears_satisfied {
-                break 'errors;
-            }
-
-            self.set_tainted_by_errors();
+        if c_variadic && provided_arg_count < minimum_input_count {
+            err_code = "E0060";
+        }
 
-            // The algorithm here is inspired by levenshtein distance and longest common subsequence.
-            // We'll try to detect 4 different types of mistakes:
-            // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
-            // - An input is missing, which isn't satisfied by *any* of the other arguments
-            // - Some number of arguments have been provided in the wrong order
-            // - A type is straight up invalid
+        for arg in provided_args.iter().skip(minimum_input_count) {
+            // Make sure we've checked this expr at least once.
+            let arg_ty = self.check_expr(&arg);
 
-            // First, let's find the errors
-            let mut compatibility: Vec<_> = compatibility.into_iter().map(Some).collect();
-            let (mut errors, matched_inputs) =
-                ArgMatrix::new(minimum_input_count, provided_arg_count, |i, j| {
-                    if i == j { compatibility[i].take().unwrap() } else { check_compatible(i, j) }
-                })
-                .find_errors();
+            // If the function is c-style variadic, we skipped a bunch of arguments
+            // so we need to check those, and write out the types
+            // Ideally this would be folded into the above, for uniform style
+            // but c-variadic is already a corner case
+            if c_variadic {
+                fn variadic_error<'tcx>(
+                    sess: &'tcx Session,
+                    span: Span,
+                    ty: Ty<'tcx>,
+                    cast_ty: &str,
+                ) {
+                    use crate::structured_errors::MissingCastForVariadicArg;
 
-            // Okay, so here's where it gets complicated in regards to what errors
-            // we emit and how.
-            // There are 3 different "types" of errors we might encounter.
-            //   1) Missing/extra/swapped arguments
-            //   2) Valid but incorrect arguments
-            //   3) Invalid arguments
-            //      - Currently I think this only comes up with `CyclicTy`
-            //
-            // We first need to go through, remove those from (3) and emit those
-            // as their own error, particularly since they're error code and
-            // message is special. From what I can tell, we *must* emit these
-            // here (vs somewhere prior to this function) since the arguments
-            // become invalid *because* of how they get used in the function.
-            // It is what it is.
-
-            let found_errors = !errors.is_empty();
-
-            errors.drain_filter(|error| {
-                let Error::Invalid(input_idx, arg_idx, Compatibility::Incompatible(Some(e))) = error else { return false };
-                let expected_ty = expected_input_tys[*arg_idx];
-                let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap_or_else(|| tcx.ty_error());
-                let cause = &self.misc(provided_args[*input_idx].span);
-                let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
-                if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
-                    self.report_and_explain_type_error(trace, e).emit();
-                    return true;
+                    MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
                 }
-                false
-            });
 
-            // We're done if we found errors, but we already emitted them.
-            // I don't think we *should* be able to enter this bit of code
-            // (`!call_appears_satisfied`) without *also* finding errors, but we
-            // don't want to accidentally not emit an error if there is some
-            // logic bug in the `ArgMatrix` code.
-            if found_errors && errors.is_empty() {
-                break 'errors;
+                // There are a few types which get autopromoted when passed via varargs
+                // in C but we just error out instead and require explicit casts.
+                let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
+                match arg_ty.kind() {
+                    ty::Float(ty::FloatTy::F32) => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
+                    }
+                    ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
+                    }
+                    ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => {
+                        variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
+                    }
+                    ty::FnDef(..) => {
+                        let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
+                        let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
+                        variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
+                    }
+                    _ => {}
+                }
             }
+        }
 
+        if !call_appears_satisfied {
             // Next, let's construct the error
             let (error_span, full_call_span, ctor_of) = match &call_expr.kind {
                 hir::ExprKind::Call(
@@ -500,524 +444,633 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Some(CtorOf::Variant) => "enum variant",
                 None => "function",
             };
-            if c_variadic && provided_arg_count < minimum_input_count {
-                err_code = "E0060";
-            }
 
-            // Next special case: The case where we expect a single tuple and
-            // wrapping all the args in parentheses (or adding a comma to
-            // already existing parentheses) will result in a tuple that
-            // satisfies the call.
-            // This isn't super ideal code, because we copy code from elsewhere
-            // and somewhat duplicate this. We also delegate to the general type
-            // mismatch suggestions for the single arg case.
-            let sugg_tuple_wrap_args =
-                self.suggested_tuple_wrap(&expected_input_tys, provided_args);
-            match sugg_tuple_wrap_args {
-                TupleMatchFound::None => {}
-                TupleMatchFound::Single => {
-                    let expected_ty = expected_input_tys[0];
-                    let provided_ty = final_arg_types[0].map(|ty| ty.0).unwrap();
-                    let expected_ty = self.resolve_vars_if_possible(expected_ty);
-                    let provided_ty = self.resolve_vars_if_possible(provided_ty);
-                    let cause = &self.misc(provided_args[0].span);
-                    let compatibility = demand_compatible(0, &mut final_arg_types);
-                    let type_error = match compatibility {
-                        Compatibility::Incompatible(Some(error)) => error,
-                        _ => TypeError::Mismatch,
-                    };
-                    let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
-                    let mut err = self.report_and_explain_type_error(trace, &type_error);
-                    self.emit_coerce_suggestions(
-                        &mut err,
-                        &provided_args[0],
-                        final_arg_types[0].map(|ty| ty.0).unwrap(),
-                        final_arg_types[0].map(|ty| ty.1).unwrap(),
-                        None,
-                        None,
-                    );
-                    err.span_label(
-                        full_call_span,
-                        format!("arguments to this {} are incorrect", call_name),
-                    );
-                    // Call out where the function is defined
-                    label_fn_like(tcx, &mut err, fn_def_id);
-                    err.emit();
-                    break 'errors;
-                }
-                TupleMatchFound::Multiple(start, end) => {
-                    let mut err = tcx.sess.struct_span_err_with_code(
-                        full_call_span,
-                        &format!(
-                            "this {} takes {}{} but {} {} supplied",
-                            call_name,
-                            if c_variadic { "at least " } else { "" },
-                            potentially_plural_count(minimum_input_count, "argument"),
-                            potentially_plural_count(provided_arg_count, "argument"),
-                            if provided_arg_count == 1 { "was" } else { "were" }
-                        ),
-                        DiagnosticId::Error(err_code.to_owned()),
-                    );
-                    // Call out where the function is defined
-                    label_fn_like(tcx, &mut err, fn_def_id);
-                    err.multipart_suggestion(
-                        "use parentheses to construct a tuple",
-                        vec![(start, '('.to_string()), (end, ')'.to_string())],
-                        Applicability::MachineApplicable,
-                    );
-                    err.emit();
-                    break 'errors;
+            let try_tuple_wrap_args = || {
+                // The case where we expect a single tuple and wrapping all the args
+                // in parentheses (or adding a comma to already existing parentheses)
+                // will result in a tuple that satisfies the call.
+                // This isn't super ideal code, because we copy code from elsewhere
+                // and somewhat duplicate this. We also delegate to the general type
+                // mismatch suggestions for the single arg case.
+                match self.suggested_tuple_wrap(&expected_input_tys, provided_args) {
+                    TupleMatchFound::Single => {
+                        let expected_ty = expected_input_tys[0];
+                        let provided_ty = final_arg_types[0].map(|ty| ty.0).unwrap();
+                        let expected_ty = self.resolve_vars_if_possible(expected_ty);
+                        let provided_ty = self.resolve_vars_if_possible(provided_ty);
+                        let cause = &self.misc(provided_args[0].span);
+                        let compatibility = demand_compatible(0, &mut final_arg_types);
+                        let type_error = match compatibility {
+                            Compatibility::Incompatible(Some(error)) => error,
+                            _ => TypeError::Mismatch,
+                        };
+                        let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+                        let mut err = self.report_and_explain_type_error(trace, &type_error);
+                        self.emit_coerce_suggestions(
+                            &mut err,
+                            &provided_args[0],
+                            final_arg_types[0].map(|ty| ty.0).unwrap(),
+                            final_arg_types[0].map(|ty| ty.1).unwrap(),
+                            None,
+                            None,
+                        );
+                        err.span_label(
+                            full_call_span,
+                            format!("arguments to this {} are incorrect", call_name),
+                        );
+                        // Call out where the function is defined
+                        label_fn_like(tcx, &mut err, fn_def_id);
+                        err.emit();
+                        return true;
+                    }
+                    TupleMatchFound::Multiple(start, end) => {
+                        let mut err = tcx.sess.struct_span_err_with_code(
+                            full_call_span,
+                            &format!(
+                                "this {} takes {}{} but {} {} supplied",
+                                call_name,
+                                if c_variadic { "at least " } else { "" },
+                                potentially_plural_count(minimum_input_count, "argument"),
+                                potentially_plural_count(provided_arg_count, "argument"),
+                                if provided_arg_count == 1 { "was" } else { "were" }
+                            ),
+                            DiagnosticId::Error(err_code.to_owned()),
+                        );
+                        // Call out where the function is defined
+                        label_fn_like(tcx, &mut err, fn_def_id);
+                        err.multipart_suggestion(
+                            "use parentheses to construct a tuple",
+                            vec![(start, '('.to_string()), (end, ')'.to_string())],
+                            Applicability::MachineApplicable,
+                        );
+                        err.emit();
+                        return true;
+                    }
+                    TupleMatchFound::None => {}
                 }
+                false
+            };
+
+            let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal);
+            let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic {
+                minimum_input_count
+            } else {
+                provided_arg_count
+            }));
+            debug_assert_eq!(
+                formal_input_tys.len(),
+                expected_input_tys.len(),
+                "expected formal_input_tys to be the same size as expected_input_tys"
+            );
+            let formal_and_expected_inputs = IndexVec::from_iter(
+                formal_input_tys
+                    .iter()
+                    .copied()
+                    .zip(expected_input_tys.iter().copied())
+                    .map(|vars| self.resolve_vars_if_possible(vars)),
+            );
+
+            self.report_arg_errors(
+                compatibility_diagonal,
+                formal_and_expected_inputs,
+                provided_args,
+                full_call_span,
+                error_span,
+                args_span,
+                call_name,
+                c_variadic,
+                err_code,
+                fn_def_id,
+                try_tuple_wrap_args,
+            );
+        }
+    }
+
+    fn report_arg_errors(
+        &self,
+        compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>,
+        formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+        provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>,
+        full_call_span: Span,
+        error_span: Span,
+        args_span: Span,
+        call_name: &str,
+        c_variadic: bool,
+        err_code: &str,
+        fn_def_id: Option<DefId>,
+        try_tuple_wrap_args: impl FnOnce() -> bool,
+    ) {
+        // Don't print if it has error types or is just plain `_`
+        fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
+            tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var())
+        }
+
+        self.set_tainted_by_errors();
+        let tcx = self.tcx;
+
+        // 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
+        let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| {
+            if provided_idx.as_usize() == expected_idx.as_usize() {
+                return compatibility_diagonal[provided_idx].clone();
+            }
+
+            let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx];
+            // If either is an error type, we defy the usual convention and consider them to *not* be
+            // coercible.  This prevents our error message heuristic from trying to pass errors into
+            // every argument.
+            if (formal_input_ty, expected_input_ty).references_error() {
+                return Compatibility::Incompatible(None);
+            }
+
+            let provided_arg: &hir::Expr<'tcx> = &provided_args[provided_idx];
+            let expectation = Expectation::rvalue_hint(self, expected_input_ty);
+            // FIXME: check that this is safe; I don't believe this commits any of the obligations, but I can't be sure.
+            //
+            //   I had another method of "soft" type checking before,
+            //   but it was failing to find the type of some expressions (like "")
+            //   so I prodded this method and made it pub(super) so I could call it, and it seems to work well.
+            let checked_ty = self.check_expr_kind(provided_arg, expectation);
+
+            let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty);
+            let can_coerce = self.can_coerce(checked_ty, coerced_ty);
+            if !can_coerce {
+                return Compatibility::Incompatible(None);
+            }
+
+            let subtyping_result = self
+                .at(&self.misc(provided_arg.span), self.param_env)
+                .sup(formal_input_ty, coerced_ty);
+
+            // Same as above: if either the coerce type or the checked type is an error type,
+            // consider them *not* compatible.
+            let references_error = (coerced_ty, checked_ty).references_error();
+            match (references_error, &subtyping_result) {
+                (false, Ok(_)) => Compatibility::Compatible,
+                _ => Compatibility::Incompatible(subtyping_result.err()),
             }
+        };
+
+        // The algorithm here is inspired by levenshtein distance and longest common subsequence.
+        // We'll try to detect 4 different types of mistakes:
+        // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs
+        // - An input is missing, which isn't satisfied by *any* of the other arguments
+        // - Some number of arguments have been provided in the wrong order
+        // - A type is straight up invalid
+
+        // First, let's find the errors
+        let (mut errors, matched_inputs) =
+            ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible)
+                .find_errors();
+
+        // Precompute the provided types and spans, since that's all we typically need for below
+        let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
+            .iter()
+            .map(|expr| {
+                let ty = self
+                    .in_progress_typeck_results
+                    .as_ref()
+                    .unwrap()
+                    .borrow()
+                    .expr_ty_adjusted_opt(*expr)
+                    .unwrap_or_else(|| tcx.ty_error());
+                (self.resolve_vars_if_possible(ty), expr.span)
+            })
+            .collect();
+
+        // Okay, so here's where it gets complicated in regards to what errors
+        // we emit and how.
+        // There are 3 different "types" of errors we might encounter.
+        //   1) Missing/extra/swapped arguments
+        //   2) Valid but incorrect arguments
+        //   3) Invalid arguments
+        //      - Currently I think this only comes up with `CyclicTy`
+        //
+        // We first need to go through, remove those from (3) and emit those
+        // as their own error, particularly since they're error code and
+        // message is special. From what I can tell, we *must* emit these
+        // here (vs somewhere prior to this function) since the arguments
+        // become invalid *because* of how they get used in the function.
+        // It is what it is.
+
+        if errors.is_empty() {
+            if cfg!(debug_assertions) {
+                span_bug!(error_span, "expected errors from argument matrix");
+            } else {
+                tcx.sess
+                    .struct_span_err(
+                        error_span,
+                        "argument type mismatch was detected, \
+                        but rustc had trouble determining where",
+                    )
+                    .note(
+                        "we would appreciate a bug report: \
+                        https://github.com/rust-lang/rust-clippy/issues/new",
+                    )
+                    .emit();
+            }
+            return;
+        }
+
+        errors.drain_filter(|error| {
+                let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false };
+                let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
+                let (expected_ty, _) = formal_and_expected_inputs[*expected_idx];
+                let cause = &self.misc(provided_span);
+                let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+                if let Some(e) = error {
+                    if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
+                        self.report_and_explain_type_error(trace, e).emit();
+                        return true;
+                    }
+                }
+                false
+            });
+
+        // We're done if we found errors, but we already emitted them.
+        if errors.is_empty() {
+            return;
+        }
+
+        // Okay, now that we've emitted the special errors separately, we
+        // are only left missing/extra/swapped and mismatched arguments, both
+        // can be collated pretty easily if needed.
+
+        // Next special case: if there is only one "Incompatible" error, just emit that
+        if let [
+            Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))),
+        ] = &errors[..]
+        {
+            let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx];
+            let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx];
+            let cause = &self.misc(provided_arg_span);
+            let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+            let mut err = self.report_and_explain_type_error(trace, err);
+            self.emit_coerce_suggestions(
+                &mut err,
+                &provided_args[*provided_idx],
+                provided_ty,
+                Expectation::rvalue_hint(self, expected_ty)
+                    .only_has_type(self)
+                    .unwrap_or(formal_ty),
+                None,
+                None,
+            );
+            err.span_label(
+                full_call_span,
+                format!("arguments to this {} are incorrect", call_name),
+            );
+            // Call out where the function is defined
+            label_fn_like(tcx, &mut err, fn_def_id);
+            err.emit();
+            return;
+        }
+
+        // Second, let's try tuple wrapping the args.
+        // FIXME(compiler-errors): This is currently in its own closure because
+        // I didn't want to factor it out.
+        if try_tuple_wrap_args() {
+            return;
+        }
+
+        let mut err = if formal_and_expected_inputs.len() == provided_args.len() {
+            struct_span_err!(
+                tcx.sess,
+                full_call_span,
+                E0308,
+                "arguments to this {} are incorrect",
+                call_name,
+            )
+        } else {
+            tcx.sess.struct_span_err_with_code(
+                full_call_span,
+                &format!(
+                    "this {} takes {}{} but {} {} supplied",
+                    call_name,
+                    if c_variadic { "at least " } else { "" },
+                    potentially_plural_count(formal_and_expected_inputs.len(), "argument"),
+                    potentially_plural_count(provided_args.len(), "argument"),
+                    if provided_args.len() == 1 { "was" } else { "were" }
+                ),
+                DiagnosticId::Error(err_code.to_owned()),
+            )
+        };
+
+        // As we encounter issues, keep track of what we want to provide for the suggestion
+        let mut labels = vec![];
+        // If there is a single error, we give a specific suggestion; otherwise, we change to
+        // "did you mean" with the suggested function call
+        enum SuggestionText {
+            None,
+            Provide(bool),
+            Remove(bool),
+            Swap,
+            Reorder,
+            DidYouMean,
+        }
+        let mut suggestion_text = SuggestionText::None;
+
+        let mut errors = errors.into_iter().peekable();
+        while let Some(error) = errors.next() {
+            match error {
+                Error::Invalid(provided_idx, expected_idx, compatibility) => {
+                    let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
+                    let (provided_ty, provided_span) = provided_arg_tys[provided_idx];
+                    if let Compatibility::Incompatible(error) = &compatibility {
+                        let cause = &self.misc(provided_span);
+                        let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+                        if let Some(e) = error {
+                            self.note_type_err(
+                                &mut err,
+                                &trace.cause,
+                                None,
+                                Some(trace.values),
+                                e,
+                                false,
+                                true,
+                            );
+                        }
+                    }
 
-            // Okay, now that we've emitted the special errors separately, we
-            // are only left missing/extra/swapped and mismatched arguments, both
-            // can be collated pretty easily if needed.
-
-            // Next special case: if there is only one "Incompatible" error, just emit that
-            if errors.len() == 1 {
-                if let Some(Error::Invalid(
-                    input_idx,
-                    arg_idx,
-                    Compatibility::Incompatible(Some(error)),
-                )) = errors.iter().next()
-                {
-                    let expected_ty = expected_input_tys[*arg_idx];
-                    let provided_ty = final_arg_types[*input_idx]
-                        .map(|ty| ty.0)
-                        .unwrap_or_else(|| tcx.ty_error());
-                    let expected_ty = self.resolve_vars_if_possible(expected_ty);
-                    let provided_ty = self.resolve_vars_if_possible(provided_ty);
-                    let cause = &self.misc(provided_args[*input_idx].span);
-                    let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
-                    let mut err = self.report_and_explain_type_error(trace, error);
                     self.emit_coerce_suggestions(
                         &mut err,
-                        &provided_args[*input_idx],
+                        &provided_args[provided_idx],
                         provided_ty,
-                        final_arg_types[*input_idx]
-                            .map(|ty| ty.1)
-                            .unwrap_or_else(|| tcx.ty_error()),
+                        Expectation::rvalue_hint(self, expected_ty)
+                            .only_has_type(self)
+                            .unwrap_or(formal_ty),
                         None,
                         None,
                     );
-                    err.span_label(
-                        full_call_span,
-                        format!("arguments to this {} are incorrect", call_name),
-                    );
-                    // Call out where the function is defined
-                    label_fn_like(tcx, &mut err, fn_def_id);
-                    err.emit();
-                    break 'errors;
                 }
-            }
-
-            let mut err = if minimum_input_count == provided_arg_count {
-                struct_span_err!(
-                    tcx.sess,
-                    full_call_span,
-                    E0308,
-                    "arguments to this {} are incorrect",
-                    call_name,
-                )
-            } else {
-                tcx.sess.struct_span_err_with_code(
-                    full_call_span,
-                    &format!(
-                        "this {} takes {}{} but {} {} supplied",
-                        call_name,
-                        if c_variadic { "at least " } else { "" },
-                        potentially_plural_count(minimum_input_count, "argument"),
-                        potentially_plural_count(provided_arg_count, "argument"),
-                        if provided_arg_count == 1 { "was" } else { "were" }
-                    ),
-                    DiagnosticId::Error(err_code.to_owned()),
-                )
-            };
-
-            // As we encounter issues, keep track of what we want to provide for the suggestion
-            let mut labels = vec![];
-            // If there is a single error, we give a specific suggestion; otherwise, we change to
-            // "did you mean" with the suggested function call
-            enum SuggestionText {
-                None,
-                Provide(bool),
-                Remove(bool),
-                Swap,
-                Reorder,
-                DidYouMean,
-            }
-            let mut suggestion_text = SuggestionText::None;
-
-            let mut errors = errors.into_iter().peekable();
-            while let Some(error) = errors.next() {
-                match error {
-                    Error::Invalid(input_idx, arg_idx, compatibility) => {
-                        let expected_ty = expected_input_tys[arg_idx];
-                        let provided_ty = final_arg_types[input_idx]
-                            .map(|ty| ty.0)
-                            .unwrap_or_else(|| tcx.ty_error());
-                        let expected_ty = self.resolve_vars_if_possible(expected_ty);
-                        let provided_ty = self.resolve_vars_if_possible(provided_ty);
-                        if let Compatibility::Incompatible(error) = &compatibility {
-                            let cause = &self.misc(provided_args[input_idx].span);
-                            let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
-                            if let Some(e) = error {
-                                self.note_type_err(
-                                    &mut err,
-                                    &trace.cause,
-                                    None,
-                                    Some(trace.values),
-                                    e,
-                                    false,
-                                    true,
-                                );
-                            }
+                Error::Extra(arg_idx) => {
+                    let (provided_ty, provided_span) = provided_arg_tys[arg_idx];
+                    let provided_ty_name = if !has_error_or_infer([provided_ty]) {
+                        // FIXME: not suggestable, use something else
+                        format!(" of type `{}`", provided_ty)
+                    } else {
+                        "".to_string()
+                    };
+                    labels
+                        .push((provided_span, format!("argument{} unexpected", provided_ty_name)));
+                    suggestion_text = match suggestion_text {
+                        SuggestionText::None => SuggestionText::Remove(false),
+                        SuggestionText::Remove(_) => SuggestionText::Remove(true),
+                        _ => SuggestionText::DidYouMean,
+                    };
+                }
+                Error::Missing(expected_idx) => {
+                    // If there are multiple missing arguments adjacent to each other,
+                    // then we can provide a single error.
+
+                    let mut missing_idxs = vec![expected_idx];
+                    while let Some(e) = errors.next_if(|e| {
+                        matches!(e, Error::Missing(next_expected_idx)
+                            if *next_expected_idx == *missing_idxs.last().unwrap() + 1)
+                    }) {
+                        match e {
+                            Error::Missing(expected_idx) => missing_idxs.push(expected_idx),
+                            _ => unreachable!(),
                         }
-
-                        self.emit_coerce_suggestions(
-                            &mut err,
-                            &provided_args[input_idx],
-                            provided_ty,
-                            // FIXME(compiler-errors): expected_ty?
-                            final_arg_types[input_idx]
-                                .map(|ty| ty.1)
-                                .unwrap_or_else(|| tcx.ty_error()),
-                            None,
-                            None,
-                        );
                     }
-                    Error::Extra(arg_idx) => {
-                        let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] {
-                            if ty.references_error() || ty.has_infer_types() {
-                                "".into()
+
+                    // NOTE: Because we might be re-arranging arguments, might have extra
+                    // arguments, etc. it's hard to *really* know where we should provide
+                    // this error label, so as a heuristic, we point to the provided arg, or
+                    // to the call if the missing inputs pass the provided args.
+                    match &missing_idxs[..] {
+                        &[expected_idx] => {
+                            let (_, input_ty) = formal_and_expected_inputs[expected_idx];
+                            let span = if let Some((_, arg_span)) =
+                                provided_arg_tys.get(expected_idx.to_provided_idx())
+                            {
+                                *arg_span
                             } else {
-                                format!(" of type `{}`", ty)
-                            }
-                        } else {
-                            "".into()
-                        };
-                        labels.push((
-                            provided_args[arg_idx].span,
-                            format!("argument{} unexpected", arg_type),
-                        ));
-                        suggestion_text = match suggestion_text {
-                            SuggestionText::None => SuggestionText::Remove(false),
-                            SuggestionText::Remove(_) => SuggestionText::Remove(true),
-                            _ => SuggestionText::DidYouMean,
-                        };
-                    }
-                    Error::Missing(input_idx) => {
-                        // If there are multiple missing arguments adjacent to each other,
-                        // then we can provide a single error.
-
-                        let mut missing_idxs = vec![input_idx];
-                        while let Some(e) = errors.next_if(|e| matches!(e, Error::Missing(input_idx) if *input_idx == (missing_idxs.last().unwrap() + 1))) {
-                            match e {
-                                Error::Missing(input_idx) => missing_idxs.push(input_idx),
-                                _ => unreachable!(),
-                            }
+                                args_span
+                            };
+                            let rendered = if !has_error_or_infer([input_ty]) {
+                                format!(" of type `{}`", input_ty)
+                            } else {
+                                "".to_string()
+                            };
+                            labels.push((span, format!("an argument{} is missing", rendered)));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None => SuggestionText::Provide(false),
+                                SuggestionText::Provide(_) => SuggestionText::Provide(true),
+                                _ => SuggestionText::DidYouMean,
+                            };
                         }
-
-                        // NOTE: Because we might be re-arranging arguments, might have extra
-                        // arguments, etc. it's hard to *really* know where we should provide
-                        // this error label, so as a heuristic, we point to the provided arg, or
-                        // to the call if the missing inputs pass the provided args.
-                        match &missing_idxs[..] {
-                            &[input_idx] => {
-                                let expected_ty = expected_input_tys[input_idx];
-                                let input_ty = self.resolve_vars_if_possible(expected_ty);
-                                let span = if input_idx < provided_arg_count {
-                                    let arg_span = provided_args[input_idx].span;
-                                    Span::new(arg_span.lo(), arg_span.hi(), arg_span.ctxt(), None)
-                                } else {
-                                    args_span
-                                };
-                                let arg_type =
-                                    if input_ty.references_error() || input_ty.has_infer_types() {
-                                        "".into()
-                                    } else {
-                                        format!(" of type `{}`", input_ty)
-                                    };
-                                labels.push((span, format!("an argument{} is missing", arg_type)));
-                                suggestion_text = match suggestion_text {
-                                    SuggestionText::None => SuggestionText::Provide(false),
-                                    SuggestionText::Provide(_) => SuggestionText::Provide(true),
-                                    _ => SuggestionText::DidYouMean,
-                                };
-                            }
-                            &[first_idx, second_idx] => {
-                                let first_input_ty =
-                                    self.resolve_vars_if_possible(expected_input_tys[first_idx]);
-                                let second_input_ty =
-                                    self.resolve_vars_if_possible(expected_input_tys[second_idx]);
-
-                                let span = if second_idx < provided_arg_count {
-                                    let first_arg_span = provided_args[first_idx].span;
-                                    let second_arg_span = provided_args[second_idx].span;
-                                    Span::new(
-                                        first_arg_span.lo(),
-                                        second_arg_span.hi(),
-                                        first_arg_span.ctxt(),
-                                        None,
-                                    )
-                                } else {
-                                    args_span
-                                };
-                                let any_unnameable = false
-                                    || first_input_ty.references_error()
-                                    || first_input_ty.has_infer_types()
-                                    || second_input_ty.references_error()
-                                    || second_input_ty.has_infer_types();
-                                let arg_type = if any_unnameable {
-                                    "".into()
-                                } else {
+                        &[first_idx, second_idx] => {
+                            let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
+                            let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
+                            let span = if let (Some((_, first_span)), Some((_, second_span))) = (
+                                provided_arg_tys.get(first_idx.to_provided_idx()),
+                                provided_arg_tys.get(second_idx.to_provided_idx()),
+                            ) {
+                                first_span.to(*second_span)
+                            } else {
+                                args_span
+                            };
+                            let rendered =
+                                if !has_error_or_infer([first_expected_ty, second_expected_ty]) {
                                     format!(
                                         " of type `{}` and `{}`",
-                                        first_input_ty, second_input_ty
-                                    )
-                                };
-                                labels
-                                    .push((span, format!("two arguments{} are missing", arg_type)));
-                                suggestion_text = match suggestion_text {
-                                    SuggestionText::None | SuggestionText::Provide(_) => {
-                                        SuggestionText::Provide(true)
-                                    }
-                                    _ => SuggestionText::DidYouMean,
-                                };
-                            }
-                            &[first_idx, second_idx, third_idx] => {
-                                let first_input_ty =
-                                    self.resolve_vars_if_possible(expected_input_tys[first_idx]);
-                                let second_input_ty =
-                                    self.resolve_vars_if_possible(expected_input_tys[second_idx]);
-                                let third_input_ty =
-                                    self.resolve_vars_if_possible(expected_input_tys[third_idx]);
-                                let span = if third_idx < provided_arg_count {
-                                    let first_arg_span = provided_args[first_idx].span;
-                                    let third_arg_span = provided_args[third_idx].span;
-                                    Span::new(
-                                        first_arg_span.lo(),
-                                        third_arg_span.hi(),
-                                        first_arg_span.ctxt(),
-                                        None,
-                                    )
-                                } else {
-                                    args_span
-                                };
-                                let any_unnameable = false
-                                    || first_input_ty.references_error()
-                                    || first_input_ty.has_infer_types()
-                                    || second_input_ty.references_error()
-                                    || second_input_ty.has_infer_types()
-                                    || third_input_ty.references_error()
-                                    || third_input_ty.has_infer_types();
-                                let arg_type = if any_unnameable {
-                                    "".into()
-                                } else {
-                                    format!(
-                                        " of type `{}`, `{}`, and `{}`",
-                                        first_input_ty, second_input_ty, third_input_ty
-                                    )
-                                };
-                                labels.push((
-                                    span,
-                                    format!("three arguments{} are missing", arg_type),
-                                ));
-                                suggestion_text = match suggestion_text {
-                                    SuggestionText::None | SuggestionText::Provide(_) => {
-                                        SuggestionText::Provide(true)
-                                    }
-                                    _ => SuggestionText::DidYouMean,
-                                };
-                            }
-                            missing_idxs => {
-                                let first_idx = *missing_idxs.first().unwrap();
-                                let last_idx = *missing_idxs.last().unwrap();
-                                // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
-                                // It's hard to *really* know where we should provide this error label, so this is a
-                                // decent heuristic
-                                let span = if last_idx < provided_arg_count {
-                                    let first_arg_span = provided_args[first_idx].span;
-                                    let last_arg_span = provided_args[last_idx].span;
-                                    Span::new(
-                                        first_arg_span.lo(),
-                                        last_arg_span.hi(),
-                                        first_arg_span.ctxt(),
-                                        None,
+                                        first_expected_ty, second_expected_ty
                                     )
                                 } else {
-                                    // Otherwise just label the whole function
-                                    args_span
-                                };
-                                labels.push((span, format!("multiple arguments are missing")));
-                                suggestion_text = match suggestion_text {
-                                    SuggestionText::None | SuggestionText::Provide(_) => {
-                                        SuggestionText::Provide(true)
-                                    }
-                                    _ => SuggestionText::DidYouMean,
+                                    "".to_string()
                                 };
-                            }
+                            labels.push((span, format!("two arguments{} are missing", rendered)));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None | SuggestionText::Provide(_) => {
+                                    SuggestionText::Provide(true)
+                                }
+                                _ => SuggestionText::DidYouMean,
+                            };
                         }
-                    }
-                    Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx) => {
-                        let first_span = provided_args[input_idx].span;
-                        let second_span = provided_args[other_input_idx].span;
-
-                        let first_expected_ty =
-                            self.resolve_vars_if_possible(expected_input_tys[arg_idx]);
-                        let first_provided_ty = if let Some((ty, _)) = final_arg_types[input_idx] {
-                            format!(", found `{}`", ty)
-                        } else {
-                            String::new()
-                        };
-                        labels.push((
-                            first_span,
-                            format!("expected `{}`{}", first_expected_ty, first_provided_ty),
-                        ));
-                        let other_expected_ty =
-                            self.resolve_vars_if_possible(expected_input_tys[other_arg_idx]);
-                        let other_provided_ty =
-                            if let Some((ty, _)) = final_arg_types[other_input_idx] {
-                                format!(", found `{}`", ty)
+                        &[first_idx, second_idx, third_idx] => {
+                            let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
+                            let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
+                            let (_, third_expected_ty) = formal_and_expected_inputs[third_idx];
+                            let span = if let (Some((_, first_span)), Some((_, third_span))) = (
+                                provided_arg_tys.get(first_idx.to_provided_idx()),
+                                provided_arg_tys.get(third_idx.to_provided_idx()),
+                            ) {
+                                first_span.to(*third_span)
                             } else {
-                                String::new()
+                                args_span
                             };
-                        labels.push((
-                            second_span,
-                            format!("expected `{}`{}", other_expected_ty, other_provided_ty),
-                        ));
-                        suggestion_text = match suggestion_text {
-                            SuggestionText::None => SuggestionText::Swap,
-                            _ => SuggestionText::DidYouMean,
-                        };
-                    }
-                    Error::Permutation(args) => {
-                        for (dst_arg, dest_input) in args {
-                            let expected_ty =
-                                self.resolve_vars_if_possible(expected_input_tys[dst_arg]);
-                            let provided_ty = if let Some((ty, _)) = final_arg_types[dest_input] {
-                                format!(", found `{}`", ty)
+                            let rendered = if !has_error_or_infer([
+                                first_expected_ty,
+                                second_expected_ty,
+                                third_expected_ty,
+                            ]) {
+                                format!(
+                                    " of type `{}`, `{}`, and `{}`",
+                                    first_expected_ty, second_expected_ty, third_expected_ty
+                                )
                             } else {
-                                String::new()
+                                "".to_string()
+                            };
+                            labels.push((span, format!("three arguments{} are missing", rendered)));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None | SuggestionText::Provide(_) => {
+                                    SuggestionText::Provide(true)
+                                }
+                                _ => SuggestionText::DidYouMean,
+                            };
+                        }
+                        missing_idxs => {
+                            let first_idx = *missing_idxs.first().unwrap();
+                            let last_idx = *missing_idxs.last().unwrap();
+                            // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
+                            // It's hard to *really* know where we should provide this error label, so this is a
+                            // decent heuristic
+                            let span = if let (Some((_, first_span)), Some((_, last_span))) = (
+                                provided_arg_tys.get(first_idx.to_provided_idx()),
+                                provided_arg_tys.get(last_idx.to_provided_idx()),
+                            ) {
+                                first_span.to(*last_span)
+                            } else {
+                                args_span
+                            };
+                            labels.push((span, format!("multiple arguments are missing")));
+                            suggestion_text = match suggestion_text {
+                                SuggestionText::None | SuggestionText::Provide(_) => {
+                                    SuggestionText::Provide(true)
+                                }
+                                _ => SuggestionText::DidYouMean,
                             };
-                            labels.push((
-                                provided_args[dest_input].span,
-                                format!("expected `{}`{}", expected_ty, provided_ty),
-                            ));
                         }
-
-                        suggestion_text = match suggestion_text {
-                            SuggestionText::None => SuggestionText::Reorder,
-                            _ => SuggestionText::DidYouMean,
-                        };
                     }
                 }
-            }
-
-            // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
-            if labels.len() <= 5 {
-                for (span, label) in labels {
-                    err.span_label(span, label);
-                }
-            }
-
-            // Call out where the function is defined
-            label_fn_like(tcx, &mut err, fn_def_id);
-
-            // And add a suggestion block for all of the parameters
-            let suggestion_text = match suggestion_text {
-                SuggestionText::None => None,
-                SuggestionText::Provide(plural) => {
-                    Some(format!("provide the argument{}", if plural { "s" } else { "" }))
-                }
-                SuggestionText::Remove(plural) => {
-                    Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
-                }
-                SuggestionText::Swap => Some("swap these arguments".to_string()),
-                SuggestionText::Reorder => Some("reorder these arguments".to_string()),
-                SuggestionText::DidYouMean => Some("did you mean".to_string()),
-            };
-            if let Some(suggestion_text) = suggestion_text {
-                let source_map = self.sess().source_map();
-                let mut suggestion = format!(
-                    "{}(",
-                    source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| String::new())
-                );
-                for (arg_index, input_idx) in matched_inputs.iter().enumerate() {
-                    let suggestion_text = if let Some(input_idx) = input_idx {
-                        let arg_span = provided_args[*input_idx].span.source_callsite();
-                        let arg_text = source_map.span_to_snippet(arg_span).unwrap();
-                        arg_text
+                Error::Swap(
+                    first_provided_idx,
+                    second_provided_idx,
+                    first_expected_idx,
+                    second_expected_idx,
+                ) => {
+                    let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx];
+                    let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx];
+                    let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
+                        format!(", found `{}`", first_provided_ty)
                     } else {
-                        // Propose a placeholder of the correct type
-                        let expected_ty = expected_input_tys[arg_index];
-                        let input_ty = self.resolve_vars_if_possible(expected_ty);
-                        if input_ty.is_unit() {
-                            "()".to_string()
-                        } else if !input_ty.is_ty_var() {
-                            format!("/* {} */", input_ty)
+                        String::new()
+                    };
+                    labels.push((
+                        first_span,
+                        format!("expected `{}`{}", first_expected_ty, first_provided_ty_name),
+                    ));
+
+                    let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx];
+                    let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx];
+                    let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
+                        format!(", found `{}`", second_provided_ty)
+                    } else {
+                        String::new()
+                    };
+                    labels.push((
+                        second_span,
+                        format!("expected `{}`{}", second_expected_ty, second_provided_ty_name),
+                    ));
+
+                    suggestion_text = match suggestion_text {
+                        SuggestionText::None => SuggestionText::Swap,
+                        _ => SuggestionText::DidYouMean,
+                    };
+                }
+                Error::Permutation(args) => {
+                    for (dst_arg, dest_input) in args {
+                        let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
+                        let (provided_ty, provided_span) = provided_arg_tys[dest_input];
+                        let provided_ty_name = if !has_error_or_infer([provided_ty]) {
+                            format!(", found `{}`", provided_ty)
                         } else {
-                            "/* value */".to_string()
+                            String::new()
+                        };
+                        // FIXME(compiler-errors): Why do we get permutations with the same type?
+                        if expected_ty != provided_ty {
+                            labels.push((
+                                provided_span,
+                                format!("expected `{}`{}", expected_ty, provided_ty_name),
+                            ));
                         }
-                    };
-                    suggestion += &suggestion_text;
-                    if arg_index < minimum_input_count - 1 {
-                        suggestion += ", ";
                     }
+
+                    suggestion_text = match suggestion_text {
+                        SuggestionText::None => SuggestionText::Reorder,
+                        _ => SuggestionText::DidYouMean,
+                    };
                 }
-                suggestion += ")";
-                err.span_suggestion_verbose(
-                    error_span,
-                    &suggestion_text,
-                    suggestion,
-                    Applicability::HasPlaceholders,
-                );
             }
-            err.emit();
         }
 
-        for arg in provided_args.iter().skip(minimum_input_count) {
-            let arg_ty = self.check_expr(&arg);
+        // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
+        if labels.len() <= 5 {
+            for (span, label) in labels {
+                err.span_label(span, label);
+            }
+        }
 
-            // If the function is c-style variadic, we skipped a bunch of arguments
-            // so we need to check those, and write out the types
-            // Ideally this would be folded into the above, for uniform style
-            // but c-variadic is already a corner case
-            if c_variadic {
-                fn variadic_error<'tcx>(
-                    sess: &'tcx Session,
-                    span: Span,
-                    ty: Ty<'tcx>,
-                    cast_ty: &str,
-                ) {
-                    use crate::structured_errors::MissingCastForVariadicArg;
+        // Call out where the function is defined
+        label_fn_like(tcx, &mut err, fn_def_id);
 
-                    MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit();
+        // And add a suggestion block for all of the parameters
+        let suggestion_text = match suggestion_text {
+            SuggestionText::None => None,
+            SuggestionText::Provide(plural) => {
+                Some(format!("provide the argument{}", if plural { "s" } else { "" }))
+            }
+            SuggestionText::Remove(plural) => {
+                Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
+            }
+            SuggestionText::Swap => Some("swap these arguments".to_string()),
+            SuggestionText::Reorder => Some("reorder these arguments".to_string()),
+            SuggestionText::DidYouMean => Some("did you mean".to_string()),
+        };
+        if let Some(suggestion_text) = suggestion_text {
+            let source_map = self.sess().source_map();
+            let mut suggestion = format!(
+                "{}(",
+                source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| fn_def_id
+                    .map_or("".to_string(), |fn_def_id| tcx.item_name(fn_def_id).to_string()))
+            );
+            let mut needs_comma = false;
+            for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
+                if needs_comma {
+                    suggestion += ", ";
+                } else {
+                    needs_comma = true;
                 }
-
-                // There are a few types which get autopromoted when passed via varargs
-                // in C but we just error out instead and require explicit casts.
-                let arg_ty = self.structurally_resolved_type(arg.span, arg_ty);
-                match arg_ty.kind() {
-                    ty::Float(ty::FloatTy::F32) => {
-                        variadic_error(tcx.sess, arg.span, arg_ty, "c_double");
-                    }
-                    ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => {
-                        variadic_error(tcx.sess, arg.span, arg_ty, "c_int");
-                    }
-                    ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => {
-                        variadic_error(tcx.sess, arg.span, arg_ty, "c_uint");
-                    }
-                    ty::FnDef(..) => {
-                        let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx));
-                        let ptr_ty = self.resolve_vars_if_possible(ptr_ty);
-                        variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string());
+                let 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.source_callsite()) {
+                    arg_text
+                } else {
+                    // Propose a placeholder of the correct type
+                    let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
+                    if expected_ty.is_unit() {
+                        "()".to_string()
+                    } else if expected_ty.is_suggestable(tcx) {
+                        format!("/* {} */", expected_ty)
+                    } else {
+                        "/* value */".to_string()
                     }
-                    _ => {}
-                }
+                };
+                suggestion += &suggestion_text;
             }
+            suggestion += ")";
+            err.span_suggestion_verbose(
+                error_span,
+                &suggestion_text,
+                suggestion,
+                Applicability::HasPlaceholders,
+            );
         }
+
+        err.emit();
     }
 
     fn suggested_tuple_wrap(
diff --git a/src/test/ui/argument-suggestions/basic.stderr b/src/test/ui/argument-suggestions/basic.stderr
index dd4812b5b2589..c495ad6b842f8 100644
--- a/src/test/ui/argument-suggestions/basic.stderr
+++ b/src/test/ui/argument-suggestions/basic.stderr
@@ -16,7 +16,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied
   --> $DIR/basic.rs:21:5
    |
 LL |     extra("");
-   |     ^^^^^ -- argument unexpected
+   |     ^^^^^ -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/basic.rs:14:4
diff --git a/src/test/ui/argument-suggestions/extra_arguments.stderr b/src/test/ui/argument-suggestions/extra_arguments.stderr
index 9b63f9bcbfae4..32b1e15737ab9 100644
--- a/src/test/ui/argument-suggestions/extra_arguments.stderr
+++ b/src/test/ui/argument-suggestions/extra_arguments.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied
   --> $DIR/extra_arguments.rs:7:3
    |
 LL |   empty("");
-   |   ^^^^^ -- argument unexpected
+   |   ^^^^^ -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:1:4
@@ -18,7 +18,7 @@ error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/extra_arguments.rs:9:3
    |
 LL |   one_arg(1, 1);
-   |   ^^^^^^^    - argument unexpected
+   |   ^^^^^^^    - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:2:4
@@ -34,7 +34,7 @@ error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/extra_arguments.rs:10:3
    |
 LL |   one_arg(1, "");
-   |   ^^^^^^^    -- argument unexpected
+   |   ^^^^^^^    -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:2:4
@@ -50,9 +50,9 @@ error[E0061]: this function takes 1 argument but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:11:3
    |
 LL |   one_arg(1, "", 1.0);
-   |   ^^^^^^^    --  --- argument unexpected
+   |   ^^^^^^^    --  --- argument of type `{float}` unexpected
    |              |
-   |              argument unexpected
+   |              argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:2:4
@@ -68,7 +68,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:13:3
    |
 LL |   two_arg_same(1, 1, 1);
-   |   ^^^^^^^^^^^^       - argument unexpected
+   |   ^^^^^^^^^^^^       - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:3:4
@@ -84,7 +84,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:14:3
    |
 LL |   two_arg_same(1, 1, 1.0);
-   |   ^^^^^^^^^^^^       --- argument unexpected
+   |   ^^^^^^^^^^^^       --- argument of type `{float}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:3:4
@@ -100,7 +100,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:16:3
    |
 LL |   two_arg_diff(1, 1, "");
-   |   ^^^^^^^^^^^^    - argument of type `&str` unexpected
+   |   ^^^^^^^^^^^^    - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:4:4
@@ -116,7 +116,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:17:3
    |
 LL |   two_arg_diff(1, "", "");
-   |   ^^^^^^^^^^^^        -- argument unexpected
+   |   ^^^^^^^^^^^^        -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:4:4
@@ -132,9 +132,9 @@ error[E0061]: this function takes 2 arguments but 4 arguments were supplied
   --> $DIR/extra_arguments.rs:18:3
    |
 LL |   two_arg_diff(1, 1, "", "");
-   |   ^^^^^^^^^^^^    -      -- argument unexpected
+   |   ^^^^^^^^^^^^    -      -- argument of type `&'static str` unexpected
    |                   |
-   |                   argument of type `&str` unexpected
+   |                   argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:4:4
@@ -150,9 +150,9 @@ error[E0061]: this function takes 2 arguments but 4 arguments were supplied
   --> $DIR/extra_arguments.rs:19:3
    |
 LL |   two_arg_diff(1, "", 1, "");
-   |   ^^^^^^^^^^^^        -  -- argument unexpected
+   |   ^^^^^^^^^^^^        -  -- argument of type `&'static str` unexpected
    |                       |
-   |                       argument unexpected
+   |                       argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:4:4
@@ -168,7 +168,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:22:3
    |
 LL |   two_arg_same(1, 1,     "");
-   |   ^^^^^^^^^^^^           -- argument unexpected
+   |   ^^^^^^^^^^^^           -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:3:4
@@ -184,7 +184,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:23:3
    |
 LL |   two_arg_diff(1, 1,     "");
-   |   ^^^^^^^^^^^^    - argument of type `&str` unexpected
+   |   ^^^^^^^^^^^^    - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:4:4
@@ -203,7 +203,7 @@ LL |   two_arg_same(
    |   ^^^^^^^^^^^^
 ...
 LL |     ""
-   |     -- argument unexpected
+   |     -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:3:4
@@ -222,7 +222,7 @@ LL |   two_arg_diff(
    |   ^^^^^^^^^^^^
 LL |     1,
 LL |     1,
-   |     - argument of type `&str` unexpected
+   |     - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/extra_arguments.rs:4:4
diff --git a/src/test/ui/argument-suggestions/issue-97484.stderr b/src/test/ui/argument-suggestions/issue-97484.stderr
index 2af24d521f380..9589e919c0a1f 100644
--- a/src/test/ui/argument-suggestions/issue-97484.stderr
+++ b/src/test/ui/argument-suggestions/issue-97484.stderr
@@ -2,21 +2,20 @@ error[E0061]: this function takes 4 arguments but 7 arguments were supplied
   --> $DIR/issue-97484.rs:12:5
    |
 LL |     foo(&&A, B, C, D, E, F, G);
-   |     ^^^      -  -        - argument unexpected
+   |     ^^^      -  -        - argument of type `F` unexpected
    |              |  |
-   |              |  argument of type `&E` unexpected
-   |              argument of type `D` unexpected
+   |              |  argument of type `C` unexpected
+   |              argument of type `B` unexpected
    |
 note: function defined here
   --> $DIR/issue-97484.rs:9:4
    |
 LL | fn foo(a: &A, d: D, e: &E, g: G) {}
    |    ^^^ -----  ----  -----  ----
-help: consider removing the ``
-   |
-LL -     foo(&&A, B, C, D, E, F, G);
-LL +     foo(&&A, B, C, D, E, F, G);
+help: consider borrowing here
    |
+LL |     foo(&&A, B, C, D, &E, F, G);
+   |                       ~~
 help: remove the extra arguments
    |
 LL |     foo(&&A, D, /* &E */, G);
diff --git a/src/test/ui/argument-suggestions/mixed_cases.stderr b/src/test/ui/argument-suggestions/mixed_cases.stderr
index 3fe4473a594e0..a52a30d7884f8 100644
--- a/src/test/ui/argument-suggestions/mixed_cases.stderr
+++ b/src/test/ui/argument-suggestions/mixed_cases.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/mixed_cases.rs:10:3
    |
 LL |   two_args(1, "", X {});
-   |   ^^^^^^^^    --  ---- argument unexpected
+   |   ^^^^^^^^    --  ---- argument of type `X` unexpected
    |               |
    |               expected `f32`, found `&str`
    |
@@ -20,9 +20,9 @@ error[E0061]: this function takes 3 arguments but 4 arguments were supplied
   --> $DIR/mixed_cases.rs:11:3
    |
 LL |   three_args(1, "", X {}, "");
-   |   ^^^^^^^^^^    --  ----  -- argument unexpected
+   |   ^^^^^^^^^^    --  ----  -- argument of type `&'static str` unexpected
    |                 |   |
-   |                 |   argument of type `&str` unexpected
+   |                 |   argument of type `X` unexpected
    |                 an argument of type `f32` is missing
    |
 note: function defined here
@@ -58,7 +58,7 @@ error[E0308]: arguments to this function are incorrect
   --> $DIR/mixed_cases.rs:17:3
    |
 LL |   three_args(1, "", X {});
-   |   ^^^^^^^^^^    --  ---- argument of type `&str` unexpected
+   |   ^^^^^^^^^^    --  ---- argument of type `X` unexpected
    |                 |
    |                 an argument of type `f32` is missing
    |
diff --git a/src/test/ui/error-codes/E0057.stderr b/src/test/ui/error-codes/E0057.stderr
index 3697b5fcf154d..2307f52c93bce 100644
--- a/src/test/ui/error-codes/E0057.stderr
+++ b/src/test/ui/error-codes/E0057.stderr
@@ -18,7 +18,7 @@ error[E0057]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/E0057.rs:5:13
    |
 LL |     let c = f(2, 3);
-   |             ^    - argument unexpected
+   |             ^    - argument of type `{integer}` unexpected
    |
 note: closure defined here
   --> $DIR/E0057.rs:2:13
diff --git a/src/test/ui/issues/issue-26094.rs b/src/test/ui/issues/issue-26094.rs
index 981c3abb4bae0..df8c2f739108d 100644
--- a/src/test/ui/issues/issue-26094.rs
+++ b/src/test/ui/issues/issue-26094.rs
@@ -1,6 +1,6 @@
 macro_rules! some_macro {
     ($other: expr) => ({
-        $other(None) //~ NOTE argument unexpected
+        $other(None) //~ NOTE argument of type `Option<_>` unexpected
     })
 }
 
diff --git a/src/test/ui/issues/issue-26094.stderr b/src/test/ui/issues/issue-26094.stderr
index 1013518e1dad3..881a6e538ee44 100644
--- a/src/test/ui/issues/issue-26094.stderr
+++ b/src/test/ui/issues/issue-26094.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied
   --> $DIR/issue-26094.rs:10:17
    |
 LL |         $other(None)
-   |                ---- argument unexpected
+   |                ---- argument of type `Option<_>` unexpected
 ...
 LL |     some_macro!(some_function);
    |                 ^^^^^^^^^^^^^
diff --git a/src/test/ui/issues/issue-4935.stderr b/src/test/ui/issues/issue-4935.stderr
index b4cebe2a68b57..aab19a699ace0 100644
--- a/src/test/ui/issues/issue-4935.stderr
+++ b/src/test/ui/issues/issue-4935.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/issue-4935.rs:5:13
    |
 LL | fn main() { foo(5, 6) }
-   |             ^^^    - argument unexpected
+   |             ^^^    - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/issue-4935.rs:3:4
diff --git a/src/test/ui/methods/method-call-err-msg.stderr b/src/test/ui/methods/method-call-err-msg.stderr
index 77308a2c5215a..57662e1e265ae 100644
--- a/src/test/ui/methods/method-call-err-msg.stderr
+++ b/src/test/ui/methods/method-call-err-msg.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 0 arguments but 1 argument was supplied
   --> $DIR/method-call-err-msg.rs:13:7
    |
 LL |     x.zero(0)
-   |       ^^^^ - argument unexpected
+   |       ^^^^ - argument of type `{integer}` unexpected
    |
 note: associated function defined here
   --> $DIR/method-call-err-msg.rs:5:8
diff --git a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr
index 4000b2ba31245..cb93a7ad9008d 100644
--- a/src/test/ui/mismatched_types/overloaded-calls-bad.stderr
+++ b/src/test/ui/mismatched_types/overloaded-calls-bad.stderr
@@ -32,7 +32,7 @@ error[E0057]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/overloaded-calls-bad.rs:31:15
    |
 LL |     let ans = s("burma", "shave");
-   |               ^ -------  ------- argument unexpected
+   |               ^ -------  ------- argument of type `&'static str` unexpected
    |                 |
    |                 expected `isize`, found `&str`
    |
diff --git a/src/test/ui/span/issue-34264.stderr b/src/test/ui/span/issue-34264.stderr
index 68da9f0dc88ba..e676d7372e891 100644
--- a/src/test/ui/span/issue-34264.stderr
+++ b/src/test/ui/span/issue-34264.stderr
@@ -54,7 +54,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/issue-34264.rs:7:5
    |
 LL |     foo(Some(42), 2, "");
-   |     ^^^              -- argument unexpected
+   |     ^^^              -- argument of type `&'static str` unexpected
    |
 note: function defined here
   --> $DIR/issue-34264.rs:1:4
@@ -84,7 +84,7 @@ error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/issue-34264.rs:10:5
    |
 LL |     bar(1, 2, 3);
-   |     ^^^       - argument unexpected
+   |     ^^^       - argument of type `{integer}` unexpected
    |
 note: function defined here
   --> $DIR/issue-34264.rs:3:4
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 3d367eca707f4..805c75f464cd5 100644
--- a/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
+++ b/src/test/ui/suggestions/args-instead-of-tuple-errors.stderr
@@ -2,7 +2,7 @@ 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);
-   |                                  ^^^^ -  - argument unexpected
+   |                                  ^^^^ -  - argument of type `{integer}` unexpected
    |                                       |
    |                                       expected tuple, found integer
    |
@@ -22,7 +22,7 @@ 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);
-   |     ^^^^^^^^ -  - argument unexpected
+   |     ^^^^^^^^ -  - argument of type `{integer}` unexpected
    |              |
    |              expected tuple, found integer
    |
diff --git a/src/test/ui/tuple/wrong_argument_ice-3.stderr b/src/test/ui/tuple/wrong_argument_ice-3.stderr
index 667b15776efd3..2733fb3149b55 100644
--- a/src/test/ui/tuple/wrong_argument_ice-3.stderr
+++ b/src/test/ui/tuple/wrong_argument_ice-3.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/wrong_argument_ice-3.rs:9:16
    |
 LL |         groups.push(new_group, vec![process]);
-   |                ^^^^ ---------  ------------- argument unexpected
+   |                ^^^^ ---------  ------------- argument of type `Vec<&Process>` unexpected
    |                     |
    |                     expected tuple, found struct `Vec`
    |
diff --git a/src/test/ui/tuple/wrong_argument_ice-4.stderr b/src/test/ui/tuple/wrong_argument_ice-4.stderr
index f8dfc4cd043cd..3645d11842f7d 100644
--- a/src/test/ui/tuple/wrong_argument_ice-4.stderr
+++ b/src/test/ui/tuple/wrong_argument_ice-4.stderr
@@ -6,7 +6,7 @@ LL |       (|| {})(|| {
 LL | |
 LL | |         let b = 1;
 LL | |     });
-   | |_____- argument unexpected
+   | |_____- argument of type `[closure@$DIR/wrong_argument_ice-4.rs:2:13: 5:6]` unexpected
    |
 note: closure defined here
   --> $DIR/wrong_argument_ice-4.rs:2:6
diff --git a/src/test/ui/type/type-ascription-instead-of-initializer.stderr b/src/test/ui/type/type-ascription-instead-of-initializer.stderr
index 18ed4986f8931..fcac6c495c4b2 100644
--- a/src/test/ui/type/type-ascription-instead-of-initializer.stderr
+++ b/src/test/ui/type/type-ascription-instead-of-initializer.stderr
@@ -11,7 +11,7 @@ error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/type-ascription-instead-of-initializer.rs:2:12
    |
 LL |     let x: Vec::with_capacity(10, 20);
-   |            ^^^^^^^^^^^^^^^^^^     -- argument unexpected
+   |            ^^^^^^^^^^^^^^^^^^     -- argument of type `{integer}` unexpected
    |
 note: associated function defined here
   --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
diff --git a/src/test/ui/typeck/remove-extra-argument.stderr b/src/test/ui/typeck/remove-extra-argument.stderr
index 815297765c18c..703032a832231 100644
--- a/src/test/ui/typeck/remove-extra-argument.stderr
+++ b/src/test/ui/typeck/remove-extra-argument.stderr
@@ -2,7 +2,7 @@ error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/remove-extra-argument.rs:6:5
    |
 LL |     l(vec![], vec![])
-   |     ^         ------ argument unexpected
+   |     ^         ------ argument of type `Vec<_>` unexpected
    |
 note: function defined here
   --> $DIR/remove-extra-argument.rs:3:4
diff --git a/src/test/ui/typeck/struct-enum-wrong-args.stderr b/src/test/ui/typeck/struct-enum-wrong-args.stderr
index 2ea822df27509..f72082d530167 100644
--- a/src/test/ui/typeck/struct-enum-wrong-args.stderr
+++ b/src/test/ui/typeck/struct-enum-wrong-args.stderr
@@ -2,7 +2,7 @@ error[E0061]: this enum variant takes 1 argument but 2 arguments were supplied
   --> $DIR/struct-enum-wrong-args.rs:6:13
    |
 LL |     let _ = Some(3, 2);
-   |             ^^^^    - argument unexpected
+   |             ^^^^    - argument of type `{integer}` unexpected
    |
 note: tuple variant defined here
   --> $SRC_DIR/core/src/option.rs:LL:COL
@@ -18,9 +18,9 @@ error[E0061]: this enum variant takes 1 argument but 3 arguments were supplied
   --> $DIR/struct-enum-wrong-args.rs:7:13
    |
 LL |     let _ = Ok(3, 6, 2);
-   |             ^^    -  - argument unexpected
+   |             ^^    -  - argument of type `{integer}` unexpected
    |                   |
-   |                   argument unexpected
+   |                   argument of type `{integer}` unexpected
    |
 note: tuple variant defined here
   --> $SRC_DIR/core/src/result.rs:LL:COL
@@ -68,7 +68,7 @@ error[E0061]: this struct takes 1 argument but 2 arguments were supplied
   --> $DIR/struct-enum-wrong-args.rs:10:13
    |
 LL |     let _ = Wrapper(5, 2);
-   |             ^^^^^^^    - argument unexpected
+   |             ^^^^^^^    - argument of type `{integer}` unexpected
    |
 note: tuple struct defined here
   --> $DIR/struct-enum-wrong-args.rs:2:8
@@ -116,7 +116,7 @@ error[E0061]: this struct takes 2 arguments but 3 arguments were supplied
   --> $DIR/struct-enum-wrong-args.rs:13:13
    |
 LL |     let _ = DoubleWrapper(5, 2, 7);
-   |             ^^^^^^^^^^^^^       - argument unexpected
+   |             ^^^^^^^^^^^^^       - argument of type `{integer}` unexpected
    |
 note: tuple struct defined here
   --> $DIR/struct-enum-wrong-args.rs:3:8