diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt new file mode 100644 index 00000000000..f86a1069c39 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt @@ -0,0 +1,59 @@ +@Retention(AnnotationRetention.BINARY) +annotation class X + +@Retention(AnnotationRetention.RUNTIME) +annotation class Y + +@X +external class A { + @X + fun f() + + @X + val p: Int + + @get:X + val r: Int +} + +@Y +external class B { + @Y + fun f() + + @Y + val p: Int + + @get:Y + val r: Int +} + +@X +class C { + @X + fun f() {} + + @X + val p: Int = 0 + + val q: Int + @X get() = 0 + + @get:X + val r: Int = 0 +} + +@Y +class D { + @Y + fun f() {} + + @Y + val p: Int = 0 + + val q: Int + @Y get() = 0 + + @get:Y + val r: Int = 0 +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.txt b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.txt new file mode 100644 index 00000000000..5c8b24f198d --- /dev/null +++ b/compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.txt @@ -0,0 +1,57 @@ +package + +@X public external final class A { + public constructor A() + @X public final val p: kotlin.Int + public final val r: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @X public final fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@Y public external final class B { + public constructor B() + @Y public final val p: kotlin.Int + public final val r: kotlin.Int + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @Y public final fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@X public final class C { + public constructor C() + @X public final val p: kotlin.Int = 0 + public final val q: kotlin.Int + public final val r: kotlin.Int = 0 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @X public final fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@Y public final class D { + public constructor D() + @Y public final val p: kotlin.Int = 0 + public final val q: kotlin.Int + public final val r: kotlin.Int = 0 + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + @Y public final fun f(): kotlin.Unit + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} + +@kotlin.annotation.Retention(value = AnnotationRetention.BINARY) public final annotation class X : kotlin.Annotation { + public constructor X() + 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 +} + +@kotlin.annotation.Retention(value = AnnotationRetention.RUNTIME) public final annotation class Y : kotlin.Annotation { + public constructor Y() + 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/testsWithJsStdLibAndBackendCompilation/unsupportedFeatures/annotations.kt b/compiler/testData/diagnostics/testsWithJsStdLibAndBackendCompilation/unsupportedFeatures/annotations.kt index 64b1aeeb9fb..4ead5ca0c2a 100644 --- a/compiler/testData/diagnostics/testsWithJsStdLibAndBackendCompilation/unsupportedFeatures/annotations.kt +++ b/compiler/testData/diagnostics/testsWithJsStdLibAndBackendCompilation/unsupportedFeatures/annotations.kt @@ -30,9 +30,9 @@ class TestBinary { } } -@AnnotationWithRuntimeRetention +@AnnotationWithRuntimeRetention class TestRuntime { - @AnnotationWithRuntimeRetention + @AnnotationWithRuntimeRetention fun baz(@AnnotationWithRuntimeRetention foo : Int) : Int { return (@AnnotationWithRuntimeRetention 1) } diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java index 33a04a81b96..c040ca9743b 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithJsStdLibGenerated.java @@ -36,6 +36,12 @@ public class DiagnosticsTestWithJsStdLibGenerated extends AbstractDiagnosticsTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/diagnostics/testsWithJsStdLib"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("runtimeAnnotations.kt") + public void testRuntimeAnnotations() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithJsStdLib/runtimeAnnotations.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/diagnostics/testsWithJsStdLib/dynamicTypes") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt index eff6640e4c4..14773d44a73 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/JsPlatformConfigurator.kt @@ -36,7 +36,8 @@ object JsPlatformConfigurator : PlatformConfigurator( NativeInvokeChecker(), NativeGetterChecker(), NativeSetterChecker(), JsNameChecker, JsModuleChecker, PlatformImplDeclarationChecker(), - JsExternalChecker() + JsExternalChecker(), + JsRuntimeAnnotationChecker ), additionalCallCheckers = listOf(ReifiedTypeParameterSubstitutionChecker(), JsModuleCallChecker, JsDynamicCallChecker), additionalTypeCheckers = listOf(), diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt index 01f745bf9e5..42c2fa41f52 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -65,6 +65,9 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by lazy { put(ErrorsJs.NATIVE_INTERFACE_AS_REIFIED_TYPE_ARGUMENT, "Cannot pass native interface {0} for reified type parameter", RENDER_TYPE) put(ErrorsJs.EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE, "External type extends non-external type") put(ErrorsJs.WRONG_OPERATION_WITH_DYNAMIC, "Wrong operation with dynamic value: {0}", Renderers.STRING) + put(ErrorsJs.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION, "Runtime annotation can't be put on external declaration") + put(ErrorsJs.RUNTIME_ANNOTATION_NOT_SUPPORTED, "Reflection is not supported in JavaScript target, therefore you won't be able " + + "to read this annotation in run-time") this } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java index b4f8b7130ed..f56ed793245 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/ErrorsJs.java @@ -65,6 +65,9 @@ public interface ErrorsJs { DiagnosticFactory0 EXTERNAL_TYPE_EXTENDS_NON_EXTERNAL_TYPE = DiagnosticFactory0.create( ERROR, PositioningStrategies.DECLARATION_SIGNATURE_OR_DEFAULT); DiagnosticFactory1 WRONG_OPERATION_WITH_DYNAMIC = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION = DiagnosticFactory0.create( + ERROR, DECLARATION_SIGNATURE_OR_DEFAULT); + DiagnosticFactory0 RUNTIME_ANNOTATION_NOT_SUPPORTED = DiagnosticFactory0.create(WARNING, DECLARATION_SIGNATURE_OR_DEFAULT); @SuppressWarnings("UnusedDeclaration") Object _initializer = new Object() { diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsRuntimeAnnotationChecker.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsRuntimeAnnotationChecker.kt new file mode 100644 index 00000000000..3669c8e63d8 --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/JsRuntimeAnnotationChecker.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2010-2016 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.js.resolve.diagnostics + +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention +import org.jetbrains.kotlin.diagnostics.DiagnosticSink +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.checkers.SimpleDeclarationChecker +import org.jetbrains.kotlin.resolve.descriptorUtil.getAnnotationRetention +import org.jetbrains.kotlin.resolve.source.PsiSourceElement + +object JsRuntimeAnnotationChecker : SimpleDeclarationChecker { + override fun check( + declaration: KtDeclaration, + descriptor: DeclarationDescriptor, + diagnosticHolder: DiagnosticSink, + bindingContext: BindingContext + ) { + for ((annotationDescriptor, _) in descriptor.annotations.getAllAnnotations()) { + val annotationClass = annotationDescriptor.type.constructor.declarationDescriptor as? ClassDescriptor ?: continue + if (annotationClass.getAnnotationRetention() != KotlinRetention.RUNTIME) continue + + val annotationPsi = (annotationDescriptor.source as? PsiSourceElement)?.psi ?: declaration + + if (descriptor is MemberDescriptor && DescriptorUtils.isEffectivelyExternal(descriptor)) { + diagnosticHolder.report(ErrorsJs.RUNTIME_ANNOTATION_ON_EXTERNAL_DECLARATION.on(annotationPsi)) + } + else { + diagnosticHolder.report(ErrorsJs.RUNTIME_ANNOTATION_NOT_SUPPORTED.on(annotationPsi)) + } + } + } +} \ No newline at end of file