about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2019-09-25 23:01:01 -0700
committerEsteban Küber <esteban@kuber.com.ar>2019-09-27 09:34:51 -0700
commit46a38dc183d2f79b764b2cca0011d0843f0fffcb (patch)
tree4bd1f375d6a1c3c99cedc1309da696af25d37649
parentfaee8e1756dbdfbe0752a8006e952395c0cd7a67 (diff)
downloadrust-46a38dc183d2f79b764b2cca0011d0843f0fffcb.tar.gz
rust-46a38dc183d2f79b764b2cca0011d0843f0fffcb.zip
Account for tail expressions when pointing at return type
When there's a type mismatch we make an effort to check if it was
caused by a function's return type. This logic now makes sure to
only point at the return type if the error happens in a tail
expression.
-rw-r--r--src/librustc/hir/map/mod.rs25
-rw-r--r--src/librustc/hir/mod.rs2
-rw-r--r--src/librustc_typeck/check/expr.rs8
-rw-r--r--src/test/ui/struct-literal-variant-in-if.stderr3
4 files changed, 31 insertions, 7 deletions
diff --git a/src/librustc/hir/map/mod.rs b/src/librustc/hir/map/mod.rs
index ca854395d7b..9854d8b04fd 100644
--- a/src/librustc/hir/map/mod.rs
+++ b/src/librustc/hir/map/mod.rs
@@ -741,7 +741,28 @@ impl<'hir> Map<'hir> {
     /// }
     /// ```
     pub fn get_return_block(&self, id: HirId) -> Option<HirId> {
-        for (hir_id, node) in ParentHirIterator::new(id, &self) {
+        let mut iter = ParentHirIterator::new(id, &self).peekable();
+        let mut ignore_tail = false;
+        if let Some(entry) = self.find_entry(id) {
+            if let Node::Expr(Expr { node: ExprKind::Ret(_), .. }) = entry.node {
+                // When dealing with `return` statements, we don't care about climbing only tail
+                // expressions.
+                ignore_tail = true;
+            }
+        }
+        while let Some((hir_id, node)) = iter.next() {
+            if let (Some((_, next_node)), false) = (iter.peek(), ignore_tail) {
+                match next_node {
+                    Node::Block(Block { expr: None, .. }) => return None,
+                    Node::Block(Block { expr: Some(expr), .. }) => {
+                        if hir_id != expr.hir_id {
+                            // The current node is not the tail expression of its parent.
+                            return None;
+                        }
+                    }
+                    _ => {}
+                }
+            }
             match node {
                 Node::Item(_) |
                 Node::ForeignItem(_) |
@@ -750,10 +771,12 @@ impl<'hir> Map<'hir> {
                 Node::ImplItem(_) => return Some(hir_id),
                 Node::Expr(ref expr) => {
                     match expr.kind {
+                        // Ignore `return`s on the first iteration
                         ExprKind::Loop(..) | ExprKind::Ret(..) => return None,
                         _ => {}
                     }
                 }
+                Node::Local(_) => return None,
                 _ => {}
             }
         }
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index 01cb5cc9bc1..0b1fcd0da3a 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -1563,7 +1563,7 @@ pub enum ExprKind {
     /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
     /// `ExprKind::MethodCall(PathSegment { foo, [Bar, Baz] }, [x, a, b, c, d])`.
     MethodCall(P<PathSegment>, Span, HirVec<Expr>),
-    /// A tuple (e.g., `(a, b, c ,d)`).
+    /// A tuple (e.g., `(a, b, c, d)`).
     Tup(HirVec<Expr>),
     /// A binary operation (e.g., `a + b`, `a * b`).
     Binary(BinOp, P<Expr>, P<Expr>),
diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs
index 58f41ca4f88..317d829d135 100644
--- a/src/librustc_typeck/check/expr.rs
+++ b/src/librustc_typeck/check/expr.rs
@@ -620,8 +620,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         expr: &'tcx hir::Expr
     ) -> Ty<'tcx> {
         if self.ret_coercion.is_none() {
-            struct_span_err!(self.tcx.sess, expr.span, E0572,
-                                "return statement outside of function body").emit();
+            struct_span_err!(
+                self.tcx.sess,
+                expr.span,
+                E0572,
+                "return statement outside of function body",
+            ).emit();
         } else if let Some(ref e) = expr_opt {
             if self.ret_coercion_span.borrow().is_none() {
                 *self.ret_coercion_span.borrow_mut() = Some(e.span);
diff --git a/src/test/ui/struct-literal-variant-in-if.stderr b/src/test/ui/struct-literal-variant-in-if.stderr
index f91b9d7dce6..85cbc787bc2 100644
--- a/src/test/ui/struct-literal-variant-in-if.stderr
+++ b/src/test/ui/struct-literal-variant-in-if.stderr
@@ -49,9 +49,6 @@ LL |     if x == E::V { field } {}
 error[E0308]: mismatched types
   --> $DIR/struct-literal-variant-in-if.rs:10:20
    |
-LL | fn test_E(x: E) {
-   |                 - help: try adding a return type: `-> bool`
-LL |     let field = true;
 LL |     if x == E::V { field } {}
    |                    ^^^^^ expected (), found bool
    |