Repository: intellij-rust/intellij-rust
Issue: "Put fields on one line" generates invalid code if comments are present

## Environment

* **IntelliJ Rust plugin version:** 0.2.112.SNAPSHOT-193-dev
* **Rust toolchain version:** 1.39.0 (4560ea788 2019-11-04) x86_64-apple-darwin
* **IDE name and version:** CLion 2019.3 (CL-193.5233.103)
* **Operating system:** macOS 10.15

## Problem description

With  #4332 changes in place.
"Put fields on one line" action produces invalid code if fields have comments.

## Steps to reproduce

```rust
struct MyStruct {
    x: u8,
    y: u8,
}

fn main() {
    MyStruct {
        x: 10,  // Some field
        y: 20,  // Another field
    };
}
```

Call "Put fields on one line" on `x` on 9 line:

```rust
struct MyStruct {
    x: u8,
    y: u8,
}

fn main() {
    MyStruct { x: 10,  // Some field y: 20,  // Another field };
}
```

Changed files:
src/test/kotlin/org/rust/ide/intentions/JoinLiteralFieldListIntentionTest.kt, src/test/kotlin/org/rust/ide/intentions/JoinVariantListIntentionTest.kt, src/main/kotlin/org/rust/ide/intentions/JoinListIntention.kt, src/test/kotlin/org/rust/ide/intentions/JoinArgumentListIntentionTest.kt, src/test/kotlin/org/rust/ide/intentions/JoinFieldListIntentionTest.kt, src/test/kotlin/org/rust/ide/intentions/JoinParameterListIntentionTest.kt

diff --git a/src/main/kotlin/org/rust/ide/intentions/JoinListIntention.kt b/src/main/kotlin/org/rust/ide/intentions/JoinListIntention.kt
index b4c9074a7..5c0d5526d 100644
--- a/src/main/kotlin/org/rust/ide/intentions/JoinListIntention.kt
+++ b/src/main/kotlin/org/rust/ide/intentions/JoinListIntention.kt
@@ -8,13 +8,12 @@ package org.rust.ide.intentions
 import com.intellij.openapi.editor.Document
 import com.intellij.openapi.editor.Editor
 import com.intellij.openapi.project.Project
+import com.intellij.psi.PsiComment
 import com.intellij.psi.PsiElement
 import com.intellij.psi.PsiWhiteSpace
+import org.rust.lang.core.parser.RustParserDefinition.Companion.EOL_COMMENT
 import org.rust.lang.core.psi.*
-import org.rust.lang.core.psi.ext.RsElement
-import org.rust.lang.core.psi.ext.elementType
-import org.rust.lang.core.psi.ext.endOffset
-import org.rust.lang.core.psi.ext.startOffset
+import org.rust.lang.core.psi.ext.*
 
 abstract class JoinListIntentionBase<TList : RsElement, TElement : RsElement>(
     listClass: Class<TList>,
@@ -26,7 +25,10 @@ abstract class JoinListIntentionBase<TList : RsElement, TElement : RsElement>(
     override fun findApplicableContext(project: Project, editor: Editor, element: PsiElement): TList? {
         val list = element.listContext ?: return null
         val elements = getElements(list)
-        if (elements.isEmpty() || !hasLineBreakBefore(elements.first()) && elements.none { hasLineBreakAfter(list, it) }) {
+        if (elements.isEmpty()
+            || !hasLineBreakBefore(elements.first()) && elements.none { hasLineBreakAfter(list, it) }
+            || list.hasEolComment()
+        ) {
             return null
         }
         return list
@@ -49,6 +51,8 @@ abstract class JoinListIntentionBase<TList : RsElement, TElement : RsElement>(
             document.replaceString(startOffset, endOffset, replaceString)
         }
     }
+
+    private fun RsElement.hasEolComment() = descendantsOfType<PsiComment>().any { it.elementType == EOL_COMMENT }
 }
 
 class JoinParameterListIntention : JoinListIntentionBase<RsValueParameterList, RsValueParameter>(
diff --git a/src/test/kotlin/org/rust/ide/intentions/JoinArgumentListIntentionTest.kt b/src/test/kotlin/org/rust/ide/intentions/JoinArgumentListIntentionTest.kt
index 92d0bd567..ef9c4198c 100644
--- a/src/test/kotlin/org/rust/ide/intentions/JoinArgumentListIntentionTest.kt
+++ b/src/test/kotlin/org/rust/ide/intentions/JoinArgumentListIntentionTest.kt
@@ -93,4 +93,14 @@ class JoinArgumentListIntentionTest : RsIntentionTestBase(JoinArgumentListIntent
                 */ 2, 3);
         }
     """)
+
+    fun `test has end-of-line comments`() = doUnavailableTest("""
+        fn foo(p1: i32, p2: i32) {}
+        fn main() {
+            foo(
+                /*caret*/1, // comment
+                2
+            );
+        }
+    """)
 }
diff --git a/src/test/kotlin/org/rust/ide/intentions/JoinFieldListIntentionTest.kt b/src/test/kotlin/org/rust/ide/intentions/JoinFieldListIntentionTest.kt
index fc1f56271..816178556 100644
--- a/src/test/kotlin/org/rust/ide/intentions/JoinFieldListIntentionTest.kt
+++ b/src/test/kotlin/org/rust/ide/intentions/JoinFieldListIntentionTest.kt
@@ -26,15 +26,15 @@ class JoinFieldListIntentionTest : RsIntentionTestBase(JoinFieldListIntention())
     """)
 
     fun `test has some line breaks`() = doAvailableTest("""
-        struct S { x: i32, /*caret*/y: i32, 
+        struct S { x: i32, /*caret*/y: i32,
                    z: i32 }
     """, """
         struct S { x: i32, y: i32, z: i32 }
     """)
 
     fun `test has some line breaks 2`() = doAvailableTest("""
-        struct S { 
-            x: i32, y: i32, z: i32/*caret*/ 
+        struct S {
+            x: i32, y: i32, z: i32/*caret*/
         }
     """, """
         struct S { x: i32, y: i32, z: i32 }
@@ -55,4 +55,11 @@ class JoinFieldListIntentionTest : RsIntentionTestBase(JoinFieldListIntention())
                    comment
                    */ y: i32, z: i32 }
     """)
+
+    fun `test has end-of-line comments`() = doUnavailableTest("""
+        struct S {
+            /*caret*/x: i32, // comment
+            y: i32
+        }
+    """)
 }
diff --git a/src/test/kotlin/org/rust/ide/intentions/JoinLiteralFieldListIntentionTest.kt b/src/test/kotlin/org/rust/ide/intentions/JoinLiteralFieldListIntentionTest.kt
index 363f80b60..95d62262b 100644
--- a/src/test/kotlin/org/rust/ide/intentions/JoinLiteralFieldListIntentionTest.kt
+++ b/src/test/kotlin/org/rust/ide/intentions/JoinLiteralFieldListIntentionTest.kt
@@ -44,7 +44,7 @@ class JoinLiteralFieldListIntentionTest : RsIntentionTestBase(JoinLiteralFieldLi
     fun `test has some line breaks`() = doAvailableTest("""
         struct S { x: i32, y: i32, z: i32 }
         fn foo() {
-            S { x: 0, /*caret*/y: 0, 
+            S { x: 0, /*caret*/y: 0,
                 z: 0 };
         }
     """, """
@@ -57,8 +57,8 @@ class JoinLiteralFieldListIntentionTest : RsIntentionTestBase(JoinLiteralFieldLi
     fun `test has some line breaks 2`() = doAvailableTest("""
         struct S { x: i32, y: i32, z: i32 }
         fn foo() {
-            S { 
-                x: 0, y: 0, z: 0/*caret*/ 
+            S {
+                x: 0, y: 0, z: 0/*caret*/
             };
         }
     """, """
@@ -145,4 +145,14 @@ class JoinLiteralFieldListIntentionTest : RsIntentionTestBase(JoinLiteralFieldLi
             let e = E::A { x: 0, y: 0, z: 0 };
         }
     """)
+
+    fun `test has end-of-line comments`() = doUnavailableTest("""
+        struct S { x: i32, y: i32 }
+        fn foo() {
+            S {
+                /*caret*/x: 0, // comment
+                y: 0
+            };
+        }
+    """)
 }
diff --git a/src/test/kotlin/org/rust/ide/intentions/JoinParameterListIntentionTest.kt b/src/test/kotlin/org/rust/ide/intentions/JoinParameterListIntentionTest.kt
index 4e857f014..f82c2f3c4 100644
--- a/src/test/kotlin/org/rust/ide/intentions/JoinParameterListIntentionTest.kt
+++ b/src/test/kotlin/org/rust/ide/intentions/JoinParameterListIntentionTest.kt
@@ -55,4 +55,11 @@ class JoinParameterListIntentionTest : RsIntentionTestBase(JoinParameterListInte
                comment
                */ p2: i32, p3: i32) {}
     """)
+
+    fun `test has end-of-line comments`() = doUnavailableTest("""
+        fn foo(
+            /*caret*/p1: i32, // comment
+            p2: i32
+        ) {}
+    """)
 }
diff --git a/src/test/kotlin/org/rust/ide/intentions/JoinVariantListIntentionTest.kt b/src/test/kotlin/org/rust/ide/intentions/JoinVariantListIntentionTest.kt
index 592bb52e9..d356b7ca8 100644
--- a/src/test/kotlin/org/rust/ide/intentions/JoinVariantListIntentionTest.kt
+++ b/src/test/kotlin/org/rust/ide/intentions/JoinVariantListIntentionTest.kt
@@ -26,15 +26,15 @@ class JoinVariantListIntentionTest : RsIntentionTestBase(JoinVariantListIntentio
     """)
 
     fun `test has some line breaks`() = doAvailableTest("""
-        enum E { A, /*caret*/B, 
+        enum E { A, /*caret*/B,
                    C }
     """, """
         enum E { A, B, C }
     """)
 
     fun `test has some line breaks 2`() = doAvailableTest("""
-        enum E { 
-            A, B, C/*caret*/ 
+        enum E {
+            A, B, C/*caret*/
         }
     """, """
         enum E { A, B, C }
@@ -55,4 +55,11 @@ class JoinVariantListIntentionTest : RsIntentionTestBase(JoinVariantListIntentio
                    comment
                    */ B, C }
     """)
+
+    fun `test has end-of-line comments`() = doUnavailableTest("""
+        enum E {
+            /*caret*/A(i32, i32), // comment
+            B
+        }
+    """)
 }