Repository: intellij-rust/intellij-rust
Issue: Complete Current Statement for type alias definitions

## Environment

* **IntelliJ Rust plugin version:** 0.4.182.4996-222
* **Rust toolchain version:** 1.65.0
* **IDE name and version:** IntelliJ IDEA 2022.2.3 (Community Edition)
* **Operating system:** Debian GNU/Linux 11 (bullseye)

## Problem description

[Complete Statement](https://www.jetbrains.com/idea/guide/tips/complete-statement/) doesn't work on type alias lines.

## Steps to reproduce

Write `type Foo = String`, keeping the cursor anywhere on the line. Press Ctrl-Shift-Enter to invoke the "Complete Current Statement" function. I expect this to insert the required semicolon at the end of the line, but instead it just inserts a new line and moves to it.

Changed files:
src/test/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessorTest.kt, src/main/kotlin/org/rust/ide/typing/assist/SemicolonFixer.kt, src/main/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessor.kt

diff --git a/src/main/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessor.kt b/src/main/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessor.kt
index 2775ea845..974438dab 100644
--- a/src/main/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessor.kt
+++ b/src/main/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessor.kt
@@ -39,13 +39,15 @@ class RsSmartEnterProcessor : SmartEnterProcessorWithFixers() {
     override fun getStatementAtCaret(editor: Editor, psiFile: PsiFile): PsiElement? {
         val atCaret = super.getStatementAtCaret(editor, psiFile) ?: return null
         if (atCaret is PsiWhiteSpace) return null
-        loop@ for (each in atCaret.ancestors) {
-            val elementType = each.node.elementType
-            when {
-                elementType == LBRACE || elementType == RBRACE -> continue@loop
-                each is RsMatchArm || each.parent is RsBlock
-                    || each.parent is RsFunction || each.parent is RsStructItem -> return each
-            }
+        for (element in atCaret.ancestors) {
+            val elementType = element.node.elementType
+            if (elementType == LBRACE || elementType == RBRACE) continue
+
+            val isSuitableElement = element is RsMatchArm || element is RsTypeAlias || element is RsTraitAlias
+                || element is RsConstant || element is RsExternCrateItem
+            val parent = element.parent
+            val stopAtParent = parent is RsBlock || parent is RsFunction || parent is RsStructItem
+            if (isSuitableElement || stopAtParent) return element
         }
         return null
     }
diff --git a/src/main/kotlin/org/rust/ide/typing/assist/SemicolonFixer.kt b/src/main/kotlin/org/rust/ide/typing/assist/SemicolonFixer.kt
index 8a608d187..c78175349 100644
--- a/src/main/kotlin/org/rust/ide/typing/assist/SemicolonFixer.kt
+++ b/src/main/kotlin/org/rust/ide/typing/assist/SemicolonFixer.kt
@@ -9,7 +9,9 @@ import com.intellij.lang.SmartEnterProcessorWithFixers
 import com.intellij.openapi.editor.Editor
 import com.intellij.psi.PsiElement
 import org.rust.lang.core.psi.RsBlock
-import org.rust.lang.core.psi.RsStmt
+import org.rust.lang.core.psi.RsElementTypes.SEMICOLON
+import org.rust.lang.core.psi.RsMembers
+import org.rust.lang.core.psi.ext.RsMod
 import org.rust.lang.core.psi.ext.endOffset
 
 /**
@@ -17,7 +19,9 @@ import org.rust.lang.core.psi.ext.endOffset
  */
 class SemicolonFixer : SmartEnterProcessorWithFixers.Fixer<RsSmartEnterProcessor>() {
     override fun apply(editor: Editor, processor: RsSmartEnterProcessor, element: PsiElement) {
-        if (element.parent !is RsBlock || (element as? RsStmt)?.semicolon != null) return
+        val parent = element.parent
+        if (parent !is RsBlock && parent !is RsMod && parent !is RsMembers) return
+        if (element.node.findChildByType(SEMICOLON) != null) return
         editor.document.insertString(element.endOffset, ";")
     }
 }
diff --git a/src/test/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessorTest.kt b/src/test/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessorTest.kt
index dac7065f4..c9d04a193 100644
--- a/src/test/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessorTest.kt
+++ b/src/test/kotlin/org/rust/ide/typing/assist/RsSmartEnterProcessorTest.kt
@@ -371,6 +371,87 @@ class RsSmartEnterProcessorTest : RsTestBase() {
         }
     """)
 
+    fun `test top-level type alias`() = doTest("""
+        type Foo = /*caret*/i32
+    """, """
+        type Foo = i32;
+        /*caret*/
+    """)
+
+    fun `test top-level type alias with semicolon`() = doTest("""
+        type Foo = /*caret*/i32;
+    """, """
+        type Foo = i32;
+        /*caret*/
+    """)
+
+    fun `test top-level trait alias`() = doTest("""
+        trait Foo = /*caret*/Bar
+    """, """
+        trait Foo = Bar;
+        /*caret*/
+    """)
+
+    fun `test type alias inside trait (no default value)`() = doTest("""
+        trait Trait {
+            type /*caret*/Foo
+        }
+    """, """
+        trait Trait {
+            type Foo;
+            /*caret*/
+        }
+    """)
+
+    fun `test type alias inside trait (with default value)`() = doTest("""
+        trait Trait {
+            type Foo = /*caret*/i32
+        }
+    """, """
+        trait Trait {
+            type Foo = i32;
+            /*caret*/
+        }
+    """)
+
+    fun `test type alias inside trait (with where clause)`() = doTest("""
+        trait Iterable {
+            type Iter<'a> where /*caret*/Self: 'a
+        }
+    """, """
+        trait Iterable {
+            type Iter<'a> where Self: 'a;
+            /*caret*/
+        }
+    """)
+
+    fun `test top-level constant`() = doTest("""
+        const C: i32 = /*caret*/0
+    """, """
+        const C: i32 = 0;
+        /*caret*/
+    """)
+
+    fun `test top-level constant (with block expr) 1`() = doTest("""
+        const C: i32 = /*caret*/{ 0 }
+    """, """
+        const C: i32 = { 0 };
+        /*caret*/
+    """)
+
+    fun `test top-level constant (with block expr) 2`() = doTest("""
+        const C: i32 = { 0/*caret*/ }
+    """, """
+        const C: i32 = { 0;/*caret*/ }
+    """)
+
+    fun `test top-level extern crate`() = doTest("""
+        extern crate /*caret*/std
+    """, """
+        extern crate std;
+        /*caret*/
+    """)
+
     private fun doTest(@Language("Rust") before: String, @Language("Rust") after: String) =
         checkEditorAction(before, after, IdeActions.ACTION_EDITOR_COMPLETE_STATEMENT)
 }