From 1951d38f40f73f992fb3e9638d7312663bd0b031 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Thu, 21 Jun 2018 18:29:19 +0200 Subject: [PATCH] Retain optional expected annotations when compiling platform code After this change, optional expected annotations will be compiled to physical class files on JVM, and stored to metadata on other platforms, to allow their usages from dependent platform modules. For example: @OptionalExpectation expect annotation class A When compiling this code on JVM, A.class will be produced as if the class A did neither have the 'expect' modifier, nor had it been annotated with OptionalExpectation. Note that if there's no actual annotation class for A, then usages (which can only be usages as annotation entries) are simply skipped. Class A will be public from Kotlin's point of view (since it should be possible to use it in Kotlin sources), but _package-private_ in Java to disallow its usages outside of the declaring module. #KT-18882 Fixed #KT-24617 Fixed --- .../kotlin/backend/common/CodegenUtil.kt | 16 +++++- .../kotlin/codegen/AnnotationCodegen.java | 6 ++- .../org/jetbrains/kotlin/codegen/AsmUtil.java | 4 ++ .../ExpectedActualDeclarationChecker.kt | 21 +++++++- .../box/multiplatform/optionalExpectation.kt | 51 +++++++------------ .../multiplatform/optionalExpectationJvm.kt | 49 ++++++++++++++++++ .../multiplatform/optionalExpectation.txt | 7 +++ .../optionalAnnotation.kt | 35 +++++++++++++ .../output.txt | 3 ++ .../ir/IrBlackBoxCodegenTestGenerated.java | 5 ++ .../codegen/BlackBoxCodegenTestGenerated.java | 5 ++ ...mpileKotlinAgainstKotlinTestGenerated.java | 5 ++ .../LightAnalysisModeTestGenerated.java | 5 ++ .../js/KotlinJavascriptSerializationUtil.kt | 15 ++++-- .../jetbrains/kotlin/js/test/BasicBoxTest.kt | 2 +- .../IrJsCodegenBoxTestGenerated.java | 5 ++ .../semantics/JsCodegenBoxTestGenerated.java | 5 ++ 17 files changed, 196 insertions(+), 43 deletions(-) create mode 100644 compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt create mode 100644 compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt diff --git a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt index 7e75076b077..c14dd4d8b21 100644 --- a/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt +++ b/compiler/backend-common/src/org/jetbrains/kotlin/backend/common/CodegenUtil.kt @@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker import org.jetbrains.kotlin.resolve.multiplatform.ExpectedActualResolver import org.jetbrains.kotlin.types.KotlinType @@ -169,11 +170,22 @@ object CodegenUtil { /** * Returns declarations in the given [file] which should be generated by the back-end. This includes all declarations - * minus all expected declarations. + * minus all expected declarations (except annotation classes annotated with @OptionalExpectation). */ @JvmStatic fun getDeclarationsToGenerate(file: KtFile, bindingContext: BindingContext): List = - file.declarations.filterNot(KtDeclaration::hasExpectModifier) + file.declarations.filter(fun(declaration: KtDeclaration): Boolean { + if (!declaration.hasExpectModifier()) return true + + if (declaration is KtClass) { + val descriptor = bindingContext.get(BindingContext.CLASS, declaration) + if (descriptor != null && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor)) { + return true + } + } + + return false + }) @JvmStatic fun findExpectedFunctionForActual(descriptor: FunctionDescriptor): FunctionDescriptor? { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java index 6620485cfd0..5b9614abc84 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AnnotationCodegen.java @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.load.java.JvmAnnotationNames; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.Name; import org.jetbrains.kotlin.resolve.AnnotationChecker; +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker; import org.jetbrains.kotlin.resolve.constants.*; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; import org.jetbrains.kotlin.types.FlexibleType; @@ -300,7 +301,10 @@ public abstract class AnnotationCodegen { return null; } - if (classDescriptor.isExpect()) { + // We do not generate annotations whose classes are optional (annotated with `@OptionalExpectation`) because if an annotation entry + // is resolved to the expected declaration, this means that annotation has no actual class, and thus should not be generated. + // (Otherwise we would've resolved the entry to the actual annotation class.) + if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(classDescriptor)) { return null; } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index e65e7dce0c0..2901e7ff1ac 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.renderer.DescriptorRenderer; import org.jetbrains.kotlin.resolve.DescriptorUtils; import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt; import org.jetbrains.kotlin.resolve.annotations.AnnotationUtilKt; +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker; import org.jetbrains.kotlin.resolve.inline.InlineUtil; import org.jetbrains.kotlin.resolve.jvm.JvmClassName; import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType; @@ -278,6 +279,9 @@ public class AsmUtil { if (descriptor instanceof SyntheticClassDescriptorForLambda) { return getVisibilityAccessFlagForAnonymous(descriptor); } + if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(descriptor)) { + return NO_FLAG_PACKAGE_PRIVATE; + } if (descriptor.getVisibility() == Visibilities.PUBLIC || descriptor.getVisibility() == Visibilities.PROTECTED || // TODO: should be package private, but for now Kotlin's reflection can't access members of such classes diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt index 47b999be5af..3496f6ea0bf 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt @@ -92,8 +92,25 @@ object ExpectedActualDeclarationChecker : DeclarationChecker { } } - internal fun isOptionalAnnotationClass(descriptor: DeclarationDescriptor): Boolean { - return descriptor.annotations.hasAnnotation(OPTIONAL_EXPECTATION_FQ_NAME) + @JvmStatic + fun isOptionalAnnotationClass(descriptor: DeclarationDescriptor): Boolean = + descriptor is ClassDescriptor && + descriptor.kind == ClassKind.ANNOTATION_CLASS && + descriptor.isExpect && + descriptor.annotations.hasAnnotation(OPTIONAL_EXPECTATION_FQ_NAME) + + // TODO: move to some other place which is accessible both from backend-common and js.serializer + @JvmStatic + fun shouldGenerateExpectClass(descriptor: ClassDescriptor): Boolean { + assert(descriptor.isExpect) { "Not an expected class: $descriptor" } + + if (ExpectedActualDeclarationChecker.isOptionalAnnotationClass(descriptor)) { + with(ExpectedActualResolver) { + return descriptor.findCompatibleActualForExpected(descriptor.module).isEmpty() + } + } + + return false } private fun ExpectActualTracker.reportExpectActual(expected: MemberDescriptor, actualMembers: Sequence) { diff --git a/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt b/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt index f707f4a305d..28f38c30821 100644 --- a/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt +++ b/compiler/testData/codegen/box/multiplatform/optionalExpectation.kt @@ -1,49 +1,34 @@ // !LANGUAGE: +MultiPlatformProjects // !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform -// TARGET_BACKEND: JVM +// IGNORE_BACKEND: JS_IR // WITH_RUNTIME -// FILE: common.kt +// MODULE: library +// FILE: expected.kt + +package a @OptionalExpectation -expect annotation class Anno(val s: String) +expect annotation class A(val x: Int) -// FILE: jvm.kt +@OptionalExpectation +expect annotation class B(val s: String) -import java.lang.reflect.AnnotatedElement +// FILE: actual.kt -@Anno("Foo") -class Foo @Anno("") constructor(@Anno("x") x: Int) { - @Anno("bar") - fun bar() {} +package a - @Anno("getX") - var x = x - @Anno("setX") - set +actual annotation class A(actual val x: Int) - @Anno("Nested") - interface Nested -} +// MODULE: main(library) +// FILE: main.kt -private fun check(element: AnnotatedElement) { - check(element.annotations) -} +package usage -private fun check(annotations: Array) { - val filtered = annotations.filterNot { it.annotationClass.java.name == "kotlin.Metadata" } - if (filtered.isNotEmpty()) { - throw AssertionError("Annotations should be empty: $filtered") - } -} +import a.A +import a.B +@A(42) +@B("OK") fun box(): String { - val foo = Foo::class.java - check(foo) - check(Foo.Nested::class.java) - check(foo.declaredMethods.single { it.name == "bar" }) - check(foo.declaredMethods.single { it.name == "getX" }) - check(foo.declaredMethods.single { it.name == "setX" }) - check(foo.constructors.single()) - check(foo.constructors.single().parameterAnnotations.single()) return "OK" } diff --git a/compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt b/compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt new file mode 100644 index 00000000000..f707f4a305d --- /dev/null +++ b/compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt @@ -0,0 +1,49 @@ +// !LANGUAGE: +MultiPlatformProjects +// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform +// TARGET_BACKEND: JVM +// WITH_RUNTIME +// FILE: common.kt + +@OptionalExpectation +expect annotation class Anno(val s: String) + +// FILE: jvm.kt + +import java.lang.reflect.AnnotatedElement + +@Anno("Foo") +class Foo @Anno("") constructor(@Anno("x") x: Int) { + @Anno("bar") + fun bar() {} + + @Anno("getX") + var x = x + @Anno("setX") + set + + @Anno("Nested") + interface Nested +} + +private fun check(element: AnnotatedElement) { + check(element.annotations) +} + +private fun check(annotations: Array) { + val filtered = annotations.filterNot { it.annotationClass.java.name == "kotlin.Metadata" } + if (filtered.isNotEmpty()) { + throw AssertionError("Annotations should be empty: $filtered") + } +} + +fun box(): String { + val foo = Foo::class.java + check(foo) + check(Foo.Nested::class.java) + check(foo.declaredMethods.single { it.name == "bar" }) + check(foo.declaredMethods.single { it.name == "getX" }) + check(foo.declaredMethods.single { it.name == "setX" }) + check(foo.constructors.single()) + check(foo.constructors.single().parameterAnnotations.single()) + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt index f697431ca92..a176442165d 100644 --- a/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt +++ b/compiler/testData/codegen/bytecodeListing/multiplatform/optionalExpectation.txt @@ -1,3 +1,10 @@ +@java.lang.annotation.Retention +@kotlin.Metadata +@kotlin.OptionalExpectation +annotation class Anno { + public abstract method s(): java.lang.String +} + @kotlin.Metadata public interface Foo$Nested { inner class Foo$Nested diff --git a/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt b/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt new file mode 100644 index 00000000000..208bf99ef02 --- /dev/null +++ b/compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt @@ -0,0 +1,35 @@ +// !LANGUAGE: +MultiPlatformProjects +// !USE_EXPERIMENTAL: kotlin.ExperimentalMultiplatform +// IGNORE_BACKEND: NATIVE +// FULL_JDK +// FILE: A.kt +package a + +@OptionalExpectation +expect annotation class A(val x: Int) + +@OptionalExpectation +expect annotation class B(val s: String) + +actual annotation class A(actual val x: Int) + +// FILE: B.kt +import a.A +import a.B +import java.lang.reflect.Modifier + +class Test { + @A(42) + @B("OK") + fun test() {} +} + +fun box(): String { + val annotations = Test::class.java.declaredMethods.single().annotations.toList() + if (annotations.toString() != "[@a.A(x=42)]") return "Fail 1: $annotations" + + // Can't use B::class.java because "Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry" + if (Modifier.isPublic(Class.forName("a.B").modifiers)) return "Fail 2: optional annotation class should not be public in the bytecode" + + return "OK" +} diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt index ed1f94881ab..4ea3cd7dd9d 100644 --- a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt @@ -26,6 +26,9 @@ fun useInSignature(a: A) = a.toString() compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:9:1: error: this annotation is not applicable to target 'class' @OptionalExpectation ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:10:14: error: expected class 'NotAnAnnotationClass' has no actual declaration in module +expect class NotAnAnnotationClass + ^ compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:12:1: error: '@OptionalExpectation' can only be used on an expected annotation class @OptionalExpectation ^ diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 248a49343ec..41422ebfb4d 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -13492,6 +13492,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); } + @TestMetadata("optionalExpectationJvm.kt") + public void testOptionalExpectationJvm() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 53d8668a96a..70a9cec1a94 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -13492,6 +13492,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); } + @TestMetadata("optionalExpectationJvm.kt") + public void testOptionalExpectationJvm() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java index f8d92f6e4ff..23a397f8523 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstKotlinTestGenerated.java @@ -211,6 +211,11 @@ public class CompileKotlinAgainstKotlinTestGenerated extends AbstractCompileKotl runTest("compiler/testData/compileKotlinAgainstKotlin/nestedObject.kt"); } + @TestMetadata("optionalAnnotation.kt") + public void testOptionalAnnotation() throws Exception { + runTest("compiler/testData/compileKotlinAgainstKotlin/optionalAnnotation.kt"); + } + @TestMetadata("platformTypes.kt") public void testPlatformTypes() throws Exception { runTest("compiler/testData/compileKotlinAgainstKotlin/platformTypes.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 521111fd46f..8ccb1383d31 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -13492,6 +13492,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); } + @TestMetadata("optionalExpectationJvm.kt") + public void testOptionalExpectationJvm() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectationJvm.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt index 6a3d48f04a9..05bf2192b0a 100644 --- a/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt +++ b/js/js.serializer/src/org/jetbrains/kotlin/serialization/js/KotlinJavascriptSerializationUtil.kt @@ -26,9 +26,10 @@ import org.jetbrains.kotlin.metadata.js.JsProtoBuf import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.protobuf.CodedInputStream import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker import org.jetbrains.kotlin.resolve.descriptorUtil.filterOutSourceAnnotations import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.serialization.AnnotationSerializer @@ -190,9 +191,15 @@ object KotlinJavascriptSerializationUtil { ): ProtoBuf.PackageFragment { val builder = ProtoBuf.PackageFragment.newBuilder() - // TODO: ModuleDescriptor should be able to return the package only with the contents of that module, without dependencies - val skip: (DeclarationDescriptor) -> Boolean = { - DescriptorUtils.getContainingModule(it) != module || (it is MemberDescriptor && it.isExpect) + val skip = fun(descriptor: DeclarationDescriptor): Boolean { + // TODO: ModuleDescriptor should be able to return the package only with the contents of that module, without dependencies + if (descriptor.module != module) return true + + if (descriptor is MemberDescriptor && descriptor.isExpect) { + return !(descriptor is ClassDescriptor && ExpectedActualDeclarationChecker.shouldGenerateExpectClass(descriptor)) + } + + return false } val fileRegistry = KotlinFileRegistry() diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt index a1d40e07d79..9648d5a35a7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/BasicBoxTest.kt @@ -132,7 +132,7 @@ abstract class BasicBoxTest( } val mainModuleName = if (TEST_MODULE in modules) TEST_MODULE else DEFAULT_MODULE - val mainModule = modules[mainModuleName]!! + val mainModule = modules[mainModuleName] ?: error("No module with name \"$mainModuleName\"") val globalCommonFiles = JsTestUtils.getFilesInDirectoryByExtension( TEST_DATA_DIR_PATH + COMMON_FILES_DIR, JavaScript.EXTENSION) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index d33b53ed56a..5d2f1e12409 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -12842,6 +12842,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS_IR, true); } + @TestMetadata("optionalExpectation.kt") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index beceda14df1..a3d019e4fae 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -11714,6 +11714,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/multiplatform"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); } + @TestMetadata("optionalExpectation.kt") + public void testOptionalExpectation() throws Exception { + runTest("compiler/testData/codegen/box/multiplatform/optionalExpectation.kt"); + } + @TestMetadata("compiler/testData/codegen/box/multiplatform/defaultArguments") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)