]> git.lizzy.rs Git - rust.git/commitdiff
Point at appropriate arm on type error on if/else/match with one non-! arm
authorEsteban Küber <esteban@kuber.com.ar>
Mon, 2 Sep 2019 03:13:59 +0000 (20:13 -0700)
committerEsteban Küber <esteban@kuber.com.ar>
Mon, 2 Sep 2019 03:15:49 +0000 (20:15 -0700)
src/librustc_typeck/check/mod.rs
src/test/ui/point-to-type-err-cause-on-impl-trait-return.rs
src/test/ui/point-to-type-err-cause-on-impl-trait-return.stderr

index a80550486d627707c735af1f02f7a66014806e94..4a0b3879cb99124bceb5cca3d85ef110549bfa49 100644 (file)
@@ -3687,6 +3687,40 @@ pub fn check_block_no_value(&self, blk: &'tcx hir::Block) {
         }
     }
 
+    /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail
+    /// expression's `Span`, otherwise return `expr.span`. This is done to give bettern errors
+    /// when given code like the following:
+    /// ```text
+    /// if false { return 0i32; } else { 1u32 }
+    /// //                               ^^^^ point at this instead of the whole `if` expression
+    /// ```
+    fn get_expr_coercion_span(&self, expr: &hir::Expr) -> syntax_pos::Span {
+        if let hir::ExprKind::Match(_, arms, _) = &expr.node {
+            let arm_spans: Vec<Span> = arms.iter().filter_map(|arm| {
+                self.in_progress_tables
+                    .and_then(|tables| tables.borrow().node_type_opt(arm.body.hir_id))
+                    .and_then(|arm_ty| {
+                        if arm_ty.is_never() {
+                            None
+                        } else {
+                            Some(match &arm.body.node {
+                                // Point at the tail expression when possible.
+                                hir::ExprKind::Block(block, _) => block.expr
+                                    .as_ref()
+                                    .map(|e| e.span)
+                                    .unwrap_or(block.span),
+                                _ => arm.body.span,
+                            })
+                        }
+                    })
+            }).collect();
+            if arm_spans.len() == 1 {
+                return arm_spans[0];
+            }
+        }
+        expr.span
+    }
+
     fn check_block_with_expected(
         &self,
         blk: &'tcx hir::Block,
@@ -3746,12 +3780,9 @@ fn check_block_with_expected(
             let coerce = ctxt.coerce.as_mut().unwrap();
             if let Some(tail_expr_ty) = tail_expr_ty {
                 let tail_expr = tail_expr.unwrap();
-                let cause = self.cause(tail_expr.span,
-                                       ObligationCauseCode::BlockTailExpression(blk.hir_id));
-                coerce.coerce(self,
-                              &cause,
-                              tail_expr,
-                              tail_expr_ty);
+                let span = self.get_expr_coercion_span(tail_expr);
+                let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id));
+                coerce.coerce(self, &cause, tail_expr, tail_expr_ty);
             } else {
                 // Subtle: if there is no explicit tail expression,
                 // that is typically equivalent to a tail expression
index 95b40368143efd6da0d1ec9be7c02153015ba075..d416db628c03f5f969868a7f1caff7b951bfdaa7 100644 (file)
@@ -17,10 +17,10 @@ fn bar() -> impl std::fmt::Display {
 
 fn baz() -> impl std::fmt::Display {
     if false {
-    //~^ ERROR mismatched types
         return 0i32;
     } else {
         1u32
+        //~^ ERROR mismatched types
     }
 }
 
index ee1e36081e778c056cbf8d4f185354bc58164237..47644d66d1a2c902cdd4a5bb88bf367123ceb30c 100644 (file)
@@ -29,18 +29,16 @@ LL |         return 1u32;
               found type `u32`
 
 error[E0308]: mismatched types
-  --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:19:5
+  --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:22:9
    |
-LL |   fn baz() -> impl std::fmt::Display {
-   |               ---------------------- expected because this return type...
-LL | /     if false {
-LL | |
-LL | |         return 0i32;
-   | |                ---- ...is found to be `i32` here
-LL | |     } else {
-LL | |         1u32
-LL | |     }
-   | |_____^ expected i32, found u32
+LL | fn baz() -> impl std::fmt::Display {
+   |             ---------------------- expected because this return type...
+LL |     if false {
+LL |         return 0i32;
+   |                ---- ...is found to be `i32` here
+LL |     } else {
+LL |         1u32
+   |         ^^^^ expected i32, found u32
    |
    = note: expected type `i32`
               found type `u32`