Support inline classes in MPP resolver/checker model
This commit is contained in:
@@ -265,7 +265,10 @@ class DeclarationsChecker(
|
||||
trace.report(EXPECTED_ENUM_CONSTRUCTOR.on(declaration))
|
||||
}
|
||||
|
||||
if (declaration is KtPrimaryConstructor && !DescriptorUtils.isAnnotationClass(constructorDescriptor.constructedClass)) {
|
||||
if (declaration is KtPrimaryConstructor &&
|
||||
!DescriptorUtils.isAnnotationClass(constructorDescriptor.constructedClass) &&
|
||||
!constructorDescriptor.constructedClass.isInline
|
||||
) {
|
||||
for (parameter in declaration.valueParameters) {
|
||||
if (parameter.hasValOrVar()) {
|
||||
trace.report(EXPECTED_CLASS_CONSTRUCTOR_PROPERTY_PARAMETER.on(parameter))
|
||||
|
||||
+19
-7
@@ -26,11 +26,9 @@ import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasActualModifier
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isAnnotationConstructor
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.isPrimaryConstructorOfInlineClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver
|
||||
import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver.Compatibility
|
||||
@@ -137,9 +135,7 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
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
|
||||
// to require every annotation constructor with additional parameters with default values be marked with the `actual` modifier
|
||||
if (checkActual && !descriptor.isAnnotationConstructor()) {
|
||||
if (checkActual && requireActualModifier(descriptor, trace)) {
|
||||
trace.report(Errors.ACTUAL_MISSING.on(reportOn))
|
||||
}
|
||||
|
||||
@@ -204,6 +200,22 @@ object ExpectedActualDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
}
|
||||
|
||||
// we don't require `actual` modifier on
|
||||
// - annotation constructors, because annotation classes can only have one constructor
|
||||
// - inline class primary constructors, because inline class must have primary constructor
|
||||
// - value parameter inside primary constructor of inline class, because inline class must have one value parameter
|
||||
private fun requireActualModifier(descriptor: MemberDescriptor, trace: BindingTrace): Boolean {
|
||||
return !descriptor.isAnnotationConstructor() &&
|
||||
!descriptor.isPrimaryConstructorOfInlineClass() &&
|
||||
!isMainPropertyOfInlineClass(descriptor, trace)
|
||||
}
|
||||
|
||||
private fun isMainPropertyOfInlineClass(descriptor: MemberDescriptor, trace: BindingTrace): Boolean {
|
||||
if (descriptor !is PropertyDescriptor) return false
|
||||
if (!descriptor.containingDeclaration.isInlineClass()) return false
|
||||
return trace.bindingContext[BindingContext.BACKING_FIELD_REQUIRED, descriptor] == true
|
||||
}
|
||||
|
||||
// This should ideally be handled by CallableMemberDescriptor.Kind, but default constructors have kind DECLARATION and non-empty source.
|
||||
// Their source is the containing KtClass instance though, as opposed to explicit constructors, whose source is KtConstructor
|
||||
private fun MemberDescriptor.isExplicitActualDeclaration(): Boolean =
|
||||
|
||||
+2
-2
@@ -197,7 +197,7 @@ object ExpectedActualResolver {
|
||||
|
||||
object ClassKind : Incompatible("class kinds are different (class, interface, object, enum, annotation)")
|
||||
|
||||
object ClassModifiers : Incompatible("modifiers are different (companion, inner)")
|
||||
object ClassModifiers : Incompatible("modifiers are different (companion, inner, inline)")
|
||||
|
||||
object Supertypes : Incompatible("some supertypes are missing in the actual declaration")
|
||||
|
||||
@@ -395,7 +395,7 @@ object ExpectedActualResolver {
|
||||
|
||||
if (a.kind != b.kind) return Incompatible.ClassKind
|
||||
|
||||
if (!equalBy(a, b) { listOf(it.isCompanionObject, it.isInner) }) return Incompatible.ClassModifiers
|
||||
if (!equalBy(a, b) { listOf(it.isCompanionObject, it.isInner, it.isInline) }) return Incompatible.ClassModifiers
|
||||
|
||||
val aTypeParams = a.declaredTypeParameters
|
||||
val bTypeParams = b.declaredTypeParameters
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// !LANGUAGE: +MultiPlatformProjects, +InlineClasses
|
||||
// MODULE: m1-common
|
||||
// FILE: common.kt
|
||||
|
||||
expect inline class Foo1(val x: Int) {
|
||||
fun bar(): String
|
||||
}
|
||||
|
||||
expect inline class Foo2(val x: Int)
|
||||
|
||||
expect <!ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS, JVM:ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS!>inline<!> class Foo3
|
||||
|
||||
expect class NonInlineExpect
|
||||
|
||||
expect inline class NonInlineActual(val x: Int)
|
||||
|
||||
// MODULE: m2-jvm(m1-common)
|
||||
// FILE: jvm.kt
|
||||
|
||||
actual inline class Foo1(val x: Int) {
|
||||
actual fun bar(): String = "Hello"
|
||||
}
|
||||
actual inline class <!NO_ACTUAL_CLASS_MEMBER_FOR_EXPECTED_CLASS!>Foo2<!>(val x: String)
|
||||
actual <!ABSENCE_OF_PRIMARY_CONSTRUCTOR_FOR_INLINE_CLASS!>inline<!> class Foo3
|
||||
|
||||
<!ACTUAL_WITHOUT_EXPECT!>actual inline<!> class NonInlineExpect(val x: Int)
|
||||
|
||||
<!ACTUAL_WITHOUT_EXPECT!>actual<!> class NonInlineActual actual constructor(actual val x: Int)
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
// -- Module: <m1-common> --
|
||||
package
|
||||
|
||||
public final expect inline class Foo1 {
|
||||
public constructor Foo1(/*0*/ x: kotlin.Int)
|
||||
public expect final val x: kotlin.Int
|
||||
public final expect fun bar(): 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 expect inline class Foo2 {
|
||||
public constructor Foo2(/*0*/ x: kotlin.Int)
|
||||
public expect final val x: kotlin.Int
|
||||
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 inline class Foo3 {
|
||||
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 inline class NonInlineActual {
|
||||
public constructor NonInlineActual(/*0*/ x: kotlin.Int)
|
||||
public expect final val x: kotlin.Int
|
||||
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 NonInlineExpect {
|
||||
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 final actual inline class Foo1 {
|
||||
public constructor Foo1(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
public final actual fun bar(): 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 inline class Foo2 {
|
||||
public constructor Foo2(/*0*/ x: kotlin.String)
|
||||
public final val 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
|
||||
}
|
||||
|
||||
public final actual inline class Foo3 {
|
||||
public constructor Foo3()
|
||||
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 NonInlineActual {
|
||||
public constructor NonInlineActual(/*0*/ x: kotlin.Int)
|
||||
public actual final val x: kotlin.Int
|
||||
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 inline class NonInlineExpect {
|
||||
public constructor NonInlineExpect(/*0*/ x: kotlin.Int)
|
||||
public final val x: kotlin.Int
|
||||
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
|
||||
}
|
||||
@@ -24,31 +24,31 @@ The following declaration is incompatible because class kinds are different (cla
|
||||
actual class N3
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:8:5: error: actual class 'N2' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner):
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public final expect class N2
|
||||
|
||||
actual inner class N2
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:9:5: error: actual class 'I2' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner):
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public final expect inner class I2
|
||||
|
||||
actual class I2
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:13:5: error: actual companion object 'Companion' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner):
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public expect object Companion
|
||||
|
||||
actual companion object {}
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:14:5: error: actual object 'Factory' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner):
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public expect companion object Factory
|
||||
|
||||
actual object Factory
|
||||
^
|
||||
compiler/testData/multiplatform/incompatibleNestedClasses/jvm.kt:18:5: error: actual object 'Companion' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner):
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public expect companion object
|
||||
|
||||
actual object Companion
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
@file:Suppress("UNSUPPORTED_FEATURE")
|
||||
|
||||
expect inline class Foo1(val x: Int)
|
||||
expect inline class Foo2(val y: String)
|
||||
@@ -0,0 +1,5 @@
|
||||
@file:Suppress("UNSUPPORTED_FEATURE")
|
||||
|
||||
actual inline class Foo1(val x: Int)
|
||||
|
||||
actual class Foo2 actual constructor(actual val y: String)
|
||||
@@ -0,0 +1,5 @@
|
||||
@file:Suppress("UNSUPPORTED_FEATURE")
|
||||
|
||||
actual inline class Foo1(val x: Int)
|
||||
|
||||
actual class Foo2 actual constructor(actual val y: String)
|
||||
@@ -0,0 +1,23 @@
|
||||
-- Common --
|
||||
Exit code: OK
|
||||
Output:
|
||||
|
||||
-- JVM --
|
||||
Exit code: COMPILATION_ERROR
|
||||
Output:
|
||||
compiler/testData/multiplatform/inlineClasses/jvm.kt:5:1: error: actual class 'Foo2' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public final expect inline class Foo2
|
||||
|
||||
actual class Foo2 actual constructor(actual val y: String)
|
||||
^
|
||||
|
||||
-- JS --
|
||||
Exit code: COMPILATION_ERROR
|
||||
Output:
|
||||
compiler/testData/multiplatform/inlineClasses/js.kt:5:1: error: actual class 'Foo2' has no corresponding expected declaration
|
||||
The following declaration is incompatible because modifiers are different (companion, inner, inline):
|
||||
public final expect inline class Foo2
|
||||
|
||||
actual class Foo2 actual constructor(actual val y: String)
|
||||
^
|
||||
@@ -13020,6 +13020,24 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/inlineClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineClasses extends AbstractDiagnosticsTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineClasses() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("expectActualInlineClass.kt")
|
||||
public void testExpectActualInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/inlineClasses/expectActualInlineClass.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/java")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+18
@@ -13020,6 +13020,24 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/inlineClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineClasses extends AbstractDiagnosticsUsingJavacTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineClasses() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/tests/multiplatform/inlineClasses"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("expectActualInlineClass.kt")
|
||||
public void testExpectActualInlineClass() throws Exception {
|
||||
runTest("compiler/testData/diagnostics/tests/multiplatform/inlineClasses/expectActualInlineClass.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/multiplatform/java")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+18
@@ -84,6 +84,11 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
|
||||
runTest("compiler/testData/multiplatform/incorrectImplInClass/");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineClasses")
|
||||
public void testInlineClasses() throws Exception {
|
||||
runTest("compiler/testData/multiplatform/inlineClasses/");
|
||||
}
|
||||
|
||||
@TestMetadata("jsNameClash")
|
||||
public void testJsNameClash() throws Exception {
|
||||
runTest("compiler/testData/multiplatform/jsNameClash/");
|
||||
@@ -540,6 +545,19 @@ public class MultiPlatformIntegrationTestGenerated extends AbstractMultiPlatform
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/multiplatform/inlineClasses")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class InlineClasses extends AbstractMultiPlatformIntegrationTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInInlineClasses() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/multiplatform/inlineClasses"), Pattern.compile("^([^\\.]+)$"), TargetBackend.ANY, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/multiplatform/jsNameClash")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -434,3 +434,6 @@ fun isParameterOfAnnotation(parameterDescriptor: ParameterDescriptor): Boolean =
|
||||
|
||||
fun DeclarationDescriptor.isAnnotationConstructor(): Boolean =
|
||||
this is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(this.constructedClass)
|
||||
|
||||
fun DeclarationDescriptor.isPrimaryConstructorOfInlineClass(): Boolean =
|
||||
this is ConstructorDescriptor && this.isPrimary && this.constructedClass.isInline
|
||||
|
||||
Reference in New Issue
Block a user