Repository: intellij-rust/intellij-rust
Issue: Wrong control flow of break/continue expanded from a macro

<!--
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.3.144.3748-211-nightly
* **Rust toolchain version:** 1.51.0-nightly (04caa632d 2021-01-30) x86_64-apple-darwin
* **IDE name and version:** IntelliJ IDEA 2021.1 Beta Ultimate Edition (IU-211.6556.6)
* **Operating system:** macOS 11.1
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description
Control-flow analysis should consider `break`/`continue` expressions expanded from a macro. Currently, macro-expanded `break`/`continue` expressions are just ignored in terms of control flow. This affects `Liveness analysis` and `Borrow checker` and leads to false-positive annotations.

## Steps to reproduce
```Rust
macro_rules! break_macro {
    () => { break };
}
fn main() {
    let x = 1; // false positive "Variable x is never used"
    loop {
        break_macro!();
    }
    x;
}
```

<!--
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/dfa/RsControlFlowGraphTest.kt, src/main/kotlin/org/rust/lang/core/dfa/CFGBuilder.kt

diff --git a/src/main/kotlin/org/rust/lang/core/dfa/CFGBuilder.kt b/src/main/kotlin/org/rust/lang/core/dfa/CFGBuilder.kt
index 52fe91a1d..2e58ac465 100644
--- a/src/main/kotlin/org/rust/lang/core/dfa/CFGBuilder.kt
+++ b/src/main/kotlin/org/rust/lang/core/dfa/CFGBuilder.kt
@@ -135,7 +135,7 @@ class CFGBuilder(
             }
         } else {
             // otherwise, try to find the corresponding loop
-            val exprBlock = expr.ancestors.filterIsInstance<RsLooplikeExpr>().firstOrNull()?.block
+            val exprBlock = expr.contexts.filterIsInstance<RsLooplikeExpr>().firstOrNull()?.block
 
             for ((loop, continueNode, breakNode) in loopScopes) {
                 if (loop.block == exprBlock) {
diff --git a/src/test/kotlin/org/rust/lang/core/dfa/RsControlFlowGraphTest.kt b/src/test/kotlin/org/rust/lang/core/dfa/RsControlFlowGraphTest.kt
index 9b870f1ef..e16de9365 100644
--- a/src/test/kotlin/org/rust/lang/core/dfa/RsControlFlowGraphTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/dfa/RsControlFlowGraphTest.kt
@@ -1066,6 +1066,32 @@ class RsControlFlowGraphTest : RsTestBase() {
         Termination
     """)
 
+    fun `test break expanded from macro`() = testCFG("""
+        macro_rules! break_macro {
+            () => { break };
+        }
+        fn main() {
+            1;
+            loop {
+                break_macro!();
+            }
+            2;
+        }
+    """, """
+        Entry
+        1
+        1;
+        Dummy
+        break
+        LOOP
+        LOOP;
+        2
+        2;
+        BLOCK
+        Exit
+        Termination
+    """)
+
     private fun testCFG(@Language("Rust") code: String, expectedIndented: String) {
         InlineFile(code)
         val function = myFixture.file.descendantsOfType<RsFunction>().firstOrNull() ?: return