From 1324667bc9ac8efe65a66b3365cd51464e3372b5 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Mon, 6 Apr 2015 16:32:27 +0300 Subject: [PATCH] Prohibit use of positioned value arguments for java annotation --- .../load/kotlin/JavaAnnotationCallChecker.kt | 45 +++++++++++++++++++ .../load/kotlin/KotlinJvmCheckerProvider.kt | 2 +- .../diagnostics/DefaultErrorMessagesJvm.java | 2 + .../resolve/jvm/diagnostics/ErrorsJvm.java | 2 + .../kotlinAnnotation.kt | 5 +++ .../kotlinAnnotation.txt | 15 +++++++ .../prohibitPositionedArgument/tooManyArgs.kt | 11 +++++ .../tooManyArgs.txt | 13 ++++++ .../typeMismatch.kt | 13 ++++++ .../typeMismatch.txt | 14 ++++++ .../prohibitPositionedArgument/withValue.kt | 17 +++++++ .../prohibitPositionedArgument/withValue.txt | 17 +++++++ .../withoutValue.kt | 17 +++++++ .../withoutValue.txt | 15 +++++++ ...JetDiagnosticsTestWithStdLibGenerated.java | 40 +++++++++++++++++ 15 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.txt diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt new file mode 100644 index 00000000000..ec41dae95ec --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/JavaAnnotationCallChecker.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2015 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.load.kotlin + +import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.load.java.descriptors.JavaConstructorDescriptor +import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker +import org.jetbrains.kotlin.resolve.calls.context.BasicCallResolutionContext +import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall +import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm + +public class JavaAnnotationCallChecker : CallChecker { + override fun check(resolvedCall: ResolvedCall, context: BasicCallResolutionContext) { + val resultingDescriptor = resolvedCall.getResultingDescriptor().getOriginal() + if (resultingDescriptor !is JavaConstructorDescriptor || + resultingDescriptor.getContainingDeclaration().getKind() != ClassKind.ANNOTATION_CLASS) return + + resolvedCall.getValueArguments().filter { + p -> p.key.getName() != JvmAnnotationNames.DEFAULT_ANNOTATION_MEMBER_NAME && + p.value is ExpressionValueArgument && + !((p.value as ExpressionValueArgument).getValueArgument()?.isNamed() ?: true) + }.forEach { + context.trace.report( + ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION.on( + it.getValue().getArguments().first().getArgumentExpression())) + } + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt index a2fb9f179b3..8a2a9142e22 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/KotlinJvmCheckerProvider.kt @@ -52,7 +52,7 @@ public object KotlinJvmCheckerProvider : AdditionalCheckerProvider( ReifiedTypeParameterAnnotationChecker(), NativeFunChecker(), OverloadsAnnotationChecker()), - additionalCallCheckers = listOf(NeedSyntheticChecker()), + additionalCallCheckers = listOf(NeedSyntheticChecker(), JavaAnnotationCallChecker()), additionalTypeCheckers = listOf(JavaNullabilityWarningsChecker()) ) diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java index 9087d7dc6f2..92550e0b3e7 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/DefaultErrorMessagesJvm.java @@ -56,6 +56,8 @@ public class DefaultErrorMessagesJvm implements DefaultErrorMessages.Extension { MAP.put(ErrorsJvm.NATIVE_DECLARATION_IN_TRAIT, "Members of traits can not be native"); MAP.put(ErrorsJvm.NATIVE_DECLARATION_CANNOT_BE_INLINED, "Members of traits can not be inlined"); + MAP.put(ErrorsJvm.POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION, "Only named arguments are available for Java annotations"); + MAP.put(ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH, "Expression ''{0}'' uses reflection which is not found in compilation classpath. Make sure you have kotlin-reflect.jar in the classpath", Renderers.ELEMENT_TEXT); MAP.put(ErrorsJvm.NULLABILITY_MISMATCH_BASED_ON_JAVA_ANNOTATIONS, diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java index ce586c3c61f..d28078e6e6b 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/diagnostics/ErrorsJvm.java @@ -51,6 +51,8 @@ public interface ErrorsJvm { DiagnosticFactory0 NATIVE_DECLARATION_IN_TRAIT = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); DiagnosticFactory0 NATIVE_DECLARATION_CANNOT_BE_INLINED = DiagnosticFactory0.create(ERROR, DECLARATION_SIGNATURE); + DiagnosticFactory0 POSITIONED_VALUE_ARGUMENT_FOR_JAVA_ANNOTATION = DiagnosticFactory0.create(ERROR); + // TODO: make this a warning DiagnosticFactory1 NO_REFLECTION_IN_CLASS_PATH = DiagnosticFactory1.create(ERROR); diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.kt new file mode 100644 index 00000000000..c55836e20a1 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.kt @@ -0,0 +1,5 @@ +annotation class Ann(val x: Int, val value: String, val y: Double) + +Ann(value = "a", x = 1, y = 1.0) fun foo1() {} +Ann(2, "b", 2.0) fun foo2() {} +Ann(3, "c", y = 2.0) fun foo3() {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.txt new file mode 100644 index 00000000000..5701694df5c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.txt @@ -0,0 +1,15 @@ +package + +Ann(value = "a": kotlin.String, x = IntegerValueType(1): IntegerValueType(1), y = 1.0.toDouble(): kotlin.Double) internal fun foo1(): kotlin.Unit +Ann(value = "b": kotlin.String, x = IntegerValueType(2): IntegerValueType(2), y = 2.0.toDouble(): kotlin.Double) internal fun foo2(): kotlin.Unit +Ann(value = "c": kotlin.String, x = IntegerValueType(3): IntegerValueType(3), y = 2.0.toDouble(): kotlin.Double) internal fun foo3(): kotlin.Unit + +internal final annotation class Ann : kotlin.Annotation { + public constructor Ann(/*0*/ x: kotlin.Int, /*1*/ value: kotlin.String, /*2*/ y: kotlin.Double) + internal final val value: kotlin.String + internal final val x: kotlin.Int + internal final val y: kotlin.Double + 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 +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.kt new file mode 100644 index 00000000000..d5c1acdfdbb --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.kt @@ -0,0 +1,11 @@ +// FILE: A.java +public @interface A { + int a(); + double b(); + boolean x(); +} + +// FILE: b.kt +A(false, +1.0, +false, 1, 2) fun foo1() {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.txt new file mode 100644 index 00000000000..b1f2a191b43 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.txt @@ -0,0 +1,13 @@ +package + +A(a = false: kotlin.Boolean, b = 1.0.toDouble(): kotlin.Double, x = false: kotlin.Boolean) internal fun foo1(): kotlin.Unit + +public final annotation class A : kotlin.Annotation { + public constructor A(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Double, /*2*/ x: kotlin.Boolean) + public abstract fun a(): kotlin.Int + public abstract fun b(): kotlin.Double + 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 abstract fun x(): kotlin.Boolean +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.kt new file mode 100644 index 00000000000..255c20e81c6 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.kt @@ -0,0 +1,13 @@ +// FILE: A.java +public @interface A { + int a(); + double b(); + boolean x(); +} + +// FILE: b.kt +A(false, +1.0, +false) fun foo1() {} + +A(2.0, x = true, b = 2.0) fun foo2() {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.txt new file mode 100644 index 00000000000..3f35ea07119 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.txt @@ -0,0 +1,14 @@ +package + +A(a = false: kotlin.Boolean, b = 1.0.toDouble(): kotlin.Double, x = false: kotlin.Boolean) internal fun foo1(): kotlin.Unit +A(a = 2.0.toDouble(): kotlin.Double, b = 2.0.toDouble(): kotlin.Double, x = true: kotlin.Boolean) internal fun foo2(): kotlin.Unit + +public final annotation class A : kotlin.Annotation { + public constructor A(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Double, /*2*/ x: kotlin.Boolean) + public abstract fun a(): kotlin.Int + public abstract fun b(): kotlin.Double + 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 abstract fun x(): kotlin.Boolean +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.kt new file mode 100644 index 00000000000..838498af4f7 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.kt @@ -0,0 +1,17 @@ +// FILE: A.java +public @interface A { + int a(); + double b(); + String value(); + boolean x(); +} + +// FILE: b.kt +A("v1", 1, +1.0, +false) fun foo1() {} + +A("v2", 2, x = true, b = 2.0) fun foo2() {} + +A("v2", x = true, b = 3.0, a = 4) fun foo3() {} +A(value = "v2", x = true, b = 3.0, a = 4) fun foo4() {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.txt new file mode 100644 index 00000000000..180bd979e51 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.txt @@ -0,0 +1,17 @@ +package + +A(a = IntegerValueType(1): IntegerValueType(1), b = 1.0.toDouble(): kotlin.Double, value = "v1": kotlin.String, x = false: kotlin.Boolean) internal fun foo1(): kotlin.Unit +A(a = IntegerValueType(2): IntegerValueType(2), b = 2.0.toDouble(): kotlin.Double, value = "v2": kotlin.String, x = true: kotlin.Boolean) internal fun foo2(): kotlin.Unit +A(a = IntegerValueType(4): IntegerValueType(4), b = 3.0.toDouble(): kotlin.Double, value = "v2": kotlin.String, x = true: kotlin.Boolean) internal fun foo3(): kotlin.Unit +A(a = IntegerValueType(4): IntegerValueType(4), b = 3.0.toDouble(): kotlin.Double, value = "v2": kotlin.String, x = true: kotlin.Boolean) internal fun foo4(): kotlin.Unit + +public final annotation class A : kotlin.Annotation { + public constructor A(/*0*/ value: kotlin.String, /*1*/ a: kotlin.Int, /*2*/ b: kotlin.Double, /*3*/ x: kotlin.Boolean) + public abstract fun a(): kotlin.Int + public abstract fun b(): kotlin.Double + 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 abstract fun value(): kotlin.String + public abstract fun x(): kotlin.Boolean +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.kt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.kt new file mode 100644 index 00000000000..09d9d985f46 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.kt @@ -0,0 +1,17 @@ +// FILE: A.java +public @interface A { + int a(); + double b(); + boolean x(); +} + +// FILE: b.kt +A(1, +1.0, +false) fun foo1() {} + +A(2, x = true, b = 2.0) fun foo2() {} + +A(x = true, b = 3.0, a = 4) fun foo3() {} + + diff --git a/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.txt b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.txt new file mode 100644 index 00000000000..249c5645985 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.txt @@ -0,0 +1,15 @@ +package + +A(a = IntegerValueType(1): IntegerValueType(1), b = 1.0.toDouble(): kotlin.Double, x = false: kotlin.Boolean) internal fun foo1(): kotlin.Unit +A(a = IntegerValueType(2): IntegerValueType(2), b = 2.0.toDouble(): kotlin.Double, x = true: kotlin.Boolean) internal fun foo2(): kotlin.Unit +A(a = IntegerValueType(4): IntegerValueType(4), b = 3.0.toDouble(): kotlin.Double, x = true: kotlin.Boolean) internal fun foo3(): kotlin.Unit + +public final annotation class A : kotlin.Annotation { + public constructor A(/*0*/ a: kotlin.Int, /*1*/ b: kotlin.Double, /*2*/ x: kotlin.Boolean) + public abstract fun a(): kotlin.Int + public abstract fun b(): kotlin.Double + 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 abstract fun x(): kotlin.Boolean +} diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java index 3bbf29a68e8..606253ba2b8 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/JetDiagnosticsTestWithStdLibGenerated.java @@ -58,6 +58,7 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic Annotations.AnnotationWithVarargParameter.class, Annotations.JvmOverloads.class, Annotations.PlatformStatic.class, + Annotations.ProhibitPositionedArgument.class, }) @RunWith(JUnit3RunnerWithInners.class) public static class Annotations extends AbstractJetDiagnosticsTestWithStdLib { @@ -262,6 +263,45 @@ public class JetDiagnosticsTestWithStdLibGenerated extends AbstractJetDiagnostic doTest(fileName); } } + + @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class ProhibitPositionedArgument extends AbstractJetDiagnosticsTestWithStdLib { + public void testAllFilesPresentInProhibitPositionedArgument() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("kotlinAnnotation.kt") + public void testKotlinAnnotation() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/kotlinAnnotation.kt"); + doTest(fileName); + } + + @TestMetadata("tooManyArgs.kt") + public void testTooManyArgs() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/tooManyArgs.kt"); + doTest(fileName); + } + + @TestMetadata("typeMismatch.kt") + public void testTypeMismatch() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/typeMismatch.kt"); + doTest(fileName); + } + + @TestMetadata("withValue.kt") + public void testWithValue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withValue.kt"); + doTest(fileName); + } + + @TestMetadata("withoutValue.kt") + public void testWithoutValue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/annotations/prohibitPositionedArgument/withoutValue.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/diagnostics/testsWithStdLib/callableReference")