Don't report incompatibility expect/actual errors on usual overloads

Divide incompatibility on two groups: strong and weak. Strong incompatibility means that if declaration with such incompatibility has no `actual` modifier then it's considered as usual overload and we'll not report any error on it.

 #KT-20540 Fixed
 #KT-20680 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2017-10-11 19:28:53 +03:00
parent eb828e3497
commit d0cbd73307
13 changed files with 258 additions and 10 deletions
@@ -84,7 +84,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
val compatibility = findActualForExpected(descriptor, platformModule) ?: return
val shouldReportError =
compatibility.isEmpty() ||
compatibility.allStrongIncompatibilities() ||
Compatible !in compatibility && compatibility.values.flatMapTo(hashSetOf()) { it }.all { actual ->
val expectedOnes = findExpectedForActual(actual, descriptor.module)
expectedOnes != null && Compatible in expectedOnes.keys
@@ -98,6 +98,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
}
}
fun Map<out Compatibility, Collection<MemberDescriptor>>.allStrongIncompatibilities(): Boolean =
this.keys.all { it is Incompatible && it.kind == Compatibility.IncompatibilityKind.STRONG }
private fun findActualForExpected(expected: MemberDescriptor, platformModule: ModuleDescriptor): Map<Compatibility, List<MemberDescriptor>>? {
return when (expected) {
is CallableMemberDescriptor -> {
@@ -131,7 +134,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
val hasActualModifier = descriptor.isActual && reportOn.hasActualModifier()
if (!hasActualModifier) {
if (compatibility.isEmpty()) return
if (compatibility.allStrongIncompatibilities()) return
if (Compatible in compatibility) {
// we suppress error, because annotation classes can only have one constructor and it's a 100% boilerplate
@@ -163,6 +166,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
val actualMember = incompatibility.values.singleOrNull()?.singleOrNull()
return actualMember != null &&
actualMember.isExplicitActualDeclaration() &&
!incompatibility.allStrongIncompatibilities() &&
findExpectedForActual(actualMember, expectedMember.module)?.values?.singleOrNull()?.singleOrNull() == expectedMember
}
@@ -293,16 +297,21 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
}
sealed class Compatibility {
// For IncompatibilityKind.STRONG `actual` declaration is considered as overload and error reports on expected declaration
enum class IncompatibilityKind {
WEAK, STRONG
}
// Note that the reason is used in the diagnostic output, see PlatformIncompatibilityDiagnosticRenderer
sealed class Incompatible(val reason: String?) : Compatibility() {
sealed class Incompatible(val reason: String?, val kind: IncompatibilityKind = IncompatibilityKind.WEAK) : Compatibility() {
// Callables
object ParameterShape : Incompatible("parameter shapes are different (extension vs non-extension)")
object ParameterCount : Incompatible("number of value parameters is different")
object ParameterCount : Incompatible("number of value parameters is different", IncompatibilityKind.STRONG)
object TypeParameterCount : Incompatible("number of type parameters is different")
object ParameterTypes : Incompatible("parameter types are different")
object ParameterTypes : Incompatible("parameter types are different", IncompatibilityKind.STRONG)
object ReturnType : Incompatible("return type is different")
object ParameterNames : Incompatible("parameter names are different")
@@ -42,6 +42,13 @@ public final expect annotation class Foo6 : kotlin.Annotation {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final expect annotation class Foo7 : kotlin.Annotation {
public constructor Foo7()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
// -- Module: <m2-jvm> --
package
@@ -89,5 +96,14 @@ public final actual annotation class Foo5 : kotlin.Annotation {
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final actual annotation class Foo6 : kotlin.Annotation {
public constructor Foo6(/*0*/ s: kotlin.String = ...)
public final val s: kotlin.String
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public typealias Foo1 = Bar1
public typealias Foo4 = Bar2
public typealias Foo7 = Bar2
@@ -9,6 +9,8 @@ expect class Foo3
expect class Bar1()
expect class Bar2()
expect class Bar3()
expect class Bar4()
expect class Bar5()
// MODULE: m2-jvm(m1-common)
@@ -30,6 +32,14 @@ actual class Foo1(val s: String)
actual class Foo2(val p: String = "value", <!UNUSED_PARAMETER!>i<!>: Int)
actual typealias Foo3 = JavaFoo
actual class Bar1<!ACTUAL_WITHOUT_EXPECT!>(val s: String)<!>
actual class Bar2<!ACTUAL_WITHOUT_EXPECT!>(val p: String = "value", <!UNUSED_PARAMETER!>i<!>: Int)<!>
<!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>actual typealias Bar3 = JavaBar<!>
actual class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Bar1<!>(val s: String)
actual class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Bar2<!>(val p: String = "value", <!UNUSED_PARAMETER!>i<!>: Int)
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Bar3<!> = JavaBar
actual class Bar4(val s: String) {
<!ACTUAL_MISSING!>constructor() : this("")<!>
}
actual class Bar5 {
actual constructor()
constructor(s: String)
}
@@ -22,6 +22,20 @@ public final expect class Bar3 {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final expect class Bar4 {
public constructor Bar4()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final expect class Bar5 {
public constructor Bar5()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final expect class Foo1 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
@@ -60,6 +74,23 @@ public final actual class Bar2 {
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final actual class Bar4 {
public constructor Bar4()
public constructor Bar4(/*0*/ s: kotlin.String)
public final val s: kotlin.String
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final actual class Bar5 {
public constructor Bar5()
public constructor Bar5(/*0*/ s: kotlin.String)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final actual class Foo1 {
public constructor Foo1(/*0*/ s: kotlin.String)
public final val s: kotlin.String
@@ -0,0 +1,27 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_PARAMETER
// MODULE: m1-common
// FILE: common.kt
expect fun foo1(x: Int)
expect fun foo2<!JVM:NO_ACTUAL_FOR_EXPECT!>(x: Int)<!>
expect class NoArgConstructor()
// MODULE: m2-jvm(m1-common)
// FILE: jvm.kt
actual fun foo1(x: Int) {}
fun foo1(x: Int, y: Int) {}
fun foo1(x: String) {}
fun foo2(x: Int, y: Int) {}
fun foo2(x: String) {}
actual class NoArgConstructor {
actual constructor()
actual constructor<!ACTUAL_WITHOUT_EXPECT!>(x: Int)<!>
constructor(x: String)
}
@@ -0,0 +1,31 @@
// -- Module: <m1-common> --
package
public expect fun foo1(/*0*/ x: kotlin.Int): kotlin.Unit
public expect fun foo2(/*0*/ x: kotlin.Int): kotlin.Unit
public final expect class NoArgConstructor {
public constructor NoArgConstructor()
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
// -- Module: <m2-jvm> --
package
public actual fun foo1(/*0*/ x: kotlin.Int): kotlin.Unit
public fun foo1(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int): kotlin.Unit
public fun foo1(/*0*/ x: kotlin.String): kotlin.Unit
public fun foo2(/*0*/ x: kotlin.Int, /*1*/ y: kotlin.Int): kotlin.Unit
public fun foo2(/*0*/ x: kotlin.String): kotlin.Unit
public final actual class NoArgConstructor {
public constructor NoArgConstructor()
public constructor NoArgConstructor(/*0*/ x: kotlin.Int)
public constructor NoArgConstructor(/*0*/ x: kotlin.String)
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -0,0 +1,23 @@
// !LANGUAGE: +MultiPlatformProjects
// !DIAGNOSTICS: -UNUSED_PARAMETER
// MODULE: m1-common
// FILE: common.kt
expect class Foo1
expect class Foo2
expect fun foo1(): Int
expect fun foo2(): Int
expect fun foo3<!JVM:NO_ACTUAL_FOR_EXPECT!>()<!>: Int
// MODULE: m2-jvm(m1-common)
// FILE: jvm.kt
<!ACTUAL_WITHOUT_EXPECT!>interface<!> Foo1
actual <!ACTUAL_WITHOUT_EXPECT!>interface<!> Foo2
actual fun foo1(): <!ACTUAL_WITHOUT_EXPECT!>String<!> = ""
fun <!ACTUAL_MISSING!>foo2<!>(): Int = 0
fun foo3(x: Int): String = ""
@@ -0,0 +1,38 @@
// -- Module: <m1-common> --
package
public expect fun foo1(): kotlin.Int
public expect fun foo2(): kotlin.Int
public expect fun foo3(): kotlin.Int
public final expect class Foo1 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public final expect class Foo2 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
// -- Module: <m2-jvm> --
package
public actual fun foo1(): kotlin.String
public fun foo2(): kotlin.Int
public fun foo3(/*0*/ x: kotlin.Int): kotlin.String
public interface Foo1 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
public actual interface Foo2 {
public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean
public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int
public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String
}
@@ -11,8 +11,8 @@ expect fun f4(s: () -> String)
expect inline fun f5(s: () -> String)
expect inline fun f6(crossinline s: () -> String)
expect fun f7(x: Any)
expect fun f8(vararg x: Any)
expect fun f7<!JVM:NO_ACTUAL_FOR_EXPECT!>(x: Any)<!>
expect fun f8<!JVM:NO_ACTUAL_FOR_EXPECT!>(vararg x: Any)<!>
// MODULE: m2-jvm(m1-common)
// FILE: jvm.kt
@@ -5,6 +5,15 @@ Output:
-- JVM --
Exit code: COMPILATION_ERROR
Output:
compiler/testData/multiplatform/classScopes/constructorIncorrectSignature/jvm.kt:1:14: error: actual class 'Foo' has no corresponding members for expected class members:
public constructor Foo(s: String)
The following declaration is incompatible because parameter types are different:
public constructor Foo(s: Array<String>)
actual class Foo {
^
compiler/testData/multiplatform/classScopes/constructorIncorrectSignature/jvm.kt:2:23: error: actual constructor of 'Foo' has no corresponding expected declaration
The following declaration is incompatible because parameter types are different:
public constructor Foo(s: String)
@@ -8,6 +8,36 @@ expect fun f21(c: suspend Unit.() -> Unit)
-- JVM --
Exit code: COMPILATION_ERROR
Output:
compiler/testData/multiplatform/incompatibleCallables/common.kt:5:14: error: expected function 'f3' has no actual declaration in module
The following declaration is incompatible because parameter types are different:
public actual fun f3(name: Double): Unit
expect fun f3(name: String)
^
compiler/testData/multiplatform/incompatibleCallables/common.kt:6:24: error: expected function 'f3ext' has no actual declaration in module
The following declaration is incompatible because parameter types are different:
public actual fun Double.f3ext(): Unit
expect fun String.f3ext()
^
compiler/testData/multiplatform/incompatibleCallables/common.kt:12:14: error: expected function 'f6' has no actual declaration in module
The following declaration is incompatible because number of value parameters is different:
public actual fun f6(p2: Int): Unit
expect fun f6(p1: String, p2: Int)
^
compiler/testData/multiplatform/incompatibleCallables/common.kt:32:15: error: expected function 'f21' has no actual declaration in module
The following declaration is incompatible because parameter types are different:
public actual fun f21(c: Unit.() -> Unit): Unit
expect fun f21(c: suspend Unit.() -> Unit)
^
compiler/testData/multiplatform/incompatibleCallables/common.kt:33:15: error: expected function 'f22' has no actual declaration in module
The following declaration is incompatible because parameter types are different:
public actual fun f22(c: suspend Unit.() -> Unit): Unit
expect fun f22(c: Unit.() -> Unit)
^
compiler/testData/multiplatform/incompatibleCallables/jvm.kt:1:18: error: actual function 'f1' has no corresponding expected declaration
The following declaration is incompatible because return type is different:
public expect fun f1(): Unit
@@ -14127,6 +14127,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName);
}
@TestMetadata("expectDeclarationWithStrongIncompatibilities.kt")
public void testExpectDeclarationWithStrongIncompatibilities() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithStrongIncompatibilities.kt");
doTest(fileName);
}
@TestMetadata("expectDeclarationWithWeakIncompatibilities.kt")
public void testExpectDeclarationWithWeakIncompatibilities() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithWeakIncompatibilities.kt");
doTest(fileName);
}
@TestMetadata("explicitConstructorDelegation.kt")
public void testExplicitConstructorDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/explicitConstructorDelegation.kt");
@@ -14127,6 +14127,18 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
doTest(fileName);
}
@TestMetadata("expectDeclarationWithStrongIncompatibilities.kt")
public void testExpectDeclarationWithStrongIncompatibilities() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithStrongIncompatibilities.kt");
doTest(fileName);
}
@TestMetadata("expectDeclarationWithWeakIncompatibilities.kt")
public void testExpectDeclarationWithWeakIncompatibilities() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/expectDeclarationWithWeakIncompatibilities.kt");
doTest(fileName);
}
@TestMetadata("explicitConstructorDelegation.kt")
public void testExplicitConstructorDelegation() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/explicitConstructorDelegation.kt");