Skip to content

Commit 5ce2d15

Browse files
committed
Highlight and simplify mismatched types
Shorten mismatched types errors by replacing subtypes that are not different with `_`, and highlighting only the subtypes that are different. Given a file ```rust struct X<T1, T2> { x: T1, y: T2, } fn foo() -> X<X<String, String>, String> { X { x: X {x: "".to_string(), y: 2}, y: "".to_string()} } fn bar() -> Option<String> { "".to_string() } ``` provide the following output ```rust error[E0308]: mismatched types --> file.rs:6:5 | 6 | X { x: X {x: "".to_string(), y: 2}, y: "".to_string()} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::string::String`, found {integer} | = note: expected type `X<X<_, std::string::String>, _>` ^^^^^^^^^^^^^^^^^^^^ // < highlighted found type `X<X<_, {integer}>, _>` ^^^^^^^^^ // < highlighted error[E0308]: mismatched types --> file.rs:6:5 | 10 | "".to_string() | ^^^^^^^^^^^^^^ expected struct `std::option::Option`, found `std::string::String` | = note: expected type `Option<std::string::String>` ^^^^^^^ ^ // < highlighted found type `std::string::String` ```
1 parent 16c94cd commit 5ce2d15

File tree

7 files changed

+336
-23
lines changed

7 files changed

+336
-23
lines changed

src/librustc/infer/error_reporting.rs

+194-5
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,155 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
555555
}
556556
}
557557

558+
fn highlight_outer(&self,
559+
value: &mut Vec<(String, bool)>,
560+
other_value: &mut Vec<(String, bool)>,
561+
name: String,
562+
sub: &ty::subst::Substs<'tcx>,
563+
pos: usize,
564+
other_ty: &ty::Ty<'tcx>) {
565+
value.push((name, true));
566+
let len = sub.len();
567+
if len > 0 {
568+
value.push(("<".to_string(), true));
569+
}
570+
571+
let sts = sub.regions();
572+
for (i, st) in sts.enumerate() {
573+
value.push((format!("{}", st), false));
574+
575+
if len > 0 && i != len - 1 {
576+
value.push((format!(", "), false));
577+
}
578+
}
579+
580+
let sts = sub.types();
581+
for (i, st) in sts.enumerate() {
582+
if i == pos {
583+
let (v, o_v) = self.cmp(st, other_ty);
584+
value.extend(v);
585+
other_value.extend(o_v);
586+
} else {
587+
value.push((format!("{}", st), true));
588+
}
589+
590+
if len > 0 && i != len - 1 {
591+
value.push((format!(", "), true));
592+
}
593+
}
594+
if len > 0 {
595+
value.push((">".to_string(), true));
596+
}
597+
}
598+
599+
600+
fn cmp(&self, t1: ty::Ty<'tcx>, t2: ty::Ty<'tcx>)
601+
-> (Vec<(String, bool)>, Vec<(String, bool)>)
602+
{
603+
match (&t1.sty, &t2.sty) {
604+
(&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => {
605+
let mut values: (Vec<(String, bool)>, Vec<(String, bool)>) = (vec![], vec![]);
606+
let name1 = self.tcx.item_path_str(def1.did.clone());
607+
let name2 = self.tcx.item_path_str(def2.did.clone());
608+
if name1 == name2 {
609+
// Easy case, replace same types with `_` to shorten the output
610+
// and highlight only the differing types.
611+
values.0.push((name1.to_string(), false));
612+
values.1.push((name2.to_string(), false));
613+
614+
let len = sub1.len();
615+
if len > 0 {
616+
values.0.push(("<".to_string(), false));
617+
values.1.push(("<".to_string(), false));
618+
}
619+
620+
let sts1 = sub1.regions();
621+
let sts2 = sub2.regions();
622+
let x = sts1.zip(sts2);
623+
for (i, (st1, st2)) in x.enumerate() {
624+
values.0.push((format!("{}", st1), st1 != st2));
625+
values.1.push((format!("{}", st2), st1 != st2));
626+
627+
if len > 0 && i != len - 1 {
628+
values.0.push((format!(", "), false));
629+
values.1.push((format!(", "), false));
630+
}
631+
}
632+
633+
let sts1 = sub1.types();
634+
let sts2 = sub2.types();
635+
let x = sts1.zip(sts2);
636+
for (i, (st1, st2)) in x.enumerate() {
637+
if st1 == st2 {
638+
values.0.push(("_".to_string(), false));
639+
values.1.push(("_".to_string(), false));
640+
} else {
641+
let (x1, x2) = self.cmp(st1, st2);
642+
values.0.extend(x1);
643+
values.1.extend(x2);
644+
}
645+
if len > 0 && i != len - 1 {
646+
values.0.push((format!(", "), false));
647+
values.1.push((format!(", "), false));
648+
}
649+
}
650+
if len > 0 {
651+
values.0.push((">".to_string(), false));
652+
values.1.push((">".to_string(), false));
653+
}
654+
values
655+
} else {
656+
// Check for simple composition
657+
for (i, st) in sub1.types().enumerate() {
658+
if st == t2 {
659+
self.highlight_outer(&mut values.0, &mut values.1, name1, sub1, i, &t2);
660+
return values;
661+
}
662+
if let &ty::TyAdt(def, _) = &st.sty {
663+
let name = self.tcx.item_path_str(def.did.clone());
664+
if name == name2 {
665+
self.highlight_outer(&mut values.0,
666+
&mut values.1,
667+
name1,
668+
sub1,
669+
i,
670+
&t2);
671+
return values;
672+
}
673+
}
674+
}
675+
for (i, st) in sub2.types().enumerate() {
676+
if st == t1 {
677+
self.highlight_outer(&mut values.1, &mut values.0, name2, sub2, i, &t1);
678+
return values;
679+
}
680+
if let &ty::TyAdt(def, _) = &st.sty {
681+
let name = self.tcx.item_path_str(def.did.clone());
682+
if name == name1 {
683+
self.highlight_outer(&mut values.1,
684+
&mut values.0,
685+
name2,
686+
sub2,
687+
i,
688+
&t1);
689+
return values;
690+
}
691+
}
692+
}
693+
694+
(vec![(format!("{}", t1), true)], vec![(format!("{}", t2), true)])
695+
}
696+
}
697+
_ => {
698+
if t1 == t2 {
699+
(vec![("_".to_string(), false)], vec![("_".to_string(), false)])
700+
} else {
701+
(vec![(format!("{}", t1), true)], vec![(format!("{}", t2), true)])
702+
}
703+
}
704+
}
705+
}
706+
558707
pub fn note_type_err(&self,
559708
diag: &mut DiagnosticBuilder<'tcx>,
560709
cause: &ObligationCause<'tcx>,
@@ -665,26 +814,64 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
665814
diag
666815
}
667816

668-
/// Returns a string of the form "expected `{}`, found `{}`".
669-
fn values_str(&self, values: &ValuePairs<'tcx>) -> Option<(String, String)> {
817+
/// Returns two `Vec`s representing portions of a type with a flag on wether it should
818+
/// be highlighted in the output.
819+
///
820+
/// For given types `X<String, usize>` and `X<usize, usize>`, this method would return
821+
///
822+
/// ```nocode
823+
/// Some((vec![
824+
/// ("X", false),
825+
/// ("<", false),
826+
/// ("String", true),
827+
/// (",", false),
828+
/// ("_", false),
829+
/// (">", false)
830+
/// ], vec![
831+
/// ("X", false),
832+
/// ("<", false),
833+
/// ("usize", true),
834+
/// (",", false),
835+
/// ("_", false),
836+
/// (">", false)
837+
/// ]))
838+
/// ```
839+
fn values_str(&self, values: &ValuePairs<'tcx>)
840+
-> Option<(Vec<(String, bool)>, Vec<(String, bool)>)>
841+
{
670842
match *values {
671-
infer::Types(ref exp_found) => self.expected_found_str(exp_found),
843+
infer::Types(ref exp_found) => self.expected_found_str_ty(exp_found),
672844
infer::TraitRefs(ref exp_found) => self.expected_found_str(exp_found),
673845
infer::PolyTraitRefs(ref exp_found) => self.expected_found_str(exp_found),
674846
}
675847
}
676848

849+
fn expected_found_str_ty(
850+
&self,
851+
exp_found: &ty::error::ExpectedFound<ty::Ty<'tcx>>)
852+
-> Option<(Vec<(String, bool)>, Vec<(String, bool)>)>
853+
{
854+
let exp_found = self.resolve_type_vars_if_possible(exp_found);
855+
if exp_found.references_error() {
856+
return None;
857+
}
858+
859+
Some(self.cmp(exp_found.expected, exp_found.found))
860+
}
861+
862+
677863
fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>(
678864
&self,
679865
exp_found: &ty::error::ExpectedFound<T>)
680-
-> Option<(String, String)>
866+
-> Option<(Vec<(String, bool)>, Vec<(String, bool)>)>
681867
{
682868
let exp_found = self.resolve_type_vars_if_possible(exp_found);
683869
if exp_found.references_error() {
684870
return None;
685871
}
686872

687-
Some((format!("{}", exp_found.expected), format!("{}", exp_found.found)))
873+
Some((vec![(format!("{}", exp_found.expected), true)],
874+
vec![(format!("{}", exp_found.found), true)]))
688875
}
689876

690877
fn report_generic_bound_failure(&self,
@@ -1140,6 +1327,8 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
11401327
match *origin {
11411328
infer::Subtype(ref trace) => {
11421329
if let Some((expected, found)) = self.values_str(&trace.values) {
1330+
let expected = expected.iter().map(|x| x.0.to_owned()).collect::<String>();
1331+
let found = found.iter().map(|x| x.0.to_owned()).collect::<String>();
11431332
// FIXME: do we want a "the" here?
11441333
err.span_note(
11451334
trace.cause.span,

src/librustc/ty/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2259,7 +2259,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> {
22592259
/// `DefId` is really just an interned def-path).
22602260
///
22612261
/// Note that if `id` is not local to this crate, the result will
2262-
// be a non-local `DefPath`.
2262+
/// be a non-local `DefPath`.
22632263
pub fn def_path(self, id: DefId) -> hir_map::DefPath {
22642264
if id.is_local() {
22652265
self.hir.def_path(id)

src/librustc_errors/diagnostic.rs

+14-12
Original file line numberDiff line numberDiff line change
@@ -81,30 +81,32 @@ impl Diagnostic {
8181

8282
pub fn note_expected_found(&mut self,
8383
label: &fmt::Display,
84-
expected: &fmt::Display,
85-
found: &fmt::Display)
84+
expected: &[(String, bool)],
85+
found: &[(String, bool)])
8686
-> &mut Self
8787
{
8888
self.note_expected_found_extra(label, expected, found, &"", &"")
8989
}
9090

9191
pub fn note_expected_found_extra(&mut self,
9292
label: &fmt::Display,
93-
expected: &fmt::Display,
94-
found: &fmt::Display,
93+
expected: &[(String, bool)],
94+
found: &[(String, bool)],
9595
expected_extra: &fmt::Display,
9696
found_extra: &fmt::Display)
9797
-> &mut Self
9898
{
99+
let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)];
100+
msg.extend(expected.iter()
101+
.map(|x| (x.0.to_owned(), if x.1 { Style::Highlight } else { Style::NoStyle })));
102+
msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
103+
msg.push((format!(" found {} `", label), Style::NoStyle));
104+
msg.extend(found.iter()
105+
.map(|x| (x.0.to_owned(), if x.1 { Style::Highlight } else { Style::NoStyle })));
106+
msg.push((format!("`{}", found_extra), Style::NoStyle));
107+
99108
// For now, just attach these as notes
100-
self.highlighted_note(vec![
101-
(format!("expected {} `", label), Style::NoStyle),
102-
(format!("{}", expected), Style::Highlight),
103-
(format!("`{}\n", expected_extra), Style::NoStyle),
104-
(format!(" found {} `", label), Style::NoStyle),
105-
(format!("{}", found), Style::Highlight),
106-
(format!("`{}", found_extra), Style::NoStyle),
107-
]);
109+
self.highlighted_note(msg);
108110
self
109111
}
110112

src/librustc_errors/diagnostic_builder.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -115,14 +115,14 @@ impl<'a> DiagnosticBuilder<'a> {
115115

116116
forward!(pub fn note_expected_found(&mut self,
117117
label: &fmt::Display,
118-
expected: &fmt::Display,
119-
found: &fmt::Display)
118+
expected: &[(String, bool)],
119+
found: &[(String, bool)])
120120
-> &mut Self);
121121

122122
forward!(pub fn note_expected_found_extra(&mut self,
123123
label: &fmt::Display,
124-
expected: &fmt::Display,
125-
found: &fmt::Display,
124+
expected: &[(String, bool)],
125+
found: &[(String, bool)],
126126
expected_extra: &fmt::Display,
127127
found_extra: &fmt::Display)
128128
-> &mut Self);

src/librustc_typeck/collect.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1037,7 +1037,9 @@ fn evaluate_disr_expr(ccx: &CrateCtxt, repr_ty: attr::IntType, body: hir::BodyId
10371037
let ty_hint = repr_ty.to_ty(ccx.tcx);
10381038
let print_err = |cv: ConstVal| {
10391039
struct_span_err!(ccx.tcx.sess, e.span, E0079, "mismatched types")
1040-
.note_expected_found(&"type", &ty_hint, &format!("{}", cv.description()))
1040+
.note_expected_found(&"type",
1041+
&[(format!("{}", ty_hint), true)],
1042+
&[(format!("{}", cv.description()), true)])
10411043
.span_label(e.span, &format!("expected '{}' type", ty_hint))
10421044
.emit();
10431045
};
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
enum Bar {
12+
Qux,
13+
Zar,
14+
}
15+
16+
struct Foo {
17+
bar: usize,
18+
}
19+
20+
struct X<T1, T2> {
21+
x: T1,
22+
y: T2,
23+
}
24+
25+
fn a() -> Foo {
26+
Some(Foo { bar: 1 })
27+
}
28+
29+
fn b() -> Option<Foo> {
30+
Foo { bar: 1 }
31+
}
32+
33+
fn c() -> Result<Foo, Bar> {
34+
Foo { bar: 1 }
35+
}
36+
37+
fn d() -> X<X<String, String>, String> {
38+
X {
39+
x: X {
40+
x: "".to_string(),
41+
y: 2,
42+
},
43+
y: 3,
44+
}
45+
}
46+
47+
fn e() -> X<X<String, String>, String> {
48+
X {
49+
x: X {
50+
x: "".to_string(),
51+
y: 2,
52+
},
53+
y: "".to_string(),
54+
}
55+
}
56+
57+
fn main() {}

0 commit comments

Comments
 (0)