Prohibit labeled returns inside kotlin.suspend argument lambdas
Probably, it would be more correct to skip such lambdas when resolving the returns' references, but it'd be more complicated and still useless since non-local returns are impossible in such lambdas (relevant parameter is noinline) #KT-22900 Fixed
This commit is contained in:
@@ -996,6 +996,7 @@ public interface Errors {
|
||||
DiagnosticFactory1<PsiElement, CallableDescriptor> ILLEGAL_SUSPEND_PROPERTY_ACCESS = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtReturnExpression> RETURN_FOR_BUILT_IN_SUSPEND = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory1<PsiElement, RenderedDiagnostic<?>> PLUGIN_ERROR = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
+1
@@ -897,6 +897,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(ILLEGAL_SUSPEND_PROPERTY_ACCESS, "Suspend property ''{0}'' should be accessed only from a coroutine or suspend function", NAME);
|
||||
MAP.put(ILLEGAL_RESTRICTED_SUSPENDING_FUNCTION_CALL, "Restricted suspending functions can only invoke member or extension suspending functions on their restricted coroutine scope");
|
||||
MAP.put(NON_MODIFIER_FORM_FOR_BUILT_IN_SUSPEND, "''suspend'' function can only be called in a form of modifier of a lambda: suspend { ... }");
|
||||
MAP.put(RETURN_FOR_BUILT_IN_SUSPEND, "Using implicit label for this lambda is prohibited");
|
||||
MAP.put(MODIFIER_FORM_FOR_NON_BUILT_IN_SUSPEND, "Calls having a form of ''suspend {}'' are deprecated because ''suspend'' in the context will have a meaning of a modifier. Add empty argument list to the call: ''suspend() { ... }''");
|
||||
|
||||
MAP.put(PLUGIN_ERROR, "{0}", (d, c) -> d.getText());
|
||||
|
||||
@@ -920,6 +920,11 @@ public class KtPsiUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isLabeledFunctionLiteral(@NotNull KtFunctionLiteral functionLiteral) {
|
||||
// KtFunctionLiteral -> KtLambdaExpression -> KtLabeledExpression
|
||||
return functionLiteral.getParent().getParent() instanceof KtLabeledExpression;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static KtExpression getLastElementDeparenthesized(
|
||||
@Nullable KtExpression expression,
|
||||
|
||||
+1
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
object LambdaWithSuspendModifierCallChecker : CallChecker {
|
||||
@JvmField
|
||||
val KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME = FqName("kotlin.suspend")
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
|
||||
+19
@@ -31,6 +31,8 @@ import org.jetbrains.kotlin.resolve.BindingContextUtils;
|
||||
import org.jetbrains.kotlin.resolve.ModifierCheckerCore;
|
||||
import org.jetbrains.kotlin.resolve.ModifiersChecker;
|
||||
import org.jetbrains.kotlin.resolve.calls.ArgumentTypeResolver;
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.LambdaWithSuspendModifierCallChecker;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.MutableDataFlowInfoForArguments;
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||
import org.jetbrains.kotlin.resolve.calls.smartcasts.DataFlowInfo;
|
||||
@@ -55,6 +57,7 @@ import org.jetbrains.kotlin.types.expressions.typeInfoFactory.TypeInfoFactoryKt;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Errors.*;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
||||
@@ -663,6 +666,22 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
context.trace.report(RETURN_NOT_ALLOWED.on(expression));
|
||||
resultType = ErrorUtils.createErrorType(RETURN_NOT_ALLOWED_MESSAGE);
|
||||
}
|
||||
else if (labelTargetElement instanceof KtFunctionLiteral
|
||||
&& Objects.equals(expression.getLabelName(), "suspend")) {
|
||||
KtExpression callExpression = KtPsiUtil.getParentCallIfPresent((KtFunction) labelTargetElement);
|
||||
ResolvedCall<? extends CallableDescriptor> resolvedCall =
|
||||
CallUtilKt.getResolvedCall(callExpression, context.trace.getBindingContext());
|
||||
|
||||
if (resolvedCall != null &&
|
||||
!KtPsiUtil.isLabeledFunctionLiteral((KtFunctionLiteral) labelTargetElement) &&
|
||||
Objects.equals(
|
||||
DescriptorUtilsKt.fqNameOrNull(resolvedCall.getResultingDescriptor()),
|
||||
LambdaWithSuspendModifierCallChecker.KOTLIN_SUSPEND_BUILT_IN_FUNCTION_FQ_NAME
|
||||
)
|
||||
) {
|
||||
context.trace.report(RETURN_FOR_BUILT_IN_SUSPEND.on(expression));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// SKIP_TXT
|
||||
fun bar() {
|
||||
suspend {
|
||||
<!RETURN_FOR_BUILT_IN_SUSPEND!>return@suspend<!>
|
||||
}
|
||||
|
||||
suspend {
|
||||
run {
|
||||
<!RETURN_FOR_BUILT_IN_SUSPEND!>return@suspend<!>
|
||||
}
|
||||
}
|
||||
|
||||
suspend l@{
|
||||
return@l
|
||||
}
|
||||
|
||||
suspend suspend@{
|
||||
return@suspend
|
||||
}
|
||||
|
||||
val <!UNUSED_VARIABLE!>x<!> = suspend@{
|
||||
suspend {
|
||||
// Might be resolved to outer lambda, but doesn't make sense because suspend-lambdas here is noinline
|
||||
<!RETURN_FOR_BUILT_IN_SUSPEND!>return<!LABEL_NAME_CLASH!>@suspend<!><!>
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
@@ -1374,6 +1374,12 @@ public class DiagnosticsTestWithStdLibGenerated extends AbstractDiagnosticsTestW
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("returnLabelForBuiltInSuspend.kt")
|
||||
public void testReturnLabelForBuiltInSuspend() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/returnLabelForBuiltInSuspend.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendApplicability.kt")
|
||||
public void testSuspendApplicability() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendApplicability.kt");
|
||||
|
||||
compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
Generated
+6
@@ -1374,6 +1374,12 @@ public class DiagnosticsTestWithStdLibUsingJavacGenerated extends AbstractDiagno
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("returnLabelForBuiltInSuspend.kt")
|
||||
public void testReturnLabelForBuiltInSuspend() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/returnLabelForBuiltInSuspend.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("suspendApplicability.kt")
|
||||
public void testSuspendApplicability() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/testsWithStdLib/coroutines/suspendApplicability.kt");
|
||||
|
||||
Reference in New Issue
Block a user