diff --git a/src/librustc/infer/error_reporting.rs b/src/librustc/infer/error_reporting.rs index 939f214407ef7..35030ed1a59d0 100644 --- a/src/librustc/infer/error_reporting.rs +++ b/src/librustc/infer/error_reporting.rs @@ -555,6 +555,155 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { } } + fn highlight_outer(&self, + value: &mut Vec<(String, bool)>, + other_value: &mut Vec<(String, bool)>, + name: String, + sub: &ty::subst::Substs<'tcx>, + pos: usize, + other_ty: &ty::Ty<'tcx>) { + value.push((name, true)); + let len = sub.len(); + if len > 0 { + value.push(("<".to_string(), true)); + } + + let sts = sub.regions(); + for (i, st) in sts.enumerate() { + value.push((format!("{}", st), false)); + + if len > 0 && i != len - 1 { + value.push((format!(", "), false)); + } + } + + let sts = sub.types(); + for (i, st) in sts.enumerate() { + if i == pos { + let (v, o_v) = self.cmp(st, other_ty); + value.extend(v); + other_value.extend(o_v); + } else { + value.push((format!("{}", st), true)); + } + + if len > 0 && i != len - 1 { + value.push((format!(", "), true)); + } + } + if len > 0 { + value.push((">".to_string(), true)); + } + } + + + fn cmp(&self, t1: ty::Ty<'tcx>, t2: ty::Ty<'tcx>) + -> (Vec<(String, bool)>, Vec<(String, bool)>) + { + match (&t1.sty, &t2.sty) { + (&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => { + let mut values: (Vec<(String, bool)>, Vec<(String, bool)>) = (vec![], vec![]); + let name1 = self.tcx.item_path_str(def1.did.clone()); + let name2 = self.tcx.item_path_str(def2.did.clone()); + if name1 == name2 { + // Easy case, replace same types with `_` to shorten the output + // and highlight only the differing types. + values.0.push((name1.to_string(), false)); + values.1.push((name2.to_string(), false)); + + let len = sub1.len(); + if len > 0 { + values.0.push(("<".to_string(), false)); + values.1.push(("<".to_string(), false)); + } + + let sts1 = sub1.regions(); + let sts2 = sub2.regions(); + let x = sts1.zip(sts2); + for (i, (st1, st2)) in x.enumerate() { + values.0.push((format!("{}", st1), st1 != st2)); + values.1.push((format!("{}", st2), st1 != st2)); + + if len > 0 && i != len - 1 { + values.0.push((format!(", "), false)); + values.1.push((format!(", "), false)); + } + } + + let sts1 = sub1.types(); + let sts2 = sub2.types(); + let x = sts1.zip(sts2); + for (i, (st1, st2)) in x.enumerate() { + if st1 == st2 { + values.0.push(("_".to_string(), false)); + values.1.push(("_".to_string(), false)); + } else { + let (x1, x2) = self.cmp(st1, st2); + values.0.extend(x1); + values.1.extend(x2); + } + if len > 0 && i != len - 1 { + values.0.push((format!(", "), false)); + values.1.push((format!(", "), false)); + } + } + if len > 0 { + values.0.push((">".to_string(), false)); + values.1.push((">".to_string(), false)); + } + values + } else { + // Check for simple composition + for (i, st) in sub1.types().enumerate() { + if st == t2 { + self.highlight_outer(&mut values.0, &mut values.1, name1, sub1, i, &t2); + return values; + } + if let &ty::TyAdt(def, _) = &st.sty { + let name = self.tcx.item_path_str(def.did.clone()); + if name == name2 { + self.highlight_outer(&mut values.0, + &mut values.1, + name1, + sub1, + i, + &t2); + return values; + } + } + } + for (i, st) in sub2.types().enumerate() { + if st == t1 { + self.highlight_outer(&mut values.1, &mut values.0, name2, sub2, i, &t1); + return values; + } + if let &ty::TyAdt(def, _) = &st.sty { + let name = self.tcx.item_path_str(def.did.clone()); + if name == name1 { + self.highlight_outer(&mut values.1, + &mut values.0, + name2, + sub2, + i, + &t1); + return values; + } + } + } + + (vec![(format!("{}", t1), true)], vec![(format!("{}", t2), true)]) + } + } + _ => { + if t1 == t2 { + (vec![("_".to_string(), false)], vec![("_".to_string(), false)]) + } else { + (vec![(format!("{}", t1), true)], vec![(format!("{}", t2), true)]) + } + } + } + } + pub fn note_type_err(&self, diag: &mut DiagnosticBuilder<'tcx>, cause: &ObligationCause<'tcx>, @@ -665,26 +814,64 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { diag } - /// Returns a string of the form "expected `{}`, found `{}`". - fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<(String, String)> { + /// Returns two `Vec`s representing portions of a type with a flag on wether it should + /// be highlighted in the output. + /// + /// For given types `X` and `X`, this method would return + /// + /// ```nocode + /// Some((vec![ + /// ("X", false), + /// ("<", false), + /// ("String", true), + /// (",", false), + /// ("_", false), + /// (">", false) + /// ], vec![ + /// ("X", false), + /// ("<", false), + /// ("usize", true), + /// (",", false), + /// ("_", false), + /// (">", false) + /// ])) + /// ``` + fn values_str(&self, values: &ValuePairs<'tcx>) + -> Option<(Vec<(String, bool)>, Vec<(String, bool)>)> + { match *values { - infer::Types(ref exp_found) => self.expected_found_str(exp_found), + infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found), infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found), infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found), } } + fn expected_found_str_ty( + &self, + exp_found: &ty::error::ExpectedFound>) + -> Option<(Vec<(String, bool)>, Vec<(String, bool)>)> + { + let exp_found = self.resolve_type_vars_if_possible(exp_found); + if exp_found.references_error() { + return None; + } + + Some(self.cmp(exp_found.expected, exp_found.found)) + } + + fn expected_found_str>( &self, exp_found: &ty::error::ExpectedFound) - -> Option<(String, String)> + -> Option<(Vec<(String, bool)>, Vec<(String, bool)>)> { let exp_found = self.resolve_type_vars_if_possible(exp_found); if exp_found.references_error() { return None; } - Some((format!("{}", exp_found.expected), format!("{}", exp_found.found))) + Some((vec![(format!("{}", exp_found.expected), true)], + vec![(format!("{}", exp_found.found), true)])) } fn report_generic_bound_failure(&self, @@ -1140,6 +1327,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { match *origin { infer::Subtype(ref trace) => { if let Some((expected, found)) = self.values_str(&trace.values) { + let expected = expected.iter().map(|x| x.0.to_owned()).collect::(); + let found = found.iter().map(|x| x.0.to_owned()).collect::(); // FIXME: do we want a "the" here? err.span_note( trace.cause.span, diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 411e14531fab3..8eccf4d7e00b7 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -2259,7 +2259,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { /// `DefId` is really just an interned def-path). /// /// Note that if `id` is not local to this crate, the result will - // be a non-local `DefPath`. + /// be a non-local `DefPath`. pub fn def_path(self, id: DefId) -> hir_map::DefPath { if id.is_local() { self.hir.def_path(id) diff --git a/src/librustc_errors/diagnostic.rs b/src/librustc_errors/diagnostic.rs index 1b77ead92deb6..85c184de27abb 100644 --- a/src/librustc_errors/diagnostic.rs +++ b/src/librustc_errors/diagnostic.rs @@ -81,8 +81,8 @@ impl Diagnostic { pub fn note_expected_found(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display) + expected: &[(String, bool)], + found: &[(String, bool)]) -> &mut Self { self.note_expected_found_extra(label, expected, found, &"", &"") @@ -90,21 +90,23 @@ impl Diagnostic { pub fn note_expected_found_extra(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display, + expected: &[(String, bool)], + found: &[(String, bool)], expected_extra: &fmt::Display, found_extra: &fmt::Display) -> &mut Self { + let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)]; + msg.extend(expected.iter() + .map(|x| (x.0.to_owned(), if x.1 { Style::Highlight } else { Style::NoStyle }))); + msg.push((format!("`{}\n", expected_extra), Style::NoStyle)); + msg.push((format!(" found {} `", label), Style::NoStyle)); + msg.extend(found.iter() + .map(|x| (x.0.to_owned(), if x.1 { Style::Highlight } else { Style::NoStyle }))); + msg.push((format!("`{}", found_extra), Style::NoStyle)); + // For now, just attach these as notes - self.highlighted_note(vec![ - (format!("expected {} `", label), Style::NoStyle), - (format!("{}", expected), Style::Highlight), - (format!("`{}\n", expected_extra), Style::NoStyle), - (format!(" found {} `", label), Style::NoStyle), - (format!("{}", found), Style::Highlight), - (format!("`{}", found_extra), Style::NoStyle), - ]); + self.highlighted_note(msg); self } diff --git a/src/librustc_errors/diagnostic_builder.rs b/src/librustc_errors/diagnostic_builder.rs index 7dfea6b8951b0..1ae5747dd27df 100644 --- a/src/librustc_errors/diagnostic_builder.rs +++ b/src/librustc_errors/diagnostic_builder.rs @@ -115,14 +115,14 @@ impl<'a> DiagnosticBuilder<'a> { forward!(pub fn note_expected_found(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display) + expected: &[(String, bool)], + found: &[(String, bool)]) -> &mut Self); forward!(pub fn note_expected_found_extra(&mut self, label: &fmt::Display, - expected: &fmt::Display, - found: &fmt::Display, + expected: &[(String, bool)], + found: &[(String, bool)], expected_extra: &fmt::Display, found_extra: &fmt::Display) -> &mut Self); diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 217405a81ec2d..77b6dce2f60e2 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1037,7 +1037,9 @@ fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId let ty_hint = repr_ty.to_ty(ccx.tcx); let print_err = |cv: ConstVal| { struct_span_err!(ccx.tcx.sess, e.span, E0079, "mismatched types") - .note_expected_found(&"type", &ty_hint, &format!("{}", cv.description())) + .note_expected_found(&"type", + &[(format!("{}", ty_hint), true)], + &[(format!("{}", cv.description()), true)]) .span_label(e.span, &format!("expected '{}' type", ty_hint)) .emit(); }; diff --git a/src/test/ui/mismatched_types/abridged.rs b/src/test/ui/mismatched_types/abridged.rs new file mode 100644 index 0000000000000..7f47770f13519 --- /dev/null +++ b/src/test/ui/mismatched_types/abridged.rs @@ -0,0 +1,57 @@ +// Copyright 2017 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Bar { + Qux, + Zar, +} + +struct Foo { + bar: usize, +} + +struct X { + x: T1, + y: T2, +} + +fn a() -> Foo { + Some(Foo { bar: 1 }) +} + +fn b() -> Option { + Foo { bar: 1 } +} + +fn c() -> Result { + Foo { bar: 1 } +} + +fn d() -> X, String> { + X { + x: X { + x: "".to_string(), + y: 2, + }, + y: 3, + } +} + +fn e() -> X, String> { + X { + x: X { + x: "".to_string(), + y: 2, + }, + y: "".to_string(), + } +} + +fn main() {} diff --git a/src/test/ui/mismatched_types/abridged.stderr b/src/test/ui/mismatched_types/abridged.stderr new file mode 100644 index 0000000000000..9f0428019f3b7 --- /dev/null +++ b/src/test/ui/mismatched_types/abridged.stderr @@ -0,0 +1,63 @@ +error[E0308]: mismatched types + --> $DIR/abridged.rs:26:5 + | +26 | Some(Foo { bar: 1 }) + | ^^^^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::option::Option` + | + = note: expected type `Foo` + found type `std::option::Option` + = help: here are some functions which might fulfill your needs: + - .unwrap() + +error[E0308]: mismatched types + --> $DIR/abridged.rs:30:5 + | +30 | Foo { bar: 1 } + | ^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `Foo` + | + = note: expected type `std::option::Option` + found type `Foo` + +error[E0308]: mismatched types + --> $DIR/abridged.rs:34:5 + | +34 | Foo { bar: 1 } + | ^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `Foo` + | + = note: expected type `std::result::Result` + found type `Foo` + +error[E0308]: mismatched types + --> $DIR/abridged.rs:38:5 + | +38 | X { + | _____^ starting here... +39 | | x: X { +40 | | x: "".to_string(), +41 | | y: 2, +42 | | }, +43 | | y: 3, +44 | | } + | |_____^ ...ending here: expected struct `std::string::String`, found integral variable + | + = note: expected type `X, std::string::String>` + found type `X, {integer}>` + +error[E0308]: mismatched types + --> $DIR/abridged.rs:48:5 + | +48 | X { + | _____^ starting here... +49 | | x: X { +50 | | x: "".to_string(), +51 | | y: 2, +52 | | }, +53 | | y: "".to_string(), +54 | | } + | |_____^ ...ending here: expected struct `std::string::String`, found integral variable + | + = note: expected type `X, _>` + found type `X, _>` + +error: aborting due to 5 previous errors +