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) {
|
public void visitBreakExpressionVoid(@NotNull JetBreakExpression expression, CFPContext context) {
|
||||||
JetElement loop = getCorrespondingLoop(expression);
|
JetElement loop = getCorrespondingLoop(expression);
|
||||||
if (loop != null) {
|
if (loop != null) {
|
||||||
|
checkJumpDoesNotCrossFunctionBoundary(expression, loop);
|
||||||
builder.jump(builder.getExitPoint(loop));
|
builder.jump(builder.getExitPoint(loop));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -644,6 +645,7 @@ public class JetControlFlowProcessor {
|
|||||||
public void visitContinueExpressionVoid(@NotNull JetContinueExpression expression, CFPContext context) {
|
public void visitContinueExpressionVoid(@NotNull JetContinueExpression expression, CFPContext context) {
|
||||||
JetElement loop = getCorrespondingLoop(expression);
|
JetElement loop = getCorrespondingLoop(expression);
|
||||||
if (loop != null) {
|
if (loop != null) {
|
||||||
|
checkJumpDoesNotCrossFunctionBoundary(expression, loop);
|
||||||
builder.jump(builder.getEntryPoint(loop));
|
builder.jump(builder.getEntryPoint(loop));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -672,6 +674,16 @@ public class JetControlFlowProcessor {
|
|||||||
return loop;
|
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
|
@Override
|
||||||
public void visitReturnExpressionVoid(@NotNull JetReturnExpression expression, CFPContext context) {
|
public void visitReturnExpressionVoid(@NotNull JetReturnExpression expression, CFPContext context) {
|
||||||
JetExpression returnedExpression = expression.getReturnedExpression();
|
JetExpression returnedExpression = expression.getReturnedExpression();
|
||||||
|
|||||||
@@ -431,6 +431,7 @@ public interface Errors {
|
|||||||
DiagnosticFactory0<JetSimpleNameExpression> AMBIGUOUS_LABEL = DiagnosticFactory0.create(ERROR);
|
DiagnosticFactory0<JetSimpleNameExpression> AMBIGUOUS_LABEL = DiagnosticFactory0.create(ERROR);
|
||||||
|
|
||||||
DiagnosticFactory0<JetLabelQualifiedExpression> BREAK_OR_CONTINUE_OUTSIDE_A_LOOP = 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<JetLabelQualifiedExpression, String> NOT_A_LOOP_LABEL = DiagnosticFactory1.create(ERROR);
|
||||||
DiagnosticFactory1<JetReturnExpression, String> NOT_A_RETURN_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(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(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_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_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);
|
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;
|
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(
|
public static void reportAmbiguousLabel(
|
||||||
@NotNull BindingTrace trace,
|
@NotNull BindingTrace trace,
|
||||||
@NotNull JetSimpleNameExpression targetLabel,
|
@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() {
|
fun foo() {
|
||||||
for (i in <!UNRESOLVED_REFERENCE!>collection<!>) {
|
for (i in <!UNRESOLVED_REFERENCE!>collection<!>) {
|
||||||
<!UNUSED_FUNCTION_LITERAL!>{
|
<!UNUSED_FUNCTION_LITERAL!>{
|
||||||
break
|
<!BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY!>break<!>
|
||||||
}<!>
|
}<!>
|
||||||
}<!SYNTAX!><!>
|
}<!SYNTAX!><!>
|
||||||
@@ -1745,6 +1745,11 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
|||||||
doTest("compiler/testData/diagnostics/tests/controlStructures/ForbidStatementAsDirectFunctionBody.kt");
|
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")
|
@TestMetadata("kt1075.kt")
|
||||||
public void testKt1075() throws Exception {
|
public void testKt1075() throws Exception {
|
||||||
doTest("compiler/testData/diagnostics/tests/controlStructures/kt1075.kt");
|
doTest("compiler/testData/diagnostics/tests/controlStructures/kt1075.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user