Repository: intellij-rust/intellij-rust
Issue: Implement members makes tuple of length one to type

<!--
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.152.4026-212
* **Rust toolchain version:** 1.55.0-nightly (952fdf2a1 2021-07-05) x86_64-pc-windows-msvc
* **IDE name and version:** CLion 2021.2 (CL-212.4746.93)
* **Operating system:** Windows 10 10.0
* **Macro expansion engine:** new
* **Name resolution engine:** new

## Problem description

Let's say we have the trait `SomeTrait` that's generic over `T`:
```rust
trait SomeTrait<T> {
    fn some_function(_: T);
}
```

if we now write an empty `impl` block where `T` is a tuple of length one:
```rust
impl SomeTrait<(u32, )> for u32 {
}
```

and invoke the context action 'Implement members', the output is
```rust 
impl SomeTrait<(u32, )> for u32 {
    fn some_function(_: (u32)) {
        todo!()
    }
}
```

instead of
```rust
impl SomeTrait<(u32, )> for u32 {
    fn some_function(_: (u32, )) {
        todo!()
    }
}
```

And since `(u32)` is just a verbose way of writing `u32`, while `(u32, )` is a tuple of length one, this leads to a compile error.


<!--
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/hints/type/RsTypeHintsPresentationFactory.kt
src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt
src/main/kotlin/org/rust/ide/presentation/TypeRendering.kt
src/test/kotlin/org/rust/ide/hints/type/RsInlayTypeHintsProviderTest.kt
src/test/kotlin/org/rust/lang/core/type/RsTypeResolvingTest.kt
src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt

diff --git a/src/main/kotlin/org/rust/ide/hints/type/RsTypeHintsPresentationFactory.kt b/src/main/kotlin/org/rust/ide/hints/type/RsTypeHintsPresentationFactory.kt
index 6017d8e4d..c609b4ee7 100644
--- a/src/main/kotlin/org/rust/ide/hints/type/RsTypeHintsPresentationFactory.kt
+++ b/src/main/kotlin/org/rust/ide/hints/type/RsTypeHintsPresentationFactory.kt
@@ -91,11 +91,17 @@ class RsTypeHintsPresentationFactory(
         factory.collapsible(
             prefix = text("("),
             collapsed = text(PLACEHOLDER),
-            expanded = { parametersHint(type.types, level + 1) },
+            expanded = { tupleTypesHint(type.types, level + 1) },
             suffix = text(")"),
             startWithPlaceholder = checkSize(level, type.types.size)
         )
 
+    private fun tupleTypesHint(types: List<Ty>, level: Int): InlayPresentation = if (types.size == 1) {
+        factory.seq(hint(types.single(), level), text(","))
+    } else {
+        types.map { hint(it, level) }.join(", ")
+    }
+
     private fun adtTypeHint(type: TyAdt, level: Int): InlayPresentation {
         val adtName = type.item.name
         val typeDeclaration = type.item
diff --git a/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt b/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
index 8dc0e0752..ca901761c 100644
--- a/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
+++ b/src/main/kotlin/org/rust/ide/presentation/RsPsiRenderer.kt
@@ -239,10 +239,16 @@ open class RsPsiRenderer(
 
     open fun appendTypeReference(sb: StringBuilder, ref: RsTypeReference) {
         when (val type = ref.skipParens()) {
-            is RsTupleType ->
-                type.typeReferenceList.joinToWithBuffer(sb, ", ", "(", ")") {
-                    appendTypeReference(it, this)
+            is RsTupleType -> {
+                val types = type.typeReferenceList
+                if (types.size == 1) {
+                    sb.append("(")
+                    appendTypeReference(sb, types.single())
+                    sb.append(",)")
+                } else {
+                    types.joinToWithBuffer(sb, ", ", "(", ")") { appendTypeReference(it, this) }
                 }
+            }
 
             is RsBaseType -> when (val kind = type.kind) {
                 RsBaseTypeKind.Unit -> sb.append("()")
diff --git a/src/main/kotlin/org/rust/ide/presentation/TypeRendering.kt b/src/main/kotlin/org/rust/ide/presentation/TypeRendering.kt
index 9d1842400..d5f3f74f4 100644
--- a/src/main/kotlin/org/rust/ide/presentation/TypeRendering.kt
+++ b/src/main/kotlin/org/rust/ide/presentation/TypeRendering.kt
@@ -134,7 +134,11 @@ private data class TypeRenderer(
             is TyFunction -> formatFnLike("fn", ty.paramTypes, ty.retType, render)
             is TySlice -> "[${render(ty.elementType)}]"
 
-            is TyTuple -> ty.types.joinToString(", ", "(", ")", transform = render)
+            is TyTuple -> if (ty.types.size == 1) {
+                "(${render(ty.types.single())},)"
+            } else {
+                ty.types.joinToString(", ", "(", ")", transform = render)
+            }
             is TyArray -> "[${render(ty.base)}; ${render(ty.const)}]"
             is TyReference -> buildString {
                 append('&')
diff --git a/src/test/kotlin/org/rust/ide/hints/type/RsInlayTypeHintsProviderTest.kt b/src/test/kotlin/org/rust/ide/hints/type/RsInlayTypeHintsProviderTest.kt
index 32e81d050..e5d9fcb7a 100644
--- a/src/test/kotlin/org/rust/ide/hints/type/RsInlayTypeHintsProviderTest.kt
+++ b/src/test/kotlin/org/rust/ide/hints/type/RsInlayTypeHintsProviderTest.kt
@@ -475,4 +475,12 @@ class RsInlayTypeHintsProviderTest : RsInlayTypeHintsTestBase(RsInlayTypeHintsPr
 //            let xs/*hint text="[:  [impl  [Iterator [< [Item = i32] >]] ]]"*/ = vec![1,2,3].into_iter();
 //        }
 //    """)
+
+    fun `test tuple type`() = checkByText("""
+        fn main() {
+            let a/*hint text="[:  [( [i32 ,] )]]"*/ = (1,);
+            let b/*hint text="[:  [( [i32 ,  i32] )]]"*/ = (1, 2);
+            let c/*hint text="[:  [( … )]]"*/ = (1, 2, 3);
+        }
+    """)
 }
diff --git a/src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt b/src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt
index 8fa08d990..7f0163b41 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt
@@ -745,6 +745,7 @@ class ImplementMembersHandlerTest : RsTestBase() {
             fn f3(u32, u64);
             fn f4() -> bool;
             fn f5(a: f64, b: bool) -> (i8, u8);
+            fn f6(a: (i32,)) -> (i8,);
         }
         struct S;
         impl T for S {/*caret*/}
@@ -753,7 +754,8 @@ class ImplementMembersHandlerTest : RsTestBase() {
         ImplementMemberSelection("f2(a: (i32, u32))", true),
         ImplementMemberSelection("f3(u32, u64)", true),
         ImplementMemberSelection("f4() -> bool", true),
-        ImplementMemberSelection("f5(a: f64, b: bool) -> (i8, u8)", true)
+        ImplementMemberSelection("f5(a: f64, b: bool) -> (i8, u8)", true),
+        ImplementMemberSelection("f6(a: (i32,)) -> (i8,)", true),
     ), """
         trait T {
             fn f1(a: i8, b: i16, c: i32, d: i64);
@@ -761,6 +763,7 @@ class ImplementMembersHandlerTest : RsTestBase() {
             fn f3(u32, u64);
             fn f4() -> bool;
             fn f5(a: f64, b: bool) -> (i8, u8);
+            fn f6(a: (i32,)) -> (i8,);
         }
         struct S;
         impl T for S {
@@ -783,6 +786,10 @@ class ImplementMembersHandlerTest : RsTestBase() {
             fn f5(a: f64, b: bool) -> (i8, u8) {
                 todo!()
             }
+
+            fn f6(a: (i32, )) -> (i8, ) {
+                todo!()
+            }
         }
     """)
 
diff --git a/src/test/kotlin/org/rust/lang/core/type/RsTypeResolvingTest.kt b/src/test/kotlin/org/rust/lang/core/type/RsTypeResolvingTest.kt
index 7304f8e3c..a3922a937 100644
--- a/src/test/kotlin/org/rust/lang/core/type/RsTypeResolvingTest.kt
+++ b/src/test/kotlin/org/rust/lang/core/type/RsTypeResolvingTest.kt
@@ -38,6 +38,14 @@ class RsTypeResolvingTest : RsTypificationTestBase() {
         }
     """)
 
+    fun `test tuple of size 1`() = testType("""
+        struct S;
+        fn main() {
+            let _: (S,) = (S,);
+                 //^ (S,)
+        }
+    """)
+
     fun `test type in parens`() = testType("""
         struct S;
         fn main() {