From 6d4e8f3781b028ada770cf6fb6db269ea76c118d Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 20 Dec 2017 19:43:03 +0100 Subject: [PATCH] Add some validation for Experimental/UseExperimental usages #KT-22759 In Progress --- .../jetbrains/kotlin/diagnostics/Errors.java | 6 ++ .../rendering/DefaultErrorMessages.java | 7 ++ .../kotlin/resolve/AnnotationChecker.kt | 13 ++- .../kotlin/resolve/TargetPlatform.kt | 4 +- ...entalMarkerDeclarationAnnotationChecker.kt | 91 +++++++++++++++++++ .../checkers/ExperimentalUsageChecker.kt | 24 ++--- .../experimental/annotation.kt | 2 +- .../experimental/annotation.txt | 2 +- .../experimental/experimentalWithNoImpact.kt | 4 + .../experimental/experimentalWithNoImpact.txt | 8 ++ ...correctTargetsForExperimentalAnnotation.kt | 23 +++++ ...orrectTargetsForExperimentalAnnotation.txt | 32 +++++++ .../experimental/incorrectUseExperimental.kt | 30 ++++++ .../experimental/incorrectUseExperimental.txt | 16 ++++ .../testsWithStdLib/experimental/topLevel.kt | 2 +- .../testsWithStdLib/experimental/topLevel.txt | 2 +- .../DiagnosticsTestWithStdLibGenerated.java | 18 ++++ ...ticsTestWithStdLibUsingJavacGenerated.java | 18 ++++ 18 files changed, 281 insertions(+), 21 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt create mode 100644 compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.txt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 89785af9329..4b697ab275e 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -229,6 +229,12 @@ public interface Errors { DiagnosticFactory2 EXPERIMENTAL_API_USAGE = DiagnosticFactory2.create(WARNING); DiagnosticFactory2 EXPERIMENTAL_API_USAGE_ERROR = DiagnosticFactory2.create(ERROR); + DiagnosticFactory0 USE_EXPERIMENTAL_WITHOUT_ARGUMENTS = DiagnosticFactory0.create(WARNING); + DiagnosticFactory1 USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER = DiagnosticFactory1.create(WARNING); + DiagnosticFactory1 USE_EXPERIMENTAL_ARGUMENT_HAS_NON_COMPILATION_IMPACT = DiagnosticFactory1.create(ERROR); + DiagnosticFactory0 EXPERIMENTAL_ANNOTATION_WITH_NO_IMPACT = DiagnosticFactory0.create(ERROR); + DiagnosticFactory1 EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET = DiagnosticFactory1.create(ERROR); + // Const DiagnosticFactory0 CONST_VAL_NOT_TOP_LEVEL_OR_OBJECT = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 CONST_VAL_WITH_GETTER = DiagnosticFactory0.create(ERROR); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index c280a0ee44f..a967280b3f1 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -151,6 +151,13 @@ public class DefaultErrorMessages { MAP.put(EXPERIMENTAL_API_USAGE, "This declaration is experimental and its usage should be marked with ''@{0}''{1}", TO_STRING, renderUseExperimental); MAP.put(EXPERIMENTAL_API_USAGE_ERROR, "This declaration is experimental and its usage must be marked with ''@{0}''{1}", TO_STRING, renderUseExperimental); + MAP.put(USE_EXPERIMENTAL_WITHOUT_ARGUMENTS, "@UseExperimental without any arguments has no effect"); + MAP.put(USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER, "Annotation ''{0}'' is not an experimental API marker, therefore its usage in @UseExperimental is ignored", TO_STRING); + MAP.put(USE_EXPERIMENTAL_ARGUMENT_HAS_NON_COMPILATION_IMPACT, + "Experimental annotation ''{0}'' has impact other than COMPILATION, therefore its usage in @UseExperimental is forbidden", TO_STRING); + MAP.put(EXPERIMENTAL_ANNOTATION_WITH_NO_IMPACT, "Experimental annotation with changesMayBreak = [] is not allowed"); + MAP.put(EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET, "Experimental annotation cannot be used on the following code elements: {0}. Please remove these targets", STRING); + MAP.put(REDUNDANT_MODIFIER, "Modifier ''{0}'' is redundant because ''{1}'' is present", TO_STRING, TO_STRING); MAP.put(REDUNDANT_OPEN_IN_INTERFACE, "Modifier 'open' is redundant for abstract interface members"); MAP.put(REDUNDANT_MODIFIER_IN_GETTER, "Visibility modifiers are redundant in getter"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt index c52af26916f..d63d926ae1d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/AnnotationChecker.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.descriptors.annotations.KotlinRetention import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget.* import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getAnnotationEntries import org.jetbrains.kotlin.resolve.constants.ArrayValue @@ -193,6 +194,8 @@ class AnnotationChecker( } companion object { + private val TARGET_ALLOWED_TARGETS = Name.identifier("allowedTargets") + private fun applicableTargetSet(entry: KtAnnotationEntry, trace: BindingTrace): Set { val descriptor = trace.get(BindingContext.ANNOTATION, entry) ?: return KotlinTarget.DEFAULT_TARGET_SET // For descriptor with error type, all targets are considered as possible @@ -207,10 +210,12 @@ class AnnotationChecker( } fun applicableTargetSet(classDescriptor: ClassDescriptor): Set? { - val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target) - ?: return null - val valueArguments = targetEntryDescriptor.allValueArguments - val valueArgument = valueArguments.entries.firstOrNull()?.value as? ArrayValue ?: return null + val targetEntryDescriptor = classDescriptor.annotations.findAnnotation(KotlinBuiltIns.FQ_NAMES.target) ?: return null + return loadAnnotationTargets(targetEntryDescriptor) + } + + fun loadAnnotationTargets(targetEntryDescriptor: AnnotationDescriptor): Set? { + val valueArgument = targetEntryDescriptor.allValueArguments[TARGET_ALLOWED_TARGETS] as? ArrayValue ?: return null return valueArgument.value.filterIsInstance().mapNotNull { KotlinTarget.valueOrNull(it.enumEntryName.asString()) }.toSet() diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt index e1e6c409aef..cd6a48498f2 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt @@ -98,7 +98,9 @@ private val DEFAULT_CLASSIFIER_USAGE_CHECKERS = listOf( DeprecatedClassifierUsageChecker(), ApiVersionClassifierUsageChecker, MissingDependencyClassChecker.ClassifierUsage, ExperimentalUsageChecker.ClassifierUsage ) -private val DEFAULT_ANNOTATION_CHECKERS = emptyList() +private val DEFAULT_ANNOTATION_CHECKERS = listOf( + ExperimentalMarkerDeclarationAnnotationChecker +) abstract class PlatformConfigurator( diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt new file mode 100644 index 00000000000..5cfaec492b1 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalMarkerDeclarationAnnotationChecker.kt @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.resolve.checkers + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.annotations.KotlinTarget +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.resolve.AdditionalAnnotationChecker +import org.jetbrains.kotlin.resolve.AnnotationChecker +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.constants.ArrayValue +import org.jetbrains.kotlin.resolve.constants.ConstantValue +import org.jetbrains.kotlin.resolve.constants.KClassValue +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe + +object ExperimentalMarkerDeclarationAnnotationChecker : AdditionalAnnotationChecker { + private val WRONG_TARGETS_FOR_MARKER = setOf(KotlinTarget.EXPRESSION, KotlinTarget.FILE) + + override fun checkEntries(entries: List, actualTargets: List, trace: BindingTrace) { + var isAnnotatedWithExperimental = false + + for (entry in entries) { + val annotation = trace.bindingContext.get(BindingContext.ANNOTATION, entry) + when (annotation?.fqName) { + ExperimentalUsageChecker.USE_EXPERIMENTAL_FQ_NAME -> { + val annotationClasses = + (annotation.allValueArguments[ExperimentalUsageChecker.USE_EXPERIMENTAL_ANNOTATION_CLASS] as? ArrayValue)?.value.orEmpty() + checkUseExperimentalUsage(annotationClasses, trace, entry) + } + ExperimentalUsageChecker.EXPERIMENTAL_FQ_NAME -> { + val impact = (annotation.allValueArguments[ExperimentalUsageChecker.IMPACT] as? ArrayValue)?.value + checkExperimentalUsage(impact, trace, entry) + isAnnotatedWithExperimental = true + } + } + } + + if (isAnnotatedWithExperimental) { + checkMarkerTargets(entries, trace) + } + } + + private fun checkUseExperimentalUsage(annotationClasses: List>, trace: BindingTrace, entry: KtAnnotationEntry) { + if (annotationClasses.isEmpty()) { + trace.report(Errors.USE_EXPERIMENTAL_WITHOUT_ARGUMENTS.on(entry)) + return + } + + for (annotationClass in annotationClasses) { + val classDescriptor = + (annotationClass as? KClassValue)?.value?.constructor?.declarationDescriptor as? ClassDescriptor + ?: continue + val experimentality = with(ExperimentalUsageChecker) { + classDescriptor.loadExperimentalityForMarkerAnnotation() + } + if (experimentality == null) { + trace.report(Errors.USE_EXPERIMENTAL_ARGUMENT_IS_NOT_MARKER.on(entry, classDescriptor.fqNameSafe)) + } else if (!experimentality.isCompilationOnly) { + trace.report(Errors.USE_EXPERIMENTAL_ARGUMENT_HAS_NON_COMPILATION_IMPACT.on(entry, experimentality.annotationFqName)) + } + } + } + + private fun checkExperimentalUsage(impact: List>?, trace: BindingTrace, entry: KtAnnotationEntry) { + if (impact != null && impact.isEmpty()) { + trace.report(Errors.EXPERIMENTAL_ANNOTATION_WITH_NO_IMPACT.on(entry)) + } + } + + private fun checkMarkerTargets(entries: List, trace: BindingTrace) { + val targetEntry = + entries.associate { entry -> entry to trace.bindingContext.get(BindingContext.ANNOTATION, entry) } + .entries + .firstOrNull { (_, descriptor) -> descriptor != null && descriptor.fqName == KotlinBuiltIns.FQ_NAMES.target } + ?: return + val (entry, descriptor) = targetEntry + val allowedTargets = AnnotationChecker.loadAnnotationTargets(descriptor!!) ?: return + val wrongTargets = allowedTargets.intersect(WRONG_TARGETS_FOR_MARKER) + if (wrongTargets.isNotEmpty()) { + trace.report( + Errors.EXPERIMENTAL_ANNOTATION_WITH_WRONG_TARGET.on(entry, wrongTargets.joinToString(transform = KotlinTarget::description)) + ) + } + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt index ae471a7fba9..3ebfacf9d00 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/ExperimentalUsageChecker.kt @@ -41,20 +41,20 @@ import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.addIfNotNull object ExperimentalUsageChecker : CallChecker { - private val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental") - private val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental") - private val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass") + internal val EXPERIMENTAL_FQ_NAME = FqName("kotlin.Experimental") + internal val USE_EXPERIMENTAL_FQ_NAME = FqName("kotlin.UseExperimental") + internal val USE_EXPERIMENTAL_ANNOTATION_CLASS = Name.identifier("markerClass") private val LEVEL = Name.identifier("level") private val WARNING_LEVEL = Name.identifier("WARNING") private val ERROR_LEVEL = Name.identifier("ERROR") - private val IMPACT = Name.identifier("changesMayBreak") + internal val IMPACT = Name.identifier("changesMayBreak") private val COMPILATION_IMPACT = Name.identifier("COMPILATION") private val LINKAGE_IMPACT = Name.identifier("LINKAGE") private val RUNTIME_IMPACT = Name.identifier("RUNTIME") - private data class Experimentality(val annotationFqName: FqName, val severity: Severity, val impact: List) { + internal data class Experimentality(val annotationFqName: FqName, val severity: Severity, private val impact: List) { val isCompilationOnly: Boolean get() = impact.all(Impact.COMPILATION::equals) enum class Severity { WARNING, ERROR } @@ -72,7 +72,7 @@ object ExperimentalUsageChecker : CallChecker { private fun checkExperimental(descriptor: DeclarationDescriptor, element: PsiElement, trace: BindingTrace) { for (experimentality in descriptor.loadExperimentalities()) { - val (annotationFqName, severity, impact) = experimentality + val (annotationFqName, severity) = experimentality val isBodyUsageOfCompilationExperimentality = experimentality.isCompilationOnly && element.isBodyUsage() @@ -95,22 +95,21 @@ object ExperimentalUsageChecker : CallChecker { val result = SmartSet.create() for (annotation in annotations) { - result.addIfNotNull(annotation.loadExperimentalityForMarkerAnnotation()) + result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation()) } val container = containingDeclaration if (container is ClassDescriptor && this !is ConstructorDescriptor) { for (annotation in container.annotations) { - result.addIfNotNull(annotation.loadExperimentalityForMarkerAnnotation()) + result.addIfNotNull(annotation.annotationClass?.loadExperimentalityForMarkerAnnotation()) } } return result } - private fun AnnotationDescriptor.loadExperimentalityForMarkerAnnotation(): Experimentality? { - val experimental = annotationClass?.annotations?.findAnnotation(EXPERIMENTAL_FQ_NAME) ?: return null - val annotationFqName = fqName ?: return null + internal fun ClassDescriptor.loadExperimentalityForMarkerAnnotation(): Experimentality? { + val experimental = annotations.findAnnotation(EXPERIMENTAL_FQ_NAME) ?: return null val severity = when ((experimental.allValueArguments[LEVEL] as? EnumValue)?.enumEntryName) { WARNING_LEVEL -> Experimentality.Severity.WARNING @@ -126,7 +125,7 @@ object ExperimentalUsageChecker : CallChecker { } } ?: Experimentality.DEFAULT_IMPACT - return Experimentality(annotationFqName, severity, impact) + return Experimentality(fqNameSafe, severity, impact) } // Returns true if this element appears in the body of some function and is not visible in any non-local declaration signature. @@ -189,4 +188,5 @@ object ExperimentalUsageChecker : CallChecker { checkExperimental(targetDescriptor, element, context.trace) } } + } diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.kt index 2918dd74429..da644733850 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.kt @@ -6,7 +6,7 @@ package api @Experimental(Experimental.Level.WARNING, [Experimental.Impact.COMPILATION]) @Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS, - AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) + AnnotationTarget.VALUE_PARAMETER) annotation class ExperimentalAPI @ExperimentalAPI diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.txt index 34f11bd2c47..c1076ad3ce9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/annotation.txt @@ -9,7 +9,7 @@ package api { public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } - @kotlin.Experimental(changesMayBreak = {Impact.COMPILATION}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION}) public final annotation class ExperimentalAPI : kotlin.Annotation { + @kotlin.Experimental(changesMayBreak = {Impact.COMPILATION}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS, AnnotationTarget.VALUE_PARAMETER}) public final annotation class ExperimentalAPI : kotlin.Annotation { public constructor ExperimentalAPI() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.kt new file mode 100644 index 00000000000..f87efbbd9b4 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.kt @@ -0,0 +1,4 @@ +// !API_VERSION: 1.3 + +@Experimental(Experimental.Level.WARNING, []) +annotation class ExperimentalAPI diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.txt new file mode 100644 index 00000000000..f5c268070ed --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.txt @@ -0,0 +1,8 @@ +package + +@kotlin.Experimental(changesMayBreak = {}, level = Level.WARNING) public final annotation class ExperimentalAPI : kotlin.Annotation { + public constructor ExperimentalAPI() + 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/experimental/incorrectTargetsForExperimentalAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt new file mode 100644 index 00000000000..c766f404c31 --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt @@ -0,0 +1,23 @@ +// !API_VERSION: 1.3 +// FILE: api.kt + +package api + +import kotlin.annotation.AnnotationTarget.* + +@Experimental(Experimental.Level.WARNING, [Experimental.Impact.RUNTIME]) +@Target(CLASS, ANNOTATION_CLASS, TYPE_PARAMETER, PROPERTY, FIELD, LOCAL_VARIABLE, VALUE_PARAMETER, CONSTRUCTOR, FUNCTION, + PROPERTY_GETTER, PROPERTY_SETTER, TYPE, TYPEALIAS) +annotation class E1 + +@Experimental(Experimental.Level.WARNING, [Experimental.Impact.RUNTIME]) +@Target(FILE) +annotation class E2 + +@Experimental(Experimental.Level.WARNING, [Experimental.Impact.RUNTIME]) +@Target(EXPRESSION) +annotation class E3 + +@Experimental(Experimental.Level.WARNING, [Experimental.Impact.RUNTIME]) +@Target(FILE, EXPRESSION) +annotation class E4 diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt new file mode 100644 index 00000000000..cd8c439ea0b --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.txt @@ -0,0 +1,32 @@ +package + +package api { + + @kotlin.Experimental(changesMayBreak = {Impact.RUNTIME}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.CLASS, AnnotationTarget.ANNOTATION_CLASS, AnnotationTarget.TYPE_PARAMETER, AnnotationTarget.PROPERTY, AnnotationTarget.FIELD, AnnotationTarget.LOCAL_VARIABLE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.TYPE, AnnotationTarget.TYPEALIAS}) public final annotation class E1 : kotlin.Annotation { + public constructor E1() + 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.Experimental(changesMayBreak = {Impact.RUNTIME}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FILE}) public final annotation class E2 : kotlin.Annotation { + public constructor E2() + 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.Experimental(changesMayBreak = {Impact.RUNTIME}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.EXPRESSION}) public final annotation class E3 : kotlin.Annotation { + public constructor E3() + 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.Experimental(changesMayBreak = {Impact.RUNTIME}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.FILE, AnnotationTarget.EXPRESSION}) public final annotation class E4 : kotlin.Annotation { + public constructor E4() + 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/experimental/incorrectUseExperimental.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt new file mode 100644 index 00000000000..791893aa09e --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt @@ -0,0 +1,30 @@ +// !API_VERSION: 1.3 +// FILE: api.kt + +package api + +@Experimental(Experimental.Level.WARNING, [Experimental.Impact.RUNTIME]) +@Target(AnnotationTarget.PROPERTY) +annotation class BinaryExperimental + +@BinaryExperimental +val x = "" + +// FILE: usage.kt + +import api.* + +@UseExperimental +fun use1(): String { + return x +} + +@UseExperimental(BinaryExperimental::class) +fun use2(): String { + return x +} + +@UseExperimental(UseExperimental::class) +fun use3(): String { + return x +} diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.txt new file mode 100644 index 00000000000..8a13c7c665c --- /dev/null +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.txt @@ -0,0 +1,16 @@ +package + +@kotlin.UseExperimental(annotationClass = {}) public fun use1(): kotlin.String +@kotlin.UseExperimental(annotationClass = {api.BinaryExperimental::class}) public fun use2(): kotlin.String +@kotlin.UseExperimental(annotationClass = {kotlin.UseExperimental::class}) public fun use3(): kotlin.String + +package api { + @api.BinaryExperimental public val x: kotlin.String = "" + + @kotlin.Experimental(changesMayBreak = {Impact.RUNTIME}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.PROPERTY}) public final annotation class BinaryExperimental : kotlin.Annotation { + public constructor BinaryExperimental() + 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/experimental/topLevel.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.kt index 8568cca1bae..65f5e5bb47e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.kt @@ -6,7 +6,7 @@ package api @Experimental(Experimental.Level.WARNING, [Experimental.Impact.COMPILATION]) @Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS, - AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) + AnnotationTarget.VALUE_PARAMETER) annotation class ExperimentalAPI @ExperimentalAPI diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.txt index 671c5cf634f..83ec2ca81d0 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.txt @@ -4,7 +4,7 @@ package api { @api.ExperimentalAPI public val property: kotlin.String = "" @api.ExperimentalAPI public fun function(): kotlin.String - @kotlin.Experimental(changesMayBreak = {Impact.COMPILATION}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION}) public final annotation class ExperimentalAPI : kotlin.Annotation { + @kotlin.Experimental(changesMayBreak = {Impact.COMPILATION}, level = Level.WARNING) @kotlin.annotation.Target(allowedTargets = {AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY, AnnotationTarget.TYPEALIAS, AnnotationTarget.VALUE_PARAMETER}) public final annotation class ExperimentalAPI : kotlin.Annotation { public constructor ExperimentalAPI() public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java index 741a1571c16..46772c74a10 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java @@ -1833,6 +1833,24 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW doTest(fileName); } + @TestMetadata("experimentalWithNoImpact.kt") + public void testExperimentalWithNoImpact() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.kt"); + doTest(fileName); + } + + @TestMetadata("incorrectTargetsForExperimentalAnnotation.kt") + public void testIncorrectTargetsForExperimentalAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt"); + doTest(fileName); + } + + @TestMetadata("incorrectUseExperimental.kt") + public void testIncorrectUseExperimental() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); + doTest(fileName); + } + @TestMetadata("topLevel.kt") public void testTopLevel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java index 9c18a301e18..8988e73d1eb 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java @@ -1833,6 +1833,24 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno doTest(fileName); } + @TestMetadata("experimentalWithNoImpact.kt") + public void testExperimentalWithNoImpact() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/experimentalWithNoImpact.kt"); + doTest(fileName); + } + + @TestMetadata("incorrectTargetsForExperimentalAnnotation.kt") + public void testIncorrectTargetsForExperimentalAnnotation() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectTargetsForExperimentalAnnotation.kt"); + doTest(fileName); + } + + @TestMetadata("incorrectUseExperimental.kt") + public void testIncorrectUseExperimental() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/incorrectUseExperimental.kt"); + doTest(fileName); + } + @TestMetadata("topLevel.kt") public void testTopLevel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/experimental/topLevel.kt");