From 93ebd83ecdbc5685f65225a22922b8dc394dddcc Mon Sep 17 00:00:00 2001 From: "Anastasia.Nekrasova" Date: Mon, 6 Nov 2023 18:27:54 +0200 Subject: [PATCH] [K2] Disappeared OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN (4) Added checking usages of markers as qualifiers in properties and function calls. ^KT-59998 --- .../FirOptInUsageQualifierChecker.kt | 33 +++++++++++++++++++ .../experimental/usageNotAsAnnotation.fir.kt | 12 +++++-- .../experimental/usageNotAsAnnotation.kt | 12 +++++-- .../experimental/usageNotAsAnnotation.txt | 1 + 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageQualifierChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageQualifierChecker.kt index b7ef555b6fb..20ae1a6b74e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageQualifierChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirOptInUsageQualifierChecker.kt @@ -5,16 +5,49 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression +import org.jetbrains.kotlin.KtSourceElement import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.expression.FirOptInUsageBaseChecker.isExperimentalMarker +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression import org.jetbrains.kotlin.fir.expressions.FirResolvedQualifier +import org.jetbrains.kotlin.fir.getContainingClassLookupTag +import org.jetbrains.kotlin.fir.resolve.toSymbol +import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol object FirOptInUsageQualifierChecker : FirResolvedQualifierChecker() { override fun check(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) { + checkNotAcceptedExperimentalities(expression, context, reporter) + checkMarkerUsedAsQualifier(expression, context, reporter) + } + + private fun checkNotAcceptedExperimentalities(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) { val symbol = expression.symbol ?: return with(FirOptInUsageBaseChecker) { val experimentalities = symbol.loadExperimentalities(context, fromSetter = false, dispatchReceiverType = null) reportNotAcceptedExperimentalities(experimentalities, expression, context, reporter) } } + + private fun checkMarkerUsedAsQualifier(expression: FirResolvedQualifier, context: CheckerContext, reporter: DiagnosticReporter) { + val containingElements = context.containingElements + val parentExpression = containingElements.lastOrNull { it is FirQualifiedAccessExpression && it.dispatchReceiver == expression } + val source = parentExpression?.source ?: return + expression.symbol?.checkContainingClasses(source, context, reporter) + } + + private tailrec fun FirClassLikeSymbol<*>.checkContainingClasses( + source: KtSourceElement, + context: CheckerContext, + reporter: DiagnosticReporter, + ) { + if (isExperimentalMarker(context.session) && context.containingDeclarations.none { it.symbol == this }) { + reporter.reportOn(source, FirErrors.OPT_IN_MARKER_CAN_ONLY_BE_USED_AS_ANNOTATION_OR_ARGUMENT_IN_OPT_IN, context) + } + + val containingClassSymbol = this.getContainingClassLookupTag()?.toSymbol(context.session) ?: return + containingClassSymbol.checkContainingClasses(source, context, reporter) + } } \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.fir.kt index 4d23a781b58..71d8d785826 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.fir.kt @@ -51,11 +51,16 @@ object A { class NestedClass() { class NestedClass2 - fun f12(m: NestedClass2){} + fun f12(m: NestedClass2){ + val x = value + bar() + } } companion object { const val value = 42 + + fun bar(){} } } } @@ -74,7 +79,10 @@ fun f9(m: // Usages of markers as qualifiers are errors as well (we can lift this restriction for select cases) fun f10(m: A.Marker.NestedClass) { - A.Marker.value + val a = A.Marker.value + A.Marker.Companion.value + A.Marker.bar() + A.Marker.Companion.bar() } fun f11(m: A.Marker.NestedClass.NestedClass2) {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.kt b/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.kt index 33f9fd44003..44c966c22e5 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.kt @@ -51,11 +51,16 @@ object A { class NestedClass() { class NestedClass2 - fun f12(m: NestedClass2){} + fun f12(m: NestedClass2){ + val x = value + bar() + } } companion object { const val value = 42 + + fun bar(){} } } } @@ -74,7 +79,10 @@ fun f9(m: // Usages of markers as qualifiers are errors as well (we can lift this restriction for select cases) fun f10(m: A.Marker.NestedClass) { - A.Marker.value + val a = A.Marker.value + A.Marker.Companion.value + A.Marker.bar() + A.Marker.Companion.bar() } fun f11(m: A.Marker.NestedClass.NestedClass2) {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.txt b/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.txt index e9f36c3cb54..617715c1178 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.txt +++ b/compiler/testData/diagnostics/testsWithStdLib/experimental/usageNotAsAnnotation.txt @@ -30,6 +30,7 @@ package test { public companion object Companion { private constructor Companion() public const final val value: kotlin.Int = 42 + public final fun bar(): kotlin.Unit 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