From 4597fff18a7ecb3c4faa8ee8337517bc812f062f Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Mon, 11 May 2020 20:36:06 +0200 Subject: [PATCH] Prohibit OptionalExpectation on nested annotation classes Because the new generation scheme based on module metadata does not support them, and we don't plan to have them in the standard library yet. --- .../jetbrains/kotlin/diagnostics/Errors.java | 1 + .../rendering/DefaultErrorMessages.java | 1 + .../kotlin/resolve/ModifiersChecker.java | 2 +- .../checkers/OptionalExpectationChecker.kt | 39 +++++++++++++++++++ .../OptionalExpectationTargetChecker.kt | 30 -------------- .../optionalExpectationIncorrectUse/common.kt | 7 ++++ .../optionalExpectationIncorrectUse/jvm.kt | 4 ++ .../output.txt | 6 +++ 8 files changed, 59 insertions(+), 31 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationChecker.kt delete mode 100644 compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java index 527c4d6e58e..8b56bc98933 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java @@ -681,6 +681,7 @@ public interface Errors { DiagnosticFactory0 ACTUAL_MISSING = DiagnosticFactory0.create(ERROR, ACTUAL_DECLARATION_NAME); DiagnosticFactory0 OPTIONAL_EXPECTATION_NOT_ON_EXPECTED = DiagnosticFactory0.create(ERROR); + DiagnosticFactory0 NESTED_OPTIONAL_EXPECTATION = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY = DiagnosticFactory0.create(ERROR); DiagnosticFactory0 OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE = 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 31b16ee8149..c5c8c113eb3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -306,6 +306,7 @@ public class DefaultErrorMessages { MAP.put(ACTUAL_MISSING, "Declaration must be marked with 'actual'"); MAP.put(OPTIONAL_EXPECTATION_NOT_ON_EXPECTED, "'@OptionalExpectation' can only be used on an expected annotation class"); + MAP.put(NESTED_OPTIONAL_EXPECTATION, "'@OptionalExpectation' cannot be used on a nested class"); MAP.put(OPTIONAL_DECLARATION_OUTSIDE_OF_ANNOTATION_ENTRY, "Declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry"); MAP.put(OPTIONAL_DECLARATION_USAGE_IN_NON_COMMON_SOURCE, "Declaration annotated with '@OptionalExpectation' can only be used in common module sources"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java index aeef55c395e..d9d3c71ed6d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/ModifiersChecker.java @@ -276,7 +276,7 @@ public class ModifiersChecker { } OperatorModifierChecker.INSTANCE.check(declaration, descriptor, trace, languageVersionSettings); PublishedApiUsageChecker.INSTANCE.check(declaration, descriptor, trace); - OptionalExpectationTargetChecker.INSTANCE.check(declaration, descriptor, trace); + OptionalExpectationChecker.INSTANCE.check(declaration, descriptor, trace); } public void checkTypeParametersModifiers(@NotNull KtModifierListOwner modifierListOwner) { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationChecker.kt new file mode 100644 index 00000000000..8a03c699f33 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationChecker.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * 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.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.descriptors.MemberDescriptor +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.diagnostics.Errors +import org.jetbrains.kotlin.psi.KtAnnotationEntry +import org.jetbrains.kotlin.psi.KtDeclaration +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.BindingTrace +import org.jetbrains.kotlin.resolve.DescriptorUtils + +object OptionalExpectationChecker { + fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, trace: BindingTrace) { + val isExpect = descriptor is MemberDescriptor && descriptor.isExpect + if (isExpect) { + if (DescriptorUtils.isAnnotationClass(descriptor) && descriptor.containingDeclaration !is PackageFragmentDescriptor) { + getOptionalExpectationEntry(declaration, trace)?.let { + trace.report(Errors.NESTED_OPTIONAL_EXPECTATION.on(it)) + } + } + } else { + getOptionalExpectationEntry(declaration, trace)?.let { + trace.report(Errors.OPTIONAL_EXPECTATION_NOT_ON_EXPECTED.on(it)) + } + } + } + + private fun getOptionalExpectationEntry(declaration: KtDeclaration, trace: BindingTrace): KtAnnotationEntry? = + declaration.annotationEntries.find { entry -> + val annotationDescriptor = trace.get(BindingContext.ANNOTATION, entry) + annotationDescriptor?.fqName == ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME + } +} diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt b/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt deleted file mode 100644 index 4e40be90197..00000000000 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/checkers/OptionalExpectationTargetChecker.kt +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. - * 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.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.MemberDescriptor -import org.jetbrains.kotlin.diagnostics.Errors -import org.jetbrains.kotlin.psi.KtDeclaration -import org.jetbrains.kotlin.resolve.BindingContext -import org.jetbrains.kotlin.resolve.BindingTrace - -object OptionalExpectationTargetChecker { - fun check( - declaration: KtDeclaration, - descriptor: DeclarationDescriptor, - trace: BindingTrace - ) { - if (descriptor is MemberDescriptor && descriptor.isExpect) return - - for (entry in declaration.annotationEntries) { - val annotationDescriptor = trace.get(BindingContext.ANNOTATION, entry) ?: continue - if (annotationDescriptor.fqName == ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME) { - trace.report(Errors.OPTIONAL_EXPECTATION_NOT_ON_EXPECTED.on(entry)) - } - } - } -} diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt index 5b0b11d7d42..709dd725e5f 100644 --- a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt @@ -18,3 +18,10 @@ annotation class InOtherAnnotation(val a: A) @InOtherAnnotation(A()) fun useInOtherAnnotation() {} + + + +expect class C { + @OptionalExpectation + annotation class Nested +} diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt index fa2b69af303..b9e13920d44 100644 --- a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt @@ -4,3 +4,7 @@ annotation class AnotherAnnotation(val a: A) @AnotherAnnotation(A()) fun useInAnotherAnnotation() {} + +actual class C { + actual annotation class Nested +} diff --git a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt index d3f482bea17..7b44c395564 100644 --- a/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt +++ b/compiler/testData/multiplatform/optionalExpectationIncorrectUse/output.txt @@ -16,6 +16,9 @@ annotation class InOtherAnnotation(val a: A) compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:19:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry @InOtherAnnotation(A()) ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:25:5: error: '@OptionalExpectation' cannot be used on a nested class + @OptionalExpectation + ^ -- JVM -- Exit code: COMPILATION_ERROR @@ -38,6 +41,9 @@ annotation class InOtherAnnotation(val a: A) compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:19:20: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry @InOtherAnnotation(A()) ^ +compiler/testData/multiplatform/optionalExpectationIncorrectUse/common.kt:25:5: error: '@OptionalExpectation' cannot be used on a nested class + @OptionalExpectation + ^ compiler/testData/multiplatform/optionalExpectationIncorrectUse/jvm.kt:1:24: error: declaration annotated with '@OptionalExpectation' can only be used inside an annotation entry fun useInReturnType(): A? = null ^