Repository: intellij-rust/intellij-rust
Issue: Extract method fails on some expressions

<!--
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.3826-211
* **Rust toolchain version:** 1.54.0-nightly (5c0292654 2021-05-11) x86_64-pc-windows-msvc
* **IDE name and version:** CLion 2021.1.1 (CL-211.7142.21)
* **Operating system:** Windows 10 10.0
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description

When some expressions are to be extracted as a method, the functionality does literally nothing when the trailing ; is selected along with the expressions for extraction.

## Steps to reproduce
```rust
// data is &[u8]
let result = data
            .array_chunks()
            .map(|data| Uuid::from_bytes(*data))
            .collect_vec();
```

When the following is selected for extraction:

```
data
            .array_chunks()
            .map(|data| Uuid::from_bytes(*data))
            .collect_vec();
```

Nothing happens after choosing the option, but if the last ; is omitted, extraction dialogs pops up and everything works as expected.

Also, if the extracted method is static, the plugin should use `Self::` instead of fully qualified name.

<!--
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/ide/refactoring/RsExtractFunctionTest.kt
src/main/kotlin/org/rust/ide/refactoring/extractFunction/RsExtractFunctionConfig.kt

diff --git a/src/main/kotlin/org/rust/ide/refactoring/extractFunction/RsExtractFunctionConfig.kt b/src/main/kotlin/org/rust/ide/refactoring/extractFunction/RsExtractFunctionConfig.kt
index 2f33cb16a..4479ddd4e 100644
--- a/src/main/kotlin/org/rust/ide/refactoring/extractFunction/RsExtractFunctionConfig.kt
+++ b/src/main/kotlin/org/rust/ide/refactoring/extractFunction/RsExtractFunctionConfig.kt
@@ -13,6 +13,7 @@ import com.intellij.psi.search.searches.ReferencesSearch
 import com.intellij.psi.util.siblings
 import org.rust.ide.presentation.renderInsertionSafe
 import org.rust.ide.refactoring.RsFunctionSignatureConfig
+import org.rust.ide.utils.findElementAtIgnoreWhitespaceAfter
 import org.rust.ide.utils.findStatementsOrExprInRange
 import org.rust.lang.core.psi.*
 import org.rust.lang.core.psi.ext.*
@@ -222,6 +223,17 @@ class RsExtractFunctionConfig private constructor(
 
     companion object {
         fun create(file: PsiFile, start: Int, end: Int): RsExtractFunctionConfig? {
+            doCreate(file, start, end)?.let { return it }
+
+            file.findElementAtIgnoreWhitespaceAfter(end - 1)?.let { lastElement ->
+                if (lastElement.elementType in listOf(RsElementTypes.COMMA, RsElementTypes.SEMICOLON)) {
+                    return doCreate(file, start, lastElement.startOffset)
+                }
+            }
+            return null
+        }
+
+        private fun doCreate(file: PsiFile, start: Int, end: Int): RsExtractFunctionConfig? {
             val elements = findStatementsOrExprInRange(file, start, end).asList()
             if (elements.isEmpty()) return null
             val first = elements.first()
diff --git a/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt b/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt
index 160966b0d..b4cc14aa6 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/RsExtractFunctionTest.kt
@@ -919,6 +919,54 @@ class RsExtractFunctionTest : RsTestBase() {
         }
     """, "bar", noSelected = listOf("a"))
 
+    fun `test extract expr with trailing semicolon`() = doTest("""
+        fn main() {
+            let a = <selection>42;</selection>
+        }
+    """, """
+        fn main() {
+            let a = bar();
+        }
+
+        fn bar() -> i32 {
+            42
+        }
+    """, "bar")
+
+    fun `test extract expr with trailing semicolon and whitespace`() = doTest("""
+        fn main() {
+            let a = <selection>42; </selection> //
+        }
+    """, """
+        fn main() {
+            let a = bar();  //
+        }
+
+        fn bar() -> i32 {
+            42
+        }
+    """, "bar")
+
+    fun `test extract expr with trailing comma`() = doTest("""
+        fn main() {
+            let s = S {
+                a: <selection>42,</selection>
+                b: 0,
+            };
+        }
+    """, """
+        fn main() {
+            let s = S {
+                a: bar(),
+                b: 0,
+            };
+        }
+
+        fn bar() -> i32 {
+            42
+        }
+    """, "bar")
+
     @MockEdition(CargoWorkspace.Edition.EDITION_2018)
     fun `test extract async function with await`() = doTest("""
         #[lang = "core::future::future::Future"]