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 b88a3be6021..c568ec409b9 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 @@ -1182,6 +1182,15 @@ object DIAGNOSTICS_LIST : DiagnosticList("FirErrors") { val OPERATOR_RENAMED_ON_IMPORT by error(PositioningStrategy.IMPORT_LAST_NAME) } + + val SUSPEND by object : DiagnosticGroup("Suspend errors") { + val ILLEGAL_SUSPEND_FUNCTION_CALL by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) { + parameter("suspendCallable") + } + val ILLEGAL_SUSPEND_PROPERTY_ACCESS by error(PositioningStrategy.REFERENCED_NAME_BY_QUALIFIED) { + parameter("suspendCallable") + } + } } private val exposedVisibilityDiagnosticInit: DiagnosticBuilder.() -> Unit = { 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 67d9fdf010a..125eabb787a 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 @@ -607,4 +607,8 @@ object FirErrors { val CONFLICTING_IMPORT by error1(SourceElementPositioningStrategies.IMPORT_LAST_NAME) val OPERATOR_RENAMED_ON_IMPORT by error0(SourceElementPositioningStrategies.IMPORT_LAST_NAME) + // Suspend errors + val ILLEGAL_SUSPEND_FUNCTION_CALL by error1>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + val ILLEGAL_SUSPEND_PROPERTY_ACCESS by error1>(SourceElementPositioningStrategies.REFERENCED_NAME_BY_QUALIFIED) + } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt index 93be9c55f02..9f6d6cdb3a0 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/CommonExpressionCheckers.kt @@ -41,7 +41,8 @@ object CommonExpressionCheckers : ExpressionCheckers() { FirSealedClassConstructorCallChecker, FirUninitializedEnumChecker, FirFunInterfaceConstructorReferenceChecker, - FirReifiedChecker + FirReifiedChecker, + FirSuspendCallChecker, ) override val functionCallCheckers: Set 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 new file mode 100644 index 00000000000..6cfdad22396 --- /dev/null +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/expression/FirSuspendCallChecker.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2010-2021 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.expression + +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.FirQualifiedAccessExpression +import org.jetbrains.kotlin.fir.references.FirResolvedCallableReference +import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference +import org.jetbrains.kotlin.fir.resolve.inference.isSuspendFunctionType +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.coneType +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 + +object FirSuspendCallChecker : FirQualifiedAccessExpressionChecker() { + private val SUSPEND_PROPERTIES_FQ_NAMES = setOf( + COROUTINE_CONTEXT_1_2_20_FQ_NAME, COROUTINE_CONTEXT_1_2_30_FQ_NAME, COROUTINE_CONTEXT_1_3_FQ_NAME + ) + + @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 + when (fir) { + is FirSimpleFunction -> if (!fir.isSuspend) return + is FirProperty -> if (symbol.callableId.asSingleFqName() !in SUSPEND_PROPERTIES_FQ_NAMES) return + else -> return + } + val enclosingSuspendFunction = findEnclosingSuspendFunction(context) + if (enclosingSuspendFunction == null) { + when (fir) { + is FirSimpleFunction -> reporter.reportOn(expression.source, FirErrors.ILLEGAL_SUSPEND_FUNCTION_CALL, symbol, context) + is FirProperty -> reporter.reportOn(expression.source, FirErrors.ILLEGAL_SUSPEND_PROPERTY_ACCESS, symbol, context) + else -> { + } + } + } + } + + private fun findEnclosingSuspendFunction(context: CheckerContext): FirFunction? { + return context.containingDeclarations.lastOrNull { + when (it) { + is FirAnonymousFunction -> it.typeRef.coneType.isSuspendFunctionType(context.session) + is FirSimpleFunction -> it.isSuspend + else -> false + } + } as? FirFunction + } +} 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 36a0565ad1b..cdb8be756d6 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 @@ -191,6 +191,8 @@ import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.HAS_NEXT_FUNCTION 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_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 import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.ILLEGAL_UNDERSCORE import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.IMPLEMENTATION_BY_DELEGATION_IN_EXPECT_CLASS import org.jetbrains.kotlin.fir.analysis.diagnostics.FirErrors.INAPPLICABLE_CANDIDATE @@ -1477,6 +1479,18 @@ class FirDefaultErrorMessages { ) map.put(OPERATOR_RENAMED_ON_IMPORT, "Operator renamed to a different operator on import") + // Suspend + map.put( + ILLEGAL_SUSPEND_FUNCTION_CALL, + "Suspend function ''{0}'' should be called only from a coroutine or another suspend function", + SYMBOL + ) + map.put( + ILLEGAL_SUSPEND_PROPERTY_ACCESS, + "Suspend property ''{0}'' should be accessed only from a coroutine or suspend function", + SYMBOL + ) + // Extended checkers group map.put(REDUNDANT_VISIBILITY_MODIFIER, "Redundant visibility modifier") map.put(REDUNDANT_MODALITY_MODIFIER, "Redundant modality modifier") diff --git a/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.fir.kt b/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.fir.kt deleted file mode 100644 index a335dcf87c4..00000000000 --- a/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.fir.kt +++ /dev/null @@ -1,8 +0,0 @@ -// !LANGUAGE: +Coroutines -// SKIP_TXT - -import kotlin.reflect.KSuspendFunction0 - -fun test(c: KSuspendFunction0) { - c() -} diff --git a/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.kt b/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.kt index bd8052c0c50..c88770bb3b0 100644 --- a/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.kt +++ b/compiler/testData/diagnostics/tests/coroutines/callableReference/invokeOutideSuspend.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +Coroutines // SKIP_TXT diff --git a/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.fir.kt b/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.fir.kt deleted file mode 100644 index 614919f121c..00000000000 --- a/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.fir.kt +++ /dev/null @@ -1,19 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER - -fun foo(x: () -> Int) {} -fun foo(x: suspend () -> Int) {} - -fun usualCall(): Int = 42 -suspend fun suspendCall(): Int = 42 - -// it's important to have ambiguity in these cases to introduce overload resolution by suspend modifier in future -fun test1() { - foo { usualCall() } - foo { suspendCall() } -} - -// candidate without suspend conversions is more specific -fun test2(f: () -> Int, g: suspend () -> Int) { - foo(f) - foo(g) -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.kt b/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.kt index 33c07109759..5597d4512e1 100644 --- a/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.kt +++ b/compiler/testData/diagnostics/tests/suspendConversion/overloadResolutionBySuspendModifier.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER fun foo(x: () -> Int) {} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt deleted file mode 100644 index e8112bf7a40..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.fir.kt +++ /dev/null @@ -1,29 +0,0 @@ -// !LANGUAGE: +UnrestrictedBuilderInference -// !DIAGNOSTICS: -UNUSED_PARAMETER - -class Builder { - suspend fun add(t: T) {} -} - -fun build(g: suspend Builder.() -> Unit): List = TODO() -fun wrongBuild(g: Builder.() -> Unit): List = TODO() - -fun Builder.extensionAdd(s: S) {} - -suspend fun Builder.safeExtensionAdd(s: S) {} - -val member = build { - add(42) -} - -val memberWithoutAnn = wrongBuild { - add(42) -} - -val extension = build { - extensionAdd("foo") -} - -val safeExtension = build { - safeExtensionAdd("foo") -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt index 871f58e247e..83b6a12e409 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/builderInference/inferCoroutineTypeInOldVersion.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !LANGUAGE: +UnrestrictedBuilderInference // !DIAGNOSTICS: -UNUSED_PARAMETER diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.fir.kt deleted file mode 100644 index b3852686867..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.fir.kt +++ /dev/null @@ -1,53 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_PARAMETER -// SKIP_TXT - -import kotlin.coroutines.* - -fun ordinal() { - kotlin.coroutines.coroutineContext - coroutineContext -} - -suspend fun named() { - kotlin.coroutines.coroutineContext - coroutineContext -} - -class A { - val coroutineContextNew = kotlin.coroutines.coroutineContext - val context = coroutineContext -} - -class Controller { - fun ordinal() { - kotlin.coroutines.coroutineContext - coroutineContext - } - - suspend fun named() { - kotlin.coroutines.coroutineContext - coroutineContext - } - - suspend fun severalArgs(s: String, a: Any) { - kotlin.coroutines.coroutineContext - coroutineContext - } -} - -fun builder(c: () -> CoroutineContext) = {} -fun builderSuspend(c: suspend () -> CoroutineContext) = {} - -fun builderSeveralArgs(c: (Int, Int, Int) -> CoroutineContext) = {} -fun builderSuspendSeveralArgs(c: suspend (Int, Int, Int) -> CoroutineContext) = {} - -fun test() { - builder { kotlin.coroutines.coroutineContext } - builder { coroutineContext } - builderSuspend { kotlin.coroutines.coroutineContext } - builderSuspend { coroutineContext } - builderSeveralArgs { _, _, _ -> kotlin.coroutines.coroutineContext } - builderSeveralArgs { _, _, _ -> coroutineContext } - builderSuspendSeveralArgs { _, _, _ -> kotlin.coroutines.coroutineContext } - builderSuspendSeveralArgs { _, _, _ -> coroutineContext } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt index dd5d072807d..732a032c16a 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/coroutineContext.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_PARAMETER // SKIP_TXT diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.fir.kt deleted file mode 100644 index 6a8211895d0..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.fir.kt +++ /dev/null @@ -1,22 +0,0 @@ -import Host.bar - -object Host { - suspend fun bar() {} -} - -suspend fun foo() {} - -fun noSuspend() { - foo() - bar() -} - -class A { - init { - foo() - bar() - } -} - -val x = foo() -val y = bar() \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.kt index 68e32270b16..4f907a2731c 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCalls.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL import Host.bar object Host { diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCallsForDelegated.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCallsForDelegated.fir.kt index 5adf399939f..3deb4c35b4e 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCallsForDelegated.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/illegalSuspendCallsForDelegated.fir.kt @@ -1,5 +1,5 @@ fun bar(d: Delegate): String { - val x: String by d + val x: String by d return x } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.fir.kt deleted file mode 100644 index ac991e566ba..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.fir.kt +++ /dev/null @@ -1,27 +0,0 @@ -// !USE_EXPERIMENTAL: kotlin.RequiresOptIn -// !LANGUAGE: +NewInference - -import kotlin.experimental.ExperimentalTypeInference - -interface In { - fun send(element: E) -} - -class InImpl : In { - override fun send(element: E) {} -} - -@OptIn(ExperimentalTypeInference::class) -public fun builder(@BuilderInference block: In.() -> Unit) { - InImpl().block() -} - -suspend fun yield() {} - -fun test() { - builder { - send(run { - yield() // No error but `yield` is not inside "suspension" context actually - }) - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.kt index 1689588bf12..ca83657298b 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inference/kt33542.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !USE_EXPERIMENTAL: kotlin.RequiresOptIn // !LANGUAGE: +NewInference diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineOrdinary.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineOrdinary.fir.kt index 18ce2f553ec..88d4dec8b30 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineOrdinary.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineOrdinary.fir.kt @@ -28,6 +28,6 @@ suspend fun calculate() = "OK" fun box() { test { - calculate() + calculate() } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineSuspend.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineSuspend.fir.kt index c2e352d3d27..f4af3db3a6f 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineSuspend.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfCrossinlineSuspend.fir.kt @@ -18,7 +18,7 @@ interface SuspendRunnable { // suspend calls possible inside lambda matching to the parameter inline fun test(crossinline c: suspend () -> Unit) { - c() + c() val o = object : SuspendRunnable { override suspend fun run() { c() diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineOrdinary.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineOrdinary.fir.kt index 405495a2714..203ec6d66e9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineOrdinary.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineOrdinary.fir.kt @@ -27,6 +27,6 @@ suspend fun calculate() = "OK" fun box() { test { - calculate() + calculate() } } diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.fir.kt deleted file mode 100644 index 54188b3b056..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.fir.kt +++ /dev/null @@ -1,37 +0,0 @@ -// !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -NOTHING_TO_INLINE -// SKIP_TXT -// WITH_COROUTINES -import kotlin.coroutines.* -import kotlin.coroutines.intrinsics.* -import helpers.* - -interface SuspendRunnable { - suspend fun run() -} - -// Function is NOT suspend -// parameter is noinline -// parameter is suspend -// Block is NOT allowed to be called inside the body of owner inline function -// Block is allowed to be called from nested classes/lambdas (as common crossinlines) -// It is possible to call startCoroutine on the parameter -// suspend calls possible inside lambda matching to the parameter - -inline fun test(noinline c: suspend () -> Unit) { - c() - val o = object : SuspendRunnable { - override suspend fun run() { - c() - } - } - val l: suspend () -> Unit = { c() } - c.startCoroutine(EmptyContinuation) -} - -suspend fun calculate() = "OK" - -fun box() { - test { - calculate() - } -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.kt index dc127f73b13..483d0085bda 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfNoinlineSuspend.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // !DIAGNOSTICS: -UNUSED_VARIABLE -UNUSED_PARAMETER -NOTHING_TO_INLINE // SKIP_TXT // WITH_COROUTINES diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfSuspend.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfSuspend.fir.kt index d9c093f898d..c00b2203366 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfSuspend.fir.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/inlineCrossinline/inlineOrdinaryOfSuspend.fir.kt @@ -18,7 +18,7 @@ interface SuspendRunnable { // suspend calls possible inside lambda matching to the parameter inline fun test(c: suspend () -> Unit) { - c() + c() val o = object: SuspendRunnable { override suspend fun run() { c() diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.fir.kt deleted file mode 100644 index c9b275d0b46..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -suspend fun foo1(q: suspend () -> Unit) = q() -suspend fun foo2(x: suspend (Int) -> String) = x(1) - - -suspend fun foo3(y: suspend String.(Int) -> Double) = "".y(1) -suspend fun String.foo4(y: suspend String.(Int) -> Double) = "".y(1) - -fun noSuspend(x: suspend (Int) -> String) { - x(1) -} diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.kt index 8e54142db22..48609d7314c 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/invoke.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL suspend fun foo1(q: suspend () -> Unit) = q() suspend fun foo2(x: suspend (Int) -> String) = x(1) diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.fir.kt deleted file mode 100644 index b83e4b42cb9..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.fir.kt +++ /dev/null @@ -1,2 +0,0 @@ -fun test1(sfn: suspend () -> Unit) = sfn() -fun test2(sfn: suspend () -> Unit) = sfn.invoke() diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt index a60a0040aa4..73a5e0c2ae3 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/noInvokeForSuspendFunction.kt @@ -1,2 +1,3 @@ +// FIR_IDENTICAL fun test1(sfn: suspend () -> Unit) = sfn() fun test2(sfn: suspend () -> Unit) = sfn.invoke() diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.fir.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.fir.kt deleted file mode 100644 index afbac3c9797..00000000000 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.fir.kt +++ /dev/null @@ -1,9 +0,0 @@ -val test1: (suspend () -> Unit)? = null -val test2: suspend (() -> Unit)? = null -val test3: suspend ( (() -> Unit)? ) = null - -fun foo() { - test1?.invoke() - test2?.invoke() - test3?.invoke() -} \ No newline at end of file diff --git a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.kt b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.kt index 9afee179d71..5f7827a0ac9 100644 --- a/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.kt +++ b/compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendFunctionType/nullableSuspendFunction.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL val test1: (suspend () -> Unit)? = null val test2: suspend (() -> Unit)? = null val test3: suspend ( (() -> Unit)? ) = null 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 8ad07304fbb..3cb6fe50028 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 @@ -3142,6 +3142,20 @@ internal val KT_DIAGNOSTIC_CONVERTER = KtDiagnosticConverterBuilder.buildConvert token, ) } + add(FirErrors.ILLEGAL_SUSPEND_FUNCTION_CALL) { firDiagnostic -> + IllegalSuspendFunctionCallImpl( + firSymbolBuilder.buildSymbol(firDiagnostic.a.fir), + firDiagnostic as FirPsiDiagnostic, + token, + ) + } + add(FirErrors.ILLEGAL_SUSPEND_PROPERTY_ACCESS) { firDiagnostic -> + IllegalSuspendPropertyAccessImpl( + firSymbolBuilder.buildSymbol(firDiagnostic.a.fir), + 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 b5712eb0dec..cba039992fa 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 @@ -2197,6 +2197,16 @@ sealed class KtFirDiagnostic : KtDiagnosticWithPsi { override val diagnosticClass get() = OperatorRenamedOnImport::class } + abstract class IllegalSuspendFunctionCall : KtFirDiagnostic() { + override val diagnosticClass get() = IllegalSuspendFunctionCall::class + abstract val suspendCallable: KtSymbol + } + + abstract class IllegalSuspendPropertyAccess : KtFirDiagnostic() { + override val diagnosticClass get() = IllegalSuspendPropertyAccess::class + abstract val suspendCallable: KtSymbol + } + 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 21f707be0f5..b2ef58b3d1b 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 @@ -3545,6 +3545,22 @@ internal class OperatorRenamedOnImportImpl( override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) } +internal class IllegalSuspendFunctionCallImpl( + override val suspendCallable: KtSymbol, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.IllegalSuspendFunctionCall(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + +internal class IllegalSuspendPropertyAccessImpl( + override val suspendCallable: KtSymbol, + firDiagnostic: FirPsiDiagnostic, + override val token: ValidityToken, +) : KtFirDiagnostic.IllegalSuspendPropertyAccess(), KtAbstractFirDiagnostic { + override val firDiagnostic: FirPsiDiagnostic by weakRef(firDiagnostic) +} + internal class ConflictingJvmDeclarationsImpl( firDiagnostic: FirPsiDiagnostic, override val token: ValidityToken,