Merge pull request #388 from dezgeg/fix-jump-across-function-boundary
KT-4334: Disallow break or continue across a function boundary
This commit is contained in:
@@ -636,6 +636,7 @@ public class JetControlFlowProcessor {
|
||||
public void visitBreakExpressionVoid(@NotNull JetBreakExpression expression, CFPContext context) {
|
||||
JetElement loop = getCorrespondingLoop(expression);
|
||||
if (loop != null) {
|
||||
checkJumpDoesNotCrossFunctionBoundary(expression, loop);
|
||||
builder.jump(builder.getExitPoint(loop));
|
||||
}
|
||||
}
|
||||
@@ -644,6 +645,7 @@ public class JetControlFlowProcessor {
|
||||
public void visitContinueExpressionVoid(@NotNull JetContinueExpression expression, CFPContext context) {
|
||||
JetElement loop = getCorrespondingLoop(expression);
|
||||
if (loop != null) {
|
||||
checkJumpDoesNotCrossFunctionBoundary(expression, loop);
|
||||
builder.jump(builder.getEntryPoint(loop));
|
||||
}
|
||||
}
|
||||
@@ -672,6 +674,16 @@ public class JetControlFlowProcessor {
|
||||
return loop;
|
||||
}
|
||||
|
||||
private void checkJumpDoesNotCrossFunctionBoundary(@NotNull JetLabelQualifiedExpression jumpExpression, @NotNull JetElement jumpTarget) {
|
||||
BindingContext bindingContext = trace.getBindingContext();
|
||||
|
||||
FunctionDescriptor labelExprEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpExpression);
|
||||
FunctionDescriptor labelTargetEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpTarget);
|
||||
if (labelExprEnclosingFunc != labelTargetEnclosingFunc) {
|
||||
trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReturnExpressionVoid(@NotNull JetReturnExpression expression, CFPContext context) {
|
||||
JetExpression returnedExpression = expression.getReturnedExpression();
|
||||
|
||||
@@ -431,6 +431,7 @@ public interface Errors {
|
||||
DiagnosticFactory0<JetSimpleNameExpression> AMBIGUOUS_LABEL = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<JetLabelQualifiedExpression> BREAK_OR_CONTINUE_OUTSIDE_A_LOOP = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<JetLabelQualifiedExpression> BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<JetLabelQualifiedExpression, String> NOT_A_LOOP_LABEL = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory1<JetReturnExpression, String> NOT_A_RETURN_LABEL = DiagnosticFactory1.create(ERROR);
|
||||
|
||||
|
||||
+1
@@ -327,6 +327,7 @@ public class DefaultErrorMessages {
|
||||
MAP.put(NO_TAIL_CALLS_FOUND, "A function is marked as tail-recursive but no tail calls are found");
|
||||
MAP.put(VALUE_PARAMETER_WITH_NO_TYPE_ANNOTATION, "A type annotation is required on a value parameter");
|
||||
MAP.put(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP, "'break' and 'continue' are only allowed inside a loop");
|
||||
MAP.put(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, "'break' or 'continue' jumps across a function boundary");
|
||||
MAP.put(NOT_A_LOOP_LABEL, "The label ''{0}'' does not denote a loop", TO_STRING);
|
||||
MAP.put(NOT_A_RETURN_LABEL, "The label ''{0}'' does not reference to a context from which we can return", TO_STRING);
|
||||
|
||||
|
||||
@@ -236,6 +236,11 @@ public class BindingContextUtils {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public static FunctionDescriptor getEnclosingFunctionDescriptor(@NotNull BindingContext context, @NotNull JetElement element) {
|
||||
JetFunction function = PsiTreeUtil.getParentOfType(element, JetFunction.class);
|
||||
return (FunctionDescriptor)context.get(DECLARATION_TO_DESCRIPTOR, function);
|
||||
}
|
||||
|
||||
public static void reportAmbiguousLabel(
|
||||
@NotNull BindingTrace trace,
|
||||
@NotNull JetSimpleNameExpression targetLabel,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
fun call(f: () -> Unit) = f()
|
||||
|
||||
fun f1() {
|
||||
@outer while (true) {
|
||||
call {
|
||||
<!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break@outer<!>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun f2() {
|
||||
do {
|
||||
fun inner() {
|
||||
<!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>continue<!>
|
||||
}
|
||||
} while (true)
|
||||
}
|
||||
+1
-1
@@ -3,6 +3,6 @@ package b
|
||||
fun foo() {
|
||||
for (i in <!UNRESOLVED_REFERENCE!>collection<!>) {
|
||||
<!UNUSED_FUNCTION_LITERAL!>{
|
||||
break
|
||||
<!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!>
|
||||
}<!>
|
||||
}<!SYNTAX!><!>
|
||||
@@ -1745,6 +1745,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest("compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("jumpAcrossFunctionBoundary.kt")
|
||||
public void testJumpAcrossFunctionBoundary() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/controlStructures/jumpAcrossFunctionBoundary.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt1075.kt")
|
||||
public void testKt1075() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/controlStructures/kt1075.kt");
|
||||
|
||||
Reference in New Issue
Block a user