ActualClassifierMustHasTheSameMembersAsNonFinalExpectClassifierChecker: reuse "expect-actual" matcher
In scope of: KT-22841 Review: https://jetbrains.team/p/kt/reviews/11867/timeline Reduce complexity by reusing "expect-actual matcher" (namely `AbstractExpectActualCompatibilityChecker.getCallablesCompatibility`) The current solution has worse algorithmic complexity. Previously it was O(n) in the best case, where `n` is a number of members. Now, it's O(m^2), where `m` is number of members in one overload group. But we prefer to have worse complexity but reuse expect-actual matcher, number of elements in one overload group shall not be big on real world examples. The previous logic was non-trivial because it compared types with with double comparison in `equals`.
This commit is contained in:
+9
-3
@@ -1297,9 +1297,15 @@ public class FirOldFrontendMPPDiagnosticsWithLightTreeTestGenerated extends Abst
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias.kt");
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias_finalMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_openMember.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias_openMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias_openMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+9
-3
@@ -1297,9 +1297,15 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias.kt");
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias_finalMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_openMember.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias_openMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias_openMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+48
-175
@@ -10,16 +10,16 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.diagnostics.Errors
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.calls.mpp.AbstractExpectActualCompatibilityChecker
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperInterfaces
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.*
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.model.TypeSubstitutorMarker
|
||||
import java.util.*
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@@ -142,198 +142,71 @@ private fun calculateExpectActualScopeDiff(
|
||||
parentSubstitutor = null
|
||||
)
|
||||
|
||||
val expectClassCallables = expect.unsubstitutedMemberScope
|
||||
.extractNonPrivateCallables(classTypeSubstitutor, ExpectActual.EXPECT, matchingContext)
|
||||
val actualClassCallables = actual.unsubstitutedMemberScope
|
||||
.extractNonPrivateCallables(classTypeSubstitutor, ExpectActual.ACTUAL, matchingContext)
|
||||
.filter { it.descriptor.kind.isReal } // Filter out fake-overrides from actual because we compare list of supertypes separately anyway
|
||||
val expectClassCallables = expect.unsubstitutedMemberScope.extractNonPrivateCallables()
|
||||
val actualClassCallables = actual.unsubstitutedMemberScope.extractNonPrivateCallables()
|
||||
.filter { it.kind.isReal } // Filter out fake-overrides from actual because we compare list of supertypes separately anyway
|
||||
|
||||
val nameAndKindToExpectCallable = expectClassCallables.groupBy { it.name to it.kind }
|
||||
val nameAndKindToExpectCallables = expectClassCallables.groupBy { it.name to it.functionVsPropertyKind }
|
||||
|
||||
return (actualClassCallables - expectClassCallables).asSequence()
|
||||
.flatMap { unmatchedActualCallable ->
|
||||
when (val expectCallablesWithTheSameNameAndKind =
|
||||
nameAndKindToExpectCallable[unmatchedActualCallable.name to unmatchedActualCallable.kind]) {
|
||||
null -> listOf(ExpectActualMemberDiff.Kind.NonPrivateCallableAdded)
|
||||
else -> expectCallablesWithTheSameNameAndKind.map {
|
||||
calculateExpectActualMemberDiffKind(
|
||||
expect = it,
|
||||
actual = unmatchedActualCallable,
|
||||
checkParameterNames = unmatchedActualCallable.descriptor.hasStableParameterNames()
|
||||
return actualClassCallables.flatMap { actualMember ->
|
||||
val potentialExpects = nameAndKindToExpectCallables[actualMember.name to actualMember.functionVsPropertyKind]
|
||||
if (potentialExpects.isNullOrEmpty()) {
|
||||
listOf(ExpectActualMemberDiff.Kind.NonPrivateCallableAdded)
|
||||
} else {
|
||||
potentialExpects
|
||||
.map { expectMember ->
|
||||
val substitutor = matchingContext.createExpectActualTypeParameterSubstitutor(
|
||||
expectMember.typeParameters,
|
||||
actualMember.typeParameters,
|
||||
classTypeSubstitutor
|
||||
)
|
||||
AbstractExpectActualCompatibilityChecker.getCallablesCompatibility(
|
||||
expectMember,
|
||||
actualMember,
|
||||
substitutor,
|
||||
expect,
|
||||
actual,
|
||||
matchingContext
|
||||
)
|
||||
}
|
||||
}.filterNotNull().map { kind -> ExpectActualMemberDiff(kind, unmatchedActualCallable.descriptor, expect) }
|
||||
.takeIf { kinds -> kinds.all { it != ExpectActualCompatibility.Compatible } }
|
||||
.orEmpty()
|
||||
.map {
|
||||
when (it) {
|
||||
is ExpectActualCompatibility.Compatible -> error("Compatible was filtered out by takeIf")
|
||||
is ExpectActualCompatibility.Incompatible -> it.toMemberDiffKind()
|
||||
// If toMemberDiffKind returns null then some Kotlin invariants described in toMemberDiffKind no longer hold.
|
||||
// We can't throw exception here because it would crash the compilation.
|
||||
// Those broken invariants just needs to be reported by other checkers.
|
||||
// But it's better to report some error (ExpectActualMemberDiff.Kind.NonPrivateCallableAdded in our case) to
|
||||
// make sure that we don't have missed compilation errors if the invariants change
|
||||
?: ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
}
|
||||
}
|
||||
}
|
||||
.toSet()
|
||||
.map { kind -> ExpectActualMemberDiff(kind, actualMember, expect) }
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private fun MemberScope.extractNonPrivateCallables(
|
||||
classTypeSubstitutor: TypeSubstitutorMarker,
|
||||
expectActual: ExpectActual,
|
||||
matchingContext: ClassicExpectActualMatchingContext,
|
||||
): Set<Callable> {
|
||||
private fun MemberScope.extractNonPrivateCallables(): Sequence<CallableMemberDescriptor> {
|
||||
val functions =
|
||||
getFunctionNames().asSequence().flatMap { getContributedFunctions(it, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) }
|
||||
val properties =
|
||||
getVariableNames().asSequence().flatMap { getContributedVariables(it, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) }
|
||||
return (functions + properties).filter { !Visibilities.isPrivate(it.visibility.delegate) }
|
||||
.map { descriptor -> Callable(descriptor, expectActual, classTypeSubstitutor, matchingContext) }
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private data class Parameter(val name: Name, val type: KotlinType)
|
||||
private enum class Kind { FUNCTION, PROPERTY }
|
||||
private enum class ExpectActual { EXPECT, ACTUAL }
|
||||
private data class TypeParameter(val name: Name, val upperBounds: List<KotlinType>)
|
||||
private class Callable(
|
||||
val descriptor: CallableMemberDescriptor,
|
||||
val expectActual: ExpectActual,
|
||||
val classTypeSubstitutor: TypeSubstitutorMarker,
|
||||
val matchingContext: ClassicExpectActualMatchingContext,
|
||||
) {
|
||||
val name: Name = descriptor.name
|
||||
val kind: Kind = when (descriptor) {
|
||||
private val CallableMemberDescriptor.functionVsPropertyKind: Kind
|
||||
get() = when (this) {
|
||||
is PropertyDescriptor -> Kind.PROPERTY
|
||||
is FunctionDescriptor -> Kind.FUNCTION
|
||||
else -> error("Unknown kind $descriptor")
|
||||
else -> error("Unknown kind $this")
|
||||
}
|
||||
val isVarProperty: Boolean = descriptor is PropertyDescriptor && descriptor.isVar
|
||||
val isLateinitProperty: Boolean = descriptor is PropertyDescriptor && descriptor.isLateInit
|
||||
val modality: Modality = descriptor.modality
|
||||
val visibility: Visibility = descriptor.visibility.delegate
|
||||
val setterVisibility: Visibility? = (descriptor as? PropertyDescriptor)?.setter?.visibility?.delegate
|
||||
val parameters: List<Parameter> = descriptor.valueParameters.map { Parameter(it.name, it.type) }
|
||||
val returnType: KotlinType = descriptor.returnType ?: error("Can't get return type")
|
||||
val extensionReceiverType: KotlinType? = descriptor.extensionReceiverParameter?.type
|
||||
val typeParameters: List<TypeParameter> = descriptor.typeParameters.map { TypeParameter(it.name, it.upperBounds) }
|
||||
val contextReceiverTypes: List<KotlinType> = descriptor.contextReceiverParameters.map(ValueDescriptor::getType)
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Callable) return false
|
||||
check(classTypeSubstitutor === other.classTypeSubstitutor)
|
||||
check(matchingContext === other.matchingContext)
|
||||
return if (expectActual == other.expectActual) {
|
||||
name == other.name &&
|
||||
kind == other.kind &&
|
||||
isVarProperty == other.isVarProperty &&
|
||||
isLateinitProperty == other.isLateinitProperty &&
|
||||
modality == other.modality &&
|
||||
visibility == other.visibility &&
|
||||
setterVisibility == other.setterVisibility &&
|
||||
parameters == other.parameters &&
|
||||
returnType == other.returnType &&
|
||||
extensionReceiverType == other.extensionReceiverType &&
|
||||
typeParameters == other.typeParameters &&
|
||||
contextReceiverTypes == other.contextReceiverTypes
|
||||
} else {
|
||||
val (expect, actual) = if (expectActual == ExpectActual.EXPECT) this to other else other to this
|
||||
calculateExpectActualMemberDiffKind(expect, actual) == null
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = // Don't hash the types because type comparison is complicated
|
||||
Objects.hash(
|
||||
name,
|
||||
kind,
|
||||
isVarProperty,
|
||||
isLateinitProperty,
|
||||
modality,
|
||||
visibility,
|
||||
setterVisibility,
|
||||
parameters.map(Parameter::name),
|
||||
extensionReceiverType != null,
|
||||
typeParameters.map(TypeParameter::name),
|
||||
contextReceiverTypes.size,
|
||||
)
|
||||
}
|
||||
|
||||
private val CallableMemberDescriptor.psiIfReal: KtCallableDeclaration?
|
||||
get() = takeIf { it.kind.isReal }?.source?.let { it as? KotlinSourceElement }?.psi as? KtCallableDeclaration
|
||||
|
||||
private fun ClassicExpectActualMatchingContext.areCompatibleWithSubstitution(
|
||||
expect: KotlinType?,
|
||||
actual: KotlinType?,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean = areCompatibleExpectActualTypes(expect?.let { substitutor.safeSubstitute(it) }, actual)
|
||||
|
||||
private fun ClassicExpectActualMatchingContext.areCompatibleListWithSubstitution(
|
||||
expect: List<KotlinType>,
|
||||
actual: List<KotlinType>,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean = expect.size == actual.size && expect.asSequence().zip(actual.asSequence())
|
||||
.all { (a, b) -> areCompatibleWithSubstitution(a, b, substitutor) }
|
||||
|
||||
private fun ClassicExpectActualMatchingContext.areCompatibleUpperBoundsWithSubstitution(
|
||||
expect: List<List<KotlinType>>,
|
||||
actual: List<List<KotlinType>>,
|
||||
substitutor: TypeSubstitutorMarker,
|
||||
): Boolean = expect.size == actual.size && expect.asSequence().zip(actual.asSequence())
|
||||
.all { (a, b) -> areCompatibleListWithSubstitution(a, b, substitutor) }
|
||||
|
||||
private fun calculateExpectActualMemberDiffKind(
|
||||
expect: Callable,
|
||||
actual: Callable,
|
||||
checkParameterNames: Boolean = true,
|
||||
): ExpectActualMemberDiff.Kind? {
|
||||
check(expect.expectActual == ExpectActual.EXPECT)
|
||||
check(actual.expectActual == ExpectActual.ACTUAL)
|
||||
check(expect.classTypeSubstitutor === actual.classTypeSubstitutor)
|
||||
check(expect.matchingContext === actual.matchingContext)
|
||||
val substitutor = actual.matchingContext.createExpectActualTypeParameterSubstitutor(
|
||||
expect.descriptor.typeParameters,
|
||||
actual.descriptor.typeParameters,
|
||||
actual.classTypeSubstitutor
|
||||
)
|
||||
with(actual.matchingContext) {
|
||||
return when {
|
||||
expect.name != actual.name ||
|
||||
expect.kind != actual.kind ||
|
||||
!areCompatibleListWithSubstitution(
|
||||
expect.parameters.map(Parameter::type),
|
||||
actual.parameters.map(Parameter::type),
|
||||
substitutor
|
||||
) ||
|
||||
!areCompatibleUpperBoundsWithSubstitution(
|
||||
expect.typeParameters.map(TypeParameter::upperBounds),
|
||||
actual.typeParameters.map(TypeParameter::upperBounds),
|
||||
substitutor
|
||||
) ||
|
||||
!areCompatibleWithSubstitution(
|
||||
expect.extensionReceiverType,
|
||||
actual.extensionReceiverType,
|
||||
substitutor
|
||||
) ||
|
||||
!areCompatibleListWithSubstitution(
|
||||
expect.contextReceiverTypes,
|
||||
actual.contextReceiverTypes,
|
||||
substitutor
|
||||
) ->
|
||||
ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
|
||||
expect.isVarProperty != actual.isVarProperty -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
|
||||
|
||||
expect.isLateinitProperty != actual.isLateinitProperty -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
|
||||
|
||||
expect.modality != actual.modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride
|
||||
|
||||
expect.visibility != actual.visibility -> ExpectActualMemberDiff.Kind.VisibilityChangedInOverride
|
||||
|
||||
expect.setterVisibility != actual.setterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride
|
||||
|
||||
checkParameterNames && expect.parameters.map(Parameter::name) != actual.parameters.map(Parameter::name) ->
|
||||
ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
|
||||
|
||||
expect.typeParameters.map(TypeParameter::name) != actual.typeParameters.map(TypeParameter::name) ->
|
||||
ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride
|
||||
|
||||
!areCompatibleWithSubstitution(expect.returnType, actual.returnType, substitutor) ->
|
||||
ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun BindingTrace.reportIfPossible(diff: ExpectActualMemberDiff<CallableMemberDescriptor, ClassDescriptor>) {
|
||||
val psi = diff.actualMember.psiIfReal ?: return
|
||||
val diagnostic = when (diff.kind) {
|
||||
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
expect open class Foo {
|
||||
fun foo()
|
||||
}
|
||||
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual open class Foo {
|
||||
// Hypothetically, it's more restricting than necessary. I can't see how actualizing final -> open can breaking anything.
|
||||
// But technically, actual and expect scopes don't match
|
||||
actual open fun foo() {
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
@@ -8,9 +9,8 @@ expect open class Foo {
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual open <!ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_MEMBERS_AS_NON_FINAL_EXPECT_CLASSIFIER!>class Foo<!> {
|
||||
// Hypothetically, it's more restricting than necessary. I can't see how actualizing final -> open can breaking anything.
|
||||
// But technically, actual and expect scopes don't match
|
||||
actual <!MODALITY_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION!>open<!> fun foo() {
|
||||
actual open class Foo {
|
||||
// final -> open is a legal modality change
|
||||
actual open fun foo() {
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -12,6 +12,8 @@ expect open class Foo {
|
||||
actual open class Foo {
|
||||
actual fun foo() {}
|
||||
|
||||
// Expected: NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION.
|
||||
// But it doesn't work because context receivers are not yet supported in expect actual matcher KT-61447
|
||||
context(Int)
|
||||
fun foo() {} // accidential override can happen with this injected fun. That's why it's prohibited
|
||||
fun foo() {}
|
||||
}
|
||||
|
||||
+4
-2
@@ -9,9 +9,11 @@ expect open class Foo {
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual open <!ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_MEMBERS_AS_NON_FINAL_EXPECT_CLASSIFIER!>class Foo<!> {
|
||||
actual open class Foo {
|
||||
actual fun foo() {}
|
||||
|
||||
// Expected: NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION.
|
||||
// But it doesn't work because context receivers are not yet supported in expect actual matcher KT-61447
|
||||
context(Int)
|
||||
fun <!ACTUAL_MISSING, NON_ACTUAL_MEMBER_DECLARED_IN_EXPECT_NON_FINAL_CLASSIFIER_ACTUALIZATION!>foo<!>() {} // accidential override can happen with this injected fun. That's why it's prohibited
|
||||
fun <!ACTUAL_MISSING!>foo<!>() {}
|
||||
}
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
<!INCOMPATIBLE_MATCHING{JVM}!>expect open class Container {
|
||||
<!INCOMPATIBLE_MATCHING{JVM}!>internal open fun internalFun()<!>
|
||||
}<!>
|
||||
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
|
||||
// FILE: foo/Foo.java
|
||||
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
public void internalFun() {}
|
||||
}
|
||||
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Container<!> = foo.Foo
|
||||
+3
-3
@@ -3,7 +3,7 @@
|
||||
// FILE: common.kt
|
||||
|
||||
expect open class Container {
|
||||
internal fun internalFun()
|
||||
internal open fun internalFun()
|
||||
}
|
||||
|
||||
// MODULE: m2-jvm()()(m1-common)
|
||||
@@ -13,9 +13,9 @@ expect open class Container {
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
public final void internalFun() {}
|
||||
public void internalFun() {}
|
||||
}
|
||||
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual typealias <!ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_MEMBERS_AS_NON_FINAL_EXPECT_CLASSIFIER!>Container<!> = foo.Foo
|
||||
actual typealias <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Container<!> = foo.Foo
|
||||
Generated
+9
-3
@@ -24166,9 +24166,15 @@ public class DiagnosticTestGenerated extends AbstractDiagnosticTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias.kt");
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias_finalMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_openMember.kt")
|
||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias_openMember() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias_openMember.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+12
-18
@@ -10,7 +10,7 @@ data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember
|
||||
/**
|
||||
* Diff kinds that are legal for fake-overrides in final `expect class`, but illegal for non-final `expect class`
|
||||
*
|
||||
* Also see: [toDiffKind]
|
||||
* Also see: [toMemberDiffKind]
|
||||
*/
|
||||
enum class Kind(val rawMessage: String) {
|
||||
NonPrivateCallableAdded(
|
||||
@@ -52,37 +52,31 @@ data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function serves a purpose of type-safe documentation. A mapping can be established between [ExpectActualCompatibility.Incompatible]
|
||||
* and [ExpectActualMemberDiff.Kind]. This exhaustive when fixates this mapping, ensuring that we won't forget to add new
|
||||
* [ExpectActualMemberDiff.Kind] when [ExpectActualCompatibility.Incompatible] is updated.
|
||||
*/
|
||||
@Suppress("unused") // The function is a documentation. That's why it's unused
|
||||
private fun ExpectActualCompatibility.Incompatible<*>.toDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
|
||||
fun ExpectActualCompatibility.Incompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
|
||||
ExpectActualCompatibility.Incompatible.CallableKind -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ParameterShape -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ParameterTypes -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
ExpectActualCompatibility.Incompatible.ClassTypeParameterCount -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.FunctionTypeParameterUpperBounds -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||
ExpectActualCompatibility.Incompatible.ClassTypeParameterUpperBounds -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
ExpectActualCompatibility.Incompatible.ClassTypeParameterUpperBounds -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.ActualFunctionWithDefaultParameters -> null // It's not possible to add default parameters in override
|
||||
ExpectActualCompatibility.Incompatible.ClassKind -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
ExpectActualCompatibility.Incompatible.ClassModifiers -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
is ExpectActualCompatibility.Incompatible.ClassScopes -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
ExpectActualCompatibility.Incompatible.EnumEntries -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> null // Not applicable because ExpectActualMemberDiff is about members
|
||||
ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier
|
||||
ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier
|
||||
ExpectActualCompatibility.Incompatible.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
is ExpectActualCompatibility.Incompatible.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.FunctionModifiersDifferent -> null // It's not possible to override with different function modifier (suspend)
|
||||
ExpectActualCompatibility.Incompatible.FunctionModifiersNotSubset -> null // It's not possible to override with different function modifier (infix, inline, operator)
|
||||
ExpectActualCompatibility.Incompatible.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const fun can't be overridden
|
||||
ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.Supertypes -> null // Not applicable because ExpectActualMemberDiff is about members. But related: ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_SUPERTYPES_AS_NON_FINAL_EXPECT_CLASSIFIER
|
||||
ExpectActualCompatibility.Incompatible.Supertypes -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||
ExpectActualCompatibility.Incompatible.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride
|
||||
ExpectActualCompatibility.Incompatible.TypeParameterReified -> null // inline fun can't be overridden
|
||||
ExpectActualCompatibility.Incompatible.TypeParameterVariance -> null // Members are not allowed to have variance
|
||||
|
||||
Reference in New Issue
Block a user