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
|
@Test
|
||||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias.kt")
|
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt")
|
||||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias() throws Exception {
|
public void testMorePermissiveVisibilityOnActualViaTypeAlias_finalMember() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias.kt");
|
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
|
@Test
|
||||||
|
|||||||
+9
-3
@@ -1297,9 +1297,15 @@ public class FirOldFrontendMPPDiagnosticsWithPsiTestGenerated extends AbstractFi
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias.kt")
|
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt")
|
||||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias() throws Exception {
|
public void testMorePermissiveVisibilityOnActualViaTypeAlias_finalMember() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias.kt");
|
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
|
@Test
|
||||||
|
|||||||
+48
-175
@@ -10,16 +10,16 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.name.Name
|
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
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.multiplatform.*
|
||||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||||
import org.jetbrains.kotlin.resolve.source.KotlinSourceElement
|
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.ExperimentalContracts
|
||||||
import kotlin.contracts.contract
|
import kotlin.contracts.contract
|
||||||
|
|
||||||
@@ -142,198 +142,71 @@ private fun calculateExpectActualScopeDiff(
|
|||||||
parentSubstitutor = null
|
parentSubstitutor = null
|
||||||
)
|
)
|
||||||
|
|
||||||
val expectClassCallables = expect.unsubstitutedMemberScope
|
val expectClassCallables = expect.unsubstitutedMemberScope.extractNonPrivateCallables()
|
||||||
.extractNonPrivateCallables(classTypeSubstitutor, ExpectActual.EXPECT, matchingContext)
|
val actualClassCallables = actual.unsubstitutedMemberScope.extractNonPrivateCallables()
|
||||||
val actualClassCallables = actual.unsubstitutedMemberScope
|
.filter { it.kind.isReal } // Filter out fake-overrides from actual because we compare list of supertypes separately anyway
|
||||||
.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 nameAndKindToExpectCallable = expectClassCallables.groupBy { it.name to it.kind }
|
val nameAndKindToExpectCallables = expectClassCallables.groupBy { it.name to it.functionVsPropertyKind }
|
||||||
|
|
||||||
return (actualClassCallables - expectClassCallables).asSequence()
|
return actualClassCallables.flatMap { actualMember ->
|
||||||
.flatMap { unmatchedActualCallable ->
|
val potentialExpects = nameAndKindToExpectCallables[actualMember.name to actualMember.functionVsPropertyKind]
|
||||||
when (val expectCallablesWithTheSameNameAndKind =
|
if (potentialExpects.isNullOrEmpty()) {
|
||||||
nameAndKindToExpectCallable[unmatchedActualCallable.name to unmatchedActualCallable.kind]) {
|
listOf(ExpectActualMemberDiff.Kind.NonPrivateCallableAdded)
|
||||||
null -> listOf(ExpectActualMemberDiff.Kind.NonPrivateCallableAdded)
|
} else {
|
||||||
else -> expectCallablesWithTheSameNameAndKind.map {
|
potentialExpects
|
||||||
calculateExpectActualMemberDiffKind(
|
.map { expectMember ->
|
||||||
expect = it,
|
val substitutor = matchingContext.createExpectActualTypeParameterSubstitutor(
|
||||||
actual = unmatchedActualCallable,
|
expectMember.typeParameters,
|
||||||
checkParameterNames = unmatchedActualCallable.descriptor.hasStableParameterNames()
|
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(
|
private fun MemberScope.extractNonPrivateCallables(): Sequence<CallableMemberDescriptor> {
|
||||||
classTypeSubstitutor: TypeSubstitutorMarker,
|
|
||||||
expectActual: ExpectActual,
|
|
||||||
matchingContext: ClassicExpectActualMatchingContext,
|
|
||||||
): Set<Callable> {
|
|
||||||
val functions =
|
val functions =
|
||||||
getFunctionNames().asSequence().flatMap { getContributedFunctions(it, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) }
|
getFunctionNames().asSequence().flatMap { getContributedFunctions(it, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) }
|
||||||
val properties =
|
val properties =
|
||||||
getVariableNames().asSequence().flatMap { getContributedVariables(it, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) }
|
getVariableNames().asSequence().flatMap { getContributedVariables(it, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) }
|
||||||
return (functions + properties).filter { !Visibilities.isPrivate(it.visibility.delegate) }
|
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 Kind { FUNCTION, PROPERTY }
|
||||||
private enum class ExpectActual { EXPECT, ACTUAL }
|
private val CallableMemberDescriptor.functionVsPropertyKind: Kind
|
||||||
private data class TypeParameter(val name: Name, val upperBounds: List<KotlinType>)
|
get() = when (this) {
|
||||||
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) {
|
|
||||||
is PropertyDescriptor -> Kind.PROPERTY
|
is PropertyDescriptor -> Kind.PROPERTY
|
||||||
is FunctionDescriptor -> Kind.FUNCTION
|
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?
|
private val CallableMemberDescriptor.psiIfReal: KtCallableDeclaration?
|
||||||
get() = takeIf { it.kind.isReal }?.source?.let { it as? KotlinSourceElement }?.psi as? 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>) {
|
private fun BindingTrace.reportIfPossible(diff: ExpectActualMemberDiff<CallableMemberDescriptor, ClassDescriptor>) {
|
||||||
val psi = diff.actualMember.psiIfReal ?: return
|
val psi = diff.actualMember.psiIfReal ?: return
|
||||||
val diagnostic = when (diff.kind) {
|
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
|
// MODULE: m1-common
|
||||||
// FILE: common.kt
|
// FILE: common.kt
|
||||||
|
|
||||||
@@ -8,9 +9,8 @@ expect open class Foo {
|
|||||||
// MODULE: m2-jvm()()(m1-common)
|
// MODULE: m2-jvm()()(m1-common)
|
||||||
// FILE: jvm.kt
|
// FILE: jvm.kt
|
||||||
|
|
||||||
actual open <!ACTUAL_CLASSIFIER_MUST_HAVE_THE_SAME_MEMBERS_AS_NON_FINAL_EXPECT_CLASSIFIER!>class Foo<!> {
|
actual open class Foo {
|
||||||
// Hypothetically, it's more restricting than necessary. I can't see how actualizing final -> open can breaking anything.
|
// final -> open is a legal modality change
|
||||||
// But technically, actual and expect scopes don't match
|
actual open fun foo() {
|
||||||
actual <!MODALITY_CHANGED_IN_NON_FINAL_EXPECT_CLASSIFIER_ACTUALIZATION!>open<!> fun foo() {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -12,6 +12,8 @@ expect open class Foo {
|
|||||||
actual open class Foo {
|
actual open class Foo {
|
||||||
actual fun 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)
|
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)
|
// MODULE: m2-jvm()()(m1-common)
|
||||||
// FILE: jvm.kt
|
// 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() {}
|
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)
|
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
|
// !DIAGNOSTICS: -UNUSED_PARAMETER
|
||||||
// MODULE: m1-common
|
// MODULE: m1-common
|
||||||
// FILE: common.kt
|
// 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
|
// FILE: common.kt
|
||||||
|
|
||||||
expect open class Container {
|
expect open class Container {
|
||||||
internal fun internalFun()
|
internal open fun internalFun()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MODULE: m2-jvm()()(m1-common)
|
// MODULE: m2-jvm()()(m1-common)
|
||||||
@@ -13,9 +13,9 @@ expect open class Container {
|
|||||||
package foo;
|
package foo;
|
||||||
|
|
||||||
public class Foo {
|
public class Foo {
|
||||||
public final void internalFun() {}
|
public void internalFun() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FILE: jvm.kt
|
// 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
|
@Test
|
||||||
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias.kt")
|
@TestMetadata("morePermissiveVisibilityOnActualViaTypeAlias_finalMember.kt")
|
||||||
public void testMorePermissiveVisibilityOnActualViaTypeAlias() throws Exception {
|
public void testMorePermissiveVisibilityOnActualViaTypeAlias_finalMember() throws Exception {
|
||||||
runTest("compiler/testData/diagnostics/tests/multiplatform/headerClass/morePermissiveVisibilityOnActualViaTypeAlias.kt");
|
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
|
@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`
|
* 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) {
|
enum class Kind(val rawMessage: String) {
|
||||||
NonPrivateCallableAdded(
|
NonPrivateCallableAdded(
|
||||||
@@ -52,37 +52,31 @@ data class ExpectActualMemberDiff<out M, out C>(val kind: Kind, val actualMember
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
fun ExpectActualCompatibility.Incompatible<*>.toMemberDiffKind(): ExpectActualMemberDiff.Kind? = when (this) {
|
||||||
* 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) {
|
|
||||||
ExpectActualCompatibility.Incompatible.CallableKind -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
ExpectActualCompatibility.Incompatible.CallableKind -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||||
ExpectActualCompatibility.Incompatible.ParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
ExpectActualCompatibility.Incompatible.ParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||||
ExpectActualCompatibility.Incompatible.ParameterShape -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
ExpectActualCompatibility.Incompatible.ParameterShape -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||||
ExpectActualCompatibility.Incompatible.ParameterTypes -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
ExpectActualCompatibility.Incompatible.ParameterTypes -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
||||||
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
|
ExpectActualCompatibility.Incompatible.ReturnType -> ExpectActualMemberDiff.Kind.ReturnTypeChangedInOverride
|
||||||
ExpectActualCompatibility.Incompatible.FunctionTypeParameterCount -> ExpectActualMemberDiff.Kind.NonPrivateCallableAdded
|
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.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.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.ClassKind -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||||
ExpectActualCompatibility.Incompatible.ClassModifiers -> null // Not applicable because ExpectActualMemberDiff is about members
|
ExpectActualCompatibility.Incompatible.ClassModifiers -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||||
is ExpectActualCompatibility.Incompatible.ClassScopes -> null // Not applicable because ExpectActualMemberDiff is about members
|
is ExpectActualCompatibility.Incompatible.ClassScopes -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||||
ExpectActualCompatibility.Incompatible.EnumEntries -> null // Not applicable because ExpectActualMemberDiff is about members
|
ExpectActualCompatibility.Incompatible.EnumEntries -> error("Not applicable because ExpectActualMemberDiff is about members")
|
||||||
ExpectActualCompatibility.Incompatible.FunInterfaceModifier -> null // 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
|
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
|
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.Modality -> ExpectActualMemberDiff.Kind.ModalityChangedInOverride
|
||||||
ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
|
ExpectActualCompatibility.Incompatible.ParameterNames -> ExpectActualMemberDiff.Kind.ParameterNameChangedInOverride
|
||||||
ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const fun can't be overridden
|
ExpectActualCompatibility.Incompatible.PropertyConstModifier -> null // const fun can't be overridden
|
||||||
ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
|
ExpectActualCompatibility.Incompatible.PropertyKind -> ExpectActualMemberDiff.Kind.PropertyKindChangedInOverride
|
||||||
ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
|
ExpectActualCompatibility.Incompatible.PropertyLateinitModifier -> ExpectActualMemberDiff.Kind.LateinitChangedInOverride
|
||||||
ExpectActualCompatibility.Incompatible.PropertySetterVisibility -> ExpectActualMemberDiff.Kind.SetterVisibilityChangedInOverride
|
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.TypeParameterNames -> ExpectActualMemberDiff.Kind.TypeParameterNamesChangedInOverride
|
||||||
ExpectActualCompatibility.Incompatible.TypeParameterReified -> null // inline fun can't be overridden
|
ExpectActualCompatibility.Incompatible.TypeParameterReified -> null // inline fun can't be overridden
|
||||||
ExpectActualCompatibility.Incompatible.TypeParameterVariance -> null // Members are not allowed to have variance
|
ExpectActualCompatibility.Incompatible.TypeParameterVariance -> null // Members are not allowed to have variance
|
||||||
|
|||||||
Reference in New Issue
Block a user