Repository: rust-analyzer/rust-analyzer (rust)

Issue:
Wrong type inference in if and match with return statement
```rust
fn foo(x: Option<i32>) {
    let _x = if let Some(y) = x {
        y
    } else {
        return;
    };
}
```

Here, `_x` is inferred as `()` instead of `i32`. Also works with `if x.is_some() { 3 } else { return; }`.

CC #1505, #1680, @SomeoneToIgnore.

Pull request description:
Handle divergence in type inference for blocks
Fixes #1944.

The `infer_basics` test is failing, not sure what to do about it.

Patch:
diff --git a/crates/ra_hir/src/ty/infer.rs b/crates/ra_hir/src/ty/infer.rs
index 2e4a489a0d..ca9aefc42f 100644
--- a/crates/ra_hir/src/ty/infer.rs
+++ b/crates/ra_hir/src/ty/infer.rs
@@ -1602,6 +1602,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
         tail: Option<ExprId>,
         expected: &Expectation,
     ) -> Ty {
+        let mut diverges = false;
         for stmt in statements {
             match stmt {
                 Statement::Let { pat, type_ref, initializer } => {
@@ -1623,16 +1624,23 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
                     self.infer_pat(*pat, &ty, BindingMode::default());
                 }
                 Statement::Expr(expr) => {
-                    self.infer_expr(*expr, &Expectation::none());
+                    if let ty_app!(TypeCtor::Never) = self.infer_expr(*expr, &Expectation::none()) {
+                        diverges = true;
+                    }
                 }
             }
         }
 
-        if let Some(expr) = tail {
+        let ty = if let Some(expr) = tail {
             self.infer_expr_coerce(expr, expected)
         } else {
             self.coerce(&Ty::unit(), &expected.ty);
             Ty::unit()
+        };
+        if diverges {
+            Ty::simple(TypeCtor::Never)
+        } else {
+            ty
         }
     }
 


Test patch:
diff --git a/crates/ra_hir/src/ty/tests.rs b/crates/ra_hir/src/ty/tests.rs
index 4df39c1915..66f63ca240 100644
--- a/crates/ra_hir/src/ty/tests.rs
+++ b/crates/ra_hir/src/ty/tests.rs
@@ -218,7 +218,7 @@ fn test(a: u32, b: isize, c: !, d: &str) {
     [17; 18) 'b': isize
     [27; 28) 'c': !
     [33; 34) 'd': &str
-    [42; 121) '{     ...f32; }': ()
+    [42; 121) '{     ...f32; }': !
     [48; 49) 'a': u32
     [55; 56) 'b': isize
     [62; 63) 'c': !
@@ -980,6 +980,67 @@ fn main(foo: Foo) {
     )
 }
 
+#[test]
+fn infer_if_match_with_return() {
+    assert_snapshot!(
+        infer(r#"
+fn foo() {
+    let _x1 = if true {
+        1
+    } else {
+        return;
+    };
+    let _x2 = if true {
+        2
+    } else {
+        return
+    };
+    let _x3 = match true {
+        true => 3,
+        _ => {
+            return;
+        }
+    };
+    let _x4 = match true {
+        true => 4,
+        _ => return
+    };
+}"#),
+        @r###"
+    [10; 323) '{     ...  }; }': ()
+    [20; 23) '_x1': i32
+    [26; 80) 'if tru...     }': i32
+    [29; 33) 'true': bool
+    [34; 51) '{     ...     }': i32
+    [44; 45) '1': i32
+    [57; 80) '{     ...     }': !
+    [67; 73) 'return': !
+    [90; 93) '_x2': i32
+    [96; 149) 'if tru...     }': i32
+    [99; 103) 'true': bool
+    [104; 121) '{     ...     }': i32
+    [114; 115) '2': i32
+    [127; 149) '{     ...     }': !
+    [137; 143) 'return': !
+    [159; 162) '_x3': i32
+    [165; 247) 'match ...     }': i32
+    [171; 175) 'true': bool
+    [186; 190) 'true': bool
+    [194; 195) '3': i32
+    [205; 206) '_': bool
+    [210; 241) '{     ...     }': !
+    [224; 230) 'return': !
+    [257; 260) '_x4': i32
+    [263; 320) 'match ...     }': i32
+    [269; 273) 'true': bool
+    [284; 288) 'true': bool
+    [292; 293) '4': i32
+    [303; 304) '_': bool
+    [308; 314) 'return': !
+    "###
+    )
+}
+
 #[test]
 fn infer_inherent_method() {
     assert_snapshot!(