Repository: intellij-rust/intellij-rust
Issue: Extract method refactoring doesn't work for expression in println 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.146.3786-211-nightly
* **Rust toolchain version:** 1.51.0 (2fd73fabe 2021-03-23) x86_64-apple-darwin
* **IDE name and version:** IntelliJ IDEA 2021.1 Ultimate Edition (IU-211.6693.111)
* **Operating system:** macOS 10.15.6
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description
I want to apply `Extract method` refactoring to `x - 1` expression in `println` refactoring. And the plugin does nothing in this case. But at the same time it works for single `x`.

## Steps to reproduce
```rust
fn main() {
    let x = 1;
    println!("{}", x - 1); // refactoring doesn't work for `x - 1` expression but works for `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/utils/SearchByOffset.kt, src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt

diff --git a/src/main/kotlin/org/rust/ide/utils/SearchByOffset.kt b/src/main/kotlin/org/rust/ide/utils/SearchByOffset.kt
index 27860432d..ae80f90d5 100644
--- a/src/main/kotlin/org/rust/ide/utils/SearchByOffset.kt
+++ b/src/main/kotlin/org/rust/ide/utils/SearchByOffset.kt
@@ -75,13 +75,15 @@ fun findStatementsOrExprInRange(file: PsiFile, startOffset: Int, endOffset: Int)
     val (element1, element2) = file.getElementRange(startOffset, endOffset) ?: return emptyArray()
     val parent = PsiTreeUtil.findCommonParent(element1, element2) ?: return emptyArray()
 
-    val mostDistantParent = parent.ancestors
-        .last { it.textRange == parent.textRange }
-
-    if (mostDistantParent is RsExpr || mostDistantParent is RsStmt) {
-        if (startOffset == mostDistantParent.startOffset && endOffset == mostDistantParent.endOffset) {
-            return arrayOf(mostDistantParent)
-        }
+    if (startOffset == parent.startOffset && endOffset == parent.endOffset) {
+        // Note that argument of macro with hardcoded PSI (like `println!`)
+        // will have parents `RsExpr` and`RsFormatMacroArgImpl` with same text range,
+        // and we should use `RsExpr`.
+        val mostDistantParent = parent.ancestors
+            .lastOrNull {
+                it.textRange == parent.textRange && (it is RsExpr || it is RsStmt)
+            }
+        mostDistantParent?.let { return arrayOf(it) }
     }
 
     return findStatementsInRange(element1, element2)
diff --git a/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt b/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt
index 10f13ef05..cb36911a7 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt
@@ -1786,6 +1786,36 @@ class RsExtractFunctionTest : RsTestBase() {
         }
     """, "bar")
 
+    @ProjectDescriptor(WithStdlibRustProjectDescriptor::class)
+    fun `test extract println! argument expression`() = doTest("""
+        fn main() {
+            println!("{}", <selection>1 + 2</selection>);
+        }
+    """, """
+        fn main() {
+            println!("{}", bar());
+        }
+
+        fn bar() -> i32 {
+            1 + 2
+        }
+    """, "bar")
+
+    @ProjectDescriptor(WithStdlibRustProjectDescriptor::class)
+    fun `test extract vec! argument expression`() = doTest("""
+        fn main() {
+            vec![<selection>1 + 2</selection>];
+        }
+    """, """
+        fn main() {
+            vec![bar()];
+        }
+
+        fn bar() -> i32 {
+            1 + 2
+        }
+    """, "bar")
+
     private fun doTest(
         @Language("Rust") code: String,
         @Language("Rust") excepted: String,