Repository: intellij-rust/intellij-rust
Issue: Extract variable in closure chooses the binding scope incorrectly

<!--
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.4.162.4282-213-nightly
* **Rust toolchain version:** 1.56.1 (59eed8a2a 2021-11-01) x86_64-unknown-linux-gnu
* **IDE name and version:** CLion 2021.3 (CL-213.5744.254)
* **Operating system:** Linux 5.4.0-91-generic
* **Macro expansion engine:** new
* **Name resolution engine:** new
* **Additional experimental features:** org.rust.cargo.features.settings.gutter, org.rust.cargo.evaluate.build.scripts, org.rust.macros.proc

## Problem description

If we have a closure whose body is just an expression (with no explicit block), then running "Extract variable" inside the body automatically places the new variable binding within a newly created block body of the closure. This happens even if some occurrences of the extracted expression happen outside of the closure, in which case they will refer to a variable binding which is inaccessible at their scope.

## Steps to reproduce

```rust
fn foo() {
    let _ = false;
    || false/*cursor*/;
    let _ = false;
}
```

Run "Extract variable" at the cursor, selecting expression "false" (or use the `let` postfix template). Choose "Replace all 2 occurences":

```rust
fn foo() {
    let _ = x;
    || {
        let x = false;
        x
    };
    let _ = 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/main/kotlin/org/rust/ide/refactoring/introduceVariable/impl.kt, src/test/kotlin/org/rust/ide/refactoring/RsIntroduceVariableHandlerTest.kt

diff --git a/src/main/kotlin/org/rust/ide/refactoring/introduceVariable/impl.kt b/src/main/kotlin/org/rust/ide/refactoring/introduceVariable/impl.kt
index b220d3285..f3a60972e 100644
--- a/src/main/kotlin/org/rust/ide/refactoring/introduceVariable/impl.kt
+++ b/src/main/kotlin/org/rust/ide/refactoring/introduceVariable/impl.kt
@@ -16,6 +16,7 @@ import org.rust.ide.utils.getTopmostParentInside
 import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.ancestorOrSelf
 import org.rust.lang.core.psi.ext.ancestorStrict
+import org.rust.lang.core.psi.ext.isAncestorOf
 import org.rust.lang.core.psi.ext.startOffset
 import org.rust.openapiext.runWriteCommandAction
 
@@ -40,7 +41,7 @@ private class ExpressionReplacer(
     fun replaceElementForAllExpr(exprs: List<PsiElement>, postfixLet: Boolean) {
         val anchor = findAnchor(exprs, chosenExpr) ?: return
         val sortedExprs = exprs.sortedBy { it.startOffset }
-        val firstExpr = sortedExprs.first()
+        val firstExpr = sortedExprs.firstOrNull() ?: chosenExpr
 
         // `inlinableExprStmt` is the element that should be replaced with the new let binding.
         // This can be either the statement surrounding the entire expression if it already had
@@ -70,9 +71,14 @@ private class ExpressionReplacer(
                 binding
             } else {
                 val parentLambda = chosenExpr.ancestorStrict<RsLambdaExpr>()
+                val lambdaMayBeAnchor = parentLambda != null && sortedExprs.all { parentLambda.isAncestorOf(it) }
                 val binding = introduceLet(anchor, let)
                 sortedExprs.forEach { it.replace(name) }
-                binding?.moveIntoLambdaBlockIfNeeded(parentLambda)
+                if (lambdaMayBeAnchor) {
+                    binding.moveIntoLambdaBlockIfNeeded(parentLambda)
+                } else {
+                    binding
+                }
             }
 
             val nameElem = moveEditorToNameElement(editor, letBinding)
@@ -95,7 +101,7 @@ private class ExpressionReplacer(
         return psiFactory.createLetDeclaration(name, chosenExpr, mutable)
     }
 
-    private fun introduceLet(anchor: PsiElement, let: RsLetDecl): PsiElement? {
+    private fun introduceLet(anchor: PsiElement, let: RsLetDecl): PsiElement {
         val context = anchor.parent
         val newline = PsiParserFacade.SERVICE.getInstance(project).createWhiteSpaceFromText("\\n")
 
diff --git a/src/test/kotlin/org/rust/ide/refactoring/RsIntroduceVariableHandlerTest.kt b/src/test/kotlin/org/rust/ide/refactoring/RsIntroduceVariableHandlerTest.kt
index 74c834db0..24d07277f 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/RsIntroduceVariableHandlerTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/RsIntroduceVariableHandlerTest.kt
@@ -402,6 +402,58 @@ class RsIntroduceVariableHandlerTest : RsTestBase() {
         }
     """, replaceAll = true)
 
+    fun `test lambda scoping 1`() = doTest("""
+        fn main() {
+            let _ = <selection>false</selection>;
+            || false;
+        }
+    """, emptyList(), 0, """
+        fn main() {
+            let x = false;
+            let _ = x;
+            || x;
+        }
+    """, replaceAll = true)
+
+    fun `test lambda scoping 2`() = doTest("""
+        fn main() {
+            let _ = false;
+            || <selection>false</selection>;
+        }
+    """, emptyList(), 0, """
+        fn main() {
+            let x = false;
+            let _ = x;
+            || x;
+        }
+    """, replaceAll = true)
+
+    fun `test lambda scoping 3`() = doTest("""
+        fn main() {
+            || false;
+            let _ = <selection>false</selection>;
+        }
+    """, emptyList(), 0, """
+        fn main() {
+            let x = false;
+            || x;
+            let _ = x;
+        }
+    """, replaceAll = true)
+
+    fun `test lambda scoping 4`() = doTest("""
+        fn main() {
+            || <selection>false</selection>;
+            let _ = false;
+        }
+    """, emptyList(), 0, """
+        fn main() {
+            let x = false;
+            || x;
+            let _ = x;
+        }
+    """, replaceAll = true)
+
     fun `test lambda has no braces with match expr`() = doTest("""
         fn main() {
             Some(1).map(|x| match <selection>x</selection> {