+ let mut suggestion_text = SuggestionText::None;
+
+ let mut errors = errors.into_iter().peekable();
+ while let Some(error) = errors.next() {
+ match error {
+ Error::Invalid(input_idx, compatibility) => {
+ let expected_ty = expected_input_tys[input_idx];
+ if let Compatibility::Incompatible(error) = &compatibility {
+ let provided_ty = final_arg_types[input_idx].map(|ty| ty.0).unwrap();
+ let cause = &self.misc(provided_args[input_idx].span);
+ let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+ if let Some(e) = error {
+ self.note_type_err(
+ &mut err,
+ &trace.cause,
+ None,
+ Some(trace.values),
+ e,
+ false,
+ true,
+ );
+ }
+ }
+
+ self.emit_coerce_suggestions(
+ &mut err,
+ &provided_args[input_idx],
+ final_arg_types[input_idx].map(|ty| ty.0).unwrap(),
+ final_arg_types[input_idx].map(|ty| ty.1).unwrap(),
+ None,
+ None,
+ );
+ }
+ Error::Extra(arg_idx) => {
+ let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] {
+ if ty.references_error() || ty.has_infer_types() {
+ "".into()
+ } else {
+ format!(" of type `{}`", ty)
+ }
+ } else {
+ "".into()
+ };
+ labels.push((
+ provided_args[arg_idx].span,
+ format!("argument{} unexpected", arg_type),
+ ));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Remove(false),
+ SuggestionText::Remove(_) => SuggestionText::Remove(true),
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ Error::Missing(input_idx) => {
+ // If there are multiple missing arguments adjacent to each other,
+ // then we can provide a single error.
+
+ let mut missing_idxs = vec![input_idx];
+ while let Some(e) = errors.next_if(|e| matches!(e, Error::Missing(input_idx) if *input_idx == (missing_idxs.last().unwrap() + 1))) {
+ match e {
+ Error::Missing(input_idx) => missing_idxs.push(input_idx),
+ _ => unreachable!(),
+ }
+ }
+
+ // NOTE: Because we might be re-arranging arguments, might have extra
+ // arguments, etc. it's hard to *really* know where we should provide
+ // this error label, so as a heuristic, we point to the provided arg, or
+ // to the call if the missing inputs pass the provided args.
+ match &missing_idxs[..] {
+ &[input_idx] => {
+ let expected_ty = expected_input_tys[input_idx];
+ let input_ty = self.resolve_vars_if_possible(expected_ty);
+ let span = if input_idx < provided_arg_count {
+ let arg_span = provided_args[input_idx].span;
+ Span::new(arg_span.lo(), arg_span.hi(), arg_span.ctxt(), None)
+ } else {
+ args_span
+ };
+ let arg_type =
+ if input_ty.references_error() || input_ty.has_infer_types() {
+ "".into()
+ } else {
+ format!(" of type `{}`", input_ty)
+ };
+ labels.push((span, format!("an argument{} is missing", arg_type)));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Provide(false),
+ SuggestionText::Provide(_) => SuggestionText::Provide(true),
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ &[first_idx, second_idx] => {
+ let first_input_ty =
+ self.resolve_vars_if_possible(expected_input_tys[first_idx]);
+ let second_input_ty =
+ self.resolve_vars_if_possible(expected_input_tys[second_idx]);
+
+ let span = if second_idx < provided_arg_count {
+ let first_arg_span = provided_args[first_idx].span;
+ let second_arg_span = provided_args[second_idx].span;
+ Span::new(
+ first_arg_span.lo(),
+ second_arg_span.hi(),
+ first_arg_span.ctxt(),
+ None,
+ )
+ } else {
+ args_span
+ };
+ let any_unnameable = false
+ || first_input_ty.references_error()
+ || first_input_ty.has_infer_types()
+ || second_input_ty.references_error()
+ || second_input_ty.has_infer_types();
+ let arg_type = if any_unnameable {
+ "".into()
+ } else {
+ format!(
+ " of type `{}` and `{}`",
+ first_input_ty, second_input_ty
+ )
+ };
+ labels
+ .push((span, format!("two arguments{} are missing", arg_type)));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None | SuggestionText::Provide(_) => {
+ SuggestionText::Provide(true)
+ }
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ &[first_idx, second_idx, third_idx] => {
+ let first_input_ty =
+ self.resolve_vars_if_possible(expected_input_tys[first_idx]);
+ let second_input_ty =
+ self.resolve_vars_if_possible(expected_input_tys[second_idx]);
+ let third_input_ty =
+ self.resolve_vars_if_possible(expected_input_tys[second_idx]);
+ let span = if third_idx < provided_arg_count {
+ let first_arg_span = provided_args[first_idx].span;
+ let third_arg_span = provided_args[third_idx].span;
+ Span::new(
+ first_arg_span.lo(),
+ third_arg_span.hi(),
+ first_arg_span.ctxt(),
+ None,
+ )
+ } else {
+ args_span
+ };
+ let any_unnameable = false
+ || first_input_ty.references_error()
+ || first_input_ty.has_infer_types()
+ || second_input_ty.references_error()
+ || second_input_ty.has_infer_types()
+ || third_input_ty.references_error()
+ || third_input_ty.has_infer_types();
+ let arg_type = if any_unnameable {
+ "".into()
+ } else {
+ format!(
+ " of type `{}`, `{}`, and `{}`",
+ first_input_ty, second_input_ty, third_input_ty
+ )
+ };
+ labels.push((
+ span,
+ format!("three arguments{} are missing", arg_type),
+ ));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None | SuggestionText::Provide(_) => {
+ SuggestionText::Provide(true)
+ }
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ missing_idxs => {
+ let first_idx = *missing_idxs.first().unwrap();
+ let second_idx = *missing_idxs.last().unwrap();
+ // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc.
+ // It's hard to *really* know where we should provide this error label, so this is a
+ // decent heuristic
+ let span = if first_idx < provided_arg_count {
+ let first_arg_span = provided_args[first_idx].span;
+ let second_arg_span = provided_args[second_idx].span;
+ Span::new(
+ first_arg_span.lo(),
+ second_arg_span.hi(),
+ first_arg_span.ctxt(),
+ None,
+ )
+ } else {
+ // Otherwise just label the whole function
+ args_span
+ };
+ labels.push((span, format!("multiple arguments are missing")));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None | SuggestionText::Provide(_) => {
+ SuggestionText::Provide(true)
+ }
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ }
+ }
+ Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx) => {
+ let first_span = provided_args[arg_idx].span;
+ let second_span = provided_args[other_arg_idx].span;
+
+ let first_expected_ty =
+ self.resolve_vars_if_possible(expected_input_tys[input_idx]);
+ let first_provided_ty = if let Some((ty, _)) = final_arg_types[arg_idx] {
+ format!(",found `{}`", ty)
+ } else {
+ "".into()
+ };
+ labels.push((
+ first_span,
+ format!("expected `{}`{}", first_expected_ty, first_provided_ty),
+ ));
+ let other_expected_ty =
+ self.resolve_vars_if_possible(expected_input_tys[other_input_idx]);
+ let other_provided_ty =
+ if let Some((ty, _)) = final_arg_types[other_arg_idx] {
+ format!(",found `{}`", ty)
+ } else {
+ "".into()
+ };
+ labels.push((
+ second_span,
+ format!("expected `{}`{}", other_expected_ty, other_provided_ty),
+ ));
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Swap,
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ Error::Permutation(args) => {
+ for (dst_arg, dest_input) in args {
+ let expected_ty =
+ self.resolve_vars_if_possible(expected_input_tys[dest_input]);
+ let provided_ty = if let Some((ty, _)) = final_arg_types[dst_arg] {
+ format!(",found `{}`", ty)
+ } else {
+ "".into()
+ };
+ labels.push((
+ provided_args[dst_arg].span,
+ format!("expected `{}`{}", expected_ty, provided_ty),
+ ));
+ }
+
+ suggestion_text = match suggestion_text {
+ SuggestionText::None => SuggestionText::Reorder,
+ _ => SuggestionText::DidYouMean,
+ };
+ }
+ }
+ }
+
+ // If we have less than 5 things to say, it would be useful to call out exactly what's wrong
+ if labels.len() <= 5 {
+ for (span, label) in labels {
+ err.span_label(span, label);
+ }
+ }
+
+ // Call out where the function is defined