From 391c4db87c655f5ba38e6bc12adf988317f4d21b Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Fri, 23 Jul 2021 16:17:36 +0300 Subject: [PATCH] FIR: introduce ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL diagnostics --- .../diagnostics/FirDiagnosticsList.kt | 1 + .../fir/analysis/diagnostics/FirErrors.kt | 1 + .../expression/FirSuspendCallChecker.kt | 77 ++++++++++- .../diagnostics/FirDefaultErrorMessages.kt | 5 + .../restrictSuspension/extensions.fir.kt | 128 ------------------ .../restrictSuspension/extensions.kt | 1 + .../restrictSuspension/memberExtension.fir.kt | 61 --------- .../restrictSuspension/memberExtension.kt | 1 + .../restrictSuspension/notRelatedFun.fir.kt | 71 ---------- .../restrictSuspension/notRelatedFun.kt | 1 + .../restrictSuspension/outerYield_1_3.fir.kt | 99 -------------- .../restrictSuspension/outerYield_1_3.kt | 1 + .../restrictSuspension/sameInstance.fir.kt | 52 ------- .../restrictSuspension/sameInstance.kt | 1 + .../restrictSuspension/simpleForbidden.fir.kt | 14 -- .../restrictSuspension/simpleForbidden.kt | 1 + .../wrongEnclosingFunction.fir.kt | 17 --- .../wrongEnclosingFunction.kt | 1 + .../diagnostics/KtFirDataClassConverters.kt | 6 + .../api/fir/diagnostics/KtFirDiagnostics.kt | 4 + .../fir/diagnostics/KtFirDiagnosticsImpl.kt | 7 + 21 files changed, 107 insertions(+), 443 deletions(-) delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.fir.kt delete mode 100644 compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.fir.kt diff --git a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt index a9a1642b9be..069747cb7c4 100644 --- a/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt +++ b/compiler/fir/checkers/checkers-component-generator/src/org/jetbrains/kotlin/fir/checkers/generator/diagnostics/FirDiagnosticsList.kt @@ -1191,6 +1191,7 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { parameter("suspendCallable") } val NON_LOCAL_SUSPENSION_POINT by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) + val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) } } diff --git a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt index 2894a6ed76f..3a599c39df5 100644 --- a/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt +++ b/compiler/fir/checkers/gen/org/jetbrains/kotlin/fir/analysis/diagnostics/FirErrors.kt @@ -611,5 +611,6 @@ object FirErrors { val ILLEGAL_SUSPEND_FUNCTION_CALL by error1>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val ILLEGAL_SUSPEND_PROPERTY_ACCESS by error1>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) val NON_LOCAL_SUSPENSION_POINT by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + val ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL by error0(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt index 2cccb4d96f4..e624ebeaac7 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt @@ -5,20 +5,32 @@ package org.jetbrains.kotlin.fir.analysis.checkers.expression +import org.jetbrains.kotlin.builtins.StandardNames +import org.jetbrains.kotlin.fir.FirSession import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext import org.jetbrains.kotlin.fir.analysis.diagnostics.DiagnosticReporter import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors import org.jetbrains.kotlin.fir.analysis.diagnostics.reportOn import org.jetbrains.kotlin.fir.declarations.* import org.jetbrains.kotlin.fir.declarations.utils.isSuspend +import org.jetbrains.kotlin.fir.expressions.FirExpression import org.jetbrains.kotlin.fir.expressions.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.expressions.FirThisReceiverExpression +import org.jetbrains.kotlin.fir.expressions.impl.FirNoReceiverExpression import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.fullyExpandedType import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType +import org.jetbrains.kotlin.fir.resolve.toFirRegularClass import org.jetbrains.kotlin.fir.symbols.SymbolInternals import org.jetbrains.kotlin.fir.symbols.ensureResolved import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol +import org.jetbrains.kotlin.fir.types.ConeClassLikeType +import org.jetbrains.kotlin.fir.types.ConeKotlinType +import org.jetbrains.kotlin.fir.types.ConeTypeParameterType import org.jetbrains.kotlin.fir.types.coneType +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_2_20_FQ_NAME import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_2_30_FQ_NAME import org.jetbrains.kotlin.resolve.calls.checkers.COROUTINE_CONTEXT_1_3_FQ_NAME @@ -29,13 +41,16 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() { COROUTINE_CONTEXT_1_2_20_FQ_NAME, COROUTINE_CONTEXT_1_2_30_FQ_NAME, COROUTINE_CONTEXT_1_3_FQ_NAME ) + private val RESTRICTS_SUSPENSION_CLASS_ID = + ClassId(StandardNames.COROUTINES_PACKAGE_FQ_NAME_RELEASE, Name.identifier("RestrictsSuspension")) + @OptIn(SymbolInternals::class) override fun check(expression: FirQualifiedAccessExpression, context: CheckerContext, reporter: DiagnosticReporter) { val reference = expression.calleeReference as? FirResolvedNamedReference ?: return if (reference is FirResolvedCallableReference) return val symbol = reference.resolvedSymbol as? FirCallableSymbol ?: return symbol.ensureResolved(FirResolvePhase.STATUS) - val fir = symbol.fir as? FirMemberDeclaration ?: return + val fir = symbol.fir as? FirCallableDeclaration ?: return when (fir) { is FirSimpleFunction -> if (!fir.isSuspend) return is FirProperty -> if (symbol.callableId.asSingleFqName() !in SUSPEND_PROPERTIES_FQ_NAMES) return @@ -53,6 +68,9 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() { if (!checkNonLocalReturnUsage(enclosingSuspendFunction, context)) { reporter.reportOn(expression.source, FirErrors.NON_LOCAL_SUSPENSION_POINT, context) } + if (!checkRestrictsSuspension(expression, enclosingSuspendFunction, fir, context)) { + reporter.reportOn(expression.source, FirErrors.ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL, context) + } } } @@ -74,4 +92,61 @@ object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() { enclosingSuspendFunction === containingFunction } } + + private fun checkRestrictsSuspension( + expression: FirQualifiedAccessExpression, + enclosingSuspendFunction: FirFunction, + calledDeclaration: FirCallableDeclaration, + context: CheckerContext + ): Boolean { + val session = context.session + val enclosingSuspendFunctionDispatchReceiverOwner = + (enclosingSuspendFunction.dispatchReceiverType as? ConeClassLikeType)?.lookupTag?.toFirRegularClass(session) + val enclosingSuspendFunctionExtensionReceiverOwner = enclosingSuspendFunction.takeIf { it.receiverTypeRef != null } + val dispatchReceiverExpression = expression.dispatchReceiver.takeIf { it !is FirNoReceiverExpression } + val extensionReceiverExpression = expression.extensionReceiver.takeIf { it !is FirNoReceiverExpression } + for (receiverExpression in listOfNotNull(dispatchReceiverExpression, extensionReceiverExpression)) { + if (!receiverExpression.typeRef.coneType.isRestrictSuspensionReceiver(session)) continue + if (sameInstanceOfReceiver(receiverExpression, enclosingSuspendFunctionDispatchReceiverOwner)) continue + if (sameInstanceOfReceiver(receiverExpression, enclosingSuspendFunctionExtensionReceiverOwner)) continue + + return false + } + if (enclosingSuspendFunctionExtensionReceiverOwner?.receiverTypeRef?.coneType?.isRestrictSuspensionReceiver(session) != true) { + return true + } + if (sameInstanceOfReceiver(dispatchReceiverExpression, enclosingSuspendFunctionExtensionReceiverOwner)) { + return true + } + if (sameInstanceOfReceiver(extensionReceiverExpression, enclosingSuspendFunctionExtensionReceiverOwner)) { + if (calledDeclaration.receiverTypeRef?.coneType?.isRestrictSuspensionReceiver(session) == true) { + return true + } + } + return false + } + + private fun ConeKotlinType.isRestrictSuspensionReceiver(session: FirSession): Boolean { + when (this) { + is ConeClassLikeType -> { + val regularClass = fullyExpandedType(session).lookupTag.toFirRegularClass(session) ?: return false + if (regularClass.hasAnnotation(RESTRICTS_SUSPENSION_CLASS_ID)) { + return true + } + return regularClass.superTypeRefs.any { it.coneType.isRestrictSuspensionReceiver(session) } + } + is ConeTypeParameterType -> { + return lookupTag.typeParameterSymbol.resolvedBounds.any { it.coneType.isRestrictSuspensionReceiver(session) } + } + else -> return false + } + } + + private fun sameInstanceOfReceiver(useSiteReceiverExpression: FirExpression?, declarationSiteReceiverOwner: FirDeclaration?): Boolean { + if (declarationSiteReceiverOwner == null || useSiteReceiverExpression == null) return false + if (useSiteReceiverExpression is FirThisReceiverExpression) { + return useSiteReceiverExpression.calleeReference.boundSymbol == declarationSiteReceiverOwner.symbol + } + return false + } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt index 79912f6c9fc..e85c6823905 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDefaultErrorMessages.kt @@ -190,6 +190,7 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.GETTER_VISIBILITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION_AMBIGUITY import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_CONST_EXPRESSION import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_KOTLIN_VERSION_STRING_VALUE +import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SELECTOR import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SUSPEND_FUNCTION_CALL import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_SUSPEND_PROPERTY_ACCESS @@ -1492,6 +1493,10 @@ class FirDefaultErrorMessages { SYMBOL ) map.put(NON_LOCAL_SUSPENSION_POINT, "Suspension functions can be called only within coroutine body") + map.put( + ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL, + "Restricted suspending functions can only invoke member or extension suspending functions on their restricted coroutine scope" + ) // Extended checkers group map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier") diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.fir.kt deleted file mode 100644 index 57997e3f91e..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.fir.kt +++ /dev/null @@ -1,128 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -SUSPENSION_CALL_MUST_BE_USED_AS_RETURN_VALUE -interface SuperInterface - -@kotlin.coroutines.RestrictsSuspension -open class RestrictedController : SuperInterface - -class SubClass : RestrictedController() - -suspend fun Any?.extAny() {} -suspend fun SuperInterface.extSuper() {} -suspend fun RestrictedController.ext() {} -suspend fun SubClass.extSub() {} - -class A { - suspend fun Any?.memExtAny() {} - suspend fun SuperInterface.memExtSuper() {} - suspend fun RestrictedController.memExt() {} - suspend fun SubClass.memExtSub() {} -} - - -fun generate1(f: suspend SuperInterface.() -> Unit) {} -fun generate2(f: suspend RestrictedController.() -> Unit) {} -fun generate3(f: suspend SubClass.() -> Unit) {} - -fun A.test() { - generate1 { - extAny() - memExtAny() - extSuper() - memExtSuper() - with(A()) { - extAny() - memExtAny() - extSuper() - memExtSuper() - } - } - generate2 { - extAny() - memExtAny() - extSuper() - memExtSuper() - - ext() - memExt() - with(A()) { - extAny() - memExtAny() - extSuper() - memExtSuper() - ext() - memExt() - } - } - generate3 { - extAny() - memExtAny() - extSuper() - memExtSuper() - - ext() - memExt() - extSub() - memExtSub() - with(A()) { - extAny() - memExtAny() - extSuper() - memExtSuper() - ext() - memExt() - extSub() - memExtSub() - } - } - - suspend fun SuperInterface.fun1() { - extAny() - memExtAny() - extSuper() - memExtSuper() - with(A()) { - extAny() - memExtAny() - extSuper() - memExtSuper() - } - } - suspend fun RestrictedController.fun2() { - extAny() - memExtAny() - extSuper() - memExtSuper() - - ext() - memExt() - with(A()) { - extAny() - memExtAny() - extSuper() - memExtSuper() - ext() - memExt() - } - } - suspend fun SubClass.fun3() { - extAny() - memExtAny() - extSuper() - memExtSuper() - - ext() - memExt() - extSub() - memExtSub() - with(A()) { - extAny() - memExtAny() - extSuper() - memExtSuper() - ext() - memExt() - extSub() - memExtSub() - } - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.kt index b3fda9f481e..335e157d20c 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/extensions.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -SUSPENSION_CALL_MUST_BE_USED_AS_RETURN_VALUE interface SuperInterface diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.fir.kt deleted file mode 100644 index 25cbbb7d0ef..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.fir.kt +++ /dev/null @@ -1,61 +0,0 @@ -// SKIP_TXT -@kotlin.coroutines.RestrictsSuspension -class RestrictedController { - suspend fun member() { - ext() - member() - memberExt() - } - - suspend fun RestrictedController.memberExt() { - ext() - member() - memberExt() - } -} - -suspend fun RestrictedController.ext() { - ext() - member() - memberExt() -} - -fun generate(c: suspend RestrictedController.() -> Unit) {} - -fun runBlocking(x: suspend () -> Unit) {} - -fun test() { - generate a@{ - ext() - member() - memberExt() - - this@a.ext() - this@a.member() - this@a.memberExt() - - generate b@{ - ext() - member() - memberExt() - - this@a.ext() - this@a.member() - this@a.memberExt() - - this@b.ext() - this@b.member() - this@b.memberExt() - } - - runBlocking { - ext() - member() - memberExt() - - this@a.ext() - this@a.member() - this@a.memberExt() - } - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.kt index 84e15ebca53..ca0a792d9e2 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/memberExtension.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT @kotlin.coroutines.RestrictsSuspension class RestrictedController { diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.fir.kt deleted file mode 100644 index ab14bac2adc..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.fir.kt +++ /dev/null @@ -1,71 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -SUSPENSION_CALL_MUST_BE_USED_AS_RETURN_VALUE -interface SuperInterface - -@kotlin.coroutines.RestrictsSuspension -open class RestrictedController : SuperInterface - -class SubClass : RestrictedController() - -suspend fun topLevel() {} - -class A { - suspend fun member() {} -} - -fun generate1(f: suspend SuperInterface.() -> Unit) {} -fun generate2(f: suspend RestrictedController.() -> Unit) {} -fun generate3(f: suspend SubClass.() -> Unit) {} - -fun A.test() { - generate1 { - topLevel() - member() - with(A()) { - topLevel() - member() - } - } - generate2 { - topLevel() - member() - with(A()) { - topLevel() - member() - } - } - generate3 { - topLevel() - member() - with(A()) { - topLevel() - member() - } - } - - suspend fun SuperInterface.fun1() { - topLevel() - member() - with(A()) { - topLevel() - member() - } - } - suspend fun RestrictedController.fun2() { - topLevel() - member() - with(A()) { - topLevel() - member() - } - } - suspend fun SubClass.fun3() { - topLevel() - member() - with(A()) { - topLevel() - member() - } - } - -} - diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.kt index 82900b4b71d..60d308c3b90 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/notRelatedFun.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -SUSPENSION_CALL_MUST_BE_USED_AS_RETURN_VALUE interface SuperInterface diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.fir.kt deleted file mode 100644 index 5135c5e569a..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.fir.kt +++ /dev/null @@ -1,99 +0,0 @@ -// !LANGUAGE: +ReleaseCoroutines +ExperimentalBuilderInference -// !USE_EXPERIMENTAL: kotlin.RequiresOptIn -// SKIP_TXT - -@file:OptIn(ExperimentalTypeInference::class) - -import kotlin.experimental.ExperimentalTypeInference - -@kotlin.coroutines.RestrictsSuspension -class RestrictedController { - suspend fun yield(x: T) {} - - suspend fun anotherYield(x: T) { - yield(x) - this.yield(x) - - yield2(x) - this.yield2(x) - - with(this) { - yield(x) - this@with.yield(x) - - yield2(x) - this@with.yield2(x) - } - } -} - -fun buildSequence(@BuilderInference c: suspend RestrictedController.() -> Unit) {} - -@BuilderInference -suspend fun RestrictedController.yield2(x: T) {} - -fun test() { - buildSequence a@{ - buildSequence b@{ - yield(1) - yield2(1) - this@b.yield(1) - this@b.yield2(1) - - this@a.yield(2) // Should be error - this@a.yield2(2) // Should be error - - with(this) { - yield(3) - this@with.yield(3) - - yield2(3) - this@with.yield2(3) - } - } - } - - buildSequence { - buildSequence { - yield("a") - yield2("a") - this.yield("b") - this.yield2("b") - - yield(1) // Should be error - yield2(1) // Should be error - - with(this) { - yield("") - this@with.yield("") - - yield2("") - this@with.yield2("") - } - } - } - - buildSequence a@{ - yield(1) - yield2(1) - buildSequence { - yield("") - yield2("") - this@a.yield(1) - this@a.yield2(1) - - with(this) { - yield("") - this@with.yield("") - - yield2("") - this@with.yield2("") - } - } - } - - buildSequence { - yield("") - RestrictedController().yield("1") - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.kt index 01429bb45da..a38c4a27bb8 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/outerYield_1_3.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +ReleaseCoroutines +ExperimentalBuilderInference // !USE_EXPERIMENTAL: kotlin.RequiresOptIn // SKIP_TXT diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.fir.kt deleted file mode 100644 index cb175b40fce..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.fir.kt +++ /dev/null @@ -1,52 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -SUSPENSION_CALL_MUST_BE_USED_AS_RETURN_VALUE -@kotlin.coroutines.RestrictsSuspension -class RestrictedController { - suspend fun member() {} -} - -suspend fun RestrictedController.extension() {} - -fun generate(f: suspend RestrictedController.() -> Unit) {} - -fun test() { - generate() l@ { - member() - extension() - - this.member() - this.extension() - - val foo = this - foo.member() - foo.extension() - - this@l.member() - this@l.extension() - - with(1) { - this@l.member() - this@l.extension() - } - } -} - -suspend fun RestrictedController.l() { - member() - extension() - - this.member() - this.extension() - - val foo = this - foo.member() - foo.extension() - - this@l.member() - this@l.extension() - - with(1) { - this@l.member() - this@l.extension() - } - -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.kt index 5d7d2a21f22..73b84b32a4d 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/sameInstance.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER -SUSPENSION_CALL_MUST_BE_USED_AS_RETURN_VALUE @kotlin.coroutines.RestrictsSuspension class RestrictedController { diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.fir.kt deleted file mode 100644 index 8e1f2ff8c03..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.fir.kt +++ /dev/null @@ -1,14 +0,0 @@ -@kotlin.coroutines.RestrictsSuspension -class RestrictedController - -suspend fun Any?.extFun() {} -suspend fun suspendFun() {} - -fun generate(c: suspend RestrictedController.() -> Unit) {} - -fun test() { - generate { - extFun() - suspendFun() - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.kt index 96d316de89e..c2cf83b8c65 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/simpleForbidden.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL @kotlin.coroutines.RestrictsSuspension class RestrictedController diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.fir.kt deleted file mode 100644 index 8c886cd1d1f..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.fir.kt +++ /dev/null @@ -1,17 +0,0 @@ -// SKIP_TXT -@kotlin.coroutines.RestrictsSuspension -class RestrictedController { - suspend fun yield() {} -} - -fun generate(c: suspend RestrictedController.() -> Unit) {} - -fun runBlocking(x: suspend () -> Unit) {} - -fun test() { - generate { - runBlocking { - yield() - } - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.kt index 54b024b5f09..23261bb7a64 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/restrictSuspension/wrongEnclosingFunction.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT @kotlin.coroutines.RestrictsSuspension class RestrictedController { diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt index 7ca6bc08cbc..b904c489d87 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDataClassConverters.kt @@ -3162,6 +3162,12 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL) { firDiagnostic -> + IllegalRestrictedSuspendingFunctionCallImpl( + firDiagnostic as FirPsiDiagnostic, + token, + ) + } add(FirJvmErrors.CONFLICTING_JVM_DECLARATIONS) { firDiagnostic -> ConflictingJvmDeclarationsImpl( firDiagnostic as FirPsiDiagnostic, diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt index 9dd2cd2461f..18a710c0604 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnostics.kt @@ -2211,6 +2211,10 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = NonLocalSuspensionPoint::class } + abstract class IllegalRestrictedSuspendingFunctionCall : KtFirDiagnostic() { + override val diagnosticClass get() = IllegalRestrictedSuspendingFunctionCall::class + } + abstract class ConflictingJvmDeclarations : KtFirDiagnostic() { override val diagnosticClass get() = ConflictingJvmDeclarations::class } diff --git a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt index 94a976fd2ba..aa5d2d0f3ad 100644 --- a/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt +++ b/idea/idea-frontend-fir/src/org/jetbrains/kotlin/idea/frontend/api/fir/diagnostics/KtFirDiagnosticsImpl.kt @@ -3568,6 +3568,13 @@ internal class NonLocalSuspensionPointImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class IllegalRestrictedSuspendingFunctionCallImpl( + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.IllegalRestrictedSuspendingFunctionCall(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class ConflictingJvmDeclarationsImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,