From df933640571861bcc2854431823cd9d4803b9f88 Mon Sep 17 00:00:00 2001 From: Steven Trotter Date: Sun, 25 Feb 2024 16:36:26 +0000 Subject: [PATCH 1/2] Added ability to report on generic argument mismatch better Needs some checking over and some tests have altered that need sanity checking, but overall this is starting to get somewhere now. --- compiler/rustc_errors/src/lib.rs | 2 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 388 +++++++++++++++++- compiler/rustc_lint_defs/src/lib.rs | 33 ++ .../extra_arguments.stderr | 14 +- .../invalid_arguments.stderr | 20 +- tests/ui/argument-suggestions/too-long.stderr | 24 +- tests/ui/async-await/coroutine-desc.stderr | 31 +- .../coerce-reborrow-multi-arg-fail.stderr | 11 +- tests/ui/coercion/coerce-to-bang.stderr | 10 +- tests/ui/fn/fn-item-type.stderr | 55 ++- ...generic-mismatch-reporting-issue-116615.rs | 12 + ...ric-mismatch-reporting-issue-116615.stderr | 75 ++++ tests/ui/never_type/issue-96335.stderr | 1 + tests/ui/parser/issues/issue-93282.stderr | 2 +- tests/ui/span/issue-34264.stderr | 2 +- .../tuple/add-tuple-within-arguments.stderr | 4 +- ...call-return-type-due-to-generic-arg.stderr | 2 +- 17 files changed, 611 insertions(+), 75 deletions(-) create mode 100644 tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs create mode 100644 tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 723f13dbe8d..b88b5bcd7ea 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::{pluralize, Applicability}; +pub use rustc_lint_defs::{a_or_an, display_list_with_comma_and, pluralize, Applicability}; pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker}; pub use rustc_span::ErrorGuaranteed; pub use snippet::Style; diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1fa1da93018..b65ac134fbc 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -17,7 +17,8 @@ use itertools::Itertools; use rustc_ast as ast; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::{ - codes::*, pluralize, Applicability, Diag, ErrorGuaranteed, MultiSpan, StashKey, + a_or_an, codes::*, display_list_with_comma_and, pluralize, Applicability, Diag, + DiagnosticBuilder, ErrorGuaranteed, MultiSpan, StashKey, }; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Res}; @@ -423,11 +424,7 @@ 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()) - .map(|vars| self.resolve_vars_if_possible(vars)), + formal_input_tys.iter().copied().zip_eq(expected_input_tys.iter().copied()), ); self.set_tainted_by_errors(self.report_arg_errors( @@ -642,6 +639,8 @@ 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. @@ -714,7 +713,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| tys.1.kind()) + formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| self.resolve_vars_if_possible(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() @@ -733,7 +732,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, *expected_ty) { + if !self.can_coerce(provided_ty, self.resolve_vars_if_possible(*expected_ty)) { satisfied = false; break; } @@ -752,10 +751,14 @@ 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_and_expected_inputs[mismatch_idx.into()], + (formal_ty, expected_ty), provided_arg_tys[mismatch_idx.into()].0, ), terr, @@ -795,6 +798,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr, None, Some(mismatch_idx), + &matched_inputs, + &formal_and_expected_inputs, is_method, ); suggest_confusable(&mut err); @@ -837,8 +842,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { return true; }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let trace = - mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); + 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); 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); @@ -866,6 +873,8 @@ 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); @@ -881,6 +890,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); err.span_label(full_call_span, format!("arguments to this {call_name} are incorrect")); + self.label_generic_mismatches( + &mut err, + fn_def_id, + &matched_inputs, + &provided_arg_tys, + &formal_and_expected_inputs, + ); + if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind && provided_idx.as_usize() == expected_idx.as_usize() { @@ -909,6 +926,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { call_expr, Some(expected_ty), Some(expected_idx.as_usize()), + &matched_inputs, + &formal_and_expected_inputs, is_method, ); suggest_confusable(&mut err); @@ -984,6 +1003,8 @@ 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); @@ -1088,6 +1109,7 @@ 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()) { @@ -1110,6 +1132,10 @@ 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()), @@ -1136,8 +1162,14 @@ 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()), @@ -1197,6 +1229,7 @@ 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 { @@ -1209,6 +1242,7 @@ 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 { @@ -1227,6 +1261,7 @@ 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}`") @@ -1247,6 +1282,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } + self.label_generic_mismatches( + &mut err, + fn_def_id, + &matched_inputs, + &provided_arg_tys, + &formal_and_expected_inputs, + ); + // Incorporate the argument changes in the removal suggestion. // When a type is *missing*, and the rest are additional, we want to suggest these with a // multipart suggestion, but in order to do so we need to figure out *where* the arg that @@ -1282,6 +1325,7 @@ 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))); } } @@ -1294,7 +1338,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method); + self.label_fn_like( + &mut err, + fn_def_id, + callee_ty, + call_expr, + None, + None, + &matched_inputs, + &formal_and_expected_inputs, + is_method, + ); // And add a suggestion block for all of the parameters let suggestion_text = match suggestion_text { @@ -1348,6 +1402,7 @@ 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; @@ -2054,6 +2109,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { expected_ty: Option>, // A specific argument should be labeled, instead of all of them expected_idx: Option, + matched_inputs: &IndexVec>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, is_method: bool, ) { let Some(mut def_id) = callable_def_id else { @@ -2145,21 +2202,200 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let mut spans: MultiSpan = def_span.into(); - let params = self + let param_generics: Vec>> = 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: 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 }); + .skip(if is_method { 1 } else { 0 }) + .collect(); - for (_, param) in params - .into_iter() - .enumerate() - .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) - { - spans.push_span_label(param.span, ""); + 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() + { + 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; + }; + + let mut found_unmatched_generic_params = vec![]; + + 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]); + } + } + + if found_unmatched_generic_params.is_empty() { + continue; + } + + 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 (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 all_param_idents: Vec = matched_params + .iter() + .map(|x| &x.1) + .chain(unmatched_params.iter()) + .map(|x| { + if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind { + format!("`{ident}`") + } else { + "{unknown}".to_string() + } + }) + .collect(); + + spans.push_span_label( + generic.span, + format!( + "{} all reference this parameter {}", + display_list_with_comma_and(&all_param_idents), + generic.name.ident().name, + ), + ); + + for unmatched_param in &unmatched_params { + let idents: Vec = 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, + format!( + "this parameter needs to match the type of {}", + display_list_with_comma_and(&idents) + ), + ); + } + } + + for matched_param in &matched_params { + let idents: Vec = unmatched_params + .iter() + .map(|x| { + if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind { + format!("`{ident}`") + } else { + "{unknown}".to_string() + } + }) + .collect(); + + spans.push_span_label( + matched_param.1.span, + format!( + "{} needs to match the type of this parameter", + display_list_with_comma_and(&idents) + ), + ); + } + } + } else { + for (_, param) in params.iter().enumerate().filter(|(idx, _)| { + expected_idx.map_or(true, |expected_idx| expected_idx == *idx) + }) { + spans.push_span_label(param.span, ""); + } } err.span_note(spans, format!("{} defined here", self.tcx.def_descr(def_id))); @@ -2225,6 +2461,118 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); } } + + fn label_generic_mismatches( + &self, + err: &mut DiagnosticBuilder<'_>, + callable_def_id: Option, + matched_inputs: &IndexVec>, + provided_arg_tys: &IndexVec, Span)>, + formal_and_expected_inputs: &IndexVec, Ty<'tcx>)>, + ) { + 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 { + 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 { + continue; + }; + + let mut formal_ty_idxs_matched: Vec = 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 Some(expected_ty) = expected_ty_matched else { + 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 = vec![]; + let mut incompatible_pats_matched: Vec = 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 { + format!( + "expected all arguments to be {} 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), + 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); + } + } } struct FindClosureArg<'tcx> { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 7f200a7b623..6279c402fc8 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -39,6 +39,39 @@ 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(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 5ad8e35920a..4cbe9124dee 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(_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(_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(_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(_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(_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(_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(_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 d26f33d098b..61a46b067f5 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 04ee9275cb3..9400f2a02a8 100644 --- a/tests/ui/argument-suggestions/too-long.stderr +++ b/tests/ui/argument-suggestions/too-long.stderr @@ -11,9 +11,31 @@ 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 e4cb0915a10..5ad54bc5b8e 100644 --- a/tests/ui/async-await/coroutine-desc.stderr +++ b/tests/ui/async-await/coroutine-desc.stderr @@ -5,6 +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 | arguments to this function are incorrect | = note: expected `async` block `{async block@$DIR/coroutine-desc.rs:10:9: 10:17}` @@ -13,14 +14,19 @@ note: function defined here --> $DIR/coroutine-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- + | ^^^ - ----- ----- + | | | | + | | | this parameter needs to match the `async` block type of `f1` + | | `f2` needs to match the type of this parameter + | `f1` and `f2` all reference this parameter F error[E0308]: mismatched types --> $DIR/coroutine-desc.rs:12:16 | LL | fun(one(), two()); - | --- ^^^^^ expected future, found a different future - | | + | --- ----- ^^^^^ expected future, found a different future + | | | + | | expected argument `f2` to be a future because that argument needs to match the type of this parameter | arguments to this function are incorrect | = help: consider `await`ing on both `Future`s @@ -29,15 +35,20 @@ note: function defined here --> $DIR/coroutine-desc.rs:8:4 | LL | fn fun>(f1: F, f2: F) {} - | ^^^ ----- + | ^^^ - ----- ----- + | | | | + | | | this parameter needs to match the future type of `f1` + | | `f2` needs to match the type of this parameter + | `f1` and `f2` all reference this parameter F error[E0308]: mismatched types --> $DIR/coroutine-desc.rs:14:26 | LL | fun((async || {})(), (async || {})()); - | --- -- ^^^^^^^^^^^^^^^ expected `async` closure body, found a different `async` closure body - | | | - | | the expected `async` closure body + | --- --------------- ^^^^^^^^^^^^^^^ 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 | arguments to this function are incorrect | = note: expected `async` closure body `{async closure body@$DIR/coroutine-desc.rs:14:19: 14:21}` @@ -46,7 +57,11 @@ note: function defined here --> $DIR/coroutine-desc.rs:8:4 | LL | fn fun>(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 + | `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 498ef33d52e..b67f91ff8e2 100644 --- a/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr +++ b/tests/ui/coercion/coerce-reborrow-multi-arg-fail.stderr @@ -2,8 +2,9 @@ error[E0308]: mismatched types --> $DIR/coerce-reborrow-multi-arg-fail.rs:4:18 | LL | test(&mut 7, &7); - | ---- ^^ types differ in mutability - | | + | ---- ------ ^^ types differ in mutability + | | | + | | expected argument `_b` to be an `&mut {integer}` because that argument needs to match the type of this parameter | arguments to this function are incorrect | = note: expected mutable reference `&mut {integer}` @@ -12,7 +13,11 @@ note: function defined here --> $DIR/coerce-reborrow-multi-arg-fail.rs:1:4 | LL | fn test(_a: T, _b: T) {} - | ^^^^ ----- + | ^^^^ - ----- ----- + | | | | + | | | this parameter needs to match the `&mut {integer}` type of `_a` + | | `_b` needs to match the 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 3c737358adc..4c21eef5c29 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 da90b8b81c8..24c7a3cb482 100644 --- a/tests/ui/fn/fn-item-type.stderr +++ b/tests/ui/fn/fn-item-type.stderr @@ -2,8 +2,9 @@ error[E0308]: mismatched types --> $DIR/fn-item-type.rs:22:19 | LL | eq(foo::, bar::); - | -- ^^^^^^^^^ expected fn item, found a different fn item - | | + | -- --------- ^^^^^^^^^ 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 | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {foo::}` @@ -13,15 +14,20 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- + | | | | + | | | this parameter needs to match the fn item type of `x` + | | `y` needs to match the 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` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:29:19 | LL | eq(foo::, foo::); - | -- ^^^^^^^^^ expected `u8`, found `i8` - | | + | -- --------- ^^^^^^^^^ expected `u8`, found `i8` + | | | + | | expected argument `y` to be a fn item because that argument needs to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {foo::}` @@ -31,15 +37,20 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- + | | | | + | | | this parameter needs to match the fn item type of `x` + | | `y` needs to match the 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` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:34:23 | LL | eq(bar::, bar::>); - | -- ^^^^^^^^^^^^^^ expected `String`, found `Vec` - | | + | -- ------------- ^^^^^^^^^^^^^^ expected `String`, found `Vec` + | | | + | | expected argument `y` to be a fn item because that argument needs to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {bar::}` @@ -49,15 +60,20 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- + | | | | + | | | this parameter needs to match the fn item type of `x` + | | `y` needs to match the 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` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:40:26 | LL | eq(::foo, ::foo); - | -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` - | | + | -- ---------------- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16` + | | | + | | expected argument `y` to be a fn item because that argument needs to match the type of this parameter | arguments to this function are incorrect | = note: expected fn item `fn() {::foo}` @@ -67,15 +83,20 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- + | | | | + | | | this parameter needs to match the fn item type of `x` + | | `y` needs to match the type of this parameter + | `x` and `y` all reference this parameter T = help: consider casting both fn items to fn pointers using `as fn()` error[E0308]: mismatched types --> $DIR/fn-item-type.rs:45:19 | LL | eq(foo::, bar:: as fn(isize) -> isize); - | -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer - | | + | -- --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 | arguments to this function are incorrect | = note: expected fn item `fn(_) -> _ {foo::}` @@ -85,7 +106,11 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(x: T, y: T) {} - | ^^ ---- + | ^^ - ---- ---- + | | | | + | | | this parameter needs to match the fn item type of `x` + | | `y` needs to match the 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 new file mode 100644 index 00000000000..9a38b948e8e --- /dev/null +++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.rs @@ -0,0 +1,12 @@ +fn foo(a: T, b: T) {} +fn foo_multi_same(a: T, b: T, c: T, d: T, e: T, f: i32) {} +fn foo_multi_generics(a: T, b: T, c: T, d: T, e: T, f: S, g: S) {} + +fn main() { + foo(1, 2.); + //~^ ERROR mismatched types + foo_multi_same("a", "b", false, true, (), 32); + //~^ ERROR arguments to this function are incorrect + foo_multi_generics("a", "b", "c", true, false, 32, 2.); + //~^ 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 new file mode 100644 index 00000000000..a4f55ce06ac --- /dev/null +++ b/tests/ui/mismatched_types/generic-mismatch-reporting-issue-116615.stderr @@ -0,0 +1,75 @@ +error[E0308]: mismatched types + --> $DIR/generic-mismatch-reporting-issue-116615.rs:6:12 + | +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 + | arguments to this function are incorrect + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:1:4 + | +LL | fn foo(a: T, b: T) {} + | ^^^ - ---- ---- + | | | | + | | | this parameter needs to match the integer type of `a` + | | `b` needs to match the type of this parameter + | `a` and `b` all reference this parameter T + +error[E0308]: arguments to this function are incorrect + --> $DIR/generic-mismatch-reporting-issue-116615.rs:8:5 + | +LL | foo_multi_same("a", "b", false, true, (), 32); + | ^^^^^^^^^^^^^^ --- --- ----- ---- -- expected `&str`, found `()` + | | | | | + | | | | 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 + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:2:4 + | +LL | fn foo_multi_same(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 + | `a`, `b`, `c`, `d` and `e` all reference this parameter T + +error[E0308]: arguments to this function are incorrect + --> $DIR/generic-mismatch-reporting-issue-116615.rs:10:5 + | +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 `&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 + | +note: function defined here + --> $DIR/generic-mismatch-reporting-issue-116615.rs:3:4 + | +LL | fn foo_multi_generics(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 `&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 + | | `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 + +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 c3d80a425e0..13cdbe842c9 100644 --- a/tests/ui/never_type/issue-96335.stderr +++ b/tests/ui/never_type/issue-96335.stderr @@ -21,6 +21,7 @@ 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 c6140bb821e..fc15865e1a7 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 f0dea66f612..1b9ad3a3f16 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 6849128eadd..8414a51bfd5 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 fbe6bfeebb1..63a98a55127 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(x: T, y: bool) -> T { - | ^^^^^^^^ ---- + | ^^^^^^^^ ---- ------- help: change the type of the numeric literal from `u32` to `u16` | LL | let x: u16 = function(0u16, true); From 8a5245e7dd006e2eb6bf6d3834b05772285efe28 Mon Sep 17 00:00:00 2001 From: Steven Trotter Date: Mon, 26 Feb 2024 21:00:35 +0000 Subject: [PATCH 2/2] 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 --- compiler/rustc_errors/src/lib.rs | 35 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 499 ++++++++---------- compiler/rustc_lint_defs/src/lib.rs | 33 -- .../extra_arguments.stderr | 14 +- .../invalid_arguments.stderr | 20 +- tests/ui/argument-suggestions/too-long.stderr | 24 +- tests/ui/async-await/coroutine-desc.stderr | 27 +- .../coerce-reborrow-multi-arg-fail.stderr | 9 +- tests/ui/coercion/coerce-to-bang.stderr | 10 +- tests/ui/fn/fn-item-type.stderr | 45 +- ...generic-mismatch-reporting-issue-116615.rs | 2 + ...ric-mismatch-reporting-issue-116615.stderr | 64 ++- tests/ui/never_type/issue-96335.stderr | 1 - tests/ui/parser/issues/issue-93282.stderr | 2 +- tests/ui/span/issue-34264.stderr | 2 +- .../tuple/add-tuple-within-arguments.stderr | 4 +- ...call-return-type-due-to-generic-arg.stderr | 2 +- 17 files changed, 362 insertions(+), 431 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(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>> = 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; - } + 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 Some(generic) = generic else { - spans.push_span_label(param.span, ""); - continue; - }; + let Some(generic) = generic else { + continue; + }; - let mut found_unmatched_generic_params = vec![]; - - 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; + } } } - - if found_unmatched_generic_params.is_empty() { - continue; - } - - 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)); - } } + false + }; - 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 check_for_matched_generics = check_for_matched_generics(); - let all_param_idents: Vec = matched_params + 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; + }; + + 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(); + + if !other_params_matched.is_empty() { + let other_param_matched_names: Vec = 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, - ), - ); - - for unmatched_param in &unmatched_params { - let idents: Vec = 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, - format!( - "this parameter needs to match the type of {}", - display_list_with_comma_and(&idents) - ), - ); - } - } - - for matched_param in &matched_params { - let idents: Vec = unmatched_params - .iter() - .map(|x| { - if let hir::PatKind::Binding(_, _, ident, _) = x.pat.kind { - format!("`{ident}`") - } else { - "{unknown}".to_string() - } - }) - .collect(); + let matched_ty = self + .resolve_vars_if_possible(formal_and_expected_inputs[idx.into()].1) + .sort_string(self.tcx); + if matched_inputs[idx.into()].is_some() { 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) + "{} {} to match the {} type of this parameter", + display_list_with_comma_and(&other_param_matched_names), + format!( + "need{}", + pluralize!(if other_param_matched_names.len() == 1 { + 0 + } else { + 1 + }) + ), + matched_ty, + ), + ); + } else { + spans.push_span_label( + param.span, + format!( + "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 = 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(¶m_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, matched_inputs: &IndexVec>, provided_arg_tys: &IndexVec, Span)>, formal_and_expected_inputs: &IndexVec, 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 = 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 = 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 = vec![]; - let mut incompatible_pats_matched: Vec = 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>> = 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(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(_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(_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(_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(_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(_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(_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(_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>(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>(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>(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(_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::, bar::); | -- --------- ^^^^^^^^^ 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::}` @@ -14,10 +14,9 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(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::, foo::); | -- --------- ^^^^^^^^^ 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::}` @@ -37,10 +36,9 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(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::, bar::>); | -- ------------- ^^^^^^^^^^^^^^ expected `String`, found `Vec` | | | - | | 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::}` @@ -60,10 +58,9 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(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(::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() {::foo}` @@ -83,10 +80,9 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(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::, bar:: 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::}` @@ -106,10 +102,9 @@ note: function defined here --> $DIR/fn-item-type.rs:11:4 | LL | fn eq(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(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(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(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(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(x: T, y: bool) -> T { - | ^^^^^^^^ ---- ------- + | ^^^^^^^^ ---- help: change the type of the numeric literal from `u32` to `u16` | LL | let x: u16 = function(0u16, true);