diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt index 89bc0efa7bb..7100164bedb 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonDeclarationCheckers.kt @@ -37,6 +37,7 @@ object CommonDeclarationCheckers : DeclarationCheckers() { FirExpectConsistencyChecker, FirOptionalExpectationDeclarationChecker, FirMissingDependencySupertypeChecker.ForDeclarations, + FirContextReceiversDeclarationChecker, ) override val classLikeCheckers: Set diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContextReceiversDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContextReceiversDeclarationChecker.kt new file mode 100644 index 00000000000..a8b0f39058e --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirContextReceiversDeclarationChecker.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2023 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.fir.analysis.checkers.declaration + +import com.intellij.lang.LighterASTNode +import org.jetbrains.kotlin.KtFakeSourceElementKind +import org.jetbrains.kotlin.KtLightSourceElement +import org.jetbrains.kotlin.KtNodeTypes.CONTEXT_RECEIVER_LIST +import org.jetbrains.kotlin.KtSourceElement +import org.jetbrains.kotlin.config.LanguageFeature +import org.jetbrains.kotlin.diagnostics.DiagnosticReporter +import org.jetbrains.kotlin.diagnostics.reportOn +import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.toKtLightSourceElement +import org.jetbrains.kotlin.util.getChildren + +object FirContextReceiversDeclarationChecker : FirBasicDeclarationChecker() { + override fun check(declaration: FirDeclaration, context: CheckerContext, reporter: DiagnosticReporter) { + if (context.languageVersionSettings.supportsFeature(LanguageFeature.ContextReceivers)) return + if (declaration.source?.kind is KtFakeSourceElementKind) return + if (!declaration.hasContextReceiver()) return + val source = declaration.source?.findContextReceiverListSource() ?: return + + reporter.reportOn( + source, + FirErrors.UNSUPPORTED_FEATURE, + LanguageFeature.ContextReceivers to context.languageVersionSettings, + context + ) + } + + private fun FirDeclaration.hasContextReceiver(): Boolean { + val contextReceivers = when (this) { + is FirCallableDeclaration -> contextReceivers + is FirRegularClass -> contextReceivers + is FirScript -> contextReceivers + else -> emptyList() + } + return contextReceivers.isNotEmpty() + } + + + private fun KtSourceElement.findContextReceiverListSource(): KtLightSourceElement? { + var contextReceiverList: LighterASTNode? = null + var contextReceiverListOffset = startOffset + + val nodes = lighterASTNode.getChildren(treeStructure) + for (node in nodes) { + if (node.tokenType == CONTEXT_RECEIVER_LIST) { + contextReceiverList = node + break + } else { + contextReceiverListOffset += node.endOffset - node.startOffset + } + } + if (contextReceiverList == null) return null + + return contextReceiverList.toKtLightSourceElement( + treeStructure, + startOffset = contextReceiverListOffset, + endOffset = contextReceiverListOffset + contextReceiverList.textLength, + ) + } +} diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/labelsFromClassNameForbidden.fir.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/labelsFromClassNameForbidden.fir.kt index 095386136a2..04e29e5a20d 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/labelsFromClassNameForbidden.fir.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/labelsFromClassNameForbidden.fir.kt @@ -3,11 +3,11 @@ fun List.f() { this@List.size } -context(String) +context(String) fun Int.f() { this@String.length this@Int.toDouble() } -context(String) +context(String) val p: String get() = this@String diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.fir.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.fir.kt index 33c347b8f2e..9348aaf6f85 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.fir.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.fir.kt @@ -1,6 +1,6 @@ // !DIAGNOSTICS: -UNCHECKED_CAST -context(Any) +context(Any) fun f(g: context(Any) () -> Unit, value: Any): context(A) () -> Unit { return value as (context(A) () -> Unit) } @@ -9,19 +9,25 @@ fun f(g: () -> Unit, value: Any) : () -> Unit { return g } -context(Any) +context(Any) fun sameAsFWithoutNonContextualCounterpart(g: () -> Unit, value: Any) : () -> Unit { return g } -context(Any) val p get() = 42 +context(Any) val p get() = 42 -context(String, Int) +context(String, Int) +class D constructor(){} + +context(String, Int) +class C(){} + +context(String, Int) class A { - context(Any) + context(Any) val p: Any get() = 42 - context(String, Int) + context(String, Int) fun m() {} } diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.kt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.kt index e36675c48ac..b220eb37c1b 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.kt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.kt @@ -16,6 +16,12 @@ fun sameAsFWithoutNonContextualCounterpart(g: () -> Unit, value: Any) : () -> Un context(Any) val p get() = 42 +context(String, Int) +class D constructor(){} + +context(String, Int) +class C(){} + context(String, Int) class A { context(Any) diff --git a/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.txt b/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.txt index b3df1924925..0c95fb31ab6 100644 --- a/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.txt +++ b/compiler/testData/diagnostics/tests/extensions/contextReceivers/unsupported.txt @@ -14,3 +14,18 @@ context(kotlin.String, kotlin.Int) public final class A { context(kotlin.String, kotlin.Int) public final fun m(): kotlin.Unit public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String } + +context(kotlin.String, kotlin.Int) public final class C { + public constructor C() + 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 +} + +context(kotlin.String, kotlin.Int) public final class D { + public constructor D() + 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/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.diag.txt b/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.diag.txt index 73cd54a9214..6c0001b8897 100644 --- a/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.diag.txt +++ b/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.diag.txt @@ -2,6 +2,8 @@ context(O) val INSTANCE: O? defined in O val INSTANCE: O defined in O +/kt59590WithContextReceiver.fir.kt:5:5: error: The feature "context receivers" is experimental and should be enabled explicitly + /kt59590WithContextReceiver.fir.kt:5:5: error: Platform declaration clash: The following declarations have the same JVM signature (INSTANCELO;): context(O) val INSTANCE: O? defined in O val INSTANCE: O defined in O diff --git a/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.kt b/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.kt index 8de38f0f016..a089e9aea18 100644 --- a/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.kt +++ b/compiler/testData/diagnostics/testsWithJvmBackend/contextReceivers/kt59590WithContextReceiver.fir.kt @@ -2,7 +2,7 @@ // !RENDER_ALL_DIAGNOSTICS_FULL_TEXT object O { - context(O) + context(O) @JvmField val INSTANCE: O? = null -} \ No newline at end of file +}