about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSteven Trotter <stevetrot@gmail.com>2024-02-26 21:00:35 +0000
committerSteven Trotter <stevetrot@gmail.com>2024-03-15 13:37:41 +0000
commit8a5245e7dd006e2eb6bf6d3834b05772285efe28 (patch)
treedec01f2c228e649620dca2b7beae19ec3018ff57
parentdf933640571861bcc2854431823cd9d4803b9f88 (diff)
downloadrust-8a5245e7dd006e2eb6bf6d3834b05772285efe28.tar.gz
rust-8a5245e7dd006e2eb6bf6d3834b05772285efe28.zip
Refactored a few bits:
- Firstly get all the information about generics matching out of the HIR
- Secondly the labelling for the function is more coherent now
- Lastly a few error message improvements
-rw-r--r--compiler/rustc_errors/src/lib.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs493
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs33
-rw-r--r--tests/ui/argument-suggestions/extra_arguments.stderr14
-rw-r--r--tests/ui/argument-suggestions/invalid_arguments.stderr20
-rw-r--r--tests/ui/argument-suggestions/too-long.stderr24
-rw-r--r--tests/ui/async-await/coroutine-desc.stderr27
-rw-r--r--tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr9
-rw-r--r--tests/ui/coercion/coerce-to-bang.stderr10
-rw-r--r--tests/ui/fn/fn-item-type.stderr45
-rw-r--r--tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs2
-rw-r--r--tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr64
-rw-r--r--tests/ui/never_type/issue-96335.stderr1
-rw-r--r--tests/ui/parser/issues/issue-93282.stderr2
-rw-r--r--tests/ui/span/issue-34264.stderr2
-rw-r--r--tests/ui/tuple/add-tuple-within-arguments.stderr4
-rw-r--r--tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr2
17 files changed, 359 insertions, 428 deletions
diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs
index b88b5bcd7ea..3098eb20be3 100644
--- a/compiler/rustc_errors/src/lib.rs
+++ b/compiler/rustc_errors/src/lib.rs
@@ -50,7 +50,7 @@ pub use rustc_error_messages::{
     fallback_fluent_bundle, fluent_bundle, DelayDm, DiagMessage, FluentBundle, LanguageIdentifier,
     LazyFallbackBundle, MultiSpan, SpanLabel, SubdiagMessage,
 };
-pub use rustc_lint_defs::{a_or_an, display_list_with_comma_and, pluralize, Applicability};
+pub use rustc_lint_defs::{pluralize, Applicability};
 pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
 pub use rustc_span::ErrorGuaranteed;
 pub use snippet::Style;
@@ -1946,6 +1946,39 @@ pub fn report_ambiguity_error<'a, G: EmissionGuarantee>(
     }
 }
 
+/// Grammatical tool for displaying messages to end users in a nice form.
+///
+/// Returns "an" if the given string starts with a vowel, and "a" otherwise.
+pub fn a_or_an(s: &str) -> &'static str {
+    let mut chars = s.chars();
+    let Some(mut first_alpha_char) = chars.next() else {
+        return "a";
+    };
+    if first_alpha_char == '`' {
+        let Some(next) = chars.next() else {
+            return "a";
+        };
+        first_alpha_char = next;
+    }
+    if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) {
+        "an"
+    } else {
+        "a"
+    }
+}
+
+/// Grammatical tool for displaying messages to end users in a nice form.
+///
+/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c"
+pub fn display_list_with_comma_and<T: std::fmt::Display>(v: &[T]) -> String {
+    match v.len() {
+        0 => "".to_string(),
+        1 => v[0].to_string(),
+        2 => format!("{} and {}", v[0], v[1]),
+        _ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])),
+    }
+}
+
 #[derive(Clone, Copy, PartialEq, Hash, Debug)]
 pub enum TerminalUrl {
     No,
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index b65ac134fbc..c0d97152354 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -18,7 +18,7 @@ use rustc_ast as ast;
 use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{
     a_or_an, codes::*, display_list_with_comma_and, pluralize, Applicability, Diag,
-    DiagnosticBuilder, ErrorGuaranteed, MultiSpan, StashKey,
+    ErrorGuaranteed, MultiSpan, StashKey,
 };
 use rustc_hir as hir;
 use rustc_hir::def::{CtorOf, DefKind, Res};
@@ -424,7 +424,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 "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_eq(expected_input_tys.iter().copied()),
+                formal_input_tys
+                    .iter()
+                    .copied()
+                    .zip_eq(expected_input_tys.iter().copied())
+                    .map(|vars| self.resolve_vars_if_possible(vars)),
             );
 
             self.set_tainted_by_errors(self.report_arg_errors(
@@ -639,8 +643,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
 
             let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx];
-            let formal_input_ty = self.resolve_vars_if_possible(formal_input_ty);
-            let expected_input_ty = self.resolve_vars_if_possible(expected_input_ty);
             // 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.
@@ -713,7 +715,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             // Do we have as many extra provided arguments as the tuple's length?
             // If so, we might have just forgotten to wrap some args in a tuple.
             if let Some(ty::Tuple(tys)) =
-                formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| self.resolve_vars_if_possible(tys.1).kind())
+                formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind())
                 // If the tuple is unit, we're not actually wrapping any arguments.
                 && !tys.is_empty()
                 && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len()
@@ -732,7 +734,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()),
                     ),
                 ) {
-                    if !self.can_coerce(provided_ty, self.resolve_vars_if_possible(*expected_ty)) {
+                    if !self.can_coerce(provided_ty, *expected_ty) {
                         satisfied = false;
                         break;
                     }
@@ -751,14 +753,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     if tys.len() == 1 {
                         // A tuple wrap suggestion actually occurs within,
                         // so don't do anything special here.
-                        let (formal_ty, expected_ty) =
-                            formal_and_expected_inputs[mismatch_idx.into()];
-                        let formal_ty = self.resolve_vars_if_possible(formal_ty);
-                        let expected_ty = self.resolve_vars_if_possible(expected_ty);
                         err = self.err_ctxt().report_and_explain_type_error(
                             mk_trace(
                                 *lo,
-                                (formal_ty, expected_ty),
+                                formal_and_expected_inputs[mismatch_idx.into()],
                                 provided_arg_tys[mismatch_idx.into()].0,
                             ),
                             terr,
@@ -842,10 +840,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 return true;
             };
             let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
-            let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx];
-            let formal_ty = self.resolve_vars_if_possible(formal_ty);
-            let expected_ty = self.resolve_vars_if_possible(expected_ty);
-            let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty);
+            let trace =
+                mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
             if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308) {
                 let mut err = self.err_ctxt().report_and_explain_type_error(trace, *e);
                 suggest_confusable(&mut err);
@@ -873,8 +869,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         ] = &errors[..]
         {
             let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx];
-            let formal_ty = self.resolve_vars_if_possible(formal_ty);
-            let expected_ty = self.resolve_vars_if_possible(expected_ty);
             let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx];
             let trace = mk_trace(provided_arg_span, (formal_ty, expected_ty), provided_ty);
             let mut err = self.err_ctxt().report_and_explain_type_error(trace, *err);
@@ -896,6 +890,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 &matched_inputs,
                 &provided_arg_tys,
                 &formal_and_expected_inputs,
+                is_method,
             );
 
             if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
@@ -1003,8 +998,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             match error {
                 Error::Invalid(provided_idx, expected_idx, compatibility) => {
                     let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx];
-                    let formal_ty = self.resolve_vars_if_possible(formal_ty);
-                    let expected_ty = self.resolve_vars_if_possible(expected_ty);
                     let (provided_ty, provided_span) = provided_arg_tys[provided_idx];
                     if let Compatibility::Incompatible(error) = compatibility {
                         let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty);
@@ -1109,7 +1102,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     match &missing_idxs[..] {
                         &[expected_idx] => {
                             let (_, input_ty) = formal_and_expected_inputs[expected_idx];
-                            let input_ty = self.resolve_vars_if_possible(input_ty);
                             let span = if let Some((_, arg_span)) =
                                 provided_arg_tys.get(expected_idx.to_provided_idx())
                             {
@@ -1132,10 +1124,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         &[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 first_expected_ty =
-                                self.resolve_vars_if_possible(first_expected_ty);
-                            let second_expected_ty =
-                                self.resolve_vars_if_possible(second_expected_ty);
                             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()),
@@ -1162,14 +1150,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         }
                         &[first_idx, second_idx, third_idx] => {
                             let (_, first_expected_ty) = formal_and_expected_inputs[first_idx];
-                            let first_expected_ty =
-                                self.resolve_vars_if_possible(first_expected_ty);
                             let (_, second_expected_ty) = formal_and_expected_inputs[second_idx];
-                            let second_expected_ty =
-                                self.resolve_vars_if_possible(second_expected_ty);
                             let (_, third_expected_ty) = formal_and_expected_inputs[third_idx];
-                            let third_expected_ty =
-                                self.resolve_vars_if_possible(third_expected_ty);
                             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()),
@@ -1229,7 +1211,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 ) => {
                     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_expected_ty = self.resolve_vars_if_possible(first_expected_ty);
                     let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) {
                         format!(", found `{first_provided_ty}`")
                     } else {
@@ -1242,7 +1223,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
                     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 = self.resolve_vars_if_possible(second_provided_ty);
                     let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) {
                         format!(", found `{second_provided_ty}`")
                     } else {
@@ -1261,7 +1241,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 Error::Permutation(args) => {
                     for (dst_arg, dest_input) in args {
                         let (_, expected_ty) = formal_and_expected_inputs[dst_arg];
-                        let expected_ty = self.resolve_vars_if_possible(expected_ty);
                         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}`")
@@ -1288,6 +1267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             &matched_inputs,
             &provided_arg_tys,
             &formal_and_expected_inputs,
+            is_method,
         );
 
         // Incorporate the argument changes in the removal suggestion.
@@ -1325,7 +1305,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // To suggest a multipart suggestion when encountering `foo(1, "")` where the def
                 // was `fn foo(())`.
                 let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
-                let expected_ty = self.resolve_vars_if_possible(expected_ty);
                 suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx)));
             }
         }
@@ -1402,7 +1381,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 } else {
                     // Propose a placeholder of the correct type
                     let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
-                    let expected_ty = self.resolve_vars_if_possible(expected_ty);
                     ty_to_snippet(expected_ty, expected_idx)
                 };
                 suggestion += &suggestion_text;
@@ -2202,118 +2180,81 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             let mut spans: MultiSpan = def_span.into();
 
-            let param_generics: Vec<Option<&hir::GenericParam<'_>>> = self
-                .tcx
-                .hir()
-                .get_if_local(def_id)
-                .and_then(|node| node.fn_decl())
-                .into_iter()
-                .flat_map(|decl| decl.inputs)
-                .skip(if is_method { 1 } else { 0 })
-                .map(|param| {
-                    if let hir::TyKind::Path(QPath::Resolved(
-                        _,
-                        hir::Path { res: Res::Def(_, res_def_id), .. },
-                    )) = param.kind
-                    {
-                        self.tcx
-                            .hir()
-                            .get_if_local(def_id)
-                            .and_then(|node| node.generics())
-                            .into_iter()
-                            .flat_map(|generics| generics.params)
-                            .find(|gen| &gen.def_id.to_def_id() == res_def_id)
-                    } else {
-                        None
-                    }
-                })
-                .collect();
+            let params_with_generics = self.get_hir_params_with_generics(def_id, is_method);
+            let mut generics_with_unmatched_params = Vec::new();
 
-            let params: Vec<&hir::Param<'_>> = self
-                .tcx
-                .hir()
-                .get_if_local(def_id)
-                .and_then(|node| node.body_id())
-                .into_iter()
-                .flat_map(|id| self.tcx.hir().body(id).params)
-                .skip(if is_method { 1 } else { 0 })
-                .collect();
-
-            if params.len() == param_generics.len() {
-                let mut generics_map: Vec<(usize, &hir::GenericParam<'_>)> = Vec::new();
-                // This is a map from the index of the generic to the index of the parameter and the
-                // parameter
-                let mut matched_params_map: Vec<(usize, usize, &hir::Param<'_>)> = Vec::new();
-                let mut unmatched_params_map: Vec<(usize, &hir::Param<'_>)> = Vec::new();
-
-                for (idx, (param, generic)) in
-                    params.iter().zip_eq(param_generics.iter()).enumerate()
+            let check_for_matched_generics = || {
+                if matched_inputs.iter().any(|x| x.is_some())
+                    && params_with_generics.iter().any(|x| x.0.is_some())
                 {
-                    if matched_inputs[idx.into()].is_none() {
-                        spans.push_span_label(param.span, "");
-                        continue;
-                    }
-
-                    let Some(generic) = generic else {
-                        spans.push_span_label(param.span, "");
-                        continue;
-                    };
+                    for (idx, (generic, _)) in params_with_generics.iter().enumerate() {
+                        // Param has to have a generic and be matched to be relevant
+                        if matched_inputs[idx.into()].is_none() {
+                            continue;
+                        }
 
-                    let mut found_unmatched_generic_params = vec![];
+                        let Some(generic) = generic else {
+                            continue;
+                        };
 
-                    for unmatching_idx in idx + 1..params.len() {
-                        if matched_inputs[unmatching_idx.into()].is_none()
-                            && let Some(unmatched_idx_param_generic) =
-                                param_generics[unmatching_idx]
-                            && unmatched_idx_param_generic.name.ident() == generic.name.ident()
-                        {
-                            found_unmatched_generic_params.push(params[unmatching_idx]);
+                        for unmatching_idx in idx + 1..params_with_generics.len() {
+                            if matched_inputs[unmatching_idx.into()].is_none()
+                                && let Some(unmatched_idx_param_generic) =
+                                    params_with_generics[unmatching_idx].0
+                                && unmatched_idx_param_generic.name.ident() == generic.name.ident()
+                            {
+                                // We found a parameter that didn't match that needed to
+                                return true;
+                            }
                         }
                     }
+                }
+                false
+            };
 
-                    if found_unmatched_generic_params.is_empty() {
-                        continue;
-                    }
+            let check_for_matched_generics = check_for_matched_generics();
 
-                    let generics_idx = generics_map
-                        .iter()
-                        .filter(|x| x.1.name.ident() == generic.name.ident())
-                        .next()
-                        .map(|x| x.0);
-
-                    let generics_idx = generics_idx.unwrap_or_else(|| {
-                        let generics_map_len = generics_map.len();
-                        generics_map.push((generics_map_len, generic));
-                        generics_map_len
-                    });
-                    matched_params_map.push((generics_idx, idx, param));
-                    if unmatched_params_map.iter().filter(|x| x.0 == generics_idx).count() > 0 {
-                        // Already processed the unmatched params
-                        continue;
-                    }
-                    for unmatched_param in &found_unmatched_generic_params {
-                        unmatched_params_map.push((generics_idx, unmatched_param));
-                    }
-                }
+            for (idx, (generic_param, param)) in
+                params_with_generics.iter().enumerate().filter(|(idx, _)| {
+                    check_for_matched_generics
+                        || expected_idx.map_or(true, |expected_idx| expected_idx == *idx)
+                })
+            {
+                let Some(generic_param) = generic_param else {
+                    spans.push_span_label(param.span, "");
+                    continue;
+                };
 
-                for (generic_idx, generic) in &generics_map {
-                    let matched_params: Vec<(usize, &hir::Param<'_>)> = matched_params_map
-                        .iter()
-                        .filter(|x| x.0 == *generic_idx)
-                        .map(|x| (x.1, x.2))
-                        .collect();
-                    let unmatched_params: Vec<&hir::Param<'_>> = unmatched_params_map
-                        .iter()
-                        .filter(|x| x.0 == *generic_idx)
-                        .map(|x| x.1)
-                        .collect();
+                let other_params_matched: Vec<(usize, &hir::Param<'_>)> = params_with_generics
+                    .iter()
+                    .enumerate()
+                    .filter(|(other_idx, (other_generic_param, _))| {
+                        if *other_idx == idx {
+                            return false;
+                        }
+                        let Some(other_generic_param) = other_generic_param else {
+                            return false;
+                        };
+                        if matched_inputs[idx.into()].is_none()
+                            && matched_inputs[(*other_idx).into()].is_none()
+                        {
+                            return false;
+                        }
+                        if matched_inputs[idx.into()].is_some()
+                            && matched_inputs[(*other_idx).into()].is_some()
+                        {
+                            return false;
+                        }
+                        other_generic_param.name.ident() == generic_param.name.ident()
+                    })
+                    .map(|(other_idx, (_, other_param))| (other_idx, *other_param))
+                    .collect();
 
-                    let all_param_idents: Vec<String> = matched_params
+                if !other_params_matched.is_empty() {
+                    let other_param_matched_names: Vec<String> = other_params_matched
                         .iter()
-                        .map(|x| &x.1)
-                        .chain(unmatched_params.iter())
-                        .map(|x| {
-                            if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind {
+                        .map(|(_, other_param)| {
+                            if let hir::PatKind::Binding(_, _, ident, _) = other_param.pat.kind {
                                 format!("`{ident}`")
                             } else {
                                 "{unknown}".to_string()
@@ -2321,83 +2262,84 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         })
                         .collect();
 
-                    spans.push_span_label(
-                        generic.span,
-                        format!(
-                            "{} all reference this parameter {}",
-                            display_list_with_comma_and(&all_param_idents),
-                            generic.name.ident().name,
-                        ),
-                    );
+                    let matched_ty = self
+                        .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
+                        .sort_string(self.tcx);
 
-                    for unmatched_param in &unmatched_params {
-                        let idents: Vec<String> = matched_params
-                            .iter()
-                            .map(|x| {
-                                if let hir::PatKind::Binding(_, _, ident, _) = x.1.pat.kind {
-                                    format!("`{ident}`")
-                                } else {
-                                    "{unknown}".to_string()
-                                }
-                            })
-                            .collect();
-
-                        let matched_ty = matched_params
-                            .iter()
-                            .next()
-                            .map(|x| formal_and_expected_inputs[x.0.into()]);
-
-                        if let Some(matched_ty) = matched_ty {
-                            let matched_ty =
-                                self.resolve_vars_if_possible(matched_ty.0).sort_string(self.tcx);
-                            spans.push_span_label(
-                                unmatched_param.span,
-                                format!(
-                                    "this parameter needs to match the {} type of {}",
-                                    matched_ty,
-                                    display_list_with_comma_and(&idents)
-                                ),
-                            );
-                        } else {
-                            spans.push_span_label(
-                                unmatched_param.span,
+                    if matched_inputs[idx.into()].is_some() {
+                        spans.push_span_label(
+                            param.span,
+                            format!(
+                                "{} {} to match the {} type of this parameter",
+                                display_list_with_comma_and(&other_param_matched_names),
                                 format!(
-                                    "this parameter needs to match the type of {}",
-                                    display_list_with_comma_and(&idents)
+                                    "need{}",
+                                    pluralize!(if other_param_matched_names.len() == 1 {
+                                        0
+                                    } else {
+                                        1
+                                    })
                                 ),
-                            );
-                        }
-                    }
-
-                    for matched_param in &matched_params {
-                        let idents: Vec<String> = unmatched_params
-                            .iter()
-                            .map(|x| {
-                                if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind {
-                                    format!("`{ident}`")
-                                } else {
-                                    "{unknown}".to_string()
-                                }
-                            })
-                            .collect();
-
+                                matched_ty,
+                            ),
+                        );
+                    } else {
                         spans.push_span_label(
-                            matched_param.1.span,
+                            param.span,
                             format!(
-                                "{} needs to match the type of this parameter",
-                                display_list_with_comma_and(&idents)
+                                "this parameter needs to match the {} type of {}",
+                                matched_ty,
+                                display_list_with_comma_and(&other_param_matched_names),
                             ),
                         );
                     }
-                }
-            } else {
-                for (_, param) in params.iter().enumerate().filter(|(idx, _)| {
-                    expected_idx.map_or(true, |expected_idx| expected_idx == *idx)
-                }) {
+                    generics_with_unmatched_params.push(generic_param);
+                } else {
                     spans.push_span_label(param.span, "");
                 }
             }
 
+            for generic_param in self
+                .tcx
+                .hir()
+                .get_if_local(def_id)
+                .and_then(|node| node.generics())
+                .into_iter()
+                .flat_map(|x| x.params)
+                .filter(|x| {
+                    generics_with_unmatched_params.iter().any(|y| x.name.ident() == y.name.ident())
+                })
+            {
+                let param_idents_matching: Vec<String> = params_with_generics
+                    .iter()
+                    .filter(|(generic, _)| {
+                        if let Some(generic) = generic {
+                            generic.name.ident() == generic_param.name.ident()
+                        } else {
+                            false
+                        }
+                    })
+                    .map(|(_, param)| {
+                        if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
+                            format!("`{ident}`")
+                        } else {
+                            "{unknown}".to_string()
+                        }
+                    })
+                    .collect();
+
+                if !param_idents_matching.is_empty() {
+                    spans.push_span_label(
+                        generic_param.span,
+                        format!(
+                            "{} all reference this parameter {}",
+                            display_list_with_comma_and(&param_idents_matching),
+                            generic_param.name.ident().name,
+                        ),
+                    );
+                }
+            }
+
             err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id)));
         } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id)
             && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind
@@ -2464,115 +2406,112 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
     fn label_generic_mismatches(
         &self,
-        err: &mut DiagnosticBuilder<'_>,
+        err: &mut Diag<'_>,
         callable_def_id: Option<DefId>,
         matched_inputs: &IndexVec<ExpectedIdx, Option<ProvidedIdx>>,
         provided_arg_tys: &IndexVec<ProvidedIdx, (Ty<'tcx>, Span)>,
         formal_and_expected_inputs: &IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>,
+        is_method: bool,
     ) {
         let Some(def_id) = callable_def_id else {
             return;
         };
 
-        for (matched_idx, matched_arg) in matched_inputs.iter_enumerated() {
-            let Some(matched_input) = matched_arg else {
+        let params_with_generics = self.get_hir_params_with_generics(def_id, is_method);
+
+        for (idx, (generic_param, _)) in params_with_generics.iter().enumerate() {
+            if matched_inputs[idx.into()].is_none() {
+                continue;
+            }
+
+            let Some((_, matched_arg_span)) = provided_arg_tys.get(idx.into()) else {
                 continue;
             };
 
-            let (_, matched_arg_span) = provided_arg_tys[*matched_input];
-            let (matched_formal_ty, _) = formal_and_expected_inputs[matched_idx];
-            let ty::Infer(ty::TyVar(a)) = matched_formal_ty.kind() else {
+            let Some(generic_param) = generic_param else {
                 continue;
             };
 
-            let mut formal_ty_idxs_matched: Vec<usize> = vec![];
-            let mut expected_ty_matched = None;
-            for (input_idx, (formal_ty, expected_ty)) in formal_and_expected_inputs
-                .iter_enumerated()
-                // Only care about args after the matched one we're checking.
-                //
-                // NB: Incompatible should always come after their matching generics.
-                // e.g. if we have a function fn f(a: T, b: T, c: T) and we call it with
-                // f(1, 2, 3.0) then the first will force T to be an integer, the second
-                // then matches and the third is the incompatible argument.
-                .filter(|(idx, _)| *idx > matched_idx)
-            {
-                if let ty::Infer(ty::TyVar(b)) = formal_ty.kind() {
-                    if self.root_var(*a) == self.root_var(*b) {
-                        formal_ty_idxs_matched.push(input_idx.into());
-                        if expected_ty_matched.is_none() {
-                            expected_ty_matched = Some(expected_ty);
-                        }
+            let mut idxs_matched: Vec<usize> = vec![];
+            for (other_idx, (_, _)) in params_with_generics.iter().enumerate().filter(
+                |(other_idx, (other_generic_param, _))| {
+                    if *other_idx == idx {
+                        return false;
                     }
-                }
+                    let Some(other_generic_param) = other_generic_param else {
+                        return false;
+                    };
+                    if matched_inputs[(*other_idx).into()].is_some() {
+                        return false;
+                    }
+                    other_generic_param.name.ident() == generic_param.name.ident()
+                },
+            ) {
+                idxs_matched.push(other_idx.into());
             }
 
-            let Some(expected_ty) = expected_ty_matched else {
+            if idxs_matched.is_empty() {
                 continue;
-            };
-
-            let params = self
-                .tcx
-                .hir()
-                .get_if_local(def_id)
-                .and_then(|node| node.body_id())
-                .into_iter()
-                .flat_map(|id| self.tcx.hir().body(id).params);
-
-            let mut all_pats_matched: Vec<String> = vec![];
-            let mut incompatible_pats_matched: Vec<String> = vec![];
-            for (idx, param) in params
-                .into_iter()
-                .enumerate()
-                .filter(|(idx, _)| formal_ty_idxs_matched.contains(idx))
-            {
-                let ident = if let hir::PatKind::Binding(_, _, ident, _) = param.pat.kind {
-                    format!("`{ident}`")
-                } else {
-                    format!("`idx:{idx}`")
-                };
-                if matched_inputs[idx.into()].is_none() {
-                    incompatible_pats_matched.push(ident.clone());
-                }
-                all_pats_matched.push(ident);
             }
 
-            let expected_display_type =
-                self.resolve_vars_if_possible(*expected_ty).sort_string(self.tcx);
-            let label = if all_pats_matched.len() == 0 {
+            let expected_display_type = self
+                .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1)
+                .sort_string(self.tcx);
+            let label = if idxs_matched.len() == params_with_generics.len() - 1 {
                 format!(
-                    "expected all arguments to be {} because they need to match the type of this parameter",
+                    "expected all arguments to be this {} type because they need to match the type of this parameter",
                     expected_display_type
                 )
-            } else if all_pats_matched.len() == incompatible_pats_matched.len() {
-                format!(
-                    "expected {} {} to be {} {} because {} to match the type of this parameter",
-                    format!("argument{}", pluralize!(incompatible_pats_matched.len())),
-                    display_list_with_comma_and(&incompatible_pats_matched),
-                    a_or_an(&expected_display_type),
-                    expected_display_type,
-                    if all_pats_matched.len() == 1 {
-                        "that argument needs"
-                    } else {
-                        "those arguments need"
-                    }
-                )
             } else {
                 format!(
-                    "expected {} {} to be {} {} because the {} {} {} to match the type of this parameter",
-                    format!("argument{}", pluralize!(incompatible_pats_matched.len())),
-                    display_list_with_comma_and(&incompatible_pats_matched),
+                    "expected some other arguments to be {} {} type to match the type of this parameter",
                     a_or_an(&expected_display_type),
                     expected_display_type,
-                    format!("argument{}", pluralize!(all_pats_matched.len())),
-                    display_list_with_comma_and(&all_pats_matched),
-                    format!("need{}", pluralize!(if all_pats_matched.len() == 1 { 0 } else { 1 })),
                 )
             };
 
-            err.span_label(matched_arg_span, label);
+            err.span_label(*matched_arg_span, label);
         }
     }
+
+    fn get_hir_params_with_generics(
+        &self,
+        def_id: DefId,
+        is_method: bool,
+    ) -> Vec<(Option<&hir::GenericParam<'_>>, &hir::Param<'_>)> {
+        let fn_node = self.tcx.hir().get_if_local(def_id);
+
+        let generic_params: Vec<Option<&hir::GenericParam<'_>>> = fn_node
+            .and_then(|node| node.fn_decl())
+            .into_iter()
+            .flat_map(|decl| decl.inputs)
+            .skip(if is_method { 1 } else { 0 })
+            .map(|param| {
+                if let hir::TyKind::Path(QPath::Resolved(
+                    _,
+                    hir::Path { res: Res::Def(_, res_def_id), .. },
+                )) = param.kind
+                {
+                    fn_node
+                        .and_then(|node| node.generics())
+                        .into_iter()
+                        .flat_map(|generics| generics.params)
+                        .find(|gen| &gen.def_id.to_def_id() == res_def_id)
+                } else {
+                    None
+                }
+            })
+            .collect();
+
+        let params: Vec<&hir::Param<'_>> = fn_node
+            .and_then(|node| node.body_id())
+            .into_iter()
+            .flat_map(|id| self.tcx.hir().body(id).params)
+            .skip(if is_method { 1 } else { 0 })
+            .collect();
+
+        generic_params.into_iter().zip(params).collect()
+    }
 }
 
 struct FindClosureArg<'tcx> {
diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs
index 6279c402fc8..7f200a7b623 100644
--- a/compiler/rustc_lint_defs/src/lib.rs
+++ b/compiler/rustc_lint_defs/src/lib.rs
@@ -39,39 +39,6 @@ macro_rules! pluralize {
     };
 }
 
-/// Grammatical tool for displaying messages to end users in a nice form.
-///
-/// Returns "an" if the given string starts with a vowel, and "a" otherwise.
-pub fn a_or_an(s: &str) -> &'static str {
-    let mut chars = s.chars();
-    let Some(mut first_alpha_char) = chars.next() else {
-        return "a";
-    };
-    if first_alpha_char == '`' {
-        let Some(next) = chars.next() else {
-            return "a";
-        };
-        first_alpha_char = next;
-    }
-    if ["a", "e", "i", "o", "u", "&"].contains(&&first_alpha_char.to_lowercase().to_string()[..]) {
-        "an"
-    } else {
-        "a"
-    }
-}
-
-/// Grammatical tool for displaying messages to end users in a nice form.
-///
-/// Take a list ["a", "b", "c"] and output a display friendly version "a, b and c"
-pub fn display_list_with_comma_and<T: std::fmt::Display>(v: &[T]) -> String {
-    match v.len() {
-        0 => "".to_string(),
-        1 => v[0].to_string(),
-        2 => format!("{} and {}", v[0], v[1]),
-        _ => format!("{}, {}", v[0], display_list_with_comma_and(&v[1..])),
-    }
-}
-
 /// Indicates the confidence in the correctness of a suggestion.
 ///
 /// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
diff --git a/tests/ui/argument-suggestions/extra_arguments.stderr b/tests/ui/argument-suggestions/extra_arguments.stderr
index 4cbe9124dee..5ad8e35920a 100644
--- a/tests/ui/argument-suggestions/extra_arguments.stderr
+++ b/tests/ui/argument-suggestions/extra_arguments.stderr
@@ -45,7 +45,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/extra_arguments.rs:23:3
@@ -60,7 +60,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 3 arguments were supplied
   --> $DIR/extra_arguments.rs:24:3
@@ -74,7 +74,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 help: remove the extra arguments
    |
 LL -   one_arg(1, "", 1.0);
@@ -319,7 +319,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/extra_arguments.rs:54:3
@@ -334,7 +334,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/extra_arguments.rs:55:3
@@ -349,7 +349,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
   --> $DIR/extra_arguments.rs:60:3
@@ -364,7 +364,7 @@ note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
 LL | fn one_arg<T>(_a: T) {}
-   |    ^^^^^^^
+   |    ^^^^^^^    -----
 
 error: aborting due to 22 previous errors
 
diff --git a/tests/ui/argument-suggestions/invalid_arguments.stderr b/tests/ui/argument-suggestions/invalid_arguments.stderr
index 61a46b067f5..d26f33d098b 100644
--- a/tests/ui/argument-suggestions/invalid_arguments.stderr
+++ b/tests/ui/argument-suggestions/invalid_arguments.stderr
@@ -24,7 +24,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:6:4
    |
 LL | fn two_arg_same(_a: i32, _b: i32) {}
-   |    ^^^^^^^^^^^^ -------  -------
+   |    ^^^^^^^^^^^^          -------
 
 error[E0308]: mismatched types
   --> $DIR/invalid_arguments.rs:17:16
@@ -38,7 +38,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:6:4
    |
 LL | fn two_arg_same(_a: i32, _b: i32) {}
-   |    ^^^^^^^^^^^^ -------  -------
+   |    ^^^^^^^^^^^^ -------
 
 error[E0308]: arguments to this function are incorrect
   --> $DIR/invalid_arguments.rs:18:3
@@ -66,7 +66,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:7:4
    |
 LL | fn two_arg_diff(_a: i32, _b: f32) {}
-   |    ^^^^^^^^^^^^ -------  -------
+   |    ^^^^^^^^^^^^          -------
 
 error[E0308]: mismatched types
   --> $DIR/invalid_arguments.rs:20:16
@@ -80,7 +80,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:7:4
    |
 LL | fn two_arg_diff(_a: i32, _b: f32) {}
-   |    ^^^^^^^^^^^^ -------  -------
+   |    ^^^^^^^^^^^^ -------
 
 error[E0308]: arguments to this function are incorrect
   --> $DIR/invalid_arguments.rs:21:3
@@ -108,7 +108,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:8:4
    |
 LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {}
-   |    ^^^^^^^^^^^^^^ -------  -------  --------
+   |    ^^^^^^^^^^^^^^ -------
 
 error[E0308]: mismatched types
   --> $DIR/invalid_arguments.rs:25:21
@@ -122,7 +122,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:8:4
    |
 LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {}
-   |    ^^^^^^^^^^^^^^ -------  -------  --------
+   |    ^^^^^^^^^^^^^^          -------
 
 error[E0308]: mismatched types
   --> $DIR/invalid_arguments.rs:26:26
@@ -136,7 +136,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:8:4
    |
 LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {}
-   |    ^^^^^^^^^^^^^^ -------  -------  --------
+   |    ^^^^^^^^^^^^^^                   --------
 
 error[E0308]: arguments to this function are incorrect
   --> $DIR/invalid_arguments.rs:28:3
@@ -207,7 +207,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:9:4
    |
 LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {}
-   |    ^^^^^^^^^^^^^^^^ -------  -------  --------
+   |    ^^^^^^^^^^^^^^^^ -------
 
 error[E0308]: mismatched types
   --> $DIR/invalid_arguments.rs:35:23
@@ -221,7 +221,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:9:4
    |
 LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {}
-   |    ^^^^^^^^^^^^^^^^ -------  -------  --------
+   |    ^^^^^^^^^^^^^^^^          -------
 
 error[E0308]: mismatched types
   --> $DIR/invalid_arguments.rs:36:26
@@ -235,7 +235,7 @@ note: function defined here
   --> $DIR/invalid_arguments.rs:9:4
    |
 LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {}
-   |    ^^^^^^^^^^^^^^^^ -------  -------  --------
+   |    ^^^^^^^^^^^^^^^^                   --------
 
 error[E0308]: arguments to this function are incorrect
   --> $DIR/invalid_arguments.rs:38:3
diff --git a/tests/ui/argument-suggestions/too-long.stderr b/tests/ui/argument-suggestions/too-long.stderr
index 9400f2a02a8..04ee9275cb3 100644
--- a/tests/ui/argument-suggestions/too-long.stderr
+++ b/tests/ui/argument-suggestions/too-long.stderr
@@ -11,31 +11,9 @@ note: method defined here
    |
 LL |     fn foo(
    |        ^^^
-LL |         &self,
-LL |         a: i32,
-   |         ------
-LL |         b: i32,
-   |         ------
-LL |         c: i32,
-   |         ------
-LL |         d: i32,
-   |         ------
-LL |         e: i32,
-   |         ------
+...
 LL |         f: i32,
    |         ------
-LL |         g: i32,
-   |         ------
-LL |         h: i32,
-   |         ------
-LL |         i: i32,
-   |         ------
-LL |         j: i32,
-   |         ------
-LL |         k: i32,
-   |         ------
-LL |         l: i32,
-   |         ------
 help: consider dereferencing the borrow
    |
 LL |     qux.foo(a, b, c, d, e, *f, g, h, i, j, k, l);
diff --git a/tests/ui/async-await/coroutine-desc.stderr b/tests/ui/async-await/coroutine-desc.stderr
index 5ad54bc5b8e..1f1e303ea4c 100644
--- a/tests/ui/async-await/coroutine-desc.stderr
+++ b/tests/ui/async-await/coroutine-desc.stderr
@@ -5,7 +5,7 @@ LL |     fun(async {}, async {});
    |     --- --------  ^^^^^^^^ expected `async` block, found a different `async` block
    |     |   |
    |     |   the expected `async` block
-   |     |   expected argument `f2` to be an `async` block because that argument needs to match the type of this parameter
+   |     |   expected all arguments to be this `async` block type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected `async` block `{async block@$DIR/coroutine-desc.rs:10:9: 10:17}`
@@ -14,10 +14,9 @@ note: function defined here
   --> $DIR/coroutine-desc.rs:8:4
    |
 LL | fn fun<F: Future<Output = ()>>(f1: F, f2: F) {}
-   |    ^^^ -                       -----  -----
-   |        |                       |      |
-   |        |                       |      this parameter needs to match the `async` block type of `f1`
-   |        |                       `f2` needs to match the type of this parameter
+   |    ^^^ -                       -----  ----- this parameter needs to match the `async` block type of `f1`
+   |        |                       |
+   |        |                       `f2` needs to match the `async` block type of this parameter
    |        `f1` and `f2` all reference this parameter F
 
 error[E0308]: mismatched types
@@ -26,7 +25,7 @@ error[E0308]: mismatched types
 LL |     fun(one(), two());
    |     --- -----  ^^^^^ expected future, found a different future
    |     |   |
-   |     |   expected argument `f2` to be a future because that argument needs to match the type of this parameter
+   |     |   expected all arguments to be this future type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = help: consider `await`ing on both `Future`s
@@ -35,10 +34,9 @@ note: function defined here
   --> $DIR/coroutine-desc.rs:8:4
    |
 LL | fn fun<F: Future<Output = ()>>(f1: F, f2: F) {}
-   |    ^^^ -                       -----  -----
-   |        |                       |      |
-   |        |                       |      this parameter needs to match the future type of `f1`
-   |        |                       `f2` needs to match the type of this parameter
+   |    ^^^ -                       -----  ----- this parameter needs to match the future type of `f1`
+   |        |                       |
+   |        |                       `f2` needs to match the future type of this parameter
    |        `f1` and `f2` all reference this parameter F
 
 error[E0308]: mismatched types
@@ -48,7 +46,7 @@ LL |     fun((async || {})(), (async || {})());
    |     --- ---------------  ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body
    |     |   |         |
    |     |   |         the expected `async` closure body
-   |     |   expected argument `f2` to be an `async` closure body because that argument needs to match the type of this parameter
+   |     |   expected all arguments to be this `async` closure body type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected `async` closure body `{async closure body@$DIR/coroutine-desc.rs:14:19: 14:21}`
@@ -57,10 +55,9 @@ note: function defined here
   --> $DIR/coroutine-desc.rs:8:4
    |
 LL | fn fun<F: Future<Output = ()>>(f1: F, f2: F) {}
-   |    ^^^ -                       -----  -----
-   |        |                       |      |
-   |        |                       |      this parameter needs to match the `async` closure body type of `f1`
-   |        |                       `f2` needs to match the type of this parameter
+   |    ^^^ -                       -----  ----- this parameter needs to match the `async` closure body type of `f1`
+   |        |                       |
+   |        |                       `f2` needs to match the `async` closure body type of this parameter
    |        `f1` and `f2` all reference this parameter F
 
 error: aborting due to 3 previous errors
diff --git a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr
index b67f91ff8e2..46723c5a297 100644
--- a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr
+++ b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     test(&mut 7, &7);
    |     ---- ------  ^^ types differ in mutability
    |     |    |
-   |     |    expected argument `_b` to be an `&mut {integer}` because that argument needs to match the type of this parameter
+   |     |    expected all arguments to be this `&mut {integer}` type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected mutable reference `&mut {integer}`
@@ -13,10 +13,9 @@ note: function defined here
   --> $DIR/coerce-reborrow-multi-arg-fail.rs:1:4
    |
 LL | fn test<T>(_a: T, _b: T) {}
-   |    ^^^^ -  -----  -----
-   |         |  |      |
-   |         |  |      this parameter needs to match the `&mut {integer}` type of `_a`
-   |         |  `_b` needs to match the type of this parameter
+   |    ^^^^ -  -----  ----- this parameter needs to match the `&mut {integer}` type of `_a`
+   |         |  |
+   |         |  `_b` needs to match the `&mut {integer}` type of this parameter
    |         `_a` and `_b` all reference this parameter T
 
 error: aborting due to 1 previous error
diff --git a/tests/ui/coercion/coerce-to-bang.stderr b/tests/ui/coercion/coerce-to-bang.stderr
index 4c21eef5c29..3c737358adc 100644
--- a/tests/ui/coercion/coerce-to-bang.stderr
+++ b/tests/ui/coercion/coerce-to-bang.stderr
@@ -12,7 +12,7 @@ note: function defined here
   --> $DIR/coerce-to-bang.rs:3:4
    |
 LL | fn foo(x: usize, y: !, z: usize) { }
-   |    ^^^ --------  ----  --------
+   |    ^^^           ----
 
 error[E0308]: mismatched types
   --> $DIR/coerce-to-bang.rs:18:13
@@ -28,7 +28,7 @@ note: function defined here
   --> $DIR/coerce-to-bang.rs:3:4
    |
 LL | fn foo(x: usize, y: !, z: usize) { }
-   |    ^^^ --------  ----  --------
+   |    ^^^           ----
 
 error[E0308]: mismatched types
   --> $DIR/coerce-to-bang.rs:26:12
@@ -44,7 +44,7 @@ note: function defined here
   --> $DIR/coerce-to-bang.rs:3:4
    |
 LL | fn foo(x: usize, y: !, z: usize) { }
-   |    ^^^ --------  ----  --------
+   |    ^^^           ----
 
 error[E0308]: mismatched types
   --> $DIR/coerce-to-bang.rs:36:12
@@ -60,7 +60,7 @@ note: function defined here
   --> $DIR/coerce-to-bang.rs:3:4
    |
 LL | fn foo(x: usize, y: !, z: usize) { }
-   |    ^^^ --------  ----  --------
+   |    ^^^           ----
 
 error[E0308]: mismatched types
   --> $DIR/coerce-to-bang.rs:45:12
@@ -76,7 +76,7 @@ note: function defined here
   --> $DIR/coerce-to-bang.rs:3:4
    |
 LL | fn foo(x: usize, y: !, z: usize) { }
-   |    ^^^ --------  ----  --------
+   |    ^^^           ----
 
 error[E0308]: mismatched types
   --> $DIR/coerce-to-bang.rs:50:21
diff --git a/tests/ui/fn/fn-item-type.stderr b/tests/ui/fn/fn-item-type.stderr
index 24c7a3cb482..76cdbcceac8 100644
--- a/tests/ui/fn/fn-item-type.stderr
+++ b/tests/ui/fn/fn-item-type.stderr
@@ -4,7 +4,7 @@ error[E0308]: mismatched types
 LL |     eq(foo::<u8>, bar::<u8>);
    |     -- ---------  ^^^^^^^^^ expected fn item, found a different fn item
    |     |  |
-   |     |  expected argument `y` to be a fn item because that argument needs to match the type of this parameter
+   |     |  expected all arguments to be this fn item type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected fn item `fn(_) -> _ {foo::<u8>}`
@@ -14,10 +14,9 @@ note: function defined here
   --> $DIR/fn-item-type.rs:11:4
    |
 LL | fn eq<T>(x: T, y: T) {}
-   |    ^^ -  ----  ----
-   |       |  |     |
-   |       |  |     this parameter needs to match the fn item type of `x`
-   |       |  `y` needs to match the type of this parameter
+   |    ^^ -  ----  ---- this parameter needs to match the fn item type of `x`
+   |       |  |
+   |       |  `y` needs to match the fn item type of this parameter
    |       `x` and `y` all reference this parameter T
    = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize`
 
@@ -27,7 +26,7 @@ error[E0308]: mismatched types
 LL |     eq(foo::<u8>, foo::<i8>);
    |     -- ---------  ^^^^^^^^^ expected `u8`, found `i8`
    |     |  |
-   |     |  expected argument `y` to be a fn item because that argument needs to match the type of this parameter
+   |     |  expected all arguments to be this fn item type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected fn item `fn(_) -> _ {foo::<u8>}`
@@ -37,10 +36,9 @@ note: function defined here
   --> $DIR/fn-item-type.rs:11:4
    |
 LL | fn eq<T>(x: T, y: T) {}
-   |    ^^ -  ----  ----
-   |       |  |     |
-   |       |  |     this parameter needs to match the fn item type of `x`
-   |       |  `y` needs to match the type of this parameter
+   |    ^^ -  ----  ---- this parameter needs to match the fn item type of `x`
+   |       |  |
+   |       |  `y` needs to match the fn item type of this parameter
    |       `x` and `y` all reference this parameter T
    = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize`
 
@@ -50,7 +48,7 @@ error[E0308]: mismatched types
 LL |     eq(bar::<String>, bar::<Vec<u8>>);
    |     -- -------------  ^^^^^^^^^^^^^^ expected `String`, found `Vec<u8>`
    |     |  |
-   |     |  expected argument `y` to be a fn item because that argument needs to match the type of this parameter
+   |     |  expected all arguments to be this fn item type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected fn item `fn(_) -> _ {bar::<String>}`
@@ -60,10 +58,9 @@ note: function defined here
   --> $DIR/fn-item-type.rs:11:4
    |
 LL | fn eq<T>(x: T, y: T) {}
-   |    ^^ -  ----  ----
-   |       |  |     |
-   |       |  |     this parameter needs to match the fn item type of `x`
-   |       |  `y` needs to match the type of this parameter
+   |    ^^ -  ----  ---- this parameter needs to match the fn item type of `x`
+   |       |  |
+   |       |  `y` needs to match the fn item type of this parameter
    |       `x` and `y` all reference this parameter T
    = help: consider casting both fn items to fn pointers using `as fn(isize) -> isize`
 
@@ -73,7 +70,7 @@ error[E0308]: mismatched types
 LL |     eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
    |     -- ----------------  ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
    |     |  |
-   |     |  expected argument `y` to be a fn item because that argument needs to match the type of this parameter
+   |     |  expected all arguments to be this fn item type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected fn item `fn() {<u8 as Foo>::foo}`
@@ -83,10 +80,9 @@ note: function defined here
   --> $DIR/fn-item-type.rs:11:4
    |
 LL | fn eq<T>(x: T, y: T) {}
-   |    ^^ -  ----  ----
-   |       |  |     |
-   |       |  |     this parameter needs to match the fn item type of `x`
-   |       |  `y` needs to match the type of this parameter
+   |    ^^ -  ----  ---- this parameter needs to match the fn item type of `x`
+   |       |  |
+   |       |  `y` needs to match the fn item type of this parameter
    |       `x` and `y` all reference this parameter T
    = help: consider casting both fn items to fn pointers using `as fn()`
 
@@ -96,7 +92,7 @@ error[E0308]: mismatched types
 LL |     eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
    |     -- ---------  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
    |     |  |
-   |     |  expected argument `y` to be a fn item because that argument needs to match the type of this parameter
+   |     |  expected all arguments to be this fn item type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
    = note: expected fn item `fn(_) -> _ {foo::<u8>}`
@@ -106,10 +102,9 @@ note: function defined here
   --> $DIR/fn-item-type.rs:11:4
    |
 LL | fn eq<T>(x: T, y: T) {}
-   |    ^^ -  ----  ----
-   |       |  |     |
-   |       |  |     this parameter needs to match the fn item type of `x`
-   |       |  `y` needs to match the type of this parameter
+   |    ^^ -  ----  ---- this parameter needs to match the fn item type of `x`
+   |       |  |
+   |       |  `y` needs to match the fn item type of this parameter
    |       `x` and `y` all reference this parameter T
 
 error: aborting due to 5 previous errors
diff --git a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs
index 9a38b948e8e..2bd10e762d9 100644
--- a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs
+++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs
@@ -9,4 +9,6 @@ fn main() {
     //~^  ERROR arguments to this function are incorrect
     foo_multi_generics("a", "b", "c", true, false, 32, 2.);
     //~^  ERROR arguments to this function are incorrect
+    foo_multi_same("a", 1, 2, "d", "e", 32);
+    //~^  ERROR arguments to this function are incorrect
 }
diff --git a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr
index a4f55ce06ac..a845dfabe93 100644
--- a/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr
+++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr
@@ -4,17 +4,16 @@ error[E0308]: mismatched types
 LL |     foo(1, 2.);
    |     --- -  ^^ expected integer, found floating-point number
    |     |   |
-   |     |   expected argument `b` to be an integer because that argument needs to match the type of this parameter
+   |     |   expected all arguments to be this integer type because they need to match the type of this parameter
    |     arguments to this function are incorrect
    |
 note: function defined here
   --> $DIR/generic-mismatch-reporting-issue-116615.rs:1:4
    |
 LL | fn foo<T>(a: T, b: T) {}
-   |    ^^^ -  ----  ----
-   |        |  |     |
-   |        |  |     this parameter needs to match the integer type of `a`
-   |        |  `b` needs to match the type of this parameter
+   |    ^^^ -  ----  ---- this parameter needs to match the integer type of `a`
+   |        |  |
+   |        |  `b` needs to match the integer type of this parameter
    |        `a` and `b` all reference this parameter T
 
 error[E0308]: arguments to this function are incorrect
@@ -25,8 +24,8 @@ LL |     foo_multi_same("a", "b", false, true, (), 32);
    |                    |    |    |      |
    |                    |    |    |      expected `&str`, found `bool`
    |                    |    |    expected `&str`, found `bool`
-   |                    |    expected arguments `c`, `d` and `e` to be an `&str` because those arguments need to match the type of this parameter
-   |                    expected arguments `c`, `d` and `e` to be an `&str` because the arguments `b`, `c`, `d` and `e` need to match the type of this parameter
+   |                    |    expected some other arguments to be an `&str` type to match the type of this parameter
+   |                    expected some other arguments to be an `&str` type to match the type of this parameter
    |
 note: function defined here
   --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4
@@ -37,8 +36,8 @@ LL | fn foo_multi_same<T>(a: T, b: T, c: T, d: T, e: T, f: i32) {}
    |                   |  |     |     |     |     this parameter needs to match the `&str` type of `a` and `b`
    |                   |  |     |     |     this parameter needs to match the `&str` type of `a` and `b`
    |                   |  |     |     this parameter needs to match the `&str` type of `a` and `b`
-   |                   |  |     `c`, `d` and `e` needs to match the type of this parameter
-   |                   |  `c`, `d` and `e` needs to match the type of this parameter
+   |                   |  |     `c`, `d` and `e` need to match the `&str` type of this parameter
+   |                   |  `c`, `d` and `e` need to match the `&str` type of this parameter
    |                   `a`, `b`, `c`, `d` and `e` all reference this parameter T
 
 error[E0308]: arguments to this function are incorrect
@@ -47,29 +46,52 @@ error[E0308]: arguments to this function are incorrect
 LL |     foo_multi_generics("a", "b", "c", true, false, 32, 2.);
    |     ^^^^^^^^^^^^^^^^^^ ---  ---  ---  ----  -----  --  -- expected integer, found floating-point number
    |                        |    |    |    |     |      |
-   |                        |    |    |    |     |      expected argument `g` to be an integer because that argument needs to match the type of this parameter
+   |                        |    |    |    |     |      expected some other arguments to be an integer type to match the type of this parameter
    |                        |    |    |    |     expected `&str`, found `bool`
    |                        |    |    |    expected `&str`, found `bool`
-   |                        |    |    expected arguments `d` and `e` to be an `&str` because those arguments need to match the type of this parameter
-   |                        |    expected arguments `d` and `e` to be an `&str` because the arguments `c`, `d` and `e` need to match the type of this parameter
-   |                        expected arguments `d` and `e` to be an `&str` because the arguments `b`, `c`, `d` and `e` need to match the type of this parameter
+   |                        |    |    expected some other arguments to be an `&str` type to match the type of this parameter
+   |                        |    expected some other arguments to be an `&str` type to match the type of this parameter
+   |                        expected some other arguments to be an `&str` type to match the type of this parameter
    |
 note: function defined here
   --> $DIR/generic-mismatch-reporting-issue-116615.rs:3:4
    |
 LL | fn foo_multi_generics<S, T>(a: T, b: T, c: T, d: T, e: T, f: S, g: S) {}
-   |    ^^^^^^^^^^^^^^^^^^ -  -  ----  ----  ----  ----  ----  ----  ----
-   |                       |  |  |     |     |     |     |     |     |
-   |                       |  |  |     |     |     |     |     |     this parameter needs to match the integer type of `f`
-   |                       |  |  |     |     |     |     |     `g` needs to match the type of this parameter
+   |    ^^^^^^^^^^^^^^^^^^ -  -  ----  ----  ----  ----  ----  ----  ---- this parameter needs to match the integer type of `f`
+   |                       |  |  |     |     |     |     |     |
+   |                       |  |  |     |     |     |     |     `g` needs to match the integer type of this parameter
    |                       |  |  |     |     |     |     this parameter needs to match the `&str` type of `a`, `b` and `c`
    |                       |  |  |     |     |     this parameter needs to match the `&str` type of `a`, `b` and `c`
-   |                       |  |  |     |     `d` and `e` needs to match the type of this parameter
-   |                       |  |  |     `d` and `e` needs to match the type of this parameter
-   |                       |  |  `d` and `e` needs to match the type of this parameter
+   |                       |  |  |     |     `d` and `e` need to match the `&str` type of this parameter
+   |                       |  |  |     `d` and `e` need to match the `&str` type of this parameter
+   |                       |  |  `d` and `e` need to match the `&str` type of this parameter
    |                       |  `a`, `b`, `c`, `d` and `e` all reference this parameter T
    |                       `f` and `g` all reference this parameter S
 
-error: aborting due to 3 previous errors
+error[E0308]: arguments to this function are incorrect
+  --> $DIR/generic-mismatch-reporting-issue-116615.rs:12:5
+   |
+LL |     foo_multi_same("a", 1, 2, "d", "e", 32);
+   |     ^^^^^^^^^^^^^^ ---  -  -  ---  --- expected some other arguments to be an `&str` type to match the type of this parameter
+   |                    |    |  |  |
+   |                    |    |  |  expected some other arguments to be an `&str` type to match the type of this parameter
+   |                    |    |  expected `&str`, found integer
+   |                    |    expected `&str`, found integer
+   |                    expected some other arguments to be an `&str` type to match the type of this parameter
+   |
+note: function defined here
+  --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4
+   |
+LL | fn foo_multi_same<T>(a: T, b: T, c: T, d: T, e: T, f: i32) {}
+   |    ^^^^^^^^^^^^^^ -  ----  ----  ----  ----  ----  ------
+   |                   |  |     |     |     |     |
+   |                   |  |     |     |     |     `b` and `c` need to match the `&str` type of this parameter
+   |                   |  |     |     |     `b` and `c` need to match the `&str` type of this parameter
+   |                   |  |     |     this parameter needs to match the `&str` type of `a`, `d` and `e`
+   |                   |  |     this parameter needs to match the `&str` type of `a`, `d` and `e`
+   |                   |  `b` and `c` need to match the `&str` type of this parameter
+   |                   `a`, `b`, `c`, `d` and `e` all reference this parameter T
+
+error: aborting due to 4 previous errors
 
 For more information about this error, try `rustc --explain E0308`.
diff --git a/tests/ui/never_type/issue-96335.stderr b/tests/ui/never_type/issue-96335.stderr
index 13cdbe842c9..c3d80a425e0 100644
--- a/tests/ui/never_type/issue-96335.stderr
+++ b/tests/ui/never_type/issue-96335.stderr
@@ -21,7 +21,6 @@ LL |     0.....{loop{}1};
    |     |   |
    |     |   expected integer, found `RangeTo<{integer}>`
    |     arguments to this function are incorrect
-   |     expected all arguments to be integer because they need to match the type of this parameter
    |
    = note: expected type `{integer}`
             found struct `RangeTo<{integer}>`
diff --git a/tests/ui/parser/issues/issue-93282.stderr b/tests/ui/parser/issues/issue-93282.stderr
index fc15865e1a7..c6140bb821e 100644
--- a/tests/ui/parser/issues/issue-93282.stderr
+++ b/tests/ui/parser/issues/issue-93282.stderr
@@ -43,7 +43,7 @@ note: function defined here
   --> $DIR/issue-93282.rs:7:4
    |
 LL | fn bar(a: usize, b: usize) -> usize {
-   |    ^^^ --------  --------
+   |    ^^^ --------
 
 error: aborting due to 4 previous errors
 
diff --git a/tests/ui/span/issue-34264.stderr b/tests/ui/span/issue-34264.stderr
index 1b9ad3a3f16..f0dea66f612 100644
--- a/tests/ui/span/issue-34264.stderr
+++ b/tests/ui/span/issue-34264.stderr
@@ -77,7 +77,7 @@ note: function defined here
   --> $DIR/issue-34264.rs:3:4
    |
 LL | fn bar(x, y: usize) {}
-   |    ^^^ -  --------
+   |    ^^^    --------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
   --> $DIR/issue-34264.rs:10:5
diff --git a/tests/ui/tuple/add-tuple-within-arguments.stderr b/tests/ui/tuple/add-tuple-within-arguments.stderr
index 8414a51bfd5..6849128eadd 100644
--- a/tests/ui/tuple/add-tuple-within-arguments.stderr
+++ b/tests/ui/tuple/add-tuple-within-arguments.stderr
@@ -8,7 +8,7 @@ note: function defined here
   --> $DIR/add-tuple-within-arguments.rs:1:4
    |
 LL | fn foo(s: &str, a: (i32, i32), s2: &str) {}
-   |    ^^^ -------  -------------  --------
+   |    ^^^          -------------
 help: wrap these arguments in parentheses to construct a tuple
    |
 LL |     foo("hi", (1, 2), "hi");
@@ -28,7 +28,7 @@ note: function defined here
   --> $DIR/add-tuple-within-arguments.rs:3:4
    |
 LL | fn bar(s: &str, a: (&str,), s2: &str) {}
-   |    ^^^ -------  ----------  --------
+   |    ^^^          ----------
 help: use a trailing comma to create a tuple with one element
    |
 LL |     bar("hi", ("hi",), "hi");
diff --git a/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr b/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr
index 63a98a55127..fbe6bfeebb1 100644
--- a/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr
+++ b/tests/ui/type/wrong-call-return-type-due-to-generic-arg.stderr
@@ -74,7 +74,7 @@ note: function defined here
   --> $DIR/wrong-call-return-type-due-to-generic-arg.rs:1:4
    |
 LL | fn function<T>(x: T, y: bool) -> T {
-   |    ^^^^^^^^    ----  -------
+   |    ^^^^^^^^    ----
 help: change the type of the numeric literal from `u32` to `u16`
    |
 LL |     let x: u16 = function(0u16, true);