From 55f1f993ff87c15597bd0620e3cbde49601de8bb Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Thu, 10 Nov 2022 20:20:25 +0000 Subject: [PATCH] More accurately report error when formal and expected signature types differ --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 29 +++++++++++------- .../formal-and-expected-differ.rs | 25 ++++++++++++++++ .../formal-and-expected-differ.stderr | 30 +++++++++++++++++++ 3 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 src/test/ui/argument-suggestions/formal-and-expected-differ.rs create mode 100644 src/test/ui/argument-suggestions/formal-and-expected-differ.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index a7a60a19bd3..8cf70eb5431 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -597,6 +597,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } }; + let mk_trace = |span, (formal_ty, expected_ty), provided_ty| { + let mismatched_ty = if expected_ty == provided_ty { + // If expected == provided, then we must have failed to sup + // the formal type. Avoid printing out "expected Ty, found Ty" + // in that case. + formal_ty + } else { + expected_ty + }; + TypeTrace::types(&self.misc(span), true, mismatched_ty, provided_ty) + }; + // The algorithm here is inspired by levenshtein distance and longest common subsequence. // We'll try to detect 4 different types of mistakes: // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs @@ -661,10 +673,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // A tuple wrap suggestion actually occurs within, // so don't do anything special here. err = self.err_ctxt().report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, + mk_trace( + *lo, + formal_and_expected_inputs[mismatch_idx.into()], provided_arg_tys[mismatch_idx.into()].0, ), terr, @@ -748,9 +759,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { errors.drain_filter(|error| { let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty); if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { self.err_ctxt().report_and_explain_type_error(trace, *e).emit(); return true; @@ -774,8 +783,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { { let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let 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); self.emit_coerce_suggestions( &mut err, @@ -847,8 +855,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; if let Compatibility::Incompatible(error) = compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); + let trace = mk_trace(provided_span, (formal_ty, expected_ty), provided_ty); if let Some(e) = error { self.err_ctxt().note_type_err( &mut err, diff --git a/src/test/ui/argument-suggestions/formal-and-expected-differ.rs b/src/test/ui/argument-suggestions/formal-and-expected-differ.rs new file mode 100644 index 00000000000..5e3b55ca525 --- /dev/null +++ b/src/test/ui/argument-suggestions/formal-and-expected-differ.rs @@ -0,0 +1,25 @@ +pub trait Foo { + type T; +} + +impl Foo for i32 { + type T = f32; +} + +pub struct U(T1, S) +where + T1: Foo; + +pub struct S(T); + +fn main() { + // The error message here isn't great -- it has to do with the fact that the + // `expected_inputs_for_expected_output` deduced inputs differs from the inputs + // that we infer from the constraints of the signature. + // + // I am not really sure what the best way of presenting this error message is, + // since right now it just suggests changing `3u32` <=> `3f32` back and forth. + let _: U<_, u32> = U(1, S(3u32)); + //~^ ERROR mismatched types + //~| ERROR mismatched types +} diff --git a/src/test/ui/argument-suggestions/formal-and-expected-differ.stderr b/src/test/ui/argument-suggestions/formal-and-expected-differ.stderr new file mode 100644 index 00000000000..905875b5277 --- /dev/null +++ b/src/test/ui/argument-suggestions/formal-and-expected-differ.stderr @@ -0,0 +1,30 @@ +error[E0308]: mismatched types + --> $DIR/formal-and-expected-differ.rs:22:29 + | +LL | let _: U<_, u32> = U(1, S(3u32)); + | - ^^^^^^^ expected `f32`, found `u32` + | | + | arguments to this struct are incorrect + | + = note: expected struct `S` + found struct `S` +note: tuple struct defined here + --> $DIR/formal-and-expected-differ.rs:9:12 + | +LL | pub struct U(T1, S) + | ^ + +error[E0308]: mismatched types + --> $DIR/formal-and-expected-differ.rs:22:24 + | +LL | let _: U<_, u32> = U(1, S(3u32)); + | --------- ^^^^^^^^^^^^^ expected `u32`, found `f32` + | | + | expected due to this + | + = note: expected struct `U<_, u32>` + found struct `U` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`.