Repository: intellij-rust/intellij-rust
Issue: Unnecessary full generic substitution when implementing members

<!--
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.131.3366-202
* **Rust toolchain version:** 1.48
* **IDE name and version:** CLion 2020.2
* **Operating system:** Windows 10

## Problem description

When using automatic implementation generation of trait functions, generic parameters have defaults explicitly provided, often with invalid imports.

## Steps to reproduce

<!--
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/).
-->

```rust
trait Foo {
    fn foo() -> Result<HashMap<String, i16>, String>;
}

impl Foo for Bar {
    // auto-generate fn implmentation here and notice RandomState gets added
    // what's worse, a random import also gets added:
    // use tracing::stdlib::collections::hash_map::RandomState;
    fn foo() -> Result<HashMap<String, i16, RandomState>, String> {
    }
}
```

Changed files:
src/main/kotlin/org/rust/lang/core/psi/RsPsiFactory.kt
src/main/kotlin/org/rust/ide/refactoring/implementMembers/impl.kt
src/main/kotlin/org/rust/ide/utils/import/RsImportHelper.kt
src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt

diff --git a/src/main/kotlin/org/rust/ide/refactoring/implementMembers/impl.kt b/src/main/kotlin/org/rust/ide/refactoring/implementMembers/impl.kt
index 34472bb40..b78b66da1 100644
--- a/src/main/kotlin/org/rust/ide/refactoring/implementMembers/impl.kt
+++ b/src/main/kotlin/org/rust/ide/refactoring/implementMembers/impl.kt
@@ -128,7 +128,7 @@ private fun insertNewTraitMembers(
         }
     }
 
-    importTypeReferencesFromElements(existingMembers, selected, trait.subst, true)
+    importTypeReferencesFromElements(existingMembers, selected, trait.subst, useAliases = true, skipUnchangedDefaultTypeArguments = true)
 
     if (needToSelect != null && editor != null) {
         selectElement(needToSelect, editor)
diff --git a/src/main/kotlin/org/rust/ide/utils/import/RsImportHelper.kt b/src/main/kotlin/org/rust/ide/utils/import/RsImportHelper.kt
index 89eabe1f8..610111d51 100644
--- a/src/main/kotlin/org/rust/ide/utils/import/RsImportHelper.kt
+++ b/src/main/kotlin/org/rust/ide/utils/import/RsImportHelper.kt
@@ -23,9 +23,10 @@ object RsImportHelper {
         context: RsElement,
         elements: Collection<RsElement>,
         subst: Substitution = emptySubstitution,
-        useAliases: Boolean = false
+        useAliases: Boolean = false,
+        skipUnchangedDefaultTypeArguments: Boolean = false
     ) {
-        val (toImport, _) = getTypeReferencesInfoFromElements(context, elements, subst, useAliases)
+        val (toImport, _) = getTypeReferencesInfoFromElements(context, elements, subst, useAliases, skipUnchangedDefaultTypeArguments)
         importElements(context, toImport)
     }
 
@@ -65,9 +66,10 @@ object RsImportHelper {
         context: RsElement,
         elements: Collection<RsElement>,
         subst: Substitution,
-        useAliases: Boolean
+        useAliases: Boolean,
+        skipUnchangedDefaultTypeArguments: Boolean
     ): TypeReferencesInfo = getTypeReferencesInfo(context, elements) { ty, result ->
-        collectImportSubjectsFromTypeReferences(ty, subst, result, useAliases)
+        collectImportSubjectsFromTypeReferences(ty, subst, result, useAliases, skipUnchangedDefaultTypeArguments)
     }
 
     /**
@@ -76,9 +78,10 @@ object RsImportHelper {
     fun getTypeReferencesInfoFromTys(
         context: RsElement,
         vararg elemTys: Ty,
-        useAliases: Boolean = false
+        useAliases: Boolean = false,
+        skipUnchangedDefaultTypeArguments: Boolean = false
     ): TypeReferencesInfo = getTypeReferencesInfo(context, elemTys.toList()) { ty, result ->
-        collectImportSubjectsFromTy(ty, emptySubstitution, result, useAliases)
+        collectImportSubjectsFromTy(ty, emptySubstitution, result, useAliases, skipUnchangedDefaultTypeArguments)
     }
 
     private fun <T> getTypeReferencesInfo(
@@ -95,7 +98,8 @@ object RsImportHelper {
         context: RsElement,
         subst: Substitution,
         result: MutableSet<RsQualifiedNamedElement>,
-        useAliases: Boolean
+        useAliases: Boolean,
+        skipUnchangedDefaultTypeArguments: Boolean
     ) {
         context.accept(object : RsVisitor() {
             override fun visitPath(path: RsPath) {
@@ -110,7 +114,7 @@ object RsImportHelper {
             }
 
             override fun visitTypeReference(reference: RsTypeReference) =
-                collectImportSubjectsFromTy(reference.type, subst, result, useAliases)
+                collectImportSubjectsFromTy(reference.type, subst, result, useAliases, skipUnchangedDefaultTypeArguments)
 
             override fun visitElement(element: RsElement) =
                 element.acceptChildren(this)
@@ -121,7 +125,8 @@ object RsImportHelper {
         ty: Ty,
         subst: Substitution,
         result: MutableSet<RsQualifiedNamedElement>,
-        useAliases: Boolean
+        useAliases: Boolean,
+        skipUnchangedDefaultTypeArguments: Boolean
     ) {
         ty.substitute(subst).visitWith(object : TypeVisitor {
             override fun visitTy(ty: Ty): Boolean {
@@ -132,6 +137,14 @@ object RsImportHelper {
                         if (alias != null) {
                             return true
                         }
+
+                        if (skipUnchangedDefaultTypeArguments) {
+                            val filteredTypeArguments = ty.typeArguments
+                                .zip(ty.item.typeParameters)
+                                .dropLastWhile { (argumentTy, param) -> argumentTy == param.typeReference?.type }
+                                .map { (argumentTy, _) -> argumentTy }
+                            return ty.copy(typeArguments = filteredTypeArguments).superVisitWith(this)
+                        }
                     }
                     is TyAnon -> result += ty.traits.map { it.element }
                     is TyTraitObject -> result += ty.traits.map { it.element }
diff --git a/src/main/kotlin/org/rust/lang/core/psi/RsPsiFactory.kt b/src/main/kotlin/org/rust/lang/core/psi/RsPsiFactory.kt
index 4d71493b2..487ea258f 100644
--- a/src/main/kotlin/org/rust/lang/core/psi/RsPsiFactory.kt
+++ b/src/main/kotlin/org/rust/lang/core/psi/RsPsiFactory.kt
@@ -8,8 +8,8 @@ package org.rust.lang.core.psi
 import com.intellij.openapi.project.Project
 import com.intellij.psi.*
 import com.intellij.util.LocalTimeCounter
-import org.rust.ide.utils.checkMatch.Pattern
 import org.rust.ide.presentation.renderInsertionSafe
+import org.rust.ide.utils.checkMatch.Pattern
 import org.rust.lang.RsFileType
 import org.rust.lang.RsLanguage
 import org.rust.lang.core.macros.MacroExpansionContext
@@ -537,7 +537,11 @@ class RsPsiFactory(
 private fun String.iff(cond: Boolean) = if (cond) "$this " else " "
 
 fun RsTypeReference.substAndGetText(subst: Substitution): String =
-    type.substitute(subst).renderInsertionSafe(includeLifetimeArguments = true, useAliasNames = true)
+    type.substitute(subst).renderInsertionSafe(
+        includeLifetimeArguments = true,
+        useAliasNames = true,
+        skipUnchangedDefaultTypeArguments = true
+    )
 
 private fun mutsToRefs(mutability: List<Mutability>): String =
     mutability.joinToString("", "", "") {
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 f270fbccf..3a81c2be5 100644
--- a/src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt
+++ b/src/test/kotlin/org/rust/ide/refactoring/implementMembers/ImplementMembersHandlerTest.kt
@@ -1233,11 +1233,141 @@ class ImplementMembersHandlerTest : RsTestBase() {
         }
     """)
 
+    fun `test default type params 1`() = doTest("""
+        struct S<T = i32>(T);
+        trait Foo {
+            fn foo() -> S;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            /*caret*/
+        }
+    """, listOf(
+        ImplementMemberSelection("foo() -> S", byDefault = true)
+    ), """
+        struct S<T = i32>(T);
+        trait Foo {
+            fn foo() -> S;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            fn foo() -> S {
+                unimplemented!()
+            }
+        }
+    """)
+
+    fun `test default type params 2`() = doTest("""
+        struct S<T = i32>(T);
+        trait Foo {
+            fn foo() -> S<u64>;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            /*caret*/
+        }
+    """, listOf(
+        ImplementMemberSelection("foo() -> S<u64>", byDefault = true)
+    ), """
+        struct S<T = i32>(T);
+        trait Foo {
+            fn foo() -> S<u64>;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            fn foo() -> S<u64> {
+                unimplemented!()
+            }
+        }
+    """)
+
+    fun `test default type params 3`() = doTest("""
+        mod m {
+            pub struct Q;
+        }
+        struct S<T = m::Q>(T);
+        trait Foo {
+            fn foo() -> S;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            /*caret*/
+        }
+    """, listOf(
+        ImplementMemberSelection("foo() -> S", byDefault = true)
+    ), """
+        mod m {
+            pub struct Q;
+        }
+        struct S<T = m::Q>(T);
+        trait Foo {
+            fn foo() -> S;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            fn foo() -> S {
+                unimplemented!()
+            }
+        }
+    """)
+
+    fun `test default type params 4`() = doTest("""
+        struct S<T1 = i32, T2 = i32, T3 = i32>(T1, T2, T3);
+        trait Foo {
+            fn foo() -> S<i32, u32, i32>;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            /*caret*/
+        }
+    """, listOf(
+        ImplementMemberSelection("foo() -> S<i32, u32, i32>", byDefault = true)
+    ), """
+        struct S<T1 = i32, T2 = i32, T3 = i32>(T1, T2, T3);
+        trait Foo {
+            fn foo() -> S<i32, u32, i32>;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            fn foo() -> S<i32, u32> {
+                unimplemented!()
+            }
+        }
+    """)
+
+    fun `test default type params for type alias`() = doTest("""
+        struct S<T = i32>(T);
+        type A<T = u32> = S<T>;
+        trait Foo {
+            fn foo(_: A<i32>) -> A<u32>;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            /*caret*/
+        }
+    """, listOf(
+        ImplementMemberSelection("foo(_: A<i32>) -> A<u32>", byDefault = true)
+    ), """
+        struct S<T = i32>(T);
+        type A<T = u32> = S<T>;
+        trait Foo {
+            fn foo(_: A<i32>) -> A<u32>;
+        }
+        struct Bar;
+        impl Foo for Bar {
+            fn foo(_: A<i32>) -> A {
+                unimplemented!()
+            }
+        }
+    """)
+
     private data class ImplementMemberSelection(val member: String, val byDefault: Boolean, val isSelected: Boolean = byDefault)
 
-    private fun doTest(@Language("Rust") code: String,
-                       chooser: List<ImplementMemberSelection>,
-                       @Language("Rust") expected: String) {
+    private fun doTest(
+        @Language("Rust") code: String,
+        chooser: List<ImplementMemberSelection>,
+        @Language("Rust") expected: String
+    ) {
 
         checkByText(code.trimIndent(), expected.trimIndent()) {
             withMockTraitMemberChooser({ _, all, selectedByDefault ->
@@ -1250,7 +1380,10 @@ class ImplementMembersHandlerTest : RsTestBase() {
         }
     }
 
-    private fun extractSelected(all: List<RsTraitMemberChooserMember>, chooser: List<ImplementMemberSelection>): List<RsTraitMemberChooserMember> {
+    private fun extractSelected(
+        all: List<RsTraitMemberChooserMember>,
+        chooser: List<ImplementMemberSelection>
+    ): List<RsTraitMemberChooserMember> {
         val selected = chooser.filter { it.isSelected }.map { it.member }
         return all.filter { selected.contains(it.formattedText()) }
     }