diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java index 5ea3543..94bb6ac 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceTest.java @@ -47,7 +47,7 @@ // Only the highest compliance level is run; add the VM argument // -Dcompliance=1.4 (for example) to lower it if needed static { -// TESTS_NAMES = new String[] { "test358827" }; +// TESTS_NAMES = new String[] { "testBug360328" }; // TESTS_NUMBERS = new int[] { 561 }; // TESTS_RANGE = new int[] { 1, 2049 }; } @@ -15124,4 +15124,67 @@ true, compilerOptions); } +// Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop) +public void testBug360328() { + Map customOptions = getCompilerOptions(); + customOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); + customOptions.put(CompilerOptions.OPTION_ReportRedundantNullCheck, CompilerOptions.ERROR); + runNegativeTest( + true, /* flushOutputDir */ + new String[] { + "X.java", + "public class X {\n" + + " void print4() {\n" + + " final String s1 = \"\";\n" + + " for (int i=0; i<4; i++)\n" + + " new Runnable() {\n" + + " public void run() {\n" + + " if (s1 != null)\n" + + " s1.toString();\n" + + " }\n" + + " }.run();\n" + + " }\n" + + " void print16(boolean b) {\n" + + " final String s3 = b ? null : \"\";\n" + + " for (int i=0; i<16; i++)\n" + + " new Runnable() {\n" + + " public void run() {\n" + + " s3.toString();\n" + + " }\n" + + " }.run();\n" + + " }\n" + + " void print23() {\n" + + " final String s23 = null;\n" + + " for (int i=0; i<23; i++)\n" + + " new Runnable() {\n" + + " public void run() {\n" + + " s23.toString();\n" + + " }\n" + + " }.run();\n" + + " }\n" + + "}\n", + + }, + null, /* classLibs */ + customOptions, + "----------\n" + + "1. ERROR in X.java (at line 7)\n" + + " if (s1 != null)\n" + + " ^^\n" + + "Redundant null check: The variable s1 cannot be null at this location\n" + + "----------\n" + + "2. ERROR in X.java (at line 17)\n" + + " s3.toString();\n" + + " ^^\n" + + "Potential null pointer access: The variable s3 may be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 26)\n" + + " s23.toString();\n" + + " ^^^\n" + + "Null pointer access: The variable s23 can only be null at this location\n" + + "----------\n", + "",/* expected output */ + "",/* expected error */ + JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); +} } \ No newline at end of file diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java index 228e6e8..995b093 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java @@ -12,8 +12,6 @@ import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.*; -import org.eclipse.jdt.internal.compiler.flow.FlowInfo; -import org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.codegen.*; @@ -68,8 +66,6 @@ throw new AbortMethod(this.compilationResult, problem); } } - - public abstract void analyseCode(ClassScope classScope, InitializationFlowContext initializationContext, FlowInfo info); /** * Bind and add argument's binding into the scope of the method diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java index 5b60ba3..5a5c24c 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java @@ -16,8 +16,8 @@ import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.flow.ExceptionHandlingFlowContext; +import org.eclipse.jdt.internal.compiler.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; -import org.eclipse.jdt.internal.compiler.flow.InitializationFlowContext; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.ClassScope; @@ -44,7 +44,7 @@ super(compilationResult); } - public void analyseCode(ClassScope classScope, InitializationFlowContext initializationContext, FlowInfo flowInfo) { + public void analyseCode(ClassScope classScope, FlowContext flowContext, FlowInfo flowInfo) { // starting of the code analysis for methods if (this.ignoreFurtherInvestigation) return; @@ -72,7 +72,7 @@ ExceptionHandlingFlowContext methodContext = new ExceptionHandlingFlowContext( - initializationContext, + flowContext, this, this.binding.thrownExceptions, null, diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java index b475932..b7ee3cf 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java @@ -684,8 +684,9 @@ if (method.ignoreFurtherInvestigation) continue; if (method.isInitializationMethod()) { + // pass down the appropriate initializerContext: if (method.isStatic()) { // - method.analyseCode( + ((Clinit)method).analyseCode( this.scope, staticInitializerContext, staticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo)); @@ -693,7 +694,9 @@ ((ConstructorDeclaration)method).analyseCode(this.scope, initializerContext, constructorInfo.copy(), flowInfo.reachMode()); } } else { // regular method - method.analyseCode(this.scope, null, flowInfo.copy()); + // pass down the parent flowContext UNLESS it is an initializer context: + FlowContext parentContext = (flowContext instanceof InitializationFlowContext) ? null : flowContext; + ((MethodDeclaration)method).analyseCode(this.scope, parentContext, flowInfo.copy()); } } }