diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorEquivalenceForOverrides.kt b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorEquivalenceForOverrides.kt new file mode 100644 index 00000000000..abc3b409498 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/DescriptorEquivalenceForOverrides.kt @@ -0,0 +1,111 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.lang.resolve + +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor +import org.jetbrains.jet.lang.descriptors.ClassDescriptor +import org.jetbrains.jet.lang.descriptors.CallableMemberDescriptor +import org.jetbrains.jet.lang.descriptors.Visibilities +import org.jetbrains.jet.lang.descriptors.PackageFragmentDescriptor +import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor +import org.jetbrains.jet.lang.resolve.OverridingUtil.OverrideCompatibilityInfo +import org.jetbrains.jet.lang.resolve + +object DescriptorEquivalenceForOverrides { + + public fun areEquivalent(a: DeclarationDescriptor?, b: DeclarationDescriptor?): Boolean { + return when { + a is ClassDescriptor && + b is ClassDescriptor -> areClassesEquivalent(a, b) + + a is TypeParameterDescriptor && + b is TypeParameterDescriptor -> areTypeParametersEquivalent(a, b) + + a is CallableMemberDescriptor && + b is CallableMemberDescriptor -> areCallableMemberDescriptorsEquivalent(a, b) + + a is PackageFragmentDescriptor && + b is PackageFragmentDescriptor -> (a).fqName == (b).fqName + + else -> a == b + } + } + + private fun areClassesEquivalent(a: ClassDescriptor, b: ClassDescriptor): Boolean { + // type constructors are compared by fqName + return a.getTypeConstructor() == b.getTypeConstructor() + } + + private fun areTypeParametersEquivalent( + a: TypeParameterDescriptor, + b: TypeParameterDescriptor, + equivalentCallables: (DeclarationDescriptor?, DeclarationDescriptor?) -> Boolean = {x, y -> false} + ): Boolean { + if (a == b) return true + if (a.getContainingDeclaration() == b.getContainingDeclaration()) return false + + if (!ownersEquivalent(a, b, equivalentCallables)) return false + + return a.getIndex() == b.getIndex() // We ignore type parameter names + } + + private fun areCallableMemberDescriptorsEquivalent(a: CallableMemberDescriptor, b: CallableMemberDescriptor): Boolean { + if (a == b) return true + if (a.getName() != b.getName()) return false + if (a.getContainingDeclaration() == b.getContainingDeclaration()) return false + + // Distinct locals are not equivalent + if (DescriptorUtils.isLocal(a) || DescriptorUtils.isLocal(b)) return false + + if (!ownersEquivalent(a, b, {x, y -> false})) return false + + val overridingUtil = OverridingUtil.createWithEqualityAxioms @eq { + (c1, c2): Boolean -> + if (c1 == c2) return@eq true + + val d1 = c1.getDeclarationDescriptor() + val d2 = c2.getDeclarationDescriptor() + + if (d1 !is TypeParameterDescriptor || d2 !is TypeParameterDescriptor) return@eq false + + areTypeParametersEquivalent(d1, d2, {x, y -> x == a && y == b}) + } + + return overridingUtil.isOverridableBy(a, b).getResult() == OverrideCompatibilityInfo.Result.OVERRIDABLE + && overridingUtil.isOverridableBy(b, a).getResult() == OverrideCompatibilityInfo.Result.OVERRIDABLE + + } + + private fun ownersEquivalent( + a: DeclarationDescriptor, + b: DeclarationDescriptor, + equivalentCallables: (DeclarationDescriptor?, DeclarationDescriptor?) -> Boolean + ): Boolean { + val aOwner = a.getContainingDeclaration() + val bOwner = b.getContainingDeclaration() + + // This check is needed when we call areTypeParametersEquivalent() from areCallableMemberDescriptorsEquivalent: + // if the type parameter owners are, e.g., functions, we'll go into infinite recursion here + if (aOwner is CallableMemberDescriptor || bOwner is CallableMemberDescriptor) { + return equivalentCallables(aOwner, bOwner) + } + else { + return areEquivalent(aOwner, bOwner) + } + } + +} \ No newline at end of file diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java index b511a33ea7c..cc151f2f595 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/OverrideResolver.java @@ -24,6 +24,7 @@ import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.LinkedMultiMap; import com.intellij.util.containers.MultiMap; +import com.intellij.util.containers.hash.EqualityPolicy; import kotlin.Function1; import kotlin.Unit; import org.jetbrains.annotations.NotNull; @@ -40,6 +41,7 @@ import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.checker.JetTypeChecker; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; import org.jetbrains.jet.lexer.JetTokens; +import org.jetbrains.jet.utils.HashSetUtil; import javax.inject.Inject; import java.util.*; @@ -225,14 +227,37 @@ public class OverrideResolver { @NotNull private static Set filterOverrides( @NotNull Set candidateSet, - @NotNull Function transform, + @NotNull final Function transform, @NotNull Filtering filtering ) { + if (candidateSet.size() <= 1) return candidateSet; + + // In a multi-module project different "copies" of the same class may be present in different libraries, + // that's why we use structural equivalence for members (DescriptorEquivalenceForOverrides). + // Here we filter out structurally equivalent descriptors before processing overrides, because such descriptors + // "override" each other (overrides(f, g) = overrides(g, f) = true) and the code below removes them all from the + // candidates, unless we first compute noDuplicates + Set noDuplicates = HashSetUtil.linkedHashSet( + candidateSet, + new EqualityPolicy() { + @Override + public int getHashCode(D d) { + return DescriptorUtils.getFqName(transform.fun(d).getContainingDeclaration()).hashCode(); + } + + @Override + public boolean isEqual(D d1, D d2) { + CallableDescriptor f = transform.fun(d1); + CallableDescriptor g = transform.fun(d2); + return DescriptorEquivalenceForOverrides.instance$.areEquivalent(f.getOriginal(), g.getOriginal()); + } + }); + Set candidates = Sets.newLinkedHashSet(); outerLoop: - for (D meD : candidateSet) { + for (D meD : noDuplicates) { CallableDescriptor me = transform.fun(meD); - for (D otherD : candidateSet) { + for (D otherD : noDuplicates) { CallableDescriptor other = transform.fun(otherD); if (me == other) continue; if (filtering == Filtering.RETAIN_OVERRIDING) { @@ -259,13 +284,22 @@ public class OverrideResolver { } candidates.add(meD); } + + assert !candidates.isEmpty() : "All candidates filtered out from " + candidateSet; + return candidates; } + // check whether f overrides g public static boolean overrides(@NotNull D f, @NotNull D g) { + // This first check cover the case of duplicate classes in different modules: + // when B is defined in modules m1 and m2, and C (indirectly) inherits from both versions, + // we'll be getting sets of members that do not override each other, but are structurally equivalent. + // As other code relies on no equal descriptors passed here, we guard against f == g, but this may not be necessary + if (!f.equals(g) && DescriptorEquivalenceForOverrides.instance$.areEquivalent(f.getOriginal(), g.getOriginal())) return true; CallableDescriptor originalG = g.getOriginal(); for (D overriddenFunction : getAllOverriddenDescriptors(f)) { - if (originalG.equals(overriddenFunction.getOriginal())) return true; + if (DescriptorEquivalenceForOverrides.instance$.areEquivalent(originalG, overriddenFunction.getOriginal())) return true; } return false; } diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParams.kt new file mode 100644 index 00000000000..5d239df76e7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParams.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T) +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: X) + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: T) +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo("") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsBoundMismatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsBoundMismatch.kt new file mode 100644 index 00000000000..538eec35c9d --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsBoundMismatch.kt @@ -0,0 +1,38 @@ +// !DIAGNOSTICS: -BASE_WITH_NULLABLE_UPPER_BOUND -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T?) +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: X?) + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait Tr + +public trait B { + public fun foo(a: T?) +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo(null) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt new file mode 100644 index 00000000000..bd30e5bc977 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -BASE_WITH_NULLABLE_UPPER_BOUND + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T?) +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: X?) + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B<_, T> { + public fun foo(a: T?) +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo(null) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsNameMismatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsNameMismatch.kt new file mode 100644 index 00000000000..2478762997c --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsNameMismatch.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T) +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: X) + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: T1) +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?, y: Y) { + if (b is C) { + b?.foo(y) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.kt new file mode 100644 index 00000000000..46478cdea13 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.kt @@ -0,0 +1,36 @@ +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T, b : R) +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: Any?, b : R) + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: T, b: R) +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?, c: C) { + b?.foo(1, 1) + c.foo(1, 1) + if (b is C) { + b?.foo(1, 1) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differenceInParamNames.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differenceInParamNames.kt new file mode 100644 index 00000000000..af81b9ffb8a --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differenceInParamNames.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: Int, b: String): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: Int, b: String): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a1: Int, b1: String): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo(1, "") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentGenericsInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentGenericsInParams.kt new file mode 100644 index 00000000000..dd765ec7db5 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentGenericsInParams.kt @@ -0,0 +1,43 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m0 +// FILE: a.kt +package p + +public trait G1 +public trait G2 + +// MODULE: m1(m0) +// FILE: a.kt +package p + +public trait B { + public fun foo(a: G1?, b: G2?) +} + +// MODULE: m2(m1, m0) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: G1?, b: G2?) + +} + +// MODULE: m3(m0) +// FILE: b.kt +package p + +public trait B { + public fun foo(a: G1?, b: G2?) +} + +// MODULE: m4(m3, m2, m0) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo(null, null) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.kt new file mode 100644 index 00000000000..6384bf6263c --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.kt @@ -0,0 +1,34 @@ +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: Int, b: String): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: Int, b: String): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: Int, b: String, c: Int = 0): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo(1, "") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentReturnTypes.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentReturnTypes.kt new file mode 100644 index 00000000000..d8fa721ba3d --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentReturnTypes.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun getParent(): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun getParent(): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun getParent(): Int +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.getParent() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/extensionMatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/extensionMatch.kt new file mode 100644 index 00000000000..1aa5dc3c464 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/extensionMatch.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun String.getParent(): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public class C : B { + override fun String.getParent(): B? = null + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun String.getParent(): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun B.test() { + if (this is C) { + "".getParent() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParams.kt new file mode 100644 index 00000000000..c536d7c9f53 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParams.kt @@ -0,0 +1,42 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: T): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo("") + } +} + +fun test1(b: B?) { + if (b is C) { + b?.foo("") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsBoundsMismatch.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsBoundsMismatch.kt new file mode 100644 index 00000000000..11e29337334 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsBoundsMismatch.kt @@ -0,0 +1,40 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: T): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait Tr + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + // hard to find parameters for an ambiguous call, so we rely on NONE_APPLICABLE here + // as opposed to diagnostics for a single unmatched candidate + b?.foo() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsEqNull.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsEqNull.kt new file mode 100644 index 00000000000..fd4333fa1f0 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsEqNull.kt @@ -0,0 +1,52 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: T): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b == null) return + b?.foo("") +} + +fun test1(b: B?) { + if (b != null) { + b?.foo("") + } +} + +fun test2(b: B?) { + if (b == null) return + b?.foo("") +} + +fun test3(b: B?) { + if (b != null) { + b?.foo("") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsNotIs.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsNotIs.kt new file mode 100644 index 00000000000..6a8340a00aa --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsNotIs.kt @@ -0,0 +1,40 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: T): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: T): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b !is C) return + b?.foo("") +} + +fun test1(b: B?) { + if (b !is C) return + b?.foo("") +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noGenericsInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noGenericsInParams.kt new file mode 100644 index 00000000000..f76da0295a7 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noGenericsInParams.kt @@ -0,0 +1,35 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: Int, b: String): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: Int, b: String): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: Int, b: String): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo(1, "") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noParams.kt new file mode 100644 index 00000000000..ef8ad5e5419 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noParams.kt @@ -0,0 +1,36 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun getParent(): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public class C : B { + override fun getParent(): B? = null + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun getParent(): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.getParent() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/sameGenericsInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/sameGenericsInParams.kt new file mode 100644 index 00000000000..00d6327e6a9 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/sameGenericsInParams.kt @@ -0,0 +1,43 @@ +// !DIAGNOSTICS: -UNNECESSARY_SAFE_CALL + +// MODULE: m0 +// FILE: a.kt +package p + +public trait G1 +public trait G2 + +// MODULE: m1(m0) +// FILE: a.kt +package p + +public trait B { + public fun foo(a: G1, b: G2): B? +} + +// MODULE: m2(m1, m0) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: G1, b: G2): B? + +} + +// MODULE: m3(m0) +// FILE: b.kt +package p + +public trait B { + public fun foo(a: G1, b: G2): B? +} + +// MODULE: m4(m3, m2, m0) +// FILE: c.kt +import p.* + +fun test(b: B?, a: G1, b1: G2) { + if (b is C) { + b?.foo(a, b1) + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt new file mode 100644 index 00000000000..3b744395658 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt @@ -0,0 +1,38 @@ +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun getParent(): B? +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun getParent(): B? + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun getParent(): B? +} + +public trait D : B { + override fun getParent(): B? +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C && b is D) { + b?.getParent() + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt new file mode 100644 index 00000000000..1e1f7af281f --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt @@ -0,0 +1,34 @@ +// MODULE: m1 +// FILE: a.kt +package p + +public trait B { + public fun foo(a: T) +} + +// MODULE: m2(m1) +// FILE: b.kt +package p + +public trait C : B { + override fun foo(a: String) + +} + +// MODULE: m3 +// FILE: b.kt +package p + +public trait B { + public fun foo(a: String) +} + +// MODULE: m4(m3, m2) +// FILE: c.kt +import p.* + +fun test(b: B?) { + if (b is C) { + b?.foo("") + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/differentSuperTraits.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/differentSuperTraits.kt new file mode 100644 index 00000000000..e464dd3e5af --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/differentSuperTraits.kt @@ -0,0 +1,31 @@ +// MODULE: m1 +// FILE: x.kt +package p + +public trait Base { + public fun foo() {} +} + +public trait A : Base { + override fun foo() {} +} + +public trait C : A + + +// MODULE: m2 +// FILE: x.kt +package p + +public trait Base { + public fun foo() {} +} + +public trait B : Base + +// MODULE: m3(m1, m2) +// FILE: x.kt + +import p.* + +class Foo: C, B \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTrait.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTrait.kt new file mode 100644 index 00000000000..beec4542a11 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTrait.kt @@ -0,0 +1,26 @@ +// MODULE: m1 +// FILE: x.kt +package p + +public trait Base { + public fun foo() {} +} + +public trait A : Base + +// MODULE: m2 +// FILE: x.kt +package p + +public trait Base { + public fun foo() {} +} + +public trait B : Base + +// MODULE: m3(m1, m2) +// FILE: x.kt + +import p.* + +class Foo: A, B \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitDifferentBounds.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitDifferentBounds.kt new file mode 100644 index 00000000000..fe614275034 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitDifferentBounds.kt @@ -0,0 +1,29 @@ +// MODULE: m1 +// FILE: x.kt +package p + +public trait Base { + public fun foo(t: Array) {} +} + +public trait A : Base + +// MODULE: m2 +// FILE: x.kt +package p + +public trait Base { + public fun foo(t: Array) {} +} + +public trait B : Base + +// MODULE: m3(m1, m2) +// FILE: x.kt + +import p.* + +class Foo: A, B { + override fun foo(t: Array) {} + override fun foo(t: Array) {} +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitGenerics.kt b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitGenerics.kt new file mode 100644 index 00000000000..ce74602efd8 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitGenerics.kt @@ -0,0 +1,26 @@ +// MODULE: m1 +// FILE: x.kt +package p + +public trait Base { + public fun foo(t: T) {} +} + +public trait A : Base + +// MODULE: m2 +// FILE: x.kt +package p + +public trait Base { + public fun foo(t: T) {} +} + +public trait B : Base + +// MODULE: m3(m1, m2) +// FILE: x.kt + +import p.* + +class Foo: A, B \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java index 91c77f1c4e5..3b1ba774e8d 100644 --- a/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/checkers/JetDiagnosticsTestGenerated.java @@ -4687,7 +4687,7 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } @TestMetadata("compiler/testData/diagnostics/tests/multimodule") - @InnerTestClasses({Multimodule.DuplicateClass.class}) + @InnerTestClasses({Multimodule.DuplicateClass.class, Multimodule.DuplicateMethod.class, Multimodule.DuplicateSuper.class}) public static class Multimodule extends AbstractJetDiagnosticsTest { public void testAllFilesPresentInMultimodule() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/multimodule"), Pattern.compile("^(.+)\\.kt$"), true); @@ -4771,10 +4771,143 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest { } + @TestMetadata("compiler/testData/diagnostics/tests/multimodule/duplicateMethod") + public static class DuplicateMethod extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInDuplicateMethod() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/multimodule/duplicateMethod"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("classGenericsInParams.kt") + public void testClassGenericsInParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParams.kt"); + } + + @TestMetadata("classGenericsInParamsBoundMismatch.kt") + public void testClassGenericsInParamsBoundMismatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsBoundMismatch.kt"); + } + + @TestMetadata("classGenericsInParamsIndexMismatch.kt") + public void testClassGenericsInParamsIndexMismatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsIndexMismatch.kt"); + } + + @TestMetadata("classGenericsInParamsNameMismatch.kt") + public void testClassGenericsInParamsNameMismatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classGenericsInParamsNameMismatch.kt"); + } + + @TestMetadata("classVsFunctionGenericsInParamsMismatch.kt") + public void testClassVsFunctionGenericsInParamsMismatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/classVsFunctionGenericsInParamsMismatch.kt"); + } + + @TestMetadata("differenceInParamNames.kt") + public void testDifferenceInParamNames() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differenceInParamNames.kt"); + } + + @TestMetadata("differentGenericsInParams.kt") + public void testDifferentGenericsInParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentGenericsInParams.kt"); + } + + @TestMetadata("differentNumberOfParams.kt") + public void testDifferentNumberOfParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentNumberOfParams.kt"); + } + + @TestMetadata("differentReturnTypes.kt") + public void testDifferentReturnTypes() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/differentReturnTypes.kt"); + } + + @TestMetadata("extensionMatch.kt") + public void testExtensionMatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/extensionMatch.kt"); + } + + @TestMetadata("functionGenericsInParams.kt") + public void testFunctionGenericsInParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParams.kt"); + } + + @TestMetadata("functionGenericsInParamsBoundsMismatch.kt") + public void testFunctionGenericsInParamsBoundsMismatch() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsBoundsMismatch.kt"); + } + + @TestMetadata("functionGenericsInParamsEqNull.kt") + public void testFunctionGenericsInParamsEqNull() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsEqNull.kt"); + } + + @TestMetadata("functionGenericsInParamsNotIs.kt") + public void testFunctionGenericsInParamsNotIs() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/functionGenericsInParamsNotIs.kt"); + } + + @TestMetadata("noGenericsInParams.kt") + public void testNoGenericsInParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noGenericsInParams.kt"); + } + + @TestMetadata("noParams.kt") + public void testNoParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/noParams.kt"); + } + + @TestMetadata("sameGenericsInParams.kt") + public void testSameGenericsInParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/sameGenericsInParams.kt"); + } + + @TestMetadata("simpleWithInheritance.kt") + public void testSimpleWithInheritance() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/simpleWithInheritance.kt"); + } + + @TestMetadata("substitutedGenericInParams.kt") + public void testSubstitutedGenericInParams() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateMethod/substitutedGenericInParams.kt"); + } + + } + + @TestMetadata("compiler/testData/diagnostics/tests/multimodule/duplicateSuper") + public static class DuplicateSuper extends AbstractJetDiagnosticsTest { + public void testAllFilesPresentInDuplicateSuper() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/diagnostics/tests/multimodule/duplicateSuper"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("differentSuperTraits.kt") + public void testDifferentSuperTraits() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateSuper/differentSuperTraits.kt"); + } + + @TestMetadata("sameSuperTrait.kt") + public void testSameSuperTrait() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTrait.kt"); + } + + @TestMetadata("sameSuperTraitDifferentBounds.kt") + public void testSameSuperTraitDifferentBounds() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitDifferentBounds.kt"); + } + + @TestMetadata("sameSuperTraitGenerics.kt") + public void testSameSuperTraitGenerics() throws Exception { + doTest("compiler/testData/diagnostics/tests/multimodule/duplicateSuper/sameSuperTraitGenerics.kt"); + } + + } + public static Test innerSuite() { TestSuite suite = new TestSuite("Multimodule"); suite.addTestSuite(Multimodule.class); suite.addTestSuite(DuplicateClass.class); + suite.addTestSuite(DuplicateMethod.class); + suite.addTestSuite(DuplicateSuper.class); return suite; } } diff --git a/compiler/util/src/org/jetbrains/jet/utils/HashSetUtil.java b/compiler/util/src/org/jetbrains/jet/utils/HashSetUtil.java new file mode 100644 index 00000000000..853fa7e352f --- /dev/null +++ b/compiler/util/src/org/jetbrains/jet/utils/HashSetUtil.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.utils; + +import com.intellij.util.containers.hash.EqualityPolicy; +import com.intellij.util.containers.hash.LinkedHashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.Map; +import java.util.Set; + +public class HashSetUtil { + @NotNull + public static Set linkedHashSet(@NotNull Set set, @NotNull EqualityPolicy policy) { + // this implementation of LinkedHashMap doesn't admit nulls as values + Map map = new LinkedHashMap(policy); + for (T t : set) { + map.put(t, ""); + } + return map.keySet(); + } +}