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:
Denis Zharkov
2016-11-30 14:59:58 +03:00
parent 240d82d167
commit aa50f1d824
6 changed files with 38 additions and 2 deletions
@@ -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
@@ -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();
@@ -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");