diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt index 2f89f18d954..ca865e3d51b 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/ControlFlowProcessor.kt @@ -793,16 +793,31 @@ class ControlFlowProcessor(private val trace: BindingTrace) { override fun visitBreakExpression(expression: KtBreakExpression) { val loop = getCorrespondingLoop(expression) if (loop != null) { - checkJumpDoesNotCrossFunctionBoundary(expression, loop) - builder.getExitPoint(loop)?.let { builder.jump(it, expression) } + if (jumpDoesNotCrossFunctionBoundary(expression, loop)) { + builder.getExitPoint(loop)?.let { builder.jump(it, expression) } + } } } override fun visitContinueExpression(expression: KtContinueExpression) { val loop = getCorrespondingLoop(expression) if (loop != null) { - checkJumpDoesNotCrossFunctionBoundary(expression, loop) - builder.jump(builder.getConditionEntryPoint(loop), expression) + if (jumpDoesNotCrossFunctionBoundary(expression, loop)) { + builder.jump(builder.getConditionEntryPoint(loop), expression) + } + } + } + + private fun getNearestLoopExpression(expression: KtExpression) = expression.getStrictParentOfType() + + private fun getCorrespondingLoopWithoutLabel(expression: KtExpression): KtLoopExpression? { + val parentLoop = getNearestLoopExpression(expression) ?: return null + val parentBody = parentLoop.body + return if (parentBody != null && parentBody.textRange.contains(expression.textRange)) { + parentLoop + } + else { + getNearestLoopExpression(parentLoop) } } @@ -821,7 +836,7 @@ class ControlFlowProcessor(private val trace: BindingTrace) { } } else { - loop = builder.currentLoop + loop = getCorrespondingLoopWithoutLabel(expression) if (loop == null) { trace.report(BREAK_OR_CONTINUE_OUTSIDE_A_LOOP.on(expression)) } @@ -842,13 +857,24 @@ class ControlFlowProcessor(private val trace: BindingTrace) { return loop } - private fun checkJumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtElement) { + private fun jumpDoesNotCrossFunctionBoundary(jumpExpression: KtExpressionWithLabel, jumpTarget: KtElement): Boolean { val bindingContext = trace.bindingContext val labelExprEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpExpression) val labelTargetEnclosingFunc = BindingContextUtils.getEnclosingFunctionDescriptor(bindingContext, jumpTarget) - if (labelExprEnclosingFunc !== labelTargetEnclosingFunc) { - trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression)) + return if (labelExprEnclosingFunc !== labelTargetEnclosingFunc) { + // Check to report only once + if (builder.getExitPoint(jumpTarget) != null || + // Local class secondary constructors are handled differently + // They are the only local class element NOT included in owner pseudocode + // See generateInitializersForScriptClassOrObject && generateDeclarationForLocalClassOrObjectIfNeeded + labelExprEnclosingFunc is ConstructorDescriptor && !labelExprEnclosingFunc.isPrimary) { + trace.report(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY.on(jumpExpression)) + } + false + } + else { + true } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java index 791e1a54e26..817e13af1ff 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java @@ -470,7 +470,7 @@ public class DefaultErrorMessages { 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_IN_WHEN, "'break' and 'continue' are not allowed in 'when' statements. Consider using labels to continue/break from the outer loop"); - MAP.put(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, "'break' or 'continue' jumps across a function boundary"); + MAP.put(BREAK_OR_CONTINUE_JUMPS_ACROSS_FUNCTION_BOUNDARY, "'break' or 'continue' jumps across a function or a class boundary"); MAP.put(NOT_A_LOOP_LABEL, "The label ''{0}'' does not denote a loop", STRING); MAP.put(NOT_A_RETURN_LABEL, "The label ''{0}'' does not reference to a context from which we can return", STRING); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java index 93e0ee0b8a9..9a746c21859 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/resolve/BindingContextUtils.java @@ -125,9 +125,18 @@ public class BindingContextUtils { return descriptor; } + @Nullable public static FunctionDescriptor getEnclosingFunctionDescriptor(@NotNull BindingContext context, @NotNull KtElement element) { - KtFunction function = PsiTreeUtil.getParentOfType(element, KtFunction.class); - return (FunctionDescriptor)context.get(DECLARATION_TO_DESCRIPTOR, function); + KtElement functionOrClass = PsiTreeUtil.getParentOfType(element, KtFunction.class, KtClassOrObject.class); + DeclarationDescriptor descriptor = context.get(DECLARATION_TO_DESCRIPTOR, functionOrClass); + if (functionOrClass instanceof KtFunction) { + if (descriptor instanceof FunctionDescriptor) return (FunctionDescriptor) descriptor; + return null; + } + else { + if (descriptor instanceof ClassDescriptor) return ((ClassDescriptor) descriptor).getUnsubstitutedPrimaryConstructor(); + return null; + } } public static void reportAmbiguousLabel( diff --git a/compiler/testData/cfg/bugs/jumpToOuterScope.instructions b/compiler/testData/cfg/bugs/jumpToOuterScope.instructions index aa59afa5146..f9370cbc0cb 100644 --- a/compiler/testData/cfg/bugs/jumpToOuterScope.instructions +++ b/compiler/testData/cfg/bugs/jumpToOuterScope.instructions @@ -31,7 +31,7 @@ L7 [after local declaration]: 3 jmp(L2) NEXT:[jmp?(L3)] L3 [loop exit point]: L5 [body exit point]: - read (Unit) PREV:[jmp?(L3), jmp(L3)] + read (Unit) PREV:[jmp?(L3)] L1: 1 NEXT:[] error: @@ -47,12 +47,11 @@ sink: L8: 5 6 mark(break) - jmp(L3) NEXT:[read (Unit)] -- 5 ret(*|!) L9 PREV:[] + 5 ret(*|!) L9 L9: - NEXT:[] PREV:[] + NEXT:[] error: PREV:[] sink: PREV:[, ] -===================== \ No newline at end of file +===================== diff --git a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions new file mode 100644 index 00000000000..8d176fbc4bf --- /dev/null +++ b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.instructions @@ -0,0 +1,137 @@ +== foo == +fun foo() { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + break + } + } + println("OK") +} +--------------------- +L0: + 1 + 2 mark({ outer@while (true) { try { while (true) { continue@outer } } finally { break } } println("OK") }) + mark(outer@while (true) { try { while (true) { continue@outer } } finally { break } }) +L2 [loop entry point]: +L6 [condition entry point]: + r(true) -> + mark(while (true) { try { while (true) { continue@outer } } finally { break } }) + magic[VALUE_CONSUMER](true|) -> +L4 [body entry point]: + 3 mark({ try { while (true) { continue@outer } } finally { break } }) + mark(try { while (true) { continue@outer } } finally { break }) + jmp?(L7) NEXT:[mark({ break }), mark({ while (true) { continue@outer } })] + 4 mark({ while (true) { continue@outer } }) +L8 [loop entry point]: +L12 [condition entry point]: + r(true) -> + mark(while (true) { continue@outer }) + magic[VALUE_CONSUMER](true|) -> +L10 [body entry point]: + 5 mark({ continue@outer }) +L13 [start finally]: + 6 mark({ break }) + jmp(L3) NEXT:[read (Unit)] +L14 [finish finally]: +- 5 jmp(L6) NEXT:[r(true) -> ] PREV:[] +- 4 jmp(L8) NEXT:[r(true) -> ] PREV:[] +L9 [loop exit point]: +L11 [body exit point]: +- read (Unit) PREV:[] +- 3 jmp(L15) NEXT:[mark({ break })] PREV:[] +L7 [onExceptionToFinallyBlock]: + 6 mark({ break }) PREV:[jmp?(L7)] + jmp(L3) NEXT:[read (Unit)] +- 3 jmp(error) NEXT:[] PREV:[] +L15 [skipFinallyToErrorBlock]: +- 6 mark({ break }) PREV:[] +- jmp(L3) NEXT:[read (Unit)] PREV:[] +- 3 merge(try { while (true) { continue@outer } } finally { break }|!) -> PREV:[] +- 2 jmp(L2) NEXT:[r(true) -> ] PREV:[] +L3 [loop exit point]: +L5 [body exit point]: + read (Unit) PREV:[jmp(L3), jmp(L3)] + mark("OK") + r("OK") -> + mark(println("OK")) + magic[UNRESOLVED_CALL](println("OK")|, !) -> +L1: + 1 NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== +== bar == +fun bar(): String { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + return "OK" + } + } +} +--------------------- +L0: + 1 + 2 mark({ outer@while (true) { try { while (true) { continue@outer } } finally { return "OK" } } }) + mark(outer@while (true) { try { while (true) { continue@outer } } finally { return "OK" } }) +L2 [loop entry point]: +L6 [condition entry point]: + r(true) -> + mark(while (true) { try { while (true) { continue@outer } } finally { return "OK" } }) + magic[VALUE_CONSUMER](true|) -> +L4 [body entry point]: + 3 mark({ try { while (true) { continue@outer } } finally { return "OK" } }) + mark(try { while (true) { continue@outer } } finally { return "OK" }) + jmp?(L7) NEXT:[mark({ return "OK" }), mark({ while (true) { continue@outer } })] + 4 mark({ while (true) { continue@outer } }) +L8 [loop entry point]: +L12 [condition entry point]: + r(true) -> + mark(while (true) { continue@outer }) + magic[VALUE_CONSUMER](true|) -> +L10 [body entry point]: + 5 mark({ continue@outer }) +L13 [start finally]: + 6 mark({ return "OK" }) + mark("OK") + r("OK") -> + ret(*|) L1 NEXT:[] +L14 [finish finally]: +- 5 jmp(L6) NEXT:[r(true) -> ] PREV:[] +- 4 jmp(L8) NEXT:[r(true) -> ] PREV:[] +L9 [loop exit point]: +L11 [body exit point]: +- read (Unit) PREV:[] +- 3 jmp(L15) NEXT:[mark({ return "OK" })] PREV:[] +L7 [onExceptionToFinallyBlock]: + 6 mark({ return "OK" }) PREV:[jmp?(L7)] + mark("OK") + r("OK") -> + ret(*|) L1 NEXT:[] +- 3 jmp(error) NEXT:[] PREV:[] +L15 [skipFinallyToErrorBlock]: +- 6 mark({ return "OK" }) PREV:[] +- mark("OK") PREV:[] +- r("OK") -> PREV:[] +- ret(*|) L1 NEXT:[] PREV:[] +- 3 merge(try { while (true) { continue@outer } } finally { return "OK" }|!) -> PREV:[] +- 2 jmp(L2) NEXT:[r(true) -> ] PREV:[] +L3 [loop exit point]: +L5 [body exit point]: +- read (Unit) PREV:[] +L1: + 1 NEXT:[] PREV:[ret(*|) L1, ret(*|) L1] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt new file mode 100644 index 00000000000..a74455e8f34 --- /dev/null +++ b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt @@ -0,0 +1,24 @@ +fun foo() { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + break + } + } + println("OK") +} + +fun bar(): String { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + return "OK" + } + } +} diff --git a/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values new file mode 100644 index 00000000000..91f1325a73c --- /dev/null +++ b/compiler/testData/cfg/controlStructures/breakContinueInTryFinally.values @@ -0,0 +1,63 @@ +== foo == +fun foo() { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + break + } + } + println("OK") +} +--------------------- + : * NEW: magic[VALUE_CONSUMER](true|) -> + : * NEW: magic[VALUE_CONSUMER](true|) -> +true : Boolean NEW: r(true) -> +true : Boolean NEW: r(true) -> +continue@outer !: * +{ continue@outer } !: * COPY +while (true) { continue@outer } !: * +{ while (true) { continue@outer } } !: * COPY +break !: * +{ break } !: * COPY +try { while (true) { continue@outer } } finally { break } : * NEW: merge(try { while (true) { continue@outer } } finally { break }|!) -> +{ try { while (true) { continue@outer } } finally { break } } : * COPY +while (true) { try { while (true) { continue@outer } } finally { break } } !: * +outer@while (true) { try { while (true) { continue@outer } } finally { break } } !: * COPY +println !: * +"OK" : * NEW: r("OK") -> +println("OK") : * NEW: magic[UNRESOLVED_CALL](println("OK")|, !) -> +{ outer@while (true) { try { while (true) { continue@outer } } finally { break } } println("OK") } : * COPY +===================== +== bar == +fun bar(): String { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + return "OK" + } + } +} +--------------------- + : * NEW: magic[VALUE_CONSUMER](true|) -> + : * NEW: magic[VALUE_CONSUMER](true|) -> +true : Boolean NEW: r(true) -> +true : Boolean NEW: r(true) -> +continue@outer !: * +{ continue@outer } !: * COPY +while (true) { continue@outer } !: * +{ while (true) { continue@outer } } !: * COPY +"OK" : String NEW: r("OK") -> +return "OK" !: * +{ return "OK" } !: * COPY +try { while (true) { continue@outer } } finally { return "OK" } : * NEW: merge(try { while (true) { continue@outer } } finally { return "OK" }|!) -> +{ try { while (true) { continue@outer } } finally { return "OK" } } : * COPY +while (true) { try { while (true) { continue@outer } } finally { return "OK" } } !: * +outer@while (true) { try { while (true) { continue@outer } } finally { return "OK" } } !: * COPY +{ outer@while (true) { try { while (true) { continue@outer } } finally { return "OK" } } } !: * COPY +===================== diff --git a/compiler/testData/cfg/controlStructures/breakInsideLocal.instructions b/compiler/testData/cfg/controlStructures/breakInsideLocal.instructions new file mode 100644 index 00000000000..79036038ce1 --- /dev/null +++ b/compiler/testData/cfg/controlStructures/breakInsideLocal.instructions @@ -0,0 +1,62 @@ +== test == +fun test() { + while (true) { + class LocalClass(val x: Int) { + init { + break + } + constructor() : this(42) { + break + } + fun foo() { + break + } + } + } +} +--------------------- +L0: + 1 + 2 mark({ while (true) { class LocalClass(val x: Int) { init { break } constructor() : this(42) { break } fun foo() { break } } } }) +L2 [loop entry point]: +L6 [condition entry point]: + r(true) -> PREV:[mark({ while (true) { class LocalClass(val x: Int) { init { break } constructor() : this(42) { break } fun foo() { break } } } }), jmp(L2)] + mark(while (true) { class LocalClass(val x: Int) { init { break } constructor() : this(42) { break } fun foo() { break } } }) + magic[VALUE_CONSUMER](true|) -> +L4 [body entry point]: + 3 mark({ class LocalClass(val x: Int) { init { break } constructor() : this(42) { break } fun foo() { break } } }) + jmp?(L7) NEXT:[jmp(L2), v(val x: Int)] + v(val x: Int) + magic[FAKE_INITIALIZER](val x: Int) -> + w(x|) + 4 mark({ break }) + 3 jmp?(L8) NEXT:[jmp(L2), d(fun foo() { break })] + d(fun foo() { break }) NEXT:[] +L7 [after local class]: +L8 [after local declaration]: + 2 jmp(L2) NEXT:[r(true) -> ] PREV:[jmp?(L7), jmp?(L8)] +L3 [loop exit point]: +L5 [body exit point]: +- read (Unit) PREV:[] +L1: + 1 NEXT:[] PREV:[] +error: + PREV:[] +sink: + PREV:[, , d(fun foo() { break })] +===================== +== foo == +fun foo() { + break + } +--------------------- +L9: + 4 + 5 mark({ break }) +L10: + 4 NEXT:[] +error: + PREV:[] +sink: + PREV:[, ] +===================== diff --git a/compiler/testData/cfg/controlStructures/breakInsideLocal.kt b/compiler/testData/cfg/controlStructures/breakInsideLocal.kt new file mode 100644 index 00000000000..02d5932e46d --- /dev/null +++ b/compiler/testData/cfg/controlStructures/breakInsideLocal.kt @@ -0,0 +1,15 @@ +fun test() { + while (true) { + class LocalClass(val x: Int) { + init { + break + } + constructor() : this(42) { + break + } + fun foo() { + break + } + } + } +} diff --git a/compiler/testData/cfg/controlStructures/breakInsideLocal.values b/compiler/testData/cfg/controlStructures/breakInsideLocal.values new file mode 100644 index 00000000000..06157e94e88 --- /dev/null +++ b/compiler/testData/cfg/controlStructures/breakInsideLocal.values @@ -0,0 +1,33 @@ +== test == +fun test() { + while (true) { + class LocalClass(val x: Int) { + init { + break + } + constructor() : this(42) { + break + } + fun foo() { + break + } + } + } +} +--------------------- + : * NEW: magic[VALUE_CONSUMER](true|) -> + : Int NEW: magic[FAKE_INITIALIZER](val x: Int) -> +true : Boolean NEW: r(true) -> +break !: * +{ break } !: * COPY +while (true) { class LocalClass(val x: Int) { init { break } constructor() : this(42) { break } fun foo() { break } } } !: * +{ while (true) { class LocalClass(val x: Int) { init { break } constructor() : this(42) { break } fun foo() { break } } } } !: * COPY +===================== +== foo == +fun foo() { + break + } +--------------------- +break !: * +{ break } !: * COPY +===================== diff --git a/compiler/testData/codegen/box/controlStructures/breakInFinally.kt b/compiler/testData/codegen/box/controlStructures/breakInFinally.kt new file mode 100644 index 00000000000..d761ee60360 --- /dev/null +++ b/compiler/testData/codegen/box/controlStructures/breakInFinally.kt @@ -0,0 +1,11 @@ +fun box(): String { + while (true) { + try { + continue; + } + finally { + break; + } + } + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.kt new file mode 100644 index 00000000000..06a1a8b1096 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.kt @@ -0,0 +1,24 @@ +fun foo() { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + break + } + } + "OK".hashCode() +} + +fun bar(): String { + outer@while (true) { + try { + while (true) { + continue@outer + } + } finally { + return "OK" + } + } +} diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.txt new file mode 100644 index 00000000000..f56a5f6c01a --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.txt @@ -0,0 +1,4 @@ +package + +public fun bar(): kotlin.String +public fun foo(): kotlin.Unit diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt new file mode 100644 index 00000000000..170a379cf7b --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt @@ -0,0 +1,86 @@ +fun test() { + while (true) { + fun local1() { + break + } + } +} + +fun test2() { + while (true) { + { + continue + } + } +} + +fun test3() { + while (true) { + class LocalClass { + init { + continue + } + + fun foo() { + break + } + } + } +} + +fun test4() { + while (true) { + object: Any() { + init { + break + } + } + } +} + +fun test5() { + while (true) { + class LocalClass(val x: Int) { + constructor() : this(42) { + break + } + constructor(y: Double) : this(y.toInt()) { + continue + } + } + } +} + +fun test6() { + while (true) { + class LocalClass(val x: Int) { + init { + break + } + init { + continue + } + } + } +} + +fun test7() { + while (true) { + class LocalClass { + val x: Int = if (true) { + break + } + else { + continue + } + } + } +} + +fun test8() { + while (true) { + class LocalClass(val x: Int) { + constructor() : this(if (true) { 42 } else { break }) + } + } +} \ No newline at end of file diff --git a/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt new file mode 100644 index 00000000000..8eb87ce2285 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.txt @@ -0,0 +1,10 @@ +package + +public fun test(): kotlin.Unit +public fun test2(): kotlin.Unit +public fun test3(): kotlin.Unit +public fun test4(): kotlin.Unit +public fun test5(): kotlin.Unit +public fun test6(): kotlin.Unit +public fun test7(): kotlin.Unit +public fun test8(): kotlin.Unit diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java index 59a8c715438..d8d55217ba5 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/ControlFlowTestGenerated.java @@ -166,6 +166,18 @@ public class ControlFlowTestGenerated extends AbstractControlFlowTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg/controlStructures"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("breakContinueInTryFinally.kt") + public void testBreakContinueInTryFinally() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt"); + doTest(fileName); + } + + @TestMetadata("breakInsideLocal.kt") + public void testBreakInsideLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/breakInsideLocal.kt"); + doTest(fileName); + } + @TestMetadata("continueInDoWhile.kt") public void testContinueInDoWhile() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/continueInDoWhile.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java index 4ca34306ff2..6ed783a7fd8 100644 --- a/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/cfg/PseudoValueTestGenerated.java @@ -168,6 +168,18 @@ public class PseudoValueTestGenerated extends AbstractPseudoValueTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/cfg/controlStructures"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("breakContinueInTryFinally.kt") + public void testBreakContinueInTryFinally() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/breakContinueInTryFinally.kt"); + doTest(fileName); + } + + @TestMetadata("breakInsideLocal.kt") + public void testBreakInsideLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/breakInsideLocal.kt"); + doTest(fileName); + } + @TestMetadata("continueInDoWhile.kt") public void testContinueInDoWhile() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/cfg/controlStructures/continueInDoWhile.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index ffba92d04a0..bd100827125 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -2847,6 +2847,18 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("breakContinueInTryFinally.kt") + public void testBreakContinueInTryFinally() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakContinueInTryFinally.kt"); + doTest(fileName); + } + + @TestMetadata("breakInsideLocal.kt") + public void testBreakInsideLocal() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakInsideLocal.kt"); + doTest(fileName); + } + @TestMetadata("breakOrContinueInLoopCondition.kt") public void testBreakOrContinueInLoopCondition() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlFlowAnalysis/breakOrContinueInLoopCondition.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 943459ffab3..5ac6f7380ef 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -3421,6 +3421,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("breakInFinally.kt") + public void testBreakInFinally() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/breakInFinally.kt"); + doTest(fileName); + } + @TestMetadata("compareBoxedIntegerToZero.kt") public void testCompareBoxedIntegerToZero() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/controlStructures/compareBoxedIntegerToZero.kt");