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:
+14
-5
@@ -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")
|
||||
|
||||
+16
@@ -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
|
||||
|
||||
+13
-3
@@ -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)
|
||||
}
|
||||
+31
@@ -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
|
||||
|
||||
+27
@@ -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)
|
||||
}
|
||||
+31
@@ -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
|
||||
}
|
||||
+23
@@ -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 = ""
|
||||
+38
@@ -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
|
||||
}
|
||||
+2
-2
@@ -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
|
||||
|
||||
+9
@@ -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");
|
||||
|
||||
+12
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user