From 32531b3afc6b5116572f9dc2407fd513ef70343f Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Fri, 15 Sep 2017 10:49:33 +0300 Subject: [PATCH] Allow to have all parameters with default values in `actual` annotation #KT-19656 In Progress --- .../ExpectedActualDeclarationChecker.kt | 27 ++++++- ...thDefaultValuesInAnnotationViaTypealias.kt | 33 +++++++++ ...hDefaultValuesInAnnotationViaTypealias.txt | 70 +++++++++++++++++++ .../checkers/DiagnosticsTestGenerated.java | 6 ++ .../DiagnosticsUsingJavacTestGenerated.java | 6 ++ 5 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.kt create mode 100644 compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.txt 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 94c7c958e2e..35d78e8147a 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExpectedActualDeclarationChecker.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 JetBrains s.r.o. + * Copyright 2010-2017 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -131,7 +131,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker { if (!hasExpectedModifier) { if (Compatible !in compatibility) return - if (checkExpected) { + // 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 (checkExpected && !descriptor.isAnnotationConstructor()) { diagnosticHolder.report(Errors.ACTUAL_MISSING.on(reportOn)) } } @@ -362,7 +364,9 @@ object ExpectedActualDeclarationChecker : DeclarationChecker { val aParams = a.valueParameters val bParams = b.valueParameters - if (aParams.size != bParams.size) return Incompatible.ParameterCount + if (!valueParametersCountCompatible(a, b, aParams, bParams)) { + return Incompatible.ParameterCount + } val aTypeParams = a.typeParameters val bTypeParams = b.typeParameters @@ -401,6 +405,23 @@ object ExpectedActualDeclarationChecker : DeclarationChecker { return Compatible } + private fun valueParametersCountCompatible( + a: CallableMemberDescriptor, + b: CallableMemberDescriptor, + aParams: List, + bParams: List + ): Boolean { + if (aParams.size == bParams.size) return true + + return if (a.isAnnotationConstructor() && b.isAnnotationConstructor()) + aParams.isEmpty() && bParams.all { it.declaresDefaultValue() } + else + false + } + + private fun MemberDescriptor.isAnnotationConstructor(): Boolean = + this is ConstructorDescriptor && DescriptorUtils.isAnnotationClass(this.constructedClass) + private fun areCompatibleTypes(a: KotlinType?, b: KotlinType?, platformModule: ModuleDescriptor): Boolean { if (a == null) return b == null if (b == null) return false diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.kt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.kt new file mode 100644 index 00000000000..5d861a8e0fa --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.kt @@ -0,0 +1,33 @@ +// !LANGUAGE: +MultiPlatformProjects +// MODULE: m1-common +// FILE: common.kt + +expect annotation class Foo1 +expect annotation class Foo2 +expect annotation class Foo3 +expect annotation class Foo4 + +// MODULE: m2-jvm(m1-common) + +// FILE: Bar1.java + +public @interface Bar1 { + String value() default ""; +} + +// FILE: Bar2.java + +public @interface Bar2 { + String value() default ""; + String path(); +} + +// FILE: jvm.kt + +actual typealias Foo1 = Bar1 + +actual typealias Foo4 = Bar2 + +actual annotation class Foo2(val p: String = "default") + +actual annotation class Foo3(val a: String = "a", val b: String = "b") \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.txt b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.txt new file mode 100644 index 00000000000..048ecc935f6 --- /dev/null +++ b/compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.txt @@ -0,0 +1,70 @@ +// -- Module: -- +package + +public final expect annotation class Foo1 : kotlin.Annotation { + public constructor Foo1() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final expect annotation class Foo2 : kotlin.Annotation { + public constructor Foo2() + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +public final expect annotation class Foo3 : kotlin.Annotation { + 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 expect annotation class Foo4 : kotlin.Annotation { + public constructor Foo4() + 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: -- +package + +public final annotation class Bar1 : kotlin.Annotation { + public constructor Bar1(/*0*/ value: kotlin.String = ...) + public final val value: 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 annotation class Bar2 : kotlin.Annotation { + public constructor Bar2(/*0*/ value: kotlin.String = ..., /*1*/ path: kotlin.String) + public final val path: kotlin.String + public final val value: 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 annotation class Foo2 : kotlin.Annotation { + public constructor Foo2(/*0*/ p: kotlin.String = ...) + public final val p: 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 annotation class Foo3 : kotlin.Annotation { + public constructor Foo3(/*0*/ a: kotlin.String = ..., /*1*/ b: kotlin.String = ...) + public final val a: kotlin.String + public final val b: kotlin.String + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} +public typealias Foo1 = Bar1 +public typealias Foo4 = Bar2 diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 1608d4ac503..c85c0289ea4 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -13934,6 +13934,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class HeaderClass extends AbstractDiagnosticsTest { + @TestMetadata("actualClassWithDefaultValuesInAnnotationViaTypealias.kt") + public void testActualClassWithDefaultValuesInAnnotationViaTypealias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.kt"); + doTest(fileName); + } + @TestMetadata("actualMissing.kt") public void testActualMissing() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java index 3c8ca920e7a..77099caf3b9 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsUsingJavacTestGenerated.java @@ -13934,6 +13934,12 @@ public class DiagnosticsUsingJavacTestGenerated extends AbstractDiagnosticsUsing @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class HeaderClass extends AbstractDiagnosticsUsingJavacTest { + @TestMetadata("actualClassWithDefaultValuesInAnnotationViaTypealias.kt") + public void testActualClassWithDefaultValuesInAnnotationViaTypealias() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/actualClassWithDefaultValuesInAnnotationViaTypealias.kt"); + doTest(fileName); + } + @TestMetadata("actualMissing.kt") public void testActualMissing() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/multiplatform/headerClass/actualMissing.kt");