diff --git a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt
index bd3242c5271..373e213d3f6 100644
--- a/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt
+++ b/core/descriptors/src/org/jetbrains/kotlin/types/TypeUtils.kt
@@ -97,3 +97,9 @@ public fun JetType.isArrayOfJavaLangClass(): Boolean =
KotlinBuiltIns.isArray(this) && getArguments().firstOrNull()?.getType()?.isJavaLangClass() ?: false
public fun JetType.isJavaLangClassOrArray(): Boolean = isJavaLangClass() || isArrayOfJavaLangClass()
+
+public fun JetTypeChecker.equalTypesOrNulls(type1: JetType?, type2: JetType?): Boolean {
+ if (type1 identityEquals type2) return true
+ if (type1 == null || type2 == null) return false
+ return equalTypes(type1, type2)
+}
\ No newline at end of file
diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt
index bc3721cd6a4..3a7e1d7fe41 100644
--- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt
+++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/CompletionUtils.kt
@@ -29,7 +29,6 @@ import org.jetbrains.kotlin.idea.caches.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.completion.handlers.CastReceiverInsertHandler
import org.jetbrains.kotlin.idea.completion.handlers.WithTailInsertHandler
import org.jetbrains.kotlin.idea.util.FuzzyType
-import org.jetbrains.kotlin.idea.util.fuzzyReturnType
import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstance
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
@@ -39,6 +38,9 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.inline.InlineUtil
+import org.jetbrains.kotlin.types.TypeConstructor
+import org.jetbrains.kotlin.types.checker.JetTypeChecker
+import org.jetbrains.kotlin.types.typeUtil.equalTypesOrNulls
import java.util.ArrayList
enum class ItemPriority {
@@ -144,12 +146,28 @@ fun descriptorsEqualWithSubstitution(descriptor1: DeclarationDescriptor?, descri
// optimization:
if (descriptor1 == descriptor1.getOriginal() && descriptor2 == descriptor2.getOriginal()) return true
- if (descriptor1.getReturnType() != descriptor2.getReturnType()) return false
+ val typeChecker = JetTypeChecker.withAxioms(object: JetTypeChecker.TypeConstructorEquality {
+ override fun equals(a: TypeConstructor, b: TypeConstructor): Boolean {
+ val typeParam1 = a.getDeclarationDescriptor() as? TypeParameterDescriptor
+ val typeParam2 = b.getDeclarationDescriptor() as? TypeParameterDescriptor
+ if (typeParam1 != null
+ && typeParam2 != null
+ && typeParam1.getContainingDeclaration() == descriptor1
+ && typeParam2.getContainingDeclaration() == descriptor2) {
+ return typeParam1.getIndex() == typeParam2.getIndex()
+ }
+
+ return a == b
+ }
+ })
+
+ if (!typeChecker.equalTypesOrNulls(descriptor1.getReturnType(), descriptor2.getReturnType())) return false
+
val parameters1 = descriptor1.getValueParameters()
val parameters2 = descriptor2.getValueParameters()
if (parameters1.size() != parameters2.size()) return false
- for (i in parameters1.indices) {
- if (parameters1[i].getType() != parameters2[i].getType()) return false
+ for ((param1, param2) in parameters1.zip(parameters2)) {
+ if (!typeChecker.equalTypes(param1.getType(), param2.getType())) return false
}
return true
}
diff --git a/idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/NoGenericFunDuplication.dependency.kt b/idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/NoGenericFunDuplication.dependency.kt
new file mode 100644
index 00000000000..01925ed7f2f
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/NoGenericFunDuplication.dependency.kt
@@ -0,0 +1,5 @@
+package dependency
+
+class MyPair(public val first: A, public val second: B)
+
+public fun A.pair(that: B): MyPair = MyPair(this, that)
diff --git a/idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/NoGenericFunDuplication.kt b/idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/NoGenericFunDuplication.kt
new file mode 100644
index 00000000000..418e3b654b2
--- /dev/null
+++ b/idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/NoGenericFunDuplication.kt
@@ -0,0 +1,9 @@
+// this test tests correct substitution equality checking for generic functions
+import dependency.pair
+
+fun foo() {
+ 1 pai
+}
+
+// EXIST: pair
+// NUMBER: 1
diff --git a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java
index 0d7b8798085..106c0676868 100644
--- a/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java
+++ b/idea/idea-completion/tests/org/jetbrains/kotlin/idea/completion/test/MultiFileJvmBasicCompletionTestGenerated.java
@@ -131,6 +131,12 @@ public class MultiFileJvmBasicCompletionTestGenerated extends AbstractMultiFileJ
doTest(fileName);
}
+ @TestMetadata("NoGenericFunDuplication")
+ public void testNoGenericFunDuplication() throws Exception {
+ String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/NoGenericFunDuplication/");
+ doTest(fileName);
+ }
+
@TestMetadata("NotImportedExtensionForImplicitReceiver")
public void testNotImportedExtensionForImplicitReceiver() throws Exception {
String fileName = JetTestUtils.navigationMetadata("idea/idea-completion/testData/basic/multifile/NotImportedExtensionForImplicitReceiver/");