about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/librustc/infer/error_reporting/mod.rs288
-rw-r--r--src/librustc/infer/error_reporting/note.rs2
-rw-r--r--src/librustc/ty/mod.rs2
-rw-r--r--src/librustc_errors/diagnostic.rs72
-rw-r--r--src/librustc_errors/diagnostic_builder.rs10
-rw-r--r--src/librustc_errors/lib.rs2
-rw-r--r--src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr4
-rw-r--r--src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr4
-rw-r--r--src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr2
-rw-r--r--src/test/ui/mismatched_types/abridged.rs61
-rw-r--r--src/test/ui/mismatched_types/abridged.stderr70
11 files changed, 485 insertions, 32 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index 9fa2bc8a2a7..dcbe50de2e9 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -70,7 +70,7 @@ use ty::{self, TyCtxt, TypeFoldable};
 use ty::{Region, Issue32330};
 use ty::error::TypeError;
 use syntax_pos::{Pos, Span};
-use errors::DiagnosticBuilder;
+use errors::{DiagnosticBuilder, DiagnosticStyledString};
 
 mod note;
 
@@ -365,6 +365,262 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
         }
     }
 
+    /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value`
+    /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and
+    /// populate `other_value` with `other_ty`.
+    ///
+    /// ```text
+    /// Foo<Bar<Qux>>
+    /// ^^^^--------^ this is highlighted
+    /// |   |
+    /// |   this type argument is exactly the same as the other type, not highlighted
+    /// this is highlighted
+    /// Bar<Qux>
+    /// -------- this type is the same as a type argument in the other type, not highlighted
+    /// ```
+    fn highlight_outer(&self,
+                       mut value: &mut DiagnosticStyledString,
+                       mut other_value: &mut DiagnosticStyledString,
+                       name: String,
+                       sub: &ty::subst::Substs<'tcx>,
+                       pos: usize,
+                       other_ty: &ty::Ty<'tcx>) {
+        // `value` and `other_value` hold two incomplete type representation for display.
+        // `name` is the path of both types being compared. `sub`
+        value.push_highlighted(name);
+        let len = sub.len();
+        if len > 0 {
+            value.push_highlighted("<");
+        }
+
+        // Output the lifetimes fot the first type
+        let lifetimes = sub.regions().map(|lifetime| {
+            let s = format!("{}", lifetime);
+            if s.is_empty() {
+                "'_".to_string()
+            } else {
+                s
+            }
+        }).collect::<Vec<_>>().join(", ");
+        if !lifetimes.is_empty() {
+            if sub.regions().count() < len {
+                value.push_normal(lifetimes + &", ");
+            } else {
+                value.push_normal(lifetimes);
+            }
+        }
+
+        // Highlight all the type arguments that aren't at `pos` and compare the type argument at
+        // `pos` and `other_ty`.
+        for (i, type_arg) in sub.types().enumerate() {
+            if i == pos {
+                let values = self.cmp(type_arg, other_ty);
+                value.0.extend((values.0).0);
+                other_value.0.extend((values.1).0);
+            } else {
+                value.push_highlighted(format!("{}", type_arg));
+            }
+
+            if len > 0 && i != len - 1 {
+                value.push_normal(", ");
+            }
+            //self.push_comma(&mut value, &mut other_value, len, i);
+        }
+        if len > 0 {
+            value.push_highlighted(">");
+        }
+    }
+
+    /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`,
+    /// as that is the difference to the other type.
+    ///
+    /// For the following code:
+    ///
+    /// ```norun
+    /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>();
+    /// ```
+    ///
+    /// The type error output will behave in the following way:
+    ///
+    /// ```text
+    /// Foo<Bar<Qux>>
+    /// ^^^^--------^ this is highlighted
+    /// |   |
+    /// |   this type argument is exactly the same as the other type, not highlighted
+    /// this is highlighted
+    /// Bar<Qux>
+    /// -------- this type is the same as a type argument in the other type, not highlighted
+    /// ```
+    fn cmp_type_arg(&self,
+                    mut t1_out: &mut DiagnosticStyledString,
+                    mut t2_out: &mut DiagnosticStyledString,
+                    path: String,
+                    sub: &ty::subst::Substs<'tcx>,
+                    other_path: String,
+                    other_ty: &ty::Ty<'tcx>) -> Option<()> {
+        for (i, ta) in sub.types().enumerate() {
+            if &ta == other_ty {
+                self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
+                return Some(());
+            }
+            if let &ty::TyAdt(def, _) = &ta.sty {
+                let path_ = self.tcx.item_path_str(def.did.clone());
+                if path_ == other_path {
+                    self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, &other_ty);
+                    return Some(());
+                }
+            }
+        }
+        None
+    }
+
+    /// Add a `,` to the type representation only if it is appropriate.
+    fn push_comma(&self,
+                  value: &mut DiagnosticStyledString,
+                  other_value: &mut DiagnosticStyledString,
+                  len: usize,
+                  pos: usize) {
+        if len > 0 && pos != len - 1 {
+            value.push_normal(", ");
+            other_value.push_normal(", ");
+        }
+    }
+
+    /// Compare two given types, eliding parts that are the same between them and highlighting
+    /// relevant differences, and return two representation of those types for highlighted printing.
+    fn cmp(&self, t1: ty::Ty<'tcx>, t2: ty::Ty<'tcx>)
+        -> (DiagnosticStyledString, DiagnosticStyledString)
+    {
+        match (&t1.sty, &t2.sty) {
+            (&ty::TyAdt(def1, sub1), &ty::TyAdt(def2, sub2)) => {
+                let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new());
+                let path1 = self.tcx.item_path_str(def1.did.clone());
+                let path2 = self.tcx.item_path_str(def2.did.clone());
+                if def1.did == def2.did {
+                    // Easy case. Replace same types with `_` to shorten the output and highlight
+                    // the differing ones.
+                    //     let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>();
+                    //     Foo<Bar, _>
+                    //     Foo<Quz, _>
+                    //         ---  ^ type argument elided
+                    //         |
+                    //         highlighted in output
+                    values.0.push_normal(path1);
+                    values.1.push_normal(path2);
+
+                    // Only draw `<...>` if there're lifetime/type arguments.
+                    let len = sub1.len();
+                    if len > 0 {
+                        values.0.push_normal("<");
+                        values.1.push_normal("<");
+                    }
+
+                    fn lifetime_display(lifetime: &Region) -> String {
+                        let s = format!("{}", lifetime);
+                        if s.is_empty() {
+                            "'_".to_string()
+                        } else {
+                            s
+                        }
+                    }
+                    // At one point we'd like to elide all lifetimes here, they are irrelevant for
+                    // all diagnostics that use this output
+                    //
+                    //     Foo<'x, '_, Bar>
+                    //     Foo<'y, '_, Qux>
+                    //         ^^  ^^  --- type arguments are not elided
+                    //         |   |
+                    //         |   elided as they were the same
+                    //         not elided, they were different, but irrelevant
+                    let lifetimes = sub1.regions().zip(sub2.regions());
+                    for (i, lifetimes) in lifetimes.enumerate() {
+                        let l1 = lifetime_display(lifetimes.0);
+                        let l2 = lifetime_display(lifetimes.1);
+                        if l1 == l2 {
+                            values.0.push_normal("'_");
+                            values.1.push_normal("'_");
+                        } else {
+                            values.0.push_highlighted(l1);
+                            values.1.push_highlighted(l2);
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // We're comparing two types with the same path, so we compare the type
+                    // arguments for both. If they are the same, do not highlight and elide from the
+                    // output.
+                    //     Foo<_, Bar>
+                    //     Foo<_, Qux>
+                    //         ^ elided type as this type argument was the same in both sides
+                    let type_arguments = sub1.types().zip(sub2.types());
+                    let regions_len = sub1.regions().collect::<Vec<_>>().len();
+                    for (i, (ta1, ta2)) in type_arguments.enumerate() {
+                        let i = i + regions_len;
+                        if ta1 == ta2 {
+                            values.0.push_normal("_");
+                            values.1.push_normal("_");
+                        } else {
+                            let (x1, x2) = self.cmp(ta1, ta2);
+                            (values.0).0.extend(x1.0);
+                            (values.1).0.extend(x2.0);
+                        }
+                        self.push_comma(&mut values.0, &mut values.1, len, i);
+                    }
+
+                    // Close the type argument bracket.
+                    // Only draw `<...>` if there're lifetime/type arguments.
+                    if len > 0 {
+                        values.0.push_normal(">");
+                        values.1.push_normal(">");
+                    }
+                    values
+                } else {
+                    // Check for case:
+                    //     let x: Foo<Bar<Qux> = foo::<Bar<Qux>>();
+                    //     Foo<Bar<Qux>
+                    //         ------- this type argument is exactly the same as the other type
+                    //     Bar<Qux>
+                    if self.cmp_type_arg(&mut values.0,
+                                         &mut values.1,
+                                         path1.clone(),
+                                         sub1,
+                                         path2.clone(),
+                                         &t2).is_some() {
+                        return values;
+                    }
+                    // Check for case:
+                    //     let x: Bar<Qux> = y:<Foo<Bar<Qux>>>();
+                    //     Bar<Qux>
+                    //     Foo<Bar<Qux>>
+                    //         ------- this type argument is exactly the same as the other type
+                    if self.cmp_type_arg(&mut values.1,
+                                         &mut values.0,
+                                         path2,
+                                         sub2,
+                                         path1,
+                                         &t1).is_some() {
+                        return values;
+                    }
+
+                    // We couldn't find anything in common, highlight everything.
+                    //     let x: Bar<Qux> = y::<Foo<Zar>>();
+                    (DiagnosticStyledString::highlighted(format!("{}", t1)),
+                     DiagnosticStyledString::highlighted(format!("{}", t2)))
+                }
+            }
+            _ => {
+                if t1 == t2 {
+                    // The two types are the same, elide and don't highlight.
+                    (DiagnosticStyledString::normal("_"), DiagnosticStyledString::normal("_"))
+                } else {
+                    // We couldn't find anything in common, highlight everything.
+                    (DiagnosticStyledString::highlighted(format!("{}", t1)),
+                     DiagnosticStyledString::highlighted(format!("{}", t2)))
+                }
+            }
+        }
+    }
+
     pub fn note_type_err(&self,
                          diag: &mut DiagnosticBuilder<'tcx>,
                          cause: &ObligationCause<'tcx>,
@@ -397,14 +653,14 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
 
         if let Some((expected, found)) = expected_found {
             match (terr, is_simple_error, expected == found) {
-                (&TypeError::Sorts(ref values), false,  true) => {
+                (&TypeError::Sorts(ref values), false, true) => {
                     diag.note_expected_found_extra(
-                        &"type", &expected, &found,
+                        &"type", expected, found,
                         &format!(" ({})", values.expected.sort_string(self.tcx)),
                         &format!(" ({})", values.found.sort_string(self.tcx)));
                 }
                 (_, false,  _) => {
-                    diag.note_expected_found(&"type", &expected, &found);
+                    diag.note_expected_found(&"type", expected, found);
                 }
                 _ => (),
             }
@@ -472,26 +728,40 @@ 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)> {
+    fn values_str(&self, values: &ValuePairs<'tcx>)
+        -> Option<(DiagnosticStyledString, DiagnosticStyledString)>
+    {
         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<ty::Ty<'tcx>>)
+                             -> Option<(DiagnosticStyledString, DiagnosticStyledString)> {
+        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))
+    }
+
+    /// Returns a string of the form "expected `{}`, found `{}`".
     fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>(
         &self,
         exp_found: &ty::error::ExpectedFound<T>)
-        -> Option<(String, String)>
+        -> Option<(DiagnosticStyledString, DiagnosticStyledString)>
     {
         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((DiagnosticStyledString::highlighted(format!("{}", exp_found.expected)),
+              DiagnosticStyledString::highlighted(format!("{}", exp_found.found))))
     }
 
     fn report_generic_bound_failure(&self,
diff --git a/src/librustc/infer/error_reporting/note.rs b/src/librustc/infer/error_reporting/note.rs
index 8f8b2603dad..8b753e0d22b 100644
--- a/src/librustc/infer/error_reporting/note.rs
+++ b/src/librustc/infer/error_reporting/note.rs
@@ -20,6 +20,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.content();
+                    let found = found.content();
                     // FIXME: do we want a "the" here?
                     err.span_note(trace.cause.span,
                                   &format!("...so that {} (expected {}, found {})",
diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs
index 23f35d3bdd7..bbeb1f1fe5f 100644
--- a/src/librustc/ty/mod.rs
+++ b/src/librustc/ty/mod.rs
@@ -2241,7 +2241,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 1b77ead92de..9715ace3e2e 100644
--- a/src/librustc_errors/diagnostic.rs
+++ b/src/librustc_errors/diagnostic.rs
@@ -35,6 +35,46 @@ pub struct SubDiagnostic {
     pub render_span: Option<RenderSpan>,
 }
 
+#[derive(PartialEq, Eq)]
+pub struct DiagnosticStyledString(pub Vec<StringPart>);
+
+impl DiagnosticStyledString {
+    pub fn new() -> DiagnosticStyledString {
+        DiagnosticStyledString(vec![])
+    }
+    pub fn push_normal<S: Into<String>>(&mut self, t: S) {
+        self.0.push(StringPart::Normal(t.into()));
+    }
+    pub fn push_highlighted<S: Into<String>>(&mut self, t: S) {
+        self.0.push(StringPart::Highlighted(t.into()));
+    }
+    pub fn normal<S: Into<String>>(t: S) -> DiagnosticStyledString {
+        DiagnosticStyledString(vec![StringPart::Normal(t.into())])
+    }
+
+    pub fn highlighted<S: Into<String>>(t: S) -> DiagnosticStyledString {
+        DiagnosticStyledString(vec![StringPart::Highlighted(t.into())])
+    }
+
+    pub fn content(&self) -> String {
+        self.0.iter().map(|x| x.content()).collect::<String>()
+    }
+}
+
+#[derive(PartialEq, Eq)]
+pub enum StringPart {
+    Normal(String),
+    Highlighted(String),
+}
+
+impl StringPart {
+    pub fn content(&self) -> String {
+        match self {
+            &StringPart::Normal(ref s) | & StringPart::Highlighted(ref s) => s.to_owned()
+        }
+    }
+}
+
 impl Diagnostic {
     pub fn new(level: Level, message: &str) -> Self {
         Diagnostic::new_with_code(level, None, message)
@@ -81,8 +121,8 @@ impl Diagnostic {
 
     pub fn note_expected_found(&mut self,
                                label: &fmt::Display,
-                               expected: &fmt::Display,
-                               found: &fmt::Display)
+                               expected: DiagnosticStyledString,
+                               found: DiagnosticStyledString)
                                -> &mut Self
     {
         self.note_expected_found_extra(label, expected, found, &"", &"")
@@ -90,21 +130,29 @@ impl Diagnostic {
 
     pub fn note_expected_found_extra(&mut self,
                                      label: &fmt::Display,
-                                     expected: &fmt::Display,
-                                     found: &fmt::Display,
+                                     expected: DiagnosticStyledString,
+                                     found: DiagnosticStyledString,
                                      expected_extra: &fmt::Display,
                                      found_extra: &fmt::Display)
                                      -> &mut Self
     {
+        let mut msg: Vec<_> = vec![(format!("expected {} `", label), Style::NoStyle)];
+        msg.extend(expected.0.iter()
+                   .map(|x| match *x {
+                       StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
+                       StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
+                   }));
+        msg.push((format!("`{}\n", expected_extra), Style::NoStyle));
+        msg.push((format!("   found {} `", label), Style::NoStyle));
+        msg.extend(found.0.iter()
+                   .map(|x| match *x {
+                       StringPart::Normal(ref s) => (s.to_owned(), Style::NoStyle),
+                       StringPart::Highlighted(ref s) => (s.to_owned(), Style::Highlight),
+                   }));
+        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 7dfea6b8951..7b27f13951b 100644
--- a/src/librustc_errors/diagnostic_builder.rs
+++ b/src/librustc_errors/diagnostic_builder.rs
@@ -9,6 +9,8 @@
 // except according to those terms.
 
 use Diagnostic;
+use DiagnosticStyledString;
+
 use Level;
 use Handler;
 use std::fmt::{self, Debug};
@@ -115,14 +117,14 @@ impl<'a> DiagnosticBuilder<'a> {
 
     forward!(pub fn note_expected_found(&mut self,
                                         label: &fmt::Display,
-                                        expected: &fmt::Display,
-                                        found: &fmt::Display)
+                                        expected: DiagnosticStyledString,
+                                        found: DiagnosticStyledString)
                                         -> &mut Self);
 
     forward!(pub fn note_expected_found_extra(&mut self,
                                               label: &fmt::Display,
-                                              expected: &fmt::Display,
-                                              found: &fmt::Display,
+                                              expected: DiagnosticStyledString,
+                                              found: DiagnosticStyledString,
                                               expected_extra: &fmt::Display,
                                               found_extra: &fmt::Display)
                                               -> &mut Self);
diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs
index 2efdaa57fba..da29e354a70 100644
--- a/src/librustc_errors/lib.rs
+++ b/src/librustc_errors/lib.rs
@@ -203,7 +203,7 @@ impl error::Error for ExplicitBug {
     }
 }
 
-pub use diagnostic::{Diagnostic, SubDiagnostic};
+pub use diagnostic::{Diagnostic, SubDiagnostic, DiagnosticStyledString, StringPart};
 pub use diagnostic_builder::DiagnosticBuilder;
 
 /// A handler deals with errors; certain errors
diff --git a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr
index 6f42a9f679a..6956a043cc6 100644
--- a/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr
+++ b/src/test/ui/lifetime-errors/ex2a-push-one-existing-name.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 16 |     x.push(y);
    |            ^ lifetime mismatch
    |
-   = note: expected type `Ref<'a, i32>`
-              found type `Ref<'_, i32>`
+   = note: expected type `Ref<'a, _>`
+              found type `Ref<'_, _>`
 note: the anonymous lifetime #2 defined on the body at 15:51...
   --> $DIR/ex2a-push-one-existing-name.rs:15:52
    |
diff --git a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr
index edc1c2362de..990ae65ba98 100644
--- a/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr
+++ b/src/test/ui/lifetime-errors/ex2b-push-no-existing-names.stderr
@@ -4,8 +4,8 @@ error[E0308]: mismatched types
 16 |     x.push(y);
    |            ^ lifetime mismatch
    |
-   = note: expected type `Ref<'_, i32>`
-              found type `Ref<'_, i32>`
+   = note: expected type `Ref<'_, _>`
+              found type `Ref<'_, _>`
 note: the anonymous lifetime #3 defined on the body at 15:43...
   --> $DIR/ex2b-push-no-existing-names.rs:15:44
    |
diff --git a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr
index 755b71d4a1d..82f6c71ec1c 100644
--- a/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr
+++ b/src/test/ui/lifetime-errors/ex2c-push-inference-variable.stderr
@@ -27,7 +27,7 @@ note: but, the lifetime must be valid for the lifetime 'b as defined on the body
 17 | |     x.push(z);
 18 | | }
    | |_^ ...ending here
-note: ...so that expression is assignable (expected Ref<'b, i32>, found Ref<'_, i32>)
+note: ...so that expression is assignable (expected Ref<'b, _>, found Ref<'_, _>)
   --> $DIR/ex2c-push-inference-variable.rs:17:12
    |
 17 |     x.push(z);
diff --git a/src/test/ui/mismatched_types/abridged.rs b/src/test/ui/mismatched_types/abridged.rs
new file mode 100644
index 00000000000..c448ad955fa
--- /dev/null
+++ b/src/test/ui/mismatched_types/abridged.rs
@@ -0,0 +1,61 @@
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T1, T2> {
+    x: T1,
+    y: T2,
+}
+
+fn a() -> Foo {
+    Some(Foo { bar: 1 })
+}
+
+fn a2() -> Foo {
+    Ok(Foo { bar: 1})
+}
+
+fn b() -> Option<Foo> {
+    Foo { bar: 1 }
+}
+
+fn c() -> Result<Foo, Bar> {
+    Foo { bar: 1 }
+}
+
+fn d() -> X<X<String, String>, String> {
+    X {
+        x: X {
+            x: "".to_string(),
+            y: 2,
+        },
+        y: 3,
+    }
+}
+
+fn e() -> X<X<String, String>, 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 00000000000..c67c6113d17
--- /dev/null
+++ b/src/test/ui/mismatched_types/abridged.stderr
@@ -0,0 +1,70 @@
+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<Foo>`
+
+error[E0308]: mismatched types
+  --> $DIR/abridged.rs:30:5
+   |
+30 |     Ok(Foo { bar: 1})
+   |     ^^^^^^^^^^^^^^^^^ expected struct `Foo`, found enum `std::result::Result`
+   |
+   = note: expected type `Foo`
+              found type `std::result::Result<Foo, _>`
+
+error[E0308]: mismatched types
+  --> $DIR/abridged.rs:34:5
+   |
+34 |     Foo { bar: 1 }
+   |     ^^^^^^^^^^^^^^ expected enum `std::option::Option`, found struct `Foo`
+   |
+   = note: expected type `std::option::Option<Foo>`
+              found type `Foo`
+
+error[E0308]: mismatched types
+  --> $DIR/abridged.rs:38:5
+   |
+38 |     Foo { bar: 1 }
+   |     ^^^^^^^^^^^^^^ expected enum `std::result::Result`, found struct `Foo`
+   |
+   = note: expected type `std::result::Result<Foo, Bar>`
+              found type `Foo`
+
+error[E0308]: mismatched types
+  --> $DIR/abridged.rs:42:5
+   |
+42 |       X {
+   |  _____^ starting here...
+43 | |         x: X {
+44 | |             x: "".to_string(),
+45 | |             y: 2,
+46 | |         },
+47 | |         y: 3,
+48 | |     }
+   | |_____^ ...ending here: expected struct `std::string::String`, found integral variable
+   |
+   = note: expected type `X<X<_, std::string::String>, std::string::String>`
+              found type `X<X<_, {integer}>, {integer}>`
+
+error[E0308]: mismatched types
+  --> $DIR/abridged.rs:52:5
+   |
+52 |       X {
+   |  _____^ starting here...
+53 | |         x: X {
+54 | |             x: "".to_string(),
+55 | |             y: 2,
+56 | |         },
+57 | |         y: "".to_string(),
+58 | |     }
+   | |_____^ ...ending here: expected struct `std::string::String`, found integral variable
+   |
+   = note: expected type `X<X<_, std::string::String>, _>`
+              found type `X<X<_, {integer}>, _>`
+
+error: aborting due to 6 previous errors
+