diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java index 3d7afcd..90d5f9f 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest.java @@ -2437,7 +2437,58 @@ " }\n" + "}\n" }, - ""); + ""); +} +// Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop) +// return/break/continue inside anonymous class inside try-catch inside initializer +public void testLocalClassInInitializer2() { + this.runNegativeTest( + new String[] { + "X.java", + "public class X {\n" + + " void f () {\n" + + " while (true) {\n" + + " class Inner1 {\n" + + " { if (true) break; }\n" + + " }\n" + + " new Inner1();\n" + + " }\n" + + " } \n" + + " void g () {\n" + + " outer: for (int i=1;true;i++) {\n" + + " class Inner2 {\n" + + " int j = 3;\n" + + " void foo () {\n" + + " if (2 == j) continue outer;\n" + + " else continue;\n" + + " }\n" + + " }\n" + + " new Inner2().foo();\n" + + " }\n" + + " } \n" + + "}\n" + }, + "----------\n" + + "1. ERROR in X.java (at line 5)\n" + + " { if (true) break; }\n" + + " ^^^^^^\n" + + "break cannot be used outside of a loop or a switch\n" + + "----------\n" + + "2. WARNING in X.java (at line 11)\n" + + " outer: for (int i=1;true;i++) {\n" + + " ^^^^^\n" + + "The label outer is never explicitly referenced\n" + + "----------\n" + + "3. ERROR in X.java (at line 15)\n" + + " if (2 == j) continue outer;\n" + + " ^^^^^^^^^^^^^^^\n" + + "The label outer is missing\n" + + "----------\n" + + "4. ERROR in X.java (at line 16)\n" + + " else continue;\n" + + " ^^^^^^^^^\n" + + "continue cannot be used outside of a loop\n" + + "----------\n"); } public static Class testClass() { return FlowAnalysisTest.class; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java index 3c59918..cc390d3 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/BreakStatement.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -72,7 +72,7 @@ targetContext.recordBreakFrom(flowInfo); break; } - } while ((traversedContext = traversedContext.parent) != null); + } while ((traversedContext = traversedContext.getLocalParent()) != null); // resize subroutines if (subCount != this.subroutines.length) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java index 1641bc5..475fef2 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ContinueStatement.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -75,7 +75,7 @@ targetContext.recordContinueFrom(flowContext, flowInfo); break; } - } while ((traversedContext = traversedContext.parent) != null); + } while ((traversedContext = traversedContext.getLocalParent()) != null); // resize subroutines if (subCount != this.subroutines.length) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java index f2b6010..fce03fa 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java @@ -99,9 +99,7 @@ currentScope.problemReporter().cannotReturnInInitializer(this); return FlowInfo.DEAD_END; } - if (traversedContext.associatedNode == referenceMethod) - break; // don't traverse beyond the enclosing method (see https://bugs.eclipse.org/360328). - } while ((traversedContext = traversedContext.parent) != null); + } while ((traversedContext = traversedContext.getLocalParent()) != null); // resize subroutines if ((this.subroutines != null) && (subCount != this.subroutines.length)) { diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java index 0590583..7f7638d 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java @@ -70,12 +70,12 @@ // any reference reported at this level is removed from the parent context // where it could also be reported again if (complained) { - FlowContext currentContext = this.parent; + FlowContext currentContext = this.getLocalParent(); while (currentContext != null) { //if (currentContext.isSubRoutine()) { currentContext.removeFinalAssignmentIfAny(this.finalAssignments[i]); //} - currentContext = currentContext.parent; + currentContext = currentContext.getLocalParent(); } } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java index 036b824..ccb5fb6 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java @@ -12,9 +12,10 @@ package org.eclipse.jdt.internal.compiler.flow; import java.util.ArrayList; + import org.eclipse.jdt.core.compiler.CharOperation; -import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.ASTNode; +import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Expression; import org.eclipse.jdt.internal.compiler.ast.LabeledStatement; import org.eclipse.jdt.internal.compiler.ast.Reference; @@ -207,7 +208,7 @@ } } } - traversedContext = traversedContext.parent; + traversedContext = traversedContext.getLocalParent(); } // if reaches this point, then there are some remaining unhandled exception types. if (isExceptionOnAutoClose) { @@ -353,7 +354,7 @@ flowInfo.addInitializationsFrom(tryStatement.subRoutineInits); // collect inits } } - traversedContext = traversedContext.parent; + traversedContext = traversedContext.getLocalParent(); } // if reaches this point, then there are some remaining unhandled exception types. nextReport: for (int i = 0; i < raisedCount; i++) { @@ -385,9 +386,9 @@ current = initializationContext.initializationParent; } else if (current instanceof ExceptionHandlingFlowContext) { ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) current; - current = exceptionContext.initializationParent == null ? exceptionContext.parent : exceptionContext.initializationParent; + current = exceptionContext.initializationParent == null ? exceptionContext.getLocalParent() : exceptionContext.initializationParent; } else { - current = current.parent; + current = current.getLocalParent(); } } while (current != null); // not found @@ -411,7 +412,7 @@ return current; return lastNonReturningSubRoutine; } - current = current.parent; + current = current.getLocalParent(); } // not found return null; @@ -448,7 +449,7 @@ // label is found, but not a continuable location return FlowContext.NotContinuableContext; } - current = current.parent; + current = current.getLocalParent(); } // not found return null; @@ -467,7 +468,7 @@ if (lastNonReturningSubRoutine == null) return current; return lastNonReturningSubRoutine; } - current = current.parent; + current = current.getLocalParent(); } // not found return null; @@ -487,10 +488,20 @@ return current; return lastNonReturningSubRoutine; } - current = current.parent; + current = current.getLocalParent(); } // not found return null; +} + +/** + * Answer the parent flow context but be careful not to cross the boundary of a nested type, + * or null if no such parent exists. + */ +public FlowContext getLocalParent() { + if (this.associatedNode instanceof AbstractMethodDeclaration || this.associatedNode instanceof TypeDeclaration) + return null; + return this.parent; } public String individualToString() { @@ -570,7 +581,7 @@ if (!context.recordFinalAssignment(variable, finalReference)) { break; // no need to keep going } - context = context.parent; + context = context.getLocalParent(); } } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LabelFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LabelFlowContext.java index 3374313..88ca4e7 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LabelFlowContext.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LabelFlowContext.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -31,14 +31,14 @@ void checkLabelValidity(BlockScope scope) { // check if label was already defined above - FlowContext current = this.parent; + FlowContext current = this.getLocalParent(); while (current != null) { char[] currentLabelName; if (((currentLabelName = current.labelName()) != null) && CharOperation.equals(currentLabelName, this.labelName)) { scope.problemReporter().alreadyDefinedLabel(this.labelName, this.associatedNode); } - current = current.parent; + current = current.getLocalParent(); } } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java index 886fea6..919bb4c 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java @@ -112,10 +112,10 @@ // any reference reported at this level is removed from the parent context where it // could also be reported again if (complained) { - FlowContext context = this.parent; + FlowContext context = this.getLocalParent(); while (context != null) { context.removeFinalAssignmentIfAny(this.finalAssignments[i]); - context = context.parent; + context = context.getLocalParent(); } } } @@ -396,6 +396,7 @@ FlowContext inner = innerFlowContext; while (inner != this && !(inner instanceof LoopingFlowContext)) { inner = inner.parent; + // we know that inner is reachable from this without crossing a type boundary } if (inner == this) { this.upstreamNullFlowInfo.