Index: org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java =================================================================== --- org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java (revision 139) +++ org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java (working copy) @@ -46,6 +46,7 @@ FlowContext traversedContext = flowContext; int subCount = 0; this.subroutines = new SubRoutineStatement[5]; + boolean deferRecordBreakFrom = false; do { SubRoutineStatement sub; @@ -68,8 +69,13 @@ } } else if (traversedContext == targetContext) { // only record break info once accumulated through subroutines, and only against target context - targetContext.recordBreakFrom(flowInfo); + if (! deferRecordBreakFrom) { + targetContext.recordBreakFrom(flowInfo); + } break; + } else if (! deferRecordBreakFrom && traversedContext.preemptNullDiagnostic) { + traversedContext.recordBreakFrom(flowInfo.unconditionalInitsWithoutSideEffect(), targetContext); + deferRecordBreakFrom = true; } } while ((traversedContext = traversedContext.parent) != null); Index: org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java =================================================================== --- org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java (revision 139) +++ org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/DoStatement.java (working copy) @@ -89,7 +89,7 @@ (this.action == null ? actionInfo : (actionInfo.mergedWith(loopingContext.initsOnContinue))).copy()); - if (!isConditionOptimizedFalse && this.continueLabel != null) { + if (!isConditionOptimizedFalse && this.continueLabel != null || loopingContext.breakTargetsNb != 0) { loopingContext.complainOnDeferredFinalChecks(currentScope, condInfo); condLoopContext.complainOnDeferredFinalChecks(currentScope, condInfo); loopingContext.complainOnDeferredNullChecks(currentScope, Index: org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java =================================================================== --- org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java (revision 139) +++ org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/WhileStatement.java (working copy) @@ -121,7 +121,7 @@ if ((actionInfo.tagBits & loopingContext.initsOnContinue.tagBits & - FlowInfo.UNREACHABLE) != 0) { + FlowInfo.UNREACHABLE) != 0 && loopingContext.breakTargetsNb == 0) { continueLabel = null; exitBranch.addInitializationsFrom(condInfo.initsWhenFalse()); } else { Index: org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java =================================================================== --- org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java (revision 139) +++ org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java (working copy) @@ -13,6 +13,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.Reference; +import org.eclipse.jdt.internal.compiler.ast.TryStatement; import org.eclipse.jdt.internal.compiler.codegen.BranchLabel; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; @@ -32,6 +33,9 @@ private LoopingFlowContext innerFlowContexts[] = null; private UnconditionalFlowInfo innerFlowInfos[] = null; private int innerFlowContextsNb = 0; + private FlowContext breakTargetContexts[] = null; + private UnconditionalFlowInfo breakTargetsInfo[] = null; + public int breakTargetsNb = 0; Reference finalAssignments[]; VariableBinding finalVariables[]; @@ -101,9 +105,9 @@ /** * Perform deferred checks relative to the null status of local variables. * @param scope the scope to which this context is associated - * @param flowInfo the flow info against which checks must be performed + * @param callerFlowInfo the flow info against which checks must be performed */ -public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo flowInfo) { +public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowInfo) { for (int i = 0 ; i < this.innerFlowContextsNb ; i++) { this.upstreamNullFlowInfo. addPotentialNullInfoFrom( @@ -111,9 +115,8 @@ addPotentialNullInfoFrom(this.innerFlowInfos[i]); } this.innerFlowContextsNb = 0; - flowInfo = this.upstreamNullFlowInfo. - addPotentialNullInfoFrom( - flowInfo.unconditionalInitsWithoutSideEffect()); + UnconditionalFlowInfo flowInfo = this.upstreamNullFlowInfo. + addPotentialNullInfoFrom(callerFlowInfo.unconditionalInitsWithoutSideEffect()); if (this.deferNullDiagnostic) { // check only immutable null checks on innermost looping context for (int i = 0; i < this.nullCount; i++) { @@ -247,6 +250,26 @@ } } } + // propagate breaks - see BreakStatement#analyseCode + for (int i = 0; i < this.breakTargetsNb; i++) { + UnconditionalFlowInfo breakFlowInfo = this.breakTargetsInfo[i].addPotentialNullInfoFrom(flowInfo); + FlowContext traversedContext = this; + do { + if (traversedContext instanceof InsideSubRoutineFlowContext) { + ASTNode node = traversedContext.associatedNode; + if (node instanceof TryStatement) { + TryStatement tryStatement = (TryStatement) node; + breakFlowInfo.addInitializationsFrom(tryStatement.subRoutineInits); + } + } else if (traversedContext == this.breakTargetContexts[i]) { + traversedContext.recordBreakFrom(breakFlowInfo); + break; + } else if (traversedContext != this && traversedContext.preemptNullDiagnostic) { + traversedContext.recordBreakFrom(breakFlowInfo, this.breakTargetContexts[i]); + break; + } + } while ((traversedContext = traversedContext.parent) != null); + } } public BranchLabel continueLabel() { @@ -270,6 +293,19 @@ return initsOnContinue != FlowInfo.DEAD_END; } +public void recordBreakFrom(UnconditionalFlowInfo flowInfo, FlowContext targetContext) { + int current; + if ((current = this.breakTargetsNb++) == 0) { + this.breakTargetsInfo = new UnconditionalFlowInfo[2]; + this.breakTargetContexts = new FlowContext[2]; + } else if (current == this.breakTargetsInfo.length) { + System.arraycopy(this.breakTargetsInfo, 0, this.breakTargetsInfo = new UnconditionalFlowInfo[current + 2], 0, current); + System.arraycopy(this.breakTargetContexts, 0, this.breakTargetContexts = new FlowContext[current + 2], 0, current); + } + this.breakTargetsInfo[current] = flowInfo; + this.breakTargetContexts[current] = targetContext; +} + public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0) { if ((initsOnContinue.tagBits & FlowInfo.UNREACHABLE) == 0) { Index: org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java =================================================================== --- org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java (revision 139) +++ org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java (working copy) @@ -42,7 +42,7 @@ // only used within try blocks; remembers upstream flow info mergedWith // any null related operation happening within the try block -boolean deferNullDiagnostic, preemptNullDiagnostic; +public boolean deferNullDiagnostic, preemptNullDiagnostic; public FlowContext(FlowContext parent, ASTNode associatedNode) { this.parent = parent; @@ -422,6 +422,10 @@ // default implementation: do nothing } +public void recordBreakFrom(UnconditionalFlowInfo flowInfo, FlowContext targetContext) { + // default implementation: do nothing +} + public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { // default implementation: do nothing } Index: org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java =================================================================== --- org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java (revision 139) +++ org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java (working copy) @@ -1343,6 +1343,24 @@ }, "1"); } +// do while and named labels +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 +// variant +public void test047() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " public void foo() {\n" + + " done: do\n" + + " break done;\n" + + " while (false);\n" + + " System.out.println();\n" + + " }\n" + + "}\n", + }, + ""); +} public static Class testClass() { return FlowAnalysisTest.class; } Index: org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java =================================================================== --- org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java (revision 139) +++ org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java (working copy) @@ -3874,7 +3874,7 @@ // null analysis - while // https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 // extraneous error in case of a labeled while(true) statement -public void _test0460_while_explicit_label() { +public void test0460_while_explicit_label() { this.runConformTest( new String[] { "X.java", @@ -3900,17 +3900,21 @@ // null analysis - while // https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 // extraneous error in case of a labeled while(true) statement -public void _test0461_while_explicit_label() { +public void test0461_while_explicit_label() { this.runConformTest( new String[] { "X.java", - "public class X {\n" + - " void foo(boolean b) {\n" + + "public class X {\n" + + " boolean test() {\n" + + " return true;\n" + + " }\n" + + " void foo() {\n" + " Object o = null;\n" + " done: while (true) {\n" + - " if (b) {\n" + + " if (test()) {\n" + " break done;\n" + - " }\n" + + " }\n" + + " o = new Object();\n" + " }\n" + " if (o == null) {\n" + " }\n" + @@ -5679,6 +5683,59 @@ ); } +// null analysis - do while +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 +// variant +public void test0616_do_while_explicit_label() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(int i) {\n" + + " Object o = null;\n" + + " done: do {\n" + + " switch (i) {\n" + + " case 0:\n" + + " o = new Object();\n" + + " break;\n" + + " case 1:\n" + + " break done;\n" + + " }\n" + + " } while (true);\n" + + " if (o == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - do while +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 +// variant +public void test0617_do_while_explicit_label() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean test() {\n" + + " return true;\n" + + " }\n" + + " void foo() {\n" + + " Object o = null;\n" + + " done: do {\n" + + " if (test()) {\n" + + " break done;\n" + + " }\n" + + " o = new Object();\n" + + " } while (true);\n" + + " if (o == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + + // null analysis -- for public void test0701_for() { this.runNegativeTest( @@ -6496,6 +6553,87 @@ ""); } +// null analysis - for +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 +// variant +public void test0739_for_explicit_label() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " void foo(int i) {\n" + + " Object o = null;\n" + + " done: for (;;) {\n" + + " switch (i) {\n" + + " case 0:\n" + + " o = new Object();\n" + + " break;\n" + + " case 1:\n" + + " break done;\n" + + " }\n" + + " }\n" + + " if (o == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - for +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 +// variant +public void test0740_for_explicit_label() { + this.runConformTest( + new String[] { + "X.java", + "public class X {\n" + + " boolean test() {\n" + + " return true;\n" + + " }\n" + + " void foo() {\n" + + " Object o = null;\n" + + " done: for (;;) {\n" + + " if (test()) {\n" + + " break done;\n" + + " }\n" + + " o = new Object();\n" + + " }\n" + + " if (o == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); +} + +// null analysis - for +// https://bugs.eclipse.org/bugs/show_bug.cgi?id=176472 +// variant +public void test0741_for_explicit_label() { + if (COMPLIANCE_1_5.compareTo(this.complianceLevel) <= 0) { + this.runConformTest( + new String[] { + "X.java", + "import java.util.List;\n" + + "public class X {\n" + + " void foo(int i, List l) {\n" + + " Object o = null;\n" + + " done: for (Object j: l) {\n" + + " switch (i) {\n" + + " case 0:\n" + + " o = new Object();\n" + + " break;\n" + + " case 1:\n" + + " break done;\n" + + " }\n" + + " }\n" + + " if (o == null) {\n" + + " }\n" + + " }\n" + + "}\n"}, + ""); + } +} + // null analysis -- switch public void test0800_switch() { this.runConformTest(