Report error when suspend call is illegal in the scope
Namely when it's not contained inside coroutine or another suspend function #KT-14924 In Progress
This commit is contained in:
@@ -862,6 +862,7 @@ public interface Errors {
|
||||
|
||||
|
||||
DiagnosticFactory0<PsiElement> NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
|
||||
// Error sets
|
||||
|
||||
+1
@@ -820,6 +820,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(INLINE_CALL_CYCLE, "The ''{0}'' invocation is a part of inline cycle", NAME);
|
||||
MAP.put(NON_LOCAL_RETURN_IN_DISABLED_INLINE, "Non-local returns are not allowed with inlining disabled");
|
||||
MAP.put(NON_LOCAL_SUSPENSION_POINT, "Suspension functions can be called only within coroutine body");
|
||||
MAP.put(ILLEGAL_SUSPEND_FUNCTION_CALL, "Suspend functions are only allowed to be called from a coroutine or another suspend function");
|
||||
|
||||
MAP.setImmutable();
|
||||
|
||||
|
||||
+5
-2
@@ -47,11 +47,11 @@ object CoroutineSuspendCallChecker : CallChecker {
|
||||
|
||||
when {
|
||||
enclosingSuspendFunction != null -> {
|
||||
// TODO: check if tail call
|
||||
// Tail calls checks happen during control flow analysis
|
||||
// Here we only record enclosing function mapping (for backends purposes)
|
||||
context.trace.record(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.call, enclosingSuspendFunction)
|
||||
}
|
||||
closestCoroutineReceiver != null -> {
|
||||
|
||||
val callElement = resolvedCall.call.callElement as KtExpression
|
||||
|
||||
if (!InlineUtil.checkNonLocalReturnUsage(closestCoroutineReceiver.declarationDescriptor, callElement, context.resolutionContext)) {
|
||||
@@ -60,6 +60,9 @@ object CoroutineSuspendCallChecker : CallChecker {
|
||||
|
||||
context.trace.record(BindingContext.COROUTINE_RECEIVER_FOR_SUSPENSION_POINT, resolvedCall.call, closestCoroutineReceiver)
|
||||
}
|
||||
else -> {
|
||||
context.trace.report(Errors.ILLEGAL_SUSPEND_FUNCTION_CALL.on(reportOn))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
suspend fun foo() {}
|
||||
|
||||
fun noSuspend() {
|
||||
<!ILLEGAL_SUSPEND_FUNCTION_CALL!>foo<!>()
|
||||
}
|
||||
|
||||
class A {
|
||||
init {
|
||||
<!ILLEGAL_SUSPEND_FUNCTION_CALL!>foo<!>()
|
||||
}
|
||||
}
|
||||
|
||||
val x = <!ILLEGAL_SUSPEND_FUNCTION_CALL!>foo<!>()
|
||||
@@ -0,0 +1,12 @@
|
||||
package
|
||||
|
||||
public val x: kotlin.Unit
|
||||
public suspend fun foo(): kotlin.Unit
|
||||
public fun noSuspend(): kotlin.Unit
|
||||
|
||||
public final class A {
|
||||
public constructor A()
|
||||
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
|
||||
}
|
||||
@@ -4252,6 +4252,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("illegalSuspendCalls.kt")
|
||||
public void testIllegalSuspendCalls() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/illegalSuspendCalls.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("interceptResume.kt")
|
||||
public void testInterceptResume() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/interceptResume.kt");
|
||||
|
||||
Reference in New Issue
Block a user