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> NON_LOCAL_SUSPENSION_POINT = DiagnosticFactory0.create(ERROR);
|
||||||
|
DiagnosticFactory0<PsiElement> ILLEGAL_SUSPEND_FUNCTION_CALL = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
|
|
||||||
// Error sets
|
// 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(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_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(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();
|
MAP.setImmutable();
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -47,11 +47,11 @@ object CoroutineSuspendCallChecker : CallChecker {
|
|||||||
|
|
||||||
when {
|
when {
|
||||||
enclosingSuspendFunction != null -> {
|
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)
|
context.trace.record(BindingContext.ENCLOSING_SUSPEND_FUNCTION_FOR_SUSPEND_FUNCTION_CALL, resolvedCall.call, enclosingSuspendFunction)
|
||||||
}
|
}
|
||||||
closestCoroutineReceiver != null -> {
|
closestCoroutineReceiver != null -> {
|
||||||
|
|
||||||
val callElement = resolvedCall.call.callElement as KtExpression
|
val callElement = resolvedCall.call.callElement as KtExpression
|
||||||
|
|
||||||
if (!InlineUtil.checkNonLocalReturnUsage(closestCoroutineReceiver.declarationDescriptor, callElement, context.resolutionContext)) {
|
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)
|
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);
|
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")
|
@TestMetadata("interceptResume.kt")
|
||||||
public void testInterceptResume() throws Exception {
|
public void testInterceptResume() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/interceptResume.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/coroutines/interceptResume.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user