about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/infer/error_reporting/mod.rs8
-rw-r--r--src/librustc/traits/error_reporting.rs2
-rw-r--r--src/librustc/traits/mod.rs5
-rw-r--r--src/librustc/traits/structural_impls.rs2
-rw-r--r--src/librustc_typeck/check/mod.rs88
-rw-r--r--src/test/ui/if-else-type-mismatch.rs34
-rw-r--r--src/test/ui/if-else-type-mismatch.stderr92
-rw-r--r--src/test/ui/if/if-branch-types.stderr6
-rw-r--r--src/test/ui/regions/region-invariant-static-error-reporting.stderr8
-rw-r--r--src/test/ui/str/str-array-assignment.stderr6
10 files changed, 238 insertions, 13 deletions
diff --git a/src/librustc/infer/error_reporting/mod.rs b/src/librustc/infer/error_reporting/mod.rs
index f71cce8273c..d61b37ef7c8 100644
--- a/src/librustc/infer/error_reporting/mod.rs
+++ b/src/librustc/infer/error_reporting/mod.rs
@@ -511,6 +511,10 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
                     }
                 }
             },
+            ObligationCauseCode::IfExpression { then, outer } => {
+                err.span_label(then, "expected because of this");
+                outer.map(|sp| err.span_label(sp, "if and else have incompatible types"));
+            }
             _ => (),
         }
     }
@@ -1460,7 +1464,7 @@ impl<'tcx> ObligationCause<'tcx> {
                 }
                 _ => "match arms have incompatible types",
             }),
-            IfExpression => Error0308("if and else have incompatible types"),
+            IfExpression { .. } => Error0308("if and else have incompatible types"),
             IfExpressionWithNoElse => Error0317("if may be missing an else clause"),
             MainFunctionType => Error0580("main function has wrong type"),
             StartFunctionType => Error0308("start function has wrong type"),
@@ -1488,7 +1492,7 @@ impl<'tcx> ObligationCause<'tcx> {
                 hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",
                 _ => "match arms have compatible types",
             },
-            IfExpression => "if and else have compatible types",
+            IfExpression { .. } => "if and else have compatible types",
             IfExpressionWithNoElse => "if missing an else returns ()",
             MainFunctionType => "`main` function has the correct type",
             StartFunctionType => "`start` function has the correct type",
diff --git a/src/librustc/traits/error_reporting.rs b/src/librustc/traits/error_reporting.rs
index 7d9e80fd60f..367a7eacdfc 100644
--- a/src/librustc/traits/error_reporting.rs
+++ b/src/librustc/traits/error_reporting.rs
@@ -1445,7 +1445,7 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
             ObligationCauseCode::ExprAssignable |
             ObligationCauseCode::MatchExpressionArm { .. } |
             ObligationCauseCode::MatchExpressionArmPattern { .. } |
-            ObligationCauseCode::IfExpression |
+            ObligationCauseCode::IfExpression { .. } |
             ObligationCauseCode::IfExpressionWithNoElse |
             ObligationCauseCode::MainFunctionType |
             ObligationCauseCode::StartFunctionType |
diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs
index 14c25b77a1b..3f3f489013c 100644
--- a/src/librustc/traits/mod.rs
+++ b/src/librustc/traits/mod.rs
@@ -229,7 +229,10 @@ pub enum ObligationCauseCode<'tcx> {
     MatchExpressionArmPattern { span: Span, ty: Ty<'tcx> },
 
     /// Computing common supertype in an if expression
-    IfExpression,
+    IfExpression {
+        then: Span,
+        outer: Option<Span>,
+    },
 
     /// Computing common supertype of an if expression with no else counter-part
     IfExpressionWithNoElse,
diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs
index 277e2ed0e87..a1dd99457d1 100644
--- a/src/librustc/traits/structural_impls.rs
+++ b/src/librustc/traits/structural_impls.rs
@@ -520,7 +520,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
             super::MatchExpressionArmPattern { span, ty } => {
                 tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
             }
-            super::IfExpression => Some(super::IfExpression),
+            super::IfExpression { then, outer } => Some(super::IfExpression { then, outer }),
             super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
             super::MainFunctionType => Some(super::MainFunctionType),
             super::StartFunctionType => Some(super::StartFunctionType),
diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs
index dbbb7f42feb..9939c2a12b8 100644
--- a/src/librustc_typeck/check/mod.rs
+++ b/src/librustc_typeck/check/mod.rs
@@ -3366,7 +3366,93 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
         let coerce_to_ty = expected.coercion_target_type(self, sp);
         let mut coerce: DynamicCoerceMany = CoerceMany::new(coerce_to_ty);
 
-        let if_cause = self.cause(sp, ObligationCauseCode::IfExpression);
+        let mut outer_sp = if self.tcx.sess.source_map().is_multiline(sp) {
+            // The `if`/`else` isn't in one line in the output, include some context to make it
+            // clear it is an if/else expression:
+            // ```
+            // LL |      let x = if true {
+            //    | _____________-
+            // LL ||         10i32
+            //    ||         ----- expected because of this
+            // LL ||     } else {
+            // LL ||         10u32
+            //    ||         ^^^^^ expected i32, found u32
+            // LL ||     };
+            //    ||_____- if and else have incompatible types
+            // ```
+            Some(sp)
+        } else {
+            // The entire expression is in one line, only point at the arms
+            // ```
+            // LL |     let x = if true { 10i32 } else { 10u32 };
+            //    |                       -----          ^^^^^ expected i32, found u32
+            //    |                       |
+            //    |                       expected because of this
+            // ```
+            None
+        };
+        let error_sp = opt_else_expr.map(|expr| {
+            if let ExprKind::Block(block, _) = &expr.node {
+                if let Some(expr) = &block.expr {
+                    expr.span
+                } else if let Some(stmt) = block.stmts.last() {
+                    // possibly incorrect trailing `;` in the else arm
+                    stmt.span
+                } else {  // empty block, point at its entirety
+                    // Avoid overlapping spans that aren't as readable:
+                    // ```
+                    // 2 |        let x = if true {
+                    //   |   _____________-
+                    // 3 |  |         3
+                    //   |  |         - expected because of this
+                    // 4 |  |     } else {
+                    //   |  |____________^
+                    // 5 | ||
+                    // 6 | ||     };
+                    //   | ||     ^
+                    //   | ||_____|
+                    //   | |______if and else have incompatible types
+                    //   |        expected integer, found ()
+                    // ```
+                    // by not pointing at the entire expression:
+                    // ```
+                    // 2 |       let x = if true {
+                    //   |               ------- if and else have incompatible types
+                    // 3 |           3
+                    //   |           - expected because of this
+                    // 4 |       } else {
+                    //   |  ____________^
+                    // 5 | |
+                    // 6 | |     };
+                    //   | |_____^ expected integer, found ()
+                    // ```
+                    if outer_sp.is_some() {
+                        outer_sp = Some(self.tcx.sess.source_map().def_span(sp));
+                    }
+                    expr.span
+                }
+            } else { // shouldn't happen unless the parser has done something weird
+                expr.span
+            }
+        }).unwrap_or(sp);  // shouldn't be needed
+        let then_sp = if let ExprKind::Block(block, _) = &then_expr.node {
+            if let Some(expr) = &block.expr {
+                expr.span
+            } else if let Some(stmt) = block.stmts.last() {
+                // possibly incorrect trailing `;` in the else arm
+                stmt.span
+            } else {  // empty block, point at its entirety
+                outer_sp = None;  // same as in `error_sp`, cleanup output
+                then_expr.span
+            }
+        } else {  // shouldn't happen unless the parser has done something weird
+            then_expr.span
+        };
+
+        let if_cause = self.cause(error_sp, ObligationCauseCode::IfExpression {
+            then: then_sp,
+            outer: outer_sp,
+        });
         coerce.coerce(self, &if_cause, then_expr, then_ty);
 
         if let Some(else_expr) = opt_else_expr {
diff --git a/src/test/ui/if-else-type-mismatch.rs b/src/test/ui/if-else-type-mismatch.rs
new file mode 100644
index 00000000000..37955a29d24
--- /dev/null
+++ b/src/test/ui/if-else-type-mismatch.rs
@@ -0,0 +1,34 @@
+fn main() {
+    let _ = if true {
+        42i32
+    } else {
+        42u32
+    };
+    //~^^ ERROR if and else have incompatible types
+    let _ = if true { 42i32 } else { 42u32 };
+    //~^ ERROR if and else have incompatible types
+    let _ = if true {
+        42i32;
+    } else {
+        42u32
+    };
+    //~^^ ERROR if and else have incompatible types
+    let _ = if true {
+        42i32
+    } else {
+        42u32;
+    };
+    //~^^ ERROR if and else have incompatible types
+    let _ = if true {
+
+    } else {
+        42u32
+    };
+    //~^^ ERROR if and else have incompatible types
+    let _ = if true {
+        42i32
+    } else {
+
+    };
+    //~^^^ ERROR if and else have incompatible types
+}
diff --git a/src/test/ui/if-else-type-mismatch.stderr b/src/test/ui/if-else-type-mismatch.stderr
new file mode 100644
index 00000000000..d20edc57cbd
--- /dev/null
+++ b/src/test/ui/if-else-type-mismatch.stderr
@@ -0,0 +1,92 @@
+error[E0308]: if and else have incompatible types
+  --> $DIR/if-else-type-mismatch.rs:5:9
+   |
+LL |       let _ = if true {
+   |  _____________-
+LL | |         42i32
+   | |         ----- expected because of this
+LL | |     } else {
+LL | |         42u32
+   | |         ^^^^^ expected i32, found u32
+LL | |     };
+   | |_____- if and else have incompatible types
+   |
+   = note: expected type `i32`
+              found type `u32`
+
+error[E0308]: if and else have incompatible types
+  --> $DIR/if-else-type-mismatch.rs:8:38
+   |
+LL |     let _ = if true { 42i32 } else { 42u32 };
+   |                       -----          ^^^^^ expected i32, found u32
+   |                       |
+   |                       expected because of this
+   |
+   = note: expected type `i32`
+              found type `u32`
+
+error[E0308]: if and else have incompatible types
+  --> $DIR/if-else-type-mismatch.rs:13:9
+   |
+LL |       let _ = if true {
+   |  _____________-
+LL | |         42i32;
+   | |         ------ expected because of this
+LL | |     } else {
+LL | |         42u32
+   | |         ^^^^^ expected (), found u32
+LL | |     };
+   | |_____- if and else have incompatible types
+   |
+   = note: expected type `()`
+              found type `u32`
+
+error[E0308]: if and else have incompatible types
+  --> $DIR/if-else-type-mismatch.rs:19:9
+   |
+LL |       let _ = if true {
+   |  _____________-
+LL | |         42i32
+   | |         ----- expected because of this
+LL | |     } else {
+LL | |         42u32;
+   | |         ^^^^^^ expected i32, found ()
+LL | |     };
+   | |_____- if and else have incompatible types
+   |
+   = note: expected type `i32`
+              found type `()`
+
+error[E0308]: if and else have incompatible types
+  --> $DIR/if-else-type-mismatch.rs:25:9
+   |
+LL |       let _ = if true {
+   |  _____________________-
+LL | |
+LL | |     } else {
+   | |_____- expected because of this
+LL |           42u32
+   |           ^^^^^ expected (), found u32
+   |
+   = note: expected type `()`
+              found type `u32`
+
+error[E0308]: if and else have incompatible types
+  --> $DIR/if-else-type-mismatch.rs:30:12
+   |
+LL |       let _ = if true {
+   |               ------- if and else have incompatible types
+LL |           42i32
+   |           ----- expected because of this
+LL |       } else {
+   |  ____________^
+LL | |
+LL | |     };
+   | |_____^ expected i32, found ()
+   |
+   = note: expected type `i32`
+              found type `()`
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
diff --git a/src/test/ui/if/if-branch-types.stderr b/src/test/ui/if/if-branch-types.stderr
index 44e172da783..74b925f72ff 100644
--- a/src/test/ui/if/if-branch-types.stderr
+++ b/src/test/ui/if/if-branch-types.stderr
@@ -1,8 +1,10 @@
 error[E0308]: if and else have incompatible types
-  --> $DIR/if-branch-types.rs:2:13
+  --> $DIR/if-branch-types.rs:2:38
    |
 LL |     let x = if true { 10i32 } else { 10u32 };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found u32
+   |                       -----          ^^^^^ expected i32, found u32
+   |                       |
+   |                       expected because of this
    |
    = note: expected type `i32`
               found type `u32`
diff --git a/src/test/ui/regions/region-invariant-static-error-reporting.stderr b/src/test/ui/regions/region-invariant-static-error-reporting.stderr
index 40a3e7e9648..60e70ddcd97 100644
--- a/src/test/ui/regions/region-invariant-static-error-reporting.stderr
+++ b/src/test/ui/regions/region-invariant-static-error-reporting.stderr
@@ -1,13 +1,15 @@
 error[E0308]: if and else have incompatible types
-  --> $DIR/region-invariant-static-error-reporting.rs:14:15
+  --> $DIR/region-invariant-static-error-reporting.rs:17:9
    |
 LL |       let bad = if x.is_some() {
-   |  _______________^
+   |  _______________-
 LL | |         x.unwrap()
+   | |         ---------- expected because of this
 LL | |     } else {
 LL | |         mk_static()
+   | |         ^^^^^^^^^^^ lifetime mismatch
 LL | |     };
-   | |_____^ lifetime mismatch
+   | |_____- if and else have incompatible types
    |
    = note: expected type `Invariant<'a>`
               found type `Invariant<'static>`
diff --git a/src/test/ui/str/str-array-assignment.stderr b/src/test/ui/str/str-array-assignment.stderr
index 24d8db481b4..87809d212d7 100644
--- a/src/test/ui/str/str-array-assignment.stderr
+++ b/src/test/ui/str/str-array-assignment.stderr
@@ -1,8 +1,10 @@
 error[E0308]: if and else have incompatible types
-  --> $DIR/str-array-assignment.rs:3:11
+  --> $DIR/str-array-assignment.rs:3:37
    |
 LL |   let t = if true { s[..2] } else { s };
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected str, found &str
+   |                     ------          ^ expected str, found &str
+   |                     |
+   |                     expected because of this
    |
    = note: expected type `str`
               found type `&str`