Repository: intellij-rust/intellij-rust
Issue: false positive: use of moved value

<!--
Hello and thank you for the issue!
If you would like to report a bug, we have added some points below that you can fill out.
Feel free to remove all the irrelevant text to request a new feature.
-->

## Environment

* **Intellij-Rust plugin version:** 0.2.0.2114-183
* **Rust toolchain version:** 1.31.1
* **IDE name and version:** IDEA Community 2018.3.3
* **Operating system:** Ubuntu 18.04

## Problem description
Displays false positive "Use of moved value", where cargo build works without warnings. See simplified demo code.

## Steps to reproduce

```rust
fn demo() -> Result<Vec<i32>, io::Error> {
    let vec = vec![1,2,4,5];

    let res : Result<Vec<i32>, io::Error> = Ok(vec);
    
    if let Ok(mut vec) = res {
        vec.sort();
        Ok(vec)
    } else {
        res //<-- false positive here
    }
}
```
<!--
Please include as much of your codebase as needed to reproduce the error.
If the relevant files are large, please provide a link to a public repository or a [Gist](https://gist.github.com/).
-->


Changed files:
src/test/kotlin/org/rust/lang/core/cfg/RsControlFlowGraphTest.kt, src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt, src/main/kotlin/org/rust/lang/core/cfg/CFGBuilder.kt

diff --git a/src/main/kotlin/org/rust/lang/core/cfg/CFGBuilder.kt b/src/main/kotlin/org/rust/lang/core/cfg/CFGBuilder.kt
index 278e2f53c..24ea53d75 100644
--- a/src/main/kotlin/org/rust/lang/core/cfg/CFGBuilder.kt
+++ b/src/main/kotlin/org/rust/lang/core/cfg/CFGBuilder.kt
@@ -174,21 +174,30 @@ class CFGBuilder(val graph: Graph<CFGNodeData, CFGEdgeData>, val entry: CFGNode,
         //        |                  |
         //       / \\                / \\
         //      /   \\              /   \\
-        //     v 2   v 3          v 2   *
+        //     v 2   *            v 2   *
+        //   [pat]?  |          [pat]?  |
+        //     |     |            |     |
+        //     v     v 3          v     |
         //   [then][else]       [then]  |
         //     |     |            |     |
         //     v 4   v 5          v 3   v 4
         //     [ifExpr]          [ifExpr]
         //
-        val conditionExit = process(ifExpr.condition, pred)
-        val thenExit = process(ifExpr.block, conditionExit)
+        val pat = ifExpr.condition?.pat
+        val expr = ifExpr.condition?.expr
+
+        val exprExit = process(expr, pred)
+        val patExit = process(pat, exprExit)
+
+        val thenExit = process(ifExpr.block, patExit)
+
         val elseBranch = ifExpr.elseBranch
 
         if (elseBranch != null) {
-            val elseExit = process(elseBranch.block, conditionExit)
+            val elseExit = process(elseBranch.block, exprExit)
             finishWith { addAstNode(ifExpr, thenExit, elseExit) }
         } else {
-            finishWith { addAstNode(ifExpr, conditionExit, thenExit) }
+            finishWith { addAstNode(ifExpr, exprExit, thenExit) }
         }
     }
 
@@ -202,24 +211,31 @@ class CFGBuilder(val graph: Graph<CFGNodeData, CFGEdgeData>, val entry: CFGNode,
         //           v 2       |
         //   +-----[cond]      |
         //   |       |         |
+        //   |       v         |
+        //   |      [pat]?     |
+        //   |       |         |
         //   |       v 4       |
         //   |     [body] -----+
         //   v 3
         // [whileExpr]
         //
         val loopBack = addDummyNode(pred)
-        val exprExit = addAstNode(whileExpr)
-        val loopScope = LoopScope(whileExpr, loopBack, exprExit)
+        val whileExprExit = addAstNode(whileExpr)
+        val loopScope = LoopScope(whileExpr, loopBack, whileExprExit)
 
         withLoopScope(loopScope) {
-            val conditionExit = process(whileExpr.condition, loopBack)
-            addContainedEdge(conditionExit, exprExit)
+            val pat = whileExpr.condition?.pat
+            val expr = whileExpr.condition?.expr
+
+            val exprExit = process(expr, loopBack)
+            addContainedEdge(exprExit, whileExprExit)
 
-            val bodyExit = process(whileExpr.block, conditionExit)
+            val patExit = process(pat, exprExit)
+            val bodyExit = process(whileExpr.block, patExit)
             addContainedEdge(bodyExit, loopBack)
         }
 
-        finishWith(exprExit)
+        finishWith(whileExprExit)
     }
 
     override fun visitLoopExpr(loopExpr: RsLoopExpr) {
@@ -246,12 +262,6 @@ class CFGBuilder(val graph: Graph<CFGNodeData, CFGEdgeData>, val entry: CFGNode,
         finishWith(exprExit)
     }
 
-    override fun visitCondition(condition: RsCondition) {
-        val initExit = process(condition.expr, pred)
-        val exit = process(condition.pat, initExit)
-        finishWith(exit)
-    }
-
     override fun visitForExpr(forExpr: RsForExpr) {
         val loopBack = addDummyNode(pred)
         val exprExit = addAstNode(forExpr)
@@ -291,6 +301,7 @@ class CFGBuilder(val graph: Graph<CFGNodeData, CFGEdgeData>, val entry: CFGNode,
 
     // TODO: this cases require regions which are not implemented yet
     override fun visitBreakExpr(breakExpr: RsBreakExpr) = finishWith(pred)
+
     override fun visitContExpr(contExpr: RsContExpr) = finishWith(pred)
 
     override fun visitArrayExpr(arrayExpr: RsArrayExpr) =
diff --git a/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt b/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt
index 4b12ae9bc..d5fbf6b0e 100644
--- a/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt
+++ b/src/test/kotlin/org/rust/ide/inspections/borrowck/RsBorrowCheckerMovesTest.kt
@@ -322,4 +322,20 @@ class RsBorrowCheckerMovesTest : RsInspectionsTestBase(RsBorrowCheckerInspection
             let s2 = s;
         }
     """, checkWarn = false)
+
+    /** Issue [#3270](https://github.com/intellij-rust/intellij-rust/issues/3270) */
+    @ProjectDescriptor(WithStdlibRustProjectDescriptor::class)
+    fun `test no move error E0382 if let`() = checkByText("""
+        struct S;
+
+        fn main() {
+            let x: Option<S> = Some(S);
+
+            if let Some(s) = x {
+                Some(s)
+            } else {
+                x
+            };
+        }
+    """, checkWarn = false)
 }
diff --git a/src/test/kotlin/org/rust/lang/core/cfg/RsControlFlowGraphTest.kt b/src/test/kotlin/org/rust/lang/core/cfg/RsControlFlowGraphTest.kt
index db0a93edb..19c159ee6 100644
--- a/src/test/kotlin/org/rust/lang/core/cfg/RsControlFlowGraphTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/cfg/RsControlFlowGraphTest.kt
@@ -18,8 +18,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         Entry
         BLOCK
         Exit
-    """
-    )
+    """)
 
     fun `test straightforward`() = testCFG("""
         fn main() {
@@ -77,8 +76,75 @@ class RsControlFlowGraphTest : RsTestBase() {
         y += x;
         BLOCK
         Exit
-    """
-    )
+    """)
+
+    fun `test if`() = testCFG("""
+        fn foo() {
+            if true { 1 };
+        }
+    """, """
+        Entry
+        true
+        1
+        BLOCK
+        IF
+        IF;
+        BLOCK
+        Exit
+    """)
+
+    fun `test if else`() = testCFG("""
+        fn foo() {
+            if true { 1 } else { 2 };
+        }
+    """, """
+        Entry
+        true
+        1
+        BLOCK
+        IF
+        IF;
+        BLOCK
+        Exit
+        2
+        BLOCK
+    """)
+
+    fun `test if let`() = testCFG("""
+        fn foo() {
+            if let Some(s) = x { 1 };
+        }
+    """, """
+        Entry
+        x
+        s
+        Some(s)
+        1
+        BLOCK
+        IF
+        IF;
+        BLOCK
+        Exit
+    """)
+
+    fun `test if let else`() = testCFG("""
+        fn foo() {
+            if let Some(s) = x { 1 } else { 2 };
+        }
+    """, """
+        Entry
+        x
+        s
+        Some(s)
+        1
+        BLOCK
+        IF
+        IF;
+        BLOCK
+        Exit
+        2
+        BLOCK
+    """)
 
     fun `test if else with unreachable`() = testCFG("""
         fn main() {
@@ -101,8 +167,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         return
         Exit
         return
-    """
-    )
+    """)
 
     fun `test loop`() = testCFG("""
         fn main() {
@@ -119,8 +184,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         x += 1
         x += 1;
         BLOCK
-    """
-    )
+    """)
 
     fun `test while`() = testCFG("""
         fn main() {
@@ -153,13 +217,12 @@ class RsControlFlowGraphTest : RsTestBase() {
         return
         IF
         BLOCK
-    """
-    )
+    """)
 
-    fun `test while with assign`() = testCFG("""
+    fun `test while let`() = testCFG("""
         fn main() {
             while let x = f() {
-                something;
+                1;
             }
         }
     """, """
@@ -167,15 +230,14 @@ class RsControlFlowGraphTest : RsTestBase() {
         Dummy
         f
         f()
-        x
         WHILE
         BLOCK
         Exit
-        something
-        something;
+        x
+        1
+        1;
         BLOCK
-    """
-    )
+    """)
 
     fun `test while with unreachable`() = testCFG("""
         fn main() {
@@ -218,8 +280,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         x += 10
         x += 10;
         return
-    """
-    )
+    """)
 
     fun `test for`() = testCFG("""
         fn main() {
@@ -255,8 +316,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         x += i
         x += i;
         BLOCK
-    """
-    )
+    """)
 
     fun `test match`() = testCFG("""
         enum E { A, B(i32), C }
@@ -299,8 +359,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         E::C
         Dummy
         4
-    """
-    )
+    """)
 
     fun `test match 1`() = testCFG("""
         enum E { A(i32), B }
@@ -339,8 +398,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         E::B
         Dummy
         return
-    """
-    )
+    """)
 
     fun `test try`() = testCFG("""
         fn main() {
@@ -360,8 +418,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         y
         y;
         BLOCK
-    """
-    )
+    """)
 
     fun `test patterns`() = testCFG("""
         struct S { data1: i32, data2: i32 }
@@ -403,8 +460,7 @@ class RsControlFlowGraphTest : RsTestBase() {
         [0, 1 + a];
         BLOCK
         Exit
-    """
-    )
+    """)
 
     fun `test noreturn`() = testCFG("""
         fn main() {
@@ -426,10 +482,9 @@ class RsControlFlowGraphTest : RsTestBase() {
         42
         42;
         BLOCK
-    """
-    )
+    """)
 
-    protected fun testCFG(@Language("Rust") code: String, expectedIndented: String) {
+    private fun testCFG(@Language("Rust") code: String, expectedIndented: String) {
         InlineFile(code)
         val block = myFixture.file.descendantsOfType<RsBlock>().firstOrNull() ?: return
         val cfg = ControlFlowGraph.buildFor(block)