Merge remote branch 'origin/master' into NullAnalysisForFields
diff --git a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/Options.java b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/Options.java
index 7b29f99..9dc391f 100644
--- a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/Options.java
+++ b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/Options.java
@@ -184,6 +184,7 @@
|| token.equals("noImplicitStringConversion")//$NON-NLS-1$
|| token.equals("null")//$NON-NLS-1$
|| token.equals("nullDereference")//$NON-NLS-1$
+ || token.equals("nullFields")//$NON-NLS-1$
|| token.equals("over-ann")//$NON-NLS-1$
|| token.equals("packageDefaultMethod")//$NON-NLS-1$
|| token.equals("paramAssign")//$NON-NLS-1$
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
index 3e46fd6..0328324 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -1668,7 +1668,7 @@
" allDeprecation deprecation including inside deprecated code\n" +
" allJavadoc invalid or missing javadoc\n" +
" allOver-ann all missing @Override annotations\n" +
- " all-static-method all method can be declared as static warnings\n" +
+ " all-static-method all method can be declared as static warnings\n" +
" assertIdentifier + ''assert'' used as identifier\n" +
" boxing autoboxing conversion\n" +
" charConcat + char[] in String concat\n" +
@@ -1703,6 +1703,7 @@
" noEffectAssign + assignment without effect\n" +
" null potential missing or redundant null check\n" +
" nullDereference + missing null check\n" +
+ " nullFields + null analysis for fields\n" +
" over-ann missing @Override annotation (superclass)\n" +
" paramAssign assignment to a parameter\n" +
" pkgDefaultMethod + attempt to override package-default method\n" +
@@ -1722,7 +1723,7 @@
" syntheticAccess synthetic access for innerclass\n" +
" tasks(<tags separated by |>) tasks identified by tags inside comments\n" +
" typeHiding + type parameter hiding another type\n" +
- " unavoidableGenericProblems + ignore unavoidable type safety problems\n" +
+ " unavoidableGenericProblems + ignore unavoidable type safety problems\n" +
" due to raw APIs\n" +
" unchecked + unchecked type operation\n" +
" unnecessaryElse unnecessary else clause\n" +
@@ -1740,7 +1741,7 @@
" unusedTypeArgs + unused type arguments for method and constructor\n" +
" uselessTypeCheck unnecessary cast/instanceof operation\n" +
" varargsCast + varargs argument need explicit cast\n" +
- " warningToken + unsupported or unnecessary @SuppressWarnings\n" +
+ " warningToken + unsupported or unnecessary @SuppressWarnings\n" +
"\n";
String expandedExpectedOutput =
MessageFormat.format(expectedOutput, new String[] {
@@ -1800,11 +1801,11 @@
" <argument value=\"---OUTPUT_DIR_PLACEHOLDER---\"/>\n" +
" </command_line>\n" +
" <options>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnull\" value=\"org.eclipse.jdt.annotation.NonNull\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault\" value=\"org.eclipse.jdt.annotation.NonNullByDefault\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullisdefault\" value=\"disabled\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullable\" value=\"org.eclipse.jdt.annotation.Nullable\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullanalysis\" value=\"disabled\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnull\" value=\"org.eclipse.jdt.annotation.NonNull\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault\" value=\"org.eclipse.jdt.annotation.NonNullByDefault\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullisdefault\" value=\"disabled\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullable\" value=\"org.eclipse.jdt.annotation.Nullable\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullanalysis\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.5\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.codegen.unusedLocal\" value=\"optimize out\"/>\n" +
@@ -1814,7 +1815,7 @@
" <option key=\"org.eclipse.jdt.core.compiler.debug.sourceFile\" value=\"generate\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.doc.comment.support\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.generateClassFiles\" value=\"enabled\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.maxProblemPerUnit\" value=\"100\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.maxProblemPerUnit\" value=\"100\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.annotationSuperInterface\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.assertIdentifier\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.autoboxing\" value=\"ignore\"/>\n" +
@@ -1835,6 +1836,7 @@
" <option key=\"org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.forbiddenReference\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock\" value=\"warning\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.includeFieldsInNullAnalysis\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch\" value=\"ignore\"/>\n" +
@@ -1863,18 +1865,18 @@
" <option key=\"org.eclipse.jdt.core.compiler.problem.noEffectAssignment\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral\" value=\"ignore\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.nullReference\" value=\"warning\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo\" value=\"warning\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.nullSpecViolation\" value=\"error\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.nullReference\" value=\"warning\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo\" value=\"warning\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.nullSpecViolation\" value=\"error\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.overridingMethodWithoutSuperInvocation\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.parameterAssignment\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.potentialNullReference\" value=\"ignore\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation\" value=\"error\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable\" value=\"ignore\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.rawTypeReference\" value=\"warning\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation\" value=\"warning\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation\" value=\"error\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable\" value=\"ignore\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.rawTypeReference\" value=\"warning\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.redundantNullCheck\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.redundantSuperinterface\" value=\"ignore\"/>\n" +
@@ -1887,7 +1889,7 @@
" <option key=\"org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.tasks\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.typeParameterHiding\" value=\"warning\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems\" value=\"enabled\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems\" value=\"enabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation\" value=\"warning\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.unclosedCloseable\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock\" value=\"ignore\"/>\n" +
@@ -1915,7 +1917,7 @@
" <option key=\"org.eclipse.jdt.core.compiler.source\" value=\"1.5\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.taskCaseSensitive\" value=\"enabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.taskPriorities\" value=\"\"/>\n" +
- " <option key=\"org.eclipse.jdt.core.compiler.taskTags\" value=\"\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.taskTags\" value=\"\"/>\n" +
" </options>\n" +
" <classpaths>NORMALIZED SECTION</classpaths>\n" +
" <sources>\n" +
@@ -12352,4 +12354,34 @@
"1 problem (1 warning)",
true);
}
+
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=325342
+// -warn option - regression tests to check option nulLFields
+// Null warnings should be flagged on fields
+public void test311_warn_options() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " void foo() {\n" +
+ " if (o == null && o.toString() == \"\"){}\n" +
+ " else {}\n" +
+ " o.toString();\n" + // toString() call above defuses null info, so no warning here
+ " }\n" +
+ "}\n",
+ },
+ "\"" + OUTPUT_DIR + File.separator + "X.java\""
+ + " -sourcepath \"" + OUTPUT_DIR + "\""
+ + " -warn:null,nullFields -proc:none -d \"" + OUTPUT_DIR + "\"",
+ "",
+ "----------\n" +
+ "1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X.java (at line 4)\n" +
+ " if (o == null && o.toString() == \"\"){}\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n" +
+ "1 problem (1 warning)",
+ true);
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
index 95fa1b1..670e436 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
@@ -724,6 +724,7 @@
expectedProblemAttributes.put("NonGenericConstructor", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("NonGenericMethod", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("NonGenericType", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
+ expectedProblemAttributes.put("NonNullFieldComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NonNullLocalVariableComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NonStaticAccessToStaticField", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
expectedProblemAttributes.put("NonStaticAccessToStaticMethod", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
@@ -736,6 +737,9 @@
expectedProblemAttributes.put("NotVisibleField", new ProblemAttributes(CategorizedProblem.CAT_MEMBER));
expectedProblemAttributes.put("NotVisibleMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER));
expectedProblemAttributes.put("NotVisibleType", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
+ expectedProblemAttributes.put("NullFieldComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("NullFieldInstanceofYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("NullFieldReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NullLocalVariableComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NullLocalVariableInstanceofYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NullLocalVariableReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
@@ -779,6 +783,7 @@
expectedProblemAttributes.put("PotentialHeapPollutionFromVararg", new ProblemAttributes(CategorizedProblem.CAT_UNCHECKED_RAW));
expectedProblemAttributes.put("PotentiallyUnclosedCloseable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("PotentiallyUnclosedCloseableAtExit", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("PotentialNullFieldReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("PotentialNullLocalVariableReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("PotentialNullMessageSendReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("PublicClassMustMatchFileName", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
@@ -788,10 +793,13 @@
expectedProblemAttributes.put("RedefinedArgument", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
expectedProblemAttributes.put("RedefinedLocal", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
expectedProblemAttributes.put("RedundantSpecificationOfTypeArguments", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
+ expectedProblemAttributes.put("RedundantFieldNullAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullAnnotation", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
+ expectedProblemAttributes.put("RedundantNullCheckOnNonNullField", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullMessageSend", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("RedundantNullCheckOnNullField", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantSuperinterface", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
expectedProblemAttributes.put("ReferenceToForwardField", new ProblemAttributes(CategorizedProblem.CAT_MEMBER));
@@ -1414,6 +1422,7 @@
expectedProblemAttributes.put("NonGenericConstructor", SKIP);
expectedProblemAttributes.put("NonGenericMethod", SKIP);
expectedProblemAttributes.put("NonGenericType", SKIP);
+ expectedProblemAttributes.put("NonNullFieldComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("NonNullLocalVariableComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("NonStaticAccessToStaticField", new ProblemAttributes(JavaCore.COMPILER_PB_STATIC_ACCESS_RECEIVER));
expectedProblemAttributes.put("NonStaticAccessToStaticMethod", new ProblemAttributes(JavaCore.COMPILER_PB_STATIC_ACCESS_RECEIVER));
@@ -1426,6 +1435,9 @@
expectedProblemAttributes.put("NotVisibleField", SKIP);
expectedProblemAttributes.put("NotVisibleMethod", SKIP);
expectedProblemAttributes.put("NotVisibleType", SKIP);
+ expectedProblemAttributes.put("NullFieldComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
+ expectedProblemAttributes.put("NullFieldInstanceofYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
+ expectedProblemAttributes.put("NullFieldReference", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_REFERENCE));
expectedProblemAttributes.put("NullLocalVariableComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("NullLocalVariableInstanceofYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("NullLocalVariableReference", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_REFERENCE));
@@ -1469,6 +1481,7 @@
expectedProblemAttributes.put("PotentialHeapPollutionFromVararg", new ProblemAttributes(JavaCore.COMPILER_PB_UNCHECKED_TYPE_OPERATION));
expectedProblemAttributes.put("PotentiallyUnclosedCloseable", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE));
expectedProblemAttributes.put("PotentiallyUnclosedCloseableAtExit", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE));
+ expectedProblemAttributes.put("PotentialNullFieldReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE));
expectedProblemAttributes.put("PotentialNullLocalVariableReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE));
expectedProblemAttributes.put("PotentialNullMessageSendReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE));
expectedProblemAttributes.put("PublicClassMustMatchFileName", SKIP);
@@ -1478,10 +1491,13 @@
expectedProblemAttributes.put("RedefinedArgument", SKIP);
expectedProblemAttributes.put("RedefinedLocal", SKIP);
expectedProblemAttributes.put("RedundantSpecificationOfTypeArguments", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_TYPE_ARGUMENTS));
+ expectedProblemAttributes.put("RedundantFieldNullAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION));
+ expectedProblemAttributes.put("RedundantNullCheckOnNonNullField", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullMessageSend", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
+ expectedProblemAttributes.put("RedundantNullCheckOnNullField", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantSuperinterface", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_SUPERINTERFACE));
expectedProblemAttributes.put("ReferenceToForwardField", SKIP);
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 4e75828..669345c 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2011 IBM Corporation and others.
+ * Copyright (c) 2005, 2012 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,7 +31,7 @@
public class FlowAnalysisTest extends AbstractRegressionTest {
static {
-// TESTS_NAMES = new String[] { "testLocalClassInInitializer1" };
+// TESTS_NAMES = new String[] { "testInnerClassesWithFields1" };
// TESTS_NUMBERS = new int[] { 69 };
}
public FlowAnalysisTest(String name) {
@@ -2496,6 +2496,119 @@
"continue cannot be used outside of a loop\n" +
"----------\n");
}
+// final field in anonymous nested class
+// witness a regression during working on Bug 247564 - [compiler][null] Detecting null field reference
+public void testFinalFieldInNested1() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " void print4() {\n" +
+ " for (int i=0; i<4; i++)\n" +
+ " new Runnable() {\n" +
+ " final String s1local;\n" +
+ " public void run() {\n" +
+ " s1local.toString();\n" +
+ " }\n" +
+ " }.run();\n" +
+ " }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 4)\n" +
+ " new Runnable() {\n" +
+ " ^^^^^^^^^^\n" +
+ "The blank final field s1local may not have been initialized\n" +
+ "----------\n");
+}
+// witness a regression during working on Bug 247564 - [compiler][null] Detecting null field reference
+// local variable in inner class triggered IAE in StackMapFrame.addStackItem
+public void testInnerClassesWithFields1() {
+ if (this.complianceLevel >= ClassFileConstants.JDK1_6) { // we're specifically interested in StackMap generation
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " int f1, f2, f3;\n" +
+ " class I1 {\n" +
+ " int f4;\n" +
+ " String m(boolean b) {\n" +
+ " String l1 = \"Hello\";\n" +
+ " if (b) {\n" +
+ " l1 += \" world!\";\n" +
+ " } else {\n" +
+ " l1 += \" test.\";\n" +
+ " }\n" +
+ " return l1;\n" +
+ " }\n" +
+ " }\n" +
+ " class I2 {\n" +
+ " int f5, f6, f7;\n" +
+ " }\n" +
+ "}\n"
+ },
+ "");
+ }
+}
+// witness a regression during working on Bug 247564 - [compiler][null] Detecting null field reference
+// local variable in local class triggered IAE in StackMapFrame.addStackItem
+public void testInnerClassesWithFields1a() {
+ if (this.complianceLevel >= ClassFileConstants.JDK1_6) { // we're specifically interested in StackMap generation
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " int f1, f2, f3;\n" +
+ " void foo() {\n" +
+ " class I1 {\n" +
+ " int f4;\n" +
+ " String m(boolean b) {\n" +
+ " String l1 = \"Hello\";\n" +
+ " if (b) {\n" +
+ " l1 += \" world!\";\n" +
+ " } else {\n" +
+ " l1 += \" test.\";\n" +
+ " }\n" +
+ " return l1;\n" +
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ " class I2 {\n" +
+ " int f5, f6, f7;\n" +
+ " }\n" +
+ "}\n"
+ },
+ "");
+ }
+}
+// witness a regression during working on Bug 247564 - [compiler][null] Detecting null field reference
+// final fields in parameterized nested were reported as uninitialized
+public void testInnerClassesWithFields2() {
+ if (this.complianceLevel >= ClassFileConstants.JDK1_5) {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final String f1 = \"f1\";\n" +
+ " static final String f2 = \"f2\";\n" +
+ " String f3;\n" +
+ " X() {\n" +
+ " f3 = \"f3\";\n" +
+ " }\n" +
+ " protected abstract class I<T> {\n" +
+ " final String f5, f6;\n" +
+ " T f7;\n" +
+ " I(T a) {\n" +
+ " f5 = \"f5\";\n" +
+ " f6 = \"f6\";\n" +
+ " f7 = a;\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"
+ },
+ "");
+ }
+}
public static Class testClass() {
return FlowAnalysisTest.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InitializationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InitializationTests.java
index 23d9410..0309b5f 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InitializationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/InitializationTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2010, 2011 IBM Corporation and others.
+ * Copyright (c) 2010, 2012 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
@@ -464,6 +464,44 @@
"The local variable b2 may not have been initialized\n" +
"----------\n");
}
+// https://bugs.eclipse.org/bugs/show_bug.cgi?id=247564
+// Change in the way field id's are calculated should not affect initializations
+public void testBug247564j() {
+ this.runNegativeTest(
+ new String[] {
+ "Z.java",
+ "public class Z {\n" +
+ " final int field1 = 0;\n" +
+ " {\n" +
+ " class ZInner {\n" +
+ " final int fieldz1;\n" +
+ " final int fieldz2 = 0;\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. WARNING in Z.java (at line 4)\n" +
+ " class ZInner {\n" +
+ " ^^^^^^\n" +
+ "The type ZInner is never used locally\n" +
+ "----------\n" +
+ "2. ERROR in Z.java (at line 4)\n" +
+ " class ZInner {\n" +
+ " ^^^^^^\n" +
+ "The blank final field fieldz1 may not have been initialized\n" +
+ "----------\n" +
+ "3. WARNING in Z.java (at line 5)\n" +
+ " final int fieldz1;\n" +
+ " ^^^^^^^\n" +
+ "The value of the field ZInner.fieldz1 is not used\n" +
+ "----------\n" +
+ "4. WARNING in Z.java (at line 6)\n" +
+ " final int fieldz2 = 0;\n" +
+ " ^^^^^^^\n" +
+ "The value of the field ZInner.fieldz2 is not used\n" +
+ "----------\n"
+ );
+}
public static Class testClass() {
return InitializationTests.class;
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java
index 95af131..2bb4c61 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2010 IBM Corporation and others.
+ * Copyright (c) 2005, 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
@@ -37,6 +37,7 @@
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
/**
* A tests series especially meant to validate the internals of our null
@@ -1083,18 +1084,21 @@
return copy;
}
-public void markAsDefinitelyNonNull(LocalVariableBinding local) {
- grow(local.id + this.maxFieldCount);
+public void markAsDefinitelyNonNull(VariableBinding local) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ grow(position);
super.markAsDefinitelyNonNull(local);
}
-public void markAsDefinitelyNull(LocalVariableBinding local) {
- grow(local.id + this.maxFieldCount);
+public void markAsDefinitelyNull(VariableBinding local) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ grow(position);
super.markAsDefinitelyNull(local);
}
-public void markAsDefinitelyUnknown(LocalVariableBinding local) {
- grow(local.id + this.maxFieldCount);
+public void markAsDefinitelyUnknown(VariableBinding local) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ grow(position);
super.markAsDefinitelyUnknown(local);
}
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 e24388f..22562bf 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
@@ -49,7 +49,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[] { "testBug360328" };
+// TESTS_NAMES = new String[] { "testBug247564k_3" };
// TESTS_NUMBERS = new int[] { 561 };
// TESTS_RANGE = new int[] { 1, 2049 };
}
@@ -72,6 +72,7 @@
defaultOptions.put(CompilerOptions.OPTION_ReportRedundantNullCheck, CompilerOptions.ERROR);
defaultOptions.put(CompilerOptions.OPTION_ReportRawTypeReference, CompilerOptions.IGNORE);
defaultOptions.put(CompilerOptions.OPTION_IncludeNullInfoFromAsserts, CompilerOptions.ENABLED);
+ defaultOptions.put(CompilerOptions.OPTION_IncludeFieldsInNullAnalysis, CompilerOptions.ENABLED);
}
return defaultOptions;
}
@@ -109,13 +110,12 @@
" o.toString();\n" +
" }\n" +
"}\n"},
- ""
-// "----------\n" +
-// "1. ERROR in X.java (at line 5)\n" +
-// " o.toString();\n" +
-// " ^\n" +
-// "The field o is likely null; it was either set to null or checked for null when last used\n" +
-// "----------\n"
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
);
}
@@ -346,13 +346,12 @@
" this.o.toString();\n" +
" }\n" +
"}\n"},
- ""
-// "----------\n" +
-// "1. ERROR in X.java (at line 5)\n" +
-// " this.o.toString();\n" +
-// " ^^^^^^\n" +
-// "The field o is likely null; it was either set to null or checked for null when last used\n" +
-// "----------\n"
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " this.o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
);
}
@@ -368,13 +367,12 @@
" o.toString();\n" +
" }\n" +
"}\n"},
- ""
-// "----------\n" +
-// "1. ERROR in X.java (at line 5)\n" +
-// " o.toString();\n" +
-// " ^\n" +
-// "The field o is likely null; it was either set to null or checked for null when last used\n" +
-// "----------\n"
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
);
}
@@ -424,13 +422,12 @@
" }\n" +
" }\n" +
"}\n"},
- ""
-// "----------\n" +
-// "1. ERROR in X.java (at line 6)\n" +
-// " X.this.o.toString();\n" +
-// " ^^^^^^^^\n" +
-// "The field o is likely null; it was either set to null or checked for null when last used\n" +
-// "----------\n"
+ "----------\n" +
+ "1. ERROR in X.java (at line 6)\n" +
+ " X.this.o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
);
}
@@ -449,13 +446,12 @@
" }\n" +
" void bar() {/* */}\n" +
"}\n"},
- ""
-// "----------\n" +
-// "1. ERROR in X.java (at line 5)\n" +
-// " o.toString();\n" +
-// " ^\n" +
-// "The field o is likely null; it was either set to null or checked for null when last used\n" +
-// "----------\n"
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
);
}
@@ -3099,7 +3095,7 @@
// null analysis -- while
public void test0413_while_unknown_field() {
- this.runConformTest(
+ this.runNegativeTest(
new String[] {
"X.java",
"public class X {\n" +
@@ -3111,7 +3107,12 @@
" o.toString();\n" +
" }\n" +
"}\n"},
- "");
+ "----------\n" +
+ "1. ERROR in X.java (at line 7)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n");
}
// null analysis -- while
@@ -15453,4 +15454,2255 @@
"",/* expected error */
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
-}
\ No newline at end of file
+
+// null analysis -- simple case for field
+public void testBug247564a() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " void foo() {\n" +
+ " if (o == null && o.toString() == \"\"){}\n" +
+ " else {}\n" +
+ " o.toString();\n" + // toString() call above defuses null info, so no warning here
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 4)\n" +
+ " if (o == null && o.toString() == \"\"){}\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for field
+// no redundant null check warnings should be obtained since value of field
+// may be changed in another thread.
+public void testBug247564a_1() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " void foo() {\n" +
+ " o = null;" +
+ " if (o == null){}\n" +
+ " if (o != null){}\n" +
+ " o.toString();\n" + // warn here
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 6)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for field
+public void testBug247564a_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " void foo() {\n" +
+ " if (o == null){\n" + // o is null inside the if block
+ " o.toString();\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for field
+// null info from one method should not be present in the other (for instance fields)
+public void testBug247564a_3() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " void foo() {\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " o.toString();\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+//null analysis -- simple case for field
+public void testBug247564a_4() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " int foo() {\n" +
+ " if (o != null && o.toString() == \"\") {\n" +
+ " } else {\n" +
+ " }\n" +
+ " return o.hashCode();\n" + // the above check has shed doubts so give a warning here
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 7)\n" +
+ " return o.hashCode();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- field accessed inside control structure
+// from https://bugs.eclipse.org/bugs/show_bug.cgi?id=247564#c121
+public void testBug247564a_5() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private Object field;\n" +
+ " void goo(Object var) throws Exception{\n" +
+ " if (field != null) field.hashCode();\n" +
+ " int i = 20;\n" +
+ " while (i<10) {\n" +
+ " if (field == null) { \n" +
+ " field = new Object();\n" +
+ " }\n" +
+ " field.toString(); //Pot. NPE\n" +
+ " i--;\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- simple case for static final field
+// once dereferenced, treat as non null. Consistent with local variables.
+public void testBug247564b() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o = null;\n" +
+ " static final Object o1 = new Object();\n" +
+ " void foo() {\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " if (o == null) {}\n" +
+ " if (o != null) {}\n" +
+ " if (o1 == null) {}\n" +
+ " if (o1 != null) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 6)\n" +
+ " if (o == null) {}\n" +
+ " ^\n" +
+ "Null comparison always yields false: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "3. WARNING in X.java (at line 6)\n" +
+ " if (o == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 7)\n" +
+ " if (o != null) {}\n" +
+ " ^\n" +
+ "Redundant null check: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 8)\n" +
+ " if (o1 == null) {}\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o1 cannot be null at this location\n" +
+ "----------\n" +
+ "6. WARNING in X.java (at line 8)\n" +
+ " if (o1 == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "7. ERROR in X.java (at line 9)\n" +
+ " if (o1 != null) {}\n" +
+ " ^^\n" +
+ "Redundant null check: The field o1 cannot be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for static final field
+public void testBug247564b_1() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1;\n" +
+ " static {\n" +
+ " o = null;\n" +
+ " o1 = new Object();\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " if (o == null) {}\n" +
+ " if (o != null) {}\n" +
+ " if (o1 == null) {}\n" +
+ " if (o1 != null) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 9)\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 10)\n" +
+ " if (o == null) {}\n" +
+ " ^\n" +
+ "Null comparison always yields false: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "3. WARNING in X.java (at line 10)\n" +
+ " if (o == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 11)\n" +
+ " if (o != null) {}\n" +
+ " ^\n" +
+ "Redundant null check: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 12)\n" +
+ " if (o1 == null) {}\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o1 cannot be null at this location\n" +
+ "----------\n" +
+ "6. WARNING in X.java (at line 12)\n" +
+ " if (o1 == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "7. ERROR in X.java (at line 13)\n" +
+ " if (o1 != null) {}\n" +
+ " ^^\n" +
+ "Redundant null check: The field o1 cannot be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for static final field
+public void testBug247564b_1_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o;\n" +
+ " static final Object o1;\n" +
+ " static {\n" +
+ " o = null;\n" +
+ " o1 = new Object();\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " if (o == null) {}\n" +
+ " if (o != null) {}\n" +
+ " if (o1 == null) {}\n" +
+ " if (o1 != null) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 35)\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 36)\n" +
+ " if (o == null) {}\n" +
+ " ^\n" +
+ "Null comparison always yields false: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "3. WARNING in X.java (at line 36)\n" +
+ " if (o == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 37)\n" +
+ " if (o != null) {}\n" +
+ " ^\n" +
+ "Redundant null check: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 38)\n" +
+ " if (o1 == null) {}\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o1 cannot be null at this location\n" +
+ "----------\n" +
+ "6. WARNING in X.java (at line 38)\n" +
+ " if (o1 == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "7. ERROR in X.java (at line 39)\n" +
+ " if (o1 != null) {}\n" +
+ " ^^\n" +
+ "Redundant null check: The field o1 cannot be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// checked before use
+public void testBug247564b_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " if (o == null) \n" +
+ " return;\n" +
+ " o.toString();\n" + // cant be null for sure, dont complain
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// checked before use
+public void testBug247564b_2_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " if (o == null) \n" +
+ " return;\n" +
+ " o.toString();\n" + // cant be null for sure, dont complain
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// Check pot. NPE case for constant fields
+// Once dereferenced, treat as non null. Just like locals.
+public void testBug247564b_3() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " if (o == null) {}\n" +
+ " if (o != null) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 12)\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 13)\n" +
+ " if (o == null) {}\n" +
+ " ^\n" +
+ "Null comparison always yields false: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "3. WARNING in X.java (at line 13)\n" +
+ " if (o == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 14)\n" +
+ " if (o != null) {}\n" +
+ " ^\n" +
+ "Redundant null check: The field o cannot be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// Check pot. NPE case for constant fields
+// Once dereferenced, treat as non null. Just like locals.
+public void testBug247564b_3_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo() {\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " if (o == null) {}\n" +
+ " if (o != null) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 38)\n" +
+ " if (o.toString() == \"\") {}\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 39)\n" +
+ " if (o == null) {}\n" +
+ " ^\n" +
+ "Null comparison always yields false: The field o cannot be null at this location\n" +
+ "----------\n" +
+ "3. WARNING in X.java (at line 39)\n" +
+ " if (o == null) {}\n" +
+ " ^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 40)\n" +
+ " if (o != null) {}\n" +
+ " ^\n" +
+ "Redundant null check: The field o cannot be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// checked before use
+public void testBug247564b_4() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " if (o == null) {\n" +
+ " o.toString(); // danger" +
+ " return;\n" +
+ " }\n" +
+ " o.toString(); // safe\n" +
+ " }\n" +
+ " void foo2() {\n" +
+ " if (o != null) {\n" +
+ " o.toString(); // safe (2)\n" +
+ " }\n" +
+ " o.toString(); // uncertain\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 13)\n" +
+ " o.toString(); // danger return;\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 21)\n" +
+ " o.toString(); // uncertain\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// checked before use
+public void testBug247564b_4_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " if (o == null) {\n" +
+ " o.toString(); // danger" +
+ " return;\n" +
+ " }\n" +
+ " o.toString(); // safe\n" +
+ " }\n" +
+ " void foo2() {\n" +
+ " if (o != null) {\n" +
+ " o.toString(); // safe (2)\n" +
+ " }\n" +
+ " o.toString(); // uncertain\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 39)\n" +
+ " o.toString(); // danger return;\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 47)\n" +
+ " o.toString(); // uncertain\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// check if the resetting works properly i.e. null status for constant fields should not be
+// reset on method calls.
+public void testBug247564b_5() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static final Object o2 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " final Object local = null;\n" +
+ " if (local == null) {\n" +
+ " local.toString();\n" +
+ " }\n" +
+ " local.toString();\n" +
+ " if (o == null) {\n" + // don't know o's nullness, so silent
+ " o.toString();\n" + // report NPE
+ " }\n" +
+ " o.toString();\n" + // already reported NPE above. So silent. Same behaviour as 'local'
+ " if (o2 == null) {\n" + // report always false null check
+ " o2.toString();\n" + // dead code
+ " }\n" +
+ " o2.toString();" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 14)\n" +
+ " if (local == null) {\n" +
+ " ^^^^^\n" +
+ "Redundant null check: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 15)\n" +
+ " local.toString();\n" +
+ " ^^^^^\n" +
+ "Null pointer access: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 19)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 22)\n" +
+ " if (o2 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 cannot be null at this location\n" +
+ "----------\n" +
+ "5. WARNING in X.java (at line 22)\n" +
+ " if (o2 == null) {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n"
+ );
+}
+
+//null analysis -- case for static final field initialized inside static block with different values
+//check if the resetting works properly i.e. null status for constant fields should not be
+//reset on method calls. This test is for constructors
+public void testBug247564b_6() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static final Object o2 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " public X() {\n" +
+ " final Object local = null;\n" +
+ " if (local == null) {\n" +
+ " local.toString();\n" +
+ " }\n" +
+ " local.toString();\n" +
+ " if (o == null) {\n" + // don't know o's nullness, so silent
+ " o.toString();\n" + // report NPE
+ " }\n" +
+ " o.toString();\n" + // already reported NPE above. So silent. Same behaviour as 'local'
+ " if (o2 == null) {\n" + // report always false null check
+ " o2.toString();\n" + // dead code
+ " }\n" +
+ " o2.toString();" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 14)\n" +
+ " if (local == null) {\n" +
+ " ^^^^^\n" +
+ "Redundant null check: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 15)\n" +
+ " local.toString();\n" +
+ " ^^^^^\n" +
+ "Null pointer access: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 19)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 22)\n" +
+ " if (o2 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 cannot be null at this location\n" +
+ "----------\n" +
+ "5. WARNING in X.java (at line 22)\n" +
+ " if (o2 == null) {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n"
+ );
+}
+
+//null analysis -- case for static final field initialized inside static block with different values
+//check if the resetting works properly i.e. null status for constant fields should not be
+//reset on method calls. This test is for constructors
+public void testBug247564b_6_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = new Object();\n" +
+ " static final Object o2 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " public X() {\n" +
+ " final Object local = null;\n" +
+ " if (local == null) {\n" +
+ " local.toString();\n" +
+ " }\n" +
+ " local.toString();\n" +
+ " if (o == null) {\n" + // don't know o's nullness, so silent
+ " o.toString();\n" + // report NPE
+ " }\n" +
+ " o.toString();\n" + // already reported NPE above. So silent. Same behaviour as 'local'
+ " if (o2 == null) {\n" + // report always false null check
+ " o2.toString();\n" + // dead code
+ " }\n" +
+ " o2.toString();" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 40)\n" +
+ " if (local == null) {\n" +
+ " ^^^^^\n" +
+ "Redundant null check: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 41)\n" +
+ " local.toString();\n" +
+ " ^^^^^\n" +
+ "Null pointer access: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 45)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Null pointer access: The field o can only be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 48)\n" +
+ " if (o2 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 cannot be null at this location\n" +
+ "----------\n" +
+ "5. WARNING in X.java (at line 48)\n" +
+ " if (o2 == null) {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// check if the resetting works properly i.e. null status for constant fields should not be
+// reset on method calls. Also, null info of constant field from static block is available in methods
+public void testBug247564b_7() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o;\n" +
+ " static final Object o1 = null;\n" +
+ " static final Object o2 = new Object();\n" +
+ " static {\n" +
+ " if (o1.hashCode() == 2){\n" + // report NPE. But dereferenced here, so later it should be treated as non null
+ " o = new Object();\n" +
+ " } else {\n" +
+ " o = null;\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " final Object local = null;\n" +
+ " if (local == null) {\n" +
+ " local.toString();\n" +
+ " }\n" +
+ " local.toString();\n" +
+ " if (o1 == null) {\n" + // can't be null, was dereferenced in static initializer
+ " o1.toString();\n" + // dead
+ " }\n" +
+ " o1.toString();\n" + // safe
+ " if (o2 == null) {\n" + // report always false null check
+ " o2.toString();\n" + // dead code
+ " }\n" +
+ " o2.toString();" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 6)\n" +
+ " if (o1.hashCode() == 2){\n" +
+ " ^^\n" +
+ "Null pointer access: The field o1 can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 14)\n" +
+ " if (local == null) {\n" +
+ " ^^^^^\n" +
+ "Redundant null check: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 15)\n" +
+ " local.toString();\n" +
+ " ^^^^^\n" +
+ "Null pointer access: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 18)\n" +
+ " if (o1 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o1 cannot be null at this location\n" +
+ "----------\n" +
+ "5. WARNING in X.java (at line 18)\n" +
+ " if (o1 == null) {\n" +
+ " o1.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 22)\n" +
+ " if (o2 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 cannot be null at this location\n" +
+ "----------\n" +
+ "7. WARNING in X.java (at line 22)\n" +
+ " if (o2 == null) {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- case for static final field initialized inside static block with different values
+// check if the resetting works properly i.e. null status for constant fields should not be
+// reset on method calls. This test is for more than 64 fields to check for extra bits.
+public void testBug247564b_8() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ false,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ "static final Object o1 = null;\n" +
+ "static final Object o2 = new Object();\n" +
+ " void foo1() {\n" +
+ " final Object local = null;\n" +
+ " if (local == null) {\n" +
+ " local.toString();\n" +
+ " }\n" +
+ " local.toString();\n" +
+ " if (o1 == null) {\n" + // report redundant null check
+ " o1.toString();\n" + // report NPE
+ " }\n" +
+ " o1.toString();\n" + // already reported NPE above. So silent. Same behaviour as 'local'
+ " if (o2 == null) {\n" + // report always false null check
+ " o2.toString();\n" + // dead code
+ " }\n" +
+ " o2.toString();" +
+ " }\n" +
+ "}\n"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 32)\n" +
+ " if (local == null) {\n" +
+ " ^^^^^\n" +
+ "Redundant null check: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 33)\n" +
+ " local.toString();\n" +
+ " ^^^^^\n" +
+ "Null pointer access: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 36)\n" +
+ " if (o1 == null) {\n" +
+ " ^^\n" +
+ "Redundant null check: The field o1 can only be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 37)\n" +
+ " o1.toString();\n" +
+ " ^^\n" +
+ "Null pointer access: The field o1 can only be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 40)\n" +
+ " if (o2 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 cannot be null at this location\n" +
+ "----------\n" +
+ "6. WARNING in X.java (at line 40)\n" +
+ " if (o2 == null) {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
+
+// null analysis -- case for static final field initialized inside static block where some locals are also present
+// check if the resetting works properly
+public void testBug247564b_9() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ false,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o1;\n" +
+ " static final Object o2 = new Object();\n" +
+ " static {\n" +
+ " int i = 10;\n" +
+ " o1 = null;\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " final Object local = null;\n" +
+ " if (local == null) {\n" +
+ " local.toString();\n" +
+ " }\n" +
+ " local.toString();\n" +
+ " if (o1 == null) {\n" + // report redundant null check
+ " o1.toString();\n" + // report NPE
+ " }\n" +
+ " o1.toString();\n" + // already reported NPE above. So silent. Same behaviour as 'local'
+ " if (o2 == null) {\n" + // report always false null check
+ " o2.toString();\n" + // dead code
+ " }\n" +
+ " o2.toString();" +
+ " }\n" +
+ "}\n"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 10)\n" +
+ " if (local == null) {\n" +
+ " ^^^^^\n" +
+ "Redundant null check: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 11)\n" +
+ " local.toString();\n" +
+ " ^^^^^\n" +
+ "Null pointer access: The variable local can only be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 14)\n" +
+ " if (o1 == null) {\n" +
+ " ^^\n" +
+ "Redundant null check: The field o1 can only be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 15)\n" +
+ " o1.toString();\n" +
+ " ^^\n" +
+ "Null pointer access: The field o1 can only be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 18)\n" +
+ " if (o2 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 cannot be null at this location\n" +
+ "----------\n" +
+ "6. WARNING in X.java (at line 18)\n" +
+ " if (o2 == null) {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " ^^^^^^^^^^^^^^^^^^^^^^\n" +
+ "Dead code\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
+
+// null analysis -- fields in synchronized methods
+// check that null analysis for fields in synchronized methods
+// behave as it does in ordinary methods.
+public void testBug247564c() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " Object o1;\n" +
+ " static final Object o2 = null;\n" +
+ " static final Object o3 = new Object();\n" +
+ " synchronized void foo() {\n" +
+ " o = null;\n" +
+ " if (o == null) {\n" +
+ " o.toString();\n" +
+ " }\n" +
+ " o1 = new Object();\n" +
+ " if (o1 == null) {\n" +
+ " o1.toString();\n" +
+ " }\n" +
+ " if (o2 != null) {\n" +
+ " }\n" +
+ " else {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " if (o3 == null) {\n" +
+ " }\n" +
+ " else {\n" +
+ " o3.toString();\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " o.toString();\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 9)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 13)\n" +
+ " o1.toString();\n" +
+ " ^^\n" +
+ "Potential null pointer access: The field o1 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 15)\n" +
+ " if (o2 != null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 can only be null at this location\n" +
+ "----------\n" +
+ "4. WARNING in X.java (at line 15)\n" +
+ " if (o2 != null) {\n" +
+ " }\n" +
+ " ^^^^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 18)\n" +
+ " o2.toString();\n" +
+ " ^^\n" +
+ "Null pointer access: The field o2 can only be null at this location\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 20)\n" +
+ " if (o3 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o3 cannot be null at this location\n" +
+ "----------\n" +
+ "7. WARNING in X.java (at line 20)\n" +
+ " if (o3 == null) {\n" +
+ " }\n" +
+ " ^^^^^\n" +
+ "Dead code\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- fields in synchronized methods
+// check that null analysis for fields in synchronized methods
+// behave as it does in ordinary methods. Higher no. of fields
+public void testBug247564c_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " Object o1;\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o2 = null;\n" +
+ " static final Object o3 = new Object();\n" +
+ " synchronized void foo() {\n" +
+ " o = null;\n" +
+ " if (o == null) {\n" +
+ " o.toString();\n" +
+ " }\n" +
+ " o1 = new Object();\n" +
+ " if (o1 == null) {\n" +
+ " o1.toString();\n" +
+ " }\n" +
+ " if (o2 != null) {\n" +
+ " }\n" +
+ " else {\n" +
+ " o2.toString();\n" +
+ " }\n" +
+ " if (o3 == null) {\n" +
+ " }\n" +
+ " else {\n" +
+ " o3.toString();\n" +
+ " }\n" +
+ " }\n" +
+ " void foo1() {\n" +
+ " o.toString();\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 35)\n" +
+ " o.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 39)\n" +
+ " o1.toString();\n" +
+ " ^^\n" +
+ "Potential null pointer access: The field o1 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 41)\n" +
+ " if (o2 != null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o2 can only be null at this location\n" +
+ "----------\n" +
+ "4. WARNING in X.java (at line 41)\n" +
+ " if (o2 != null) {\n" +
+ " }\n" +
+ " ^^^^^\n" +
+ "Dead code\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 44)\n" +
+ " o2.toString();\n" +
+ " ^^\n" +
+ "Null pointer access: The field o2 can only be null at this location\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 46)\n" +
+ " if (o3 == null) {\n" +
+ " ^^\n" +
+ "Null comparison always yields false: The field o3 cannot be null at this location\n" +
+ "----------\n" +
+ "7. WARNING in X.java (at line 46)\n" +
+ " if (o3 == null) {\n" +
+ " }\n" +
+ " ^^^^^\n" +
+ "Dead code\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- test redundant instanceof warning for static final field
+public void testBug247564d() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o = null;\n" +
+ " static final Object o1 = new Object();\n" +
+ " void foo() {\n" +
+ " if (o instanceof String) {}\n" +
+ " if (o1 instanceof String) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 5)\n" +
+ " if (o instanceof String) {}\n" +
+ " ^\n" +
+ "instanceof always yields false: The field o can only be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- test redundant instanceof warning for static final field. More fields
+public void testBug247564d_1() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ " static final Object o = null;\n" +
+ " static final Object o1 = new Object();\n" +
+ " void foo() {\n" +
+ " if (o instanceof String) {}\n" +
+ " if (o1 instanceof String) {}\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 31)\n" +
+ " if (o instanceof String) {}\n" +
+ " ^\n" +
+ "instanceof always yields false: The field o can only be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- test redundant instanceof warning for static final fields
+public void testBug247564e_1() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static final Object o = null;\n" +
+ " void foo() {\n" +
+ " if (o instanceof X) return;\n" +
+ " }\n" +
+ "}"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 4)\n" +
+ " if (o instanceof X) return;\n" +
+ " ^\n" +
+ "instanceof always yields false: The field o can only be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
+
+// null analysis -- test potential null ptr access warning because of static field access through object returned by method call
+public void testBug247564f() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ false,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static Object o;\n" +
+ " static Object o1;\n" +
+ " Object o2;\n" +
+ " X getX() { return new X();\n}\n" +
+ " void foo() {\n" +
+ " if (getX().o == null && this.o.hashCode() == 0) return;\n" +
+ " if (getX().o2 == null && this.o2.hashCode() == 0) return;\n" +
+ " }\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 8)\n" +
+ " if (getX().o == null && this.o.hashCode() == 0) return;\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
+
+// null analysis -- test potential null ptr access warning because of static field access through object returned by method call
+public void testBug247564f_1() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ false,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static Object o;\n" +
+ " X getX() { return new X();\n}\n" +
+ " Y getY() { return new Y();\n}\n" +
+ " void foo() {\n" +
+ " if (getY().o == null && this.o.hashCode() == 0) return;\n" +
+ " if (getX().o == null && this.o.hashCode() == 0) return;\n" +
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " static Object o;\n" +
+ "}\n"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 9)\n" +
+ " if (getX().o == null && this.o.hashCode() == 0) return;\n" +
+ " ^\n" +
+ "Potential null pointer access: The field o may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
+
+// null analysis -- test field analysis in case of more than 64 fields
+public void testBug247564g() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ false,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ "Object field0, \n" +
+ "field1, field2, field3, field4, \n" +
+ "field5, field6, field7, field8, \n" +
+ "field9, field10, field11, field12, \n" +
+ "field13, field14, field15, field16, \n" +
+ "field17, field18, field19, field20, \n" +
+ "field21, field22, field23, field24, \n" +
+ "field25, field26, field27, field28, \n" +
+ "field29, field30, field31, field32, \n" +
+ "field33, field34, field35, field36, \n" +
+ "field37, field38, field39, field40, \n" +
+ "field41, field42, field43, field44, \n" +
+ "field45, field46, field47, field48, \n" +
+ "field49, field50, field51, field52, \n" +
+ "field53, field54, field55, field56, \n" +
+ "field57, field58, field59, field60, \n" +
+ "field61, field62, field63, field64, \n" +
+ "field65, field66, field67, field68, \n" +
+ "field69, field70, field71, field72, \n" +
+ "field73, field74, field75, field76, \n" +
+ "field77, field78, field79, field80, \n" +
+ "field81, field82, field83, field84, \n" +
+ "field85, field86, field87, field88, \n" +
+ "field89, field90, field91, field92, \n" +
+ "field93, field94, field95, field96, \n" +
+ "field97, field98, field99;\n" +
+ "static final Object field100 = null;\n" +
+ " void foo() {\n" +
+ " int i = 0;" +
+ " while (i<10){\n" +
+ " i++;\n" +
+ " if (this.field99 == null && this.field99.hashCode() == 0){}\n" +
+ " this.field98 = null;\n" +
+ " }\n" +
+ " if (this.field98.hashCode() == 0) {}\n" + // should not complain
+ " this.field97 = null;\n" +
+ " if (this.field97.hashCode() == 0) {}\n" +
+ " if (this.field100.hashCode() == 0) {}\n" +
+ " }\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 32)\n" +
+ " if (this.field99 == null && this.field99.hashCode() == 0){}\n" +
+ " ^^^^^^^\n" +
+ "Potential null pointer access: The field field99 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 35)\n" +
+ " if (this.field98.hashCode() == 0) {}\n" +
+ " ^^^^^^^\n" +
+ "Potential null pointer access: The field field98 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 37)\n" +
+ " if (this.field97.hashCode() == 0) {}\n" +
+ " ^^^^^^^\n" +
+ "Potential null pointer access: The field field97 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 38)\n" +
+ " if (this.field100.hashCode() == 0) {}\n" +
+ " ^^^^^^^^\n" +
+ "Null pointer access: The field field100 can only be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
+}
+
+// null analysis -- simple case for field for inner class
+// to make sure field id's of inner and outer classes are not same for flow analysis
+public void testBug247564h() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object o;\n" +
+ " class X1 {\n" +
+ " Object x;" +
+ " Object x1;" +
+ " Object x2;" +
+ " void goo() {\n" +
+ " if (o == null && x.toString() == \"\"){}\n" +
+ " if (o2 == null && o2.toString() == \"\"){}\n" +
+ " if (o2 == null && x2.toString() == \"\"){}\n" +
+ " }\n" +
+
+ " }\n" +
+ " Object o1;\n" +
+ " static Object o2;\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 6)\n" +
+ " if (o2 == null && o2.toString() == \"\"){}\n" +
+ " ^^\n" +
+ "Potential null pointer access: The field o2 may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for field for inner class
+// to make sure that id's of local variables in inner classes dotn conflict with those of fields.
+public void testBug247564h_1() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object field0;\n" +
+ " Object field1;\n" +
+ " class X1 {\n" +
+ " Object field2;" +
+ " Object field3;" +
+ " void goo(Object var) {\n" +
+ " if (var == null && field2.toString() == \"\"){}\n" +
+ " if (var == null && field3.toString() == \"\"){}\n" +
+ " if (field2 == null && field2.toString() == \"\"){}\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 8)\n" +
+ " if (field2 == null && field2.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field2 may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for field for inner class
+// to make sure that id's of local variables in inner classes dotn conflict with those of fields.
+public void testBug247564h_2() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " Object field0;\n" +
+ " Object field1;\n" +
+ " class X1 {\n" +
+ " Object field2;\n" +
+ " Object field3;\n" +
+ " class X2 {\n" +
+ " Object field4;\n" +
+ " Object field5;\n" +
+ " void goo(Object var) {\n" +
+ " if (var == null && field4.toString() == \"\"){}\n" +
+ " if (var == null && field5.toString() == \"\"){}\n" +
+ " if (field3 == null && field3.toString() == \"\"){}\n" +
+ " if (field3 == null && field1.toString() == \"\"){}\n" +
+ " }\n" +
+ " }\n" +
+ " Object field22;\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 13)\n" +
+ " if (field3 == null && field3.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field3 may be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- simple case for static fields
+// to make sure that static field only of the current type is assigned potentially null when compared against null
+// Static fields belonging to any other class should be ignored
+// Qualified access to static fields should also work
+public void testBug247564i_1() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ true,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static Object field0;\n" +
+ " static Object field1;\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX() { return new X();}\n" +
+ " void goo(Object var) {\n" +
+ " if (Y.yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (Y.yField1 == null && this.field0.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" + // warn, qualified access
+ " if (Y.yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (this.field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (X.field0 == null && this.field0.toString() == \"\"){}\n" + // warn
+ " if (X.field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX(){ return new X();}\n" +
+ " static Object yField1;" +
+ " static X xiny;\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 9)\n" +
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 12)\n" +
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 13)\n" +
+ " if (this.field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 14)\n" +
+ " if (X.field0 == null && this.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError
+ );
+}
+
+// null analysis -- static fields accessed via MessageSend
+// to make sure that static field only of the current type is assigned potentially null when compared against null
+// Static fields belonging to any other class should be ignored
+// Qualified access to static fields should also work
+public void testBug247564i_2() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ true,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static Object field0;\n" +
+ " static Object field1;\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX() { return new X();}\n" +
+ " void goo(Object var) {\n" +
+ " if (new Y().getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && this.field0.toString() == \"\"){}\n" + // no warn
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && this.field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn, getX() wipes out null info
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" + // warn, qualified access
+ // fields from other types, don't warn
+ " if (new Y().getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getX().field0 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX(){ return new X();}\n" +
+ " static Object yField1;\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 10)\n" +
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 11)\n" +
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 12)\n" +
+ " if (getX().field0 == null && this.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 14)\n" +
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError
+ );
+}
+
+// null analysis -- static fields accessed from Member type
+// Qualified access to static fields should also work
+public void testBug247564i_3() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ true,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static Object field0;\n" +
+ " static Object field1;\n" +
+ " static Y getY(){ return new Y();}\n" +
+ " static X getX() { return new X();}\n" +
+ " static class XInner{\n" +
+ " static Object xinnerfield;\n" +
+ " XInner getXInner() { return new XInner();\n}" +
+ " void goo(Object var) {\n" +
+ " if (new Y().getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn, getX() wipes out null info
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" + // warn, qualified access
+ " if (getXInner().xinnerfield == null && xinnerfield.toString() == \"\"){}\n" + // warn
+ " if (getXInner().xinnerfield == null && this.xinnerfield.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && this.xinnerfield.toString() == \"\"){}\n" + // no warn
+ " if (getXInner().xinnerfield == null && field0.toString() == \"\"){}\n" + // no warn
+ // fields from other types, don't warn
+ " if (new Y().getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getX().field0 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ // qualified accesses
+ " if (Y.yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" + // warn, qualified access
+ " if (Y.yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (X.field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX(){ return new X();}\n" +
+ " static Object yField1;\n" +
+ " static X xiny;\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 12)\n" +
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 13)\n" +
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 15)\n" +
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 16)\n" +
+ " if (getXInner().xinnerfield == null && xinnerfield.toString() == \"\"){}\n" +
+ " ^^^^^^^^^^^\n" +
+ "Potential null pointer access: The field xinnerfield may be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 17)\n" +
+ " if (getXInner().xinnerfield == null && this.xinnerfield.toString() == \"\"){}\n" +
+ " ^^^^^^^^^^^\n" +
+ "Potential null pointer access: The field xinnerfield may be null at this location\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 24)\n" +
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "7. ERROR in X.java (at line 27)\n" +
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError
+ );
+}
+
+// null analysis -- static fields accessed from a local type
+// Qualified access to static fields should also work
+public void testBug247564i_4() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ true,
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " static Object field0;\n" +
+ " static Object field1;\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX() { return new X();}\n" +
+ " void goo(Object var) {\n" +
+ " class Local{\n" +
+ " void localfoo(){\n " +
+ " if (new Y().getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn, getX() wipes out null info
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" + // warn, qualified access
+ // fields from other types, don't warn
+ " if (new Y().getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getX().field0 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ // qualified accesses
+ " if (Y.yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" + // warn, qualified access
+ " if (Y.yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (X.field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn
+ " }\n" +
+ " }\n" +
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX(){ return new X();}\n" +
+ " static Object yField1;\n" +
+ " static X xiny;\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. WARNING in X.java (at line 7)\n" +
+ " class Local{\n" +
+ " ^^^^^\n" +
+ "The type Local is never used locally\n" +
+ "----------\n" +
+ "2. WARNING in X.java (at line 8)\n" +
+ " void localfoo(){\n" +
+ " ^^^^^^^^^^\n" +
+ "The method localfoo() from the type Local is never used locally\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 11)\n" +
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 12)\n" +
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 14)\n" +
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 19)\n" +
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "7. ERROR in X.java (at line 22)\n" +
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError
+ );
+}
+
+// null analysis -- static fields from an anonymous type
+// Qualified access to static fields should also work
+public void testBug247564i_5() {
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ true,
+ new String[] {
+ "X.java",
+ "interface Anon{}\n" +
+ "public class X {\n" +
+ " static Object field0;\n" +
+ " static Object field1;\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX() { return new X();}\n" +
+ " void goo(Object var) {\n" +
+ " new Anon(){\n" +
+ " void localfoo(){\n " +
+ " if (new Y().getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn, getX() wipes out null info
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" + // warn, qualified access
+ // fields from other types, don't warn
+ " if (new Y().getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getX().field0 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ // qualified accesses
+ " if (Y.yField1 == null && field0.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" + // warn, qualified access
+ " if (Y.yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (Y.xiny.field1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (X.field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn
+ " }\n" +
+ " };\n" +
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX(){ return new X();}\n" +
+ " static Object yField1;\n" +
+ " static X xiny;\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. WARNING in X.java (at line 9)\n" +
+ " void localfoo(){\n" +
+ " ^^^^^^^^^^\n" +
+ "The method localfoo() from the type new Anon(){} is never used locally\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 12)\n" +
+ " if (new Y().getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 13)\n" +
+ " if (getX().field0 == null && field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "4. ERROR in X.java (at line 15)\n" +
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "5. ERROR in X.java (at line 20)\n" +
+ " if (Y.xiny.field0 == null && Y.xiny.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "6. ERROR in X.java (at line 23)\n" +
+ " if (X.field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError
+ );
+}
+// null analysis -- static fields
+// to check static field access from fieldReference and QualifiedReference when the type is parameterized
+public void testBug247564i_6() {
+ if (this.complianceLevel < ClassFileConstants.JDK1_5) return;
+ Map compilerOptions = getCompilerOptions();
+ compilerOptions.put(CompilerOptions.OPTION_ReportNonStaticAccessToStatic, CompilerOptions.IGNORE);
+ this.runNegativeTest(
+ true,
+ new String[] {
+ "X.java",
+ "public class X<T> {\n" +
+ " static Object field0;\n" +
+ " static Object field1;\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX() { return new X();}\n" +
+ " void goo(Object var) {\n" +
+ " if (new Y().getY().yField1 == null && X.field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && X.field0.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && this.field0.toString() == \"\"){}\n" + // no warn
+ " if (new Y().getX().field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && this.field0.toString() == \"\"){}\n" + // warn
+ " if (getX().field0 == null && getX().field0.toString() == \"\"){}\n" + // no warn, getX() wipes out null info
+ // fields from other types, don't warn
+ " if (new Y().getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getY().yField1 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " if (getX().field0 == null && Y.yField1.toString() == \"\"){}\n" + // no warn
+ " }\n" +
+ "}\n" +
+ "class Y<K>{\n" +
+ " Y getY(){ return new Y();}\n" +
+ " X getX(){ return new X();}\n" +
+ " static Object yField1;\n" +
+ "}"},
+ null,
+ compilerOptions,
+ "----------\n" +
+ "1. ERROR in X.java (at line 10)\n" +
+ " if (new Y().getX().field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 11)\n" +
+ " if (getX().field0 == null && X.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n" +
+ "3. ERROR in X.java (at line 12)\n" +
+ " if (getX().field0 == null && this.field0.toString() == \"\"){}\n" +
+ " ^^^^^^\n" +
+ "Potential null pointer access: The field field0 may be null at this location\n" +
+ "----------\n",
+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError
+ );
+}
+//null analysis -- simple case for field of parent type
+public void testBug247564j() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X extends Y {\n" +
+ " private Object fieldx;\n" +
+ " void goo(Object var) {\n" +
+ " if (fieldx == null && fieldy.toString() == \"\"){}\n" + // don't flag fieldy, nothing known
+ " }\n" +
+ "}\n" +
+ "class Y{\n" +
+ " protected Object fieldy = null;\n" +
+ "}\n" +
+ ""},
+ ""
+ );
+}
+
+// null analysis -- simple case for field in try-finally
+public void testBug247564k() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private Object f;\n" +
+ " void goo(Object var) {\n" +
+ " try {\n" +
+ " int i = 10;\n" +
+ " while (i<20){\n" +
+ " if (i == 15) {\n" +
+ " f = null;\n" +
+ " break;\n" +
+ " }\n" +
+ " i++;\n" +
+ " }\n" +
+ " return;\n" +
+ " } finally {\n" +
+ " if (f != null && f.hashCode() == 0){}\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- simple case for field in try-finally
+// presence or absence of throw should not affect the behaviour
+public void testBug247564k_1() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private Object f;\n" +
+ " void goo(Object var) throws Exception{\n" +
+ " try {\n" +
+ " int i = 10;\n" +
+ " } catch(Exception e) {\n" +
+ " f = null;\n" +
+ " throw e;\n" +
+ " } finally {\n" +
+ " if (f != null && f.hashCode() == 0){}\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- simple case for field in try-finally
+// presence or absence of method call in finally should not affect the behaviour
+public void testBug247564k_2() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private Object f;\n" +
+ " void gooCalls(){}\n" +
+ " void goo(Object var) throws Exception{\n" +
+ " try {\n" +
+ " if (f != null) {}\n" +
+ " } finally {\n" +
+ " if (f != null ) {\n" +
+ " gooCalls();\n" +
+ " f.toString();\n" +
+ " }\n" + // silent
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- simple case for constant field in try-catch-finally
+// presence or absence of method call in finally should not affect the behaviour
+public void testBug247564k_3() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private static final Object f = null;\n" +
+ " void gooCalls() throws NumberFormatException{}\n" +
+ " void goo(Object var) throws Exception{\n" +
+ " try {\n" +
+ " gooCalls();\n" +
+ " } catch(NumberFormatException e) {\n" +
+ " if (f.hashCode() == 0){}\n" +
+ " } finally {\n" +
+ " gooCalls();\n" +
+ " if (f.hashCode() == 0){}\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. ERROR in X.java (at line 8)\n" +
+ " if (f.hashCode() == 0){}\n" +
+ " ^\n" +
+ "Null pointer access: The field f can only be null at this location\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 11)\n" +
+ " if (f.hashCode() == 0){}\n" +
+ " ^\n" +
+ "Null pointer access: The field f can only be null at this location\n" +
+ "----------\n"
+ );
+}
+
+// null analysis -- potentially redundant checks against the same field
+public void testBug247564l_1() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private Object f;\n" +
+ " int foo() throws Exception{\n" +
+ " if (f == null && f != null)\n" +
+ " return 13;\n" +
+ " return -13;\n" +
+ " }\n" +
+ " int goo() throws Exception{\n" +
+ " if (f == null && f == null)\n" +
+ " return 14;\n" +
+ " return -14;\n" +
+ " }\n" +
+ " boolean hoo() throws Exception{\n" +
+ " if (f == null)\n" +
+ " return f != null;\n" +
+ " return f == null;\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- fields assigned a def. non null value in a loop
+// comment 121
+public void testBug247564l_2() {
+ this.runConformTest(
+ new String[] {
+ "X.java",
+ "public class X {\n" +
+ " private Object field;\n" +
+ " void foo() throws Exception{\n" +
+ " if (field != null) field.hashCode();\n" +
+ " int i = 10;\n" +
+ " while (i<20) {\n" +
+ " if (field == null) field = new Object();\n" +
+ " field.toString();\n" + // should not warn
+ " i++;\n" +
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ ""
+ );
+}
+
+// null analysis -- checked and unchecked exceptions
+public void testBug247564m() {
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class MyException extends Exception{}" +
+ "public class X {\n" +
+ " private Object f;\n" +
+ " void gooCalls() throws MyException{}\n" +
+ " void goo(){\n" +
+ " try {\n" +
+ " if (f == null) return;\n" +
+ " gooCalls();\n" +
+ " } catch(MyException e) {\n" + // checked Exception
+ " f.toString();\n" + // silent - at gooCalls() in 'try', f is not going to be null
+ " } catch(NumberFormatException e) {\n" + // unchecked Exception
+ " f.toString();\n" + // could have come from anywhere, f can be null as doubted in 'try'
+ " }\n" +
+ " }\n" +
+ "}\n"},
+ "----------\n" +
+ "1. WARNING in X.java (at line 1)\n" +
+ " class MyException extends Exception{}public class X {\n" +
+ " ^^^^^^^^^^^\n" +
+ "The serializable class MyException does not declare a static final serialVersionUID field of type long\n" +
+ "----------\n" +
+ "2. ERROR in X.java (at line 11)\n" +
+ " f.toString();\n" +
+ " ^\n" +
+ "Potential null pointer access: The field f may be null at this location\n" +
+ "----------\n"
+ );
+}
+}
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
index a67f3cc..89562a4 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -3404,7 +3404,12 @@
setSeverity(CompilerOptions.OPTION_ReportRedundantNullCheck, ProblemSeverities.Ignore, isEnabling);
}
return;
- }
+ } else if (token.equals("nullFields")) { //$NON-NLS-1$
+ this.options.put(
+ CompilerOptions.OPTION_IncludeFieldsInNullAnalysis,
+ isEnabling ? CompilerOptions.ENABLED : CompilerOptions.DISABLED);
+ return;
+ }
break;
case 'o' :
if (token.equals("over-sync") /*|| token.equals("syncOverride")*/) { //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
index 2a5f956..e2e6a74 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -307,6 +307,7 @@
\ noEffectAssign + assignment without effect\n\
\ null potential missing or redundant null check\n\
\ nullDereference + missing null check\n\
+\ nullFields + null analysis for fields\n\
\ over-ann missing @Override annotation (superclass)\n\
\ paramAssign assignment to a parameter\n\
\ pkgDefaultMethod + attempt to override package-default method\n\
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
index 6b2f414..d31e466 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -1281,6 +1281,26 @@
/** @since 3.4 */
int UnusedTypeArgumentsForConstructorInvocation = MethodRelated + 660;
+ /**
+ * Null analysis for fields
+ */
+ /** @since 3.8*/
+ int NullFieldReference = Internal + FieldRelated + 670;
+ /** @since 3.8*/
+ int PotentialNullFieldReference = Internal + FieldRelated + 671;
+ /** @since 3.8*/
+ int RedundantNullCheckOnNullField = Internal + FieldRelated + 672;
+ /** @since 3.8*/
+ int NullFieldComparisonYieldsFalse = Internal + FieldRelated + 673;
+ /** @since 3.8*/
+ int RedundantNullCheckOnNonNullField = Internal + FieldRelated + 674;
+ /** @since 3.8*/
+ int NonNullFieldComparisonYieldsFalse = Internal + FieldRelated + 675;
+ /** @since 3.8*/
+ int RedundantFieldNullAssignment = Internal + FieldRelated + 676;
+ /** @since 3.8*/
+ int NullFieldInstanceofYieldsFalse = Internal + FieldRelated + 677;
+
/**
* Corrupted binaries
*/
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
index 5035d83..a7e3ca7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ClassFile.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -3934,6 +3934,7 @@
// retrieve the enclosing one guaranteed to be the one matching the propagated flow info
// 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check)
+ // see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=247564#c65
this.codeStream.maxFieldCount = aType.scope.outerMostClassScope().referenceType().maxFieldCount;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
index ea1af7b..a5c7405 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java
@@ -43,43 +43,46 @@
// record setting a variable: various scenarii are possible, setting an array reference,
// a field reference, a blank final field reference, a field of an enclosing instance or
// just a local variable.
- LocalVariableBinding local = this.lhs.localVariableBinding();
+ VariableBinding var = this.lhs.variableBinding(currentScope);
if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) {
this.expression.checkNPE(currentScope, flowContext, flowInfo);
}
FlowInfo preInitInfo = null;
- boolean shouldAnalyseResource = local != null
+ LocalVariableBinding localToAnalyseAsResource = null;
+ if (var instanceof LocalVariableBinding
&& flowInfo.reachMode() == FlowInfo.REACHABLE
&& (FakedTrackingVariable.isAnyCloseable(this.expression.resolvedType)
- || this.expression.resolvedType == TypeBinding.NULL);
- if (shouldAnalyseResource) {
+ || this.expression.resolvedType == TypeBinding.NULL)) {
+ localToAnalyseAsResource = (LocalVariableBinding) var;
+
preInitInfo = flowInfo.unconditionalCopy();
// analysis of resource leaks needs additional context while analyzing the RHS:
- FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, local, this.expression);
+ FakedTrackingVariable.preConnectTrackerAcrossAssignment(this, localToAnalyseAsResource, this.expression);
}
flowInfo = ((Reference) this.lhs)
.analyseAssignment(currentScope, flowContext, flowInfo, this, false)
.unconditionalInits();
- if (shouldAnalyseResource)
- FakedTrackingVariable.handleResourceAssignment(currentScope, preInitInfo, flowInfo, this, this.expression, local);
- else
+ if (localToAnalyseAsResource != null) {
+ FakedTrackingVariable.handleResourceAssignment(currentScope, preInitInfo, flowInfo, this, this.expression, localToAnalyseAsResource);
+ } else {
FakedTrackingVariable.cleanUpAfterAssignment(currentScope, this.lhs.bits, this.expression);
+ }
int nullStatus = this.expression.nullStatus(flowInfo);
- if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
+ if (var != null && (var.type.tagBits & TagBits.IsBaseType) == 0) {
if (nullStatus == FlowInfo.NULL) {
- flowContext.recordUsingNullReference(currentScope, local, this.lhs,
+ flowContext.recordUsingNullReference(currentScope, var, this.lhs,
FlowContext.CAN_ONLY_NULL | FlowContext.IN_ASSIGNMENT, flowInfo);
}
}
- nullStatus = checkAssignmentAgainstNullAnnotation(currentScope, flowContext, local, nullStatus, this.expression);
- if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
- flowInfo.markNullStatus(local, nullStatus);
+ nullStatus = checkAssignmentAgainstNullAnnotation(currentScope, flowContext, var, nullStatus, this.expression);
+ if (var != null && (var.type.tagBits & TagBits.IsBaseType) == 0) {
+ flowInfo.markNullStatus(var, nullStatus);
if (flowContext.initsOnFinally != null)
- flowContext.initsOnFinally.markNullStatus(local, nullStatus);
+ flowContext.initsOnFinally.markNullStatus(var, nullStatus);
}
return flowInfo;
}
@@ -233,4 +236,7 @@
public LocalVariableBinding localVariableBinding() {
return this.lhs.localVariableBinding();
}
+public VariableBinding variableBinding(Scope scope) {
+ return this.lhs.variableBinding(scope);
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
index 90b2f96..c61629e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Block.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -37,6 +37,7 @@
flowInfo = stat.analyseCode(this.scope, flowContext, flowInfo);
}
}
+
if (this.explicitDeclarations > 0) // if block has its own scope analyze tracking vars now:
this.scope.checkUnclosedCloseables(flowInfo, null, null);
return flowInfo;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
index 396276a..03bd333 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -33,6 +33,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
public class CastExpression extends Expression {
@@ -445,6 +446,13 @@
return this.expression.localVariableBinding();
}
+/**
+ * @see org.eclipse.jdt.internal.compiler.ast.Expression#variableBinding(Scope)
+ */
+public VariableBinding variableBinding(Scope scope) {
+ return this.expression.variableBinding(scope);
+}
+
public int nullStatus(FlowInfo flowInfo) {
return this.expression.nullStatus(flowInfo);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
index 9763111..7f47925 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -39,16 +39,16 @@
// TODO: handle all kinds of expressions (cf. also https://bugs.eclipse.org/364326)
}
- LocalVariableBinding local = this.left.localVariableBinding();
+ VariableBinding local = this.left.variableBinding(scope);
if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, rightStatus, this.left);
}
- local = this.right.localVariableBinding();
+ local = this.right.variableBinding(scope);
if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
checkVariableComparison(scope, flowContext, flowInfo, initsWhenTrue, initsWhenFalse, local, leftStatus, this.right);
}
}
- private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, LocalVariableBinding local, int nullStatus, Expression reference) {
+ private void checkVariableComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse, VariableBinding local, int nullStatus, Expression reference) {
switch (nullStatus) {
case FlowInfo.NULL :
if (((this.bits & OperatorMASK) >> OperatorSHIFT) == EQUAL_EQUAL) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
index df21615..87d886d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -36,6 +36,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.eclipse.jdt.internal.compiler.problem.ShouldNotImplement;
import org.eclipse.jdt.internal.compiler.util.Messages;
@@ -525,14 +526,14 @@
* @param flowInfo the upstream flow info; caveat: may get modified
*/
public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
- LocalVariableBinding local = localVariableBinding();
+ VariableBinding local = variableBinding(scope);
if (local != null &&
(local.type.tagBits & TagBits.IsBaseType) == 0) {
if ((this.bits & ASTNode.IsNonNull) == 0) {
flowContext.recordUsingNullReference(scope, local, this,
FlowContext.MAY_NULL, flowInfo);
}
- flowInfo.markAsComparedEqualToNonNull(local);
+ flowInfo.markAsComparedEqualToNonNull(local );
// from thereon it is set
if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) {
flowInfo.markedAsNullOrNonNullInAssertExpression(local);
@@ -872,7 +873,7 @@
this.constant != null && this.constant != Constant.NotAConstant)
return FlowInfo.NON_NULL; // constant expression cannot be null
- LocalVariableBinding local = localVariableBinding();
+ VariableBinding local = variableBinding(null);
if (local != null)
return flowInfo.nullStatus(local);
return FlowInfo.NON_NULL;
@@ -1112,4 +1113,15 @@
public void traverse(ASTVisitor visitor, ClassScope scope) {
// nothing to do
}
+
+/**
+ * Returns the field or local variable referenced by this node. Can be a direct reference (SingleNameReference)
+ * or thru a cast expression etc...
+ * This is used for the purpose of obtaining a local variable or field binding for the purpose of null analysis.
+ * @param scope This is the current scope in which binding is requested and is needed to ascertain if a static field
+ * belongs to the current type for null analysis. A <code>null</code> value may be passed to this parameter
+*/
+public VariableBinding variableBinding(Scope scope) {
+ return null;
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
index 30ab54e..a31b905 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -74,6 +74,13 @@
.analyseCode(initializationScope, flowContext, flowInfo)
.unconditionalInits();
flowInfo.markAsDefinitelyAssigned(this.binding);
+ if (this.binding.isFinal() && this.binding.isStatic()) {
+ int nullStatus = this.initialization.nullStatus(flowInfo);
+ // static final field being initialized. Record its null status for future reference
+ // since the flowInfo from an initialization wont be available in a method
+ flowInfo.markNullStatus(this.binding, nullStatus);
+// this.binding.setNullStatusForStaticFinalField(nullStatus);
+ }
}
return flowInfo;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
index 66f16b6..e8c6fab 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -18,9 +18,11 @@
import org.eclipse.jdt.internal.compiler.codegen.Opcodes;
import org.eclipse.jdt.internal.compiler.flow.FlowContext;
import org.eclipse.jdt.internal.compiler.flow.FlowInfo;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
+import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
@@ -35,6 +37,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
public class FieldReference extends Reference implements InvocationSite {
@@ -668,4 +671,22 @@
}
visitor.endVisit(this, scope);
}
+
+public VariableBinding variableBinding(Scope scope) {
+ if (scope != null) {
+ CompilerOptions options = scope.compilerOptions();
+ if(!options.includeFieldsInNullAnalysis) return null;
+ if (this.receiver.isThis()) return this.binding;
+ if (this.binding != null && this.binding.isStatic()) {
+ // does the static field belong to the current type or one of the enclosing ones?
+ ClassScope enclosingClass = scope.enclosingClassScope();
+ while (enclosingClass != null) {
+ TypeDeclaration type = enclosingClass.referenceContext;
+ if (type != null && (this.binding.declaringClass.original() == type.binding)) return this.binding;
+ enclosingClass = enclosingClass.enclosingClassScope();
+ }
+ }
+ }
+ return null;
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java
index 3382ebb..4687113 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/InstanceOfExpression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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,16 +31,16 @@
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
- LocalVariableBinding local = this.expression.localVariableBinding();
- if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) {
+ VariableBinding variable = this.expression.variableBinding(currentScope);
+ if (variable != null && (variable.type.tagBits & TagBits.IsBaseType) == 0) {
flowInfo = this.expression.analyseCode(currentScope, flowContext, flowInfo).
unconditionalInits();
FlowInfo initsWhenTrue = flowInfo.copy();
- initsWhenTrue.markAsComparedEqualToNonNull(local);
+ initsWhenTrue.markAsComparedEqualToNonNull(variable );
if ((flowContext.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) {
- initsWhenTrue.markedAsNullOrNonNullInAssertExpression(local);
+ initsWhenTrue.markedAsNullOrNonNullInAssertExpression(variable);
}
- flowContext.recordUsingNullReference(currentScope, local,
+ flowContext.recordUsingNullReference(currentScope, variable,
this.expression, FlowContext.CAN_ONLY_NULL | FlowContext.IN_INSTANCEOF, flowInfo);
// no impact upon enclosing try context
return FlowInfo.conditional(initsWhenTrue, flowInfo.copy());
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 0dda53f..6469df3 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -116,6 +116,11 @@
// NullReferenceTest#test0510
}
manageSyntheticAccessIfNecessary(currentScope, flowInfo);
+ // a method call can result in changed values for fields,
+ // so wipe out null info for fields collected till now.
+ CompilerOptions options = currentScope.compilerOptions();
+ if(options.includeFieldsInNullAnalysis)
+ flowInfo.resetNullInfoForFields();
return flowInfo;
}
public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
index a9f98e2..95bb17b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
@@ -1070,4 +1070,32 @@
public String unboundReferenceErrorName() {
return new String(this.tokens[0]);
}
+
+public VariableBinding variableBinding(Scope scope) {
+ // if this is a *static* field and its actualResolvedType is the type in which we currently are asking for the binding,
+ // we can safely return the field binding
+ if (scope != null) {
+ CompilerOptions options = scope.compilerOptions();
+ if(!options.includeFieldsInNullAnalysis) return null;
+ if (this.binding != null && (this.bits & RestrictiveFlagMASK) == Binding.FIELD) {
+ FieldBinding fieldBinding;
+ if (this.otherBindings == null) {
+ fieldBinding = (FieldBinding) this.binding;
+ } else {
+ fieldBinding = this.otherBindings[this.otherBindings.length - 1];
+ }
+ if (fieldBinding.isStatic()) {
+ // does the static field belong to the current type or one of the enclosing ones?
+ ClassScope enclosingClass = scope.enclosingClassScope();
+ while (enclosingClass != null) {
+ TypeDeclaration type = enclosingClass.referenceContext;
+ if (type != null && fieldBinding.declaringClass.original() == type.binding)
+ return fieldBinding;
+ enclosingClass = enclosingClass.enclosingClassScope();
+ }
+ }
+ }
+ }
+ return super.variableBinding(scope);
+}
}
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 f8955db..18c1ebf 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
@@ -143,7 +143,7 @@
} catch (NullPointerException npe) {
// chain of references in try-block has several potential nulls;
// any null means we cannot perform the following check
- return;
+ return;
}
if ((tagBits & TagBits.AnnotationNonNull) != 0) {
flowContext.recordNullityMismatch(scope, this.expression, nullStatus, methodBinding.returnType);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
index d625d5b..f0891de 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -806,6 +806,20 @@
return null;
}
+public VariableBinding variableBinding(Scope scope) {
+ switch (this.bits & ASTNode.RestrictiveFlagMASK) {
+ case Binding.FIELD :
+ // reading a field
+ if (scope != null) {
+ CompilerOptions options = scope.compilerOptions();
+ if(!options.includeFieldsInNullAnalysis) return null;
+ }
+ //$FALL-THROUGH$
+ case Binding.LOCAL : // reading a local variable
+ return (VariableBinding) this.binding;
+ }
+ return null;
+}
public void manageEnclosingInstanceAccessIfNecessary(BlockScope currentScope, FlowInfo flowInfo) {
//If inlinable field, forget the access emulation, the code gen will directly target it
if (((this.bits & ASTNode.DepthMASK) == 0) || (this.constant != Constant.NotAConstant)) {
@@ -858,11 +872,10 @@
}
switch (this.bits & ASTNode.RestrictiveFlagMASK) {
case Binding.FIELD : // reading a field
- return FlowInfo.UNKNOWN;
case Binding.LOCAL : // reading a local variable
- LocalVariableBinding local = (LocalVariableBinding) this.binding;
- if (local != null)
- return flowInfo.nullStatus(local);
+ VariableBinding variable = (VariableBinding) this.binding;
+ if (variable != null)
+ return flowInfo.nullStatus(variable);
}
return FlowInfo.NON_NULL; // never get there
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
index 8f68b7c..66c17f5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
@@ -59,7 +59,6 @@
public static final int COMPLAINED_FAKE_REACHABLE = 1;
public static final int COMPLAINED_UNREACHABLE = 2;
-
/** Analysing arguments of MessageSend, ExplicitConstructorCall, AllocationExpression. */
protected void analyseArguments(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, MethodBinding methodBinding, Expression[] arguments)
{
@@ -98,12 +97,12 @@
/** Check null-ness of 'local' against a possible null annotation */
protected int checkAssignmentAgainstNullAnnotation(BlockScope currentScope, FlowContext flowContext,
- LocalVariableBinding local, int nullStatus, Expression expression)
+ VariableBinding var, int nullStatus, Expression expression)
{
- if (local != null
- && (local.tagBits & TagBits.AnnotationNonNull) != 0
+ if (var != null
+ && (var.tagBits & TagBits.AnnotationNonNull) != 0
&& nullStatus != FlowInfo.NON_NULL) {
- flowContext.recordNullityMismatch(currentScope, expression, nullStatus, local.type);
+ flowContext.recordNullityMismatch(currentScope, expression, nullStatus, var.type);
nullStatus=FlowInfo.NON_NULL;
}
return nullStatus;
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 4b32c61..a3b569e 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
@@ -43,7 +43,7 @@
public MethodScope initializerScope;
public MethodScope staticInitializerScope;
public boolean ignoreFurtherInvestigation = false;
- public int maxFieldCount;
+ public int maxFieldCount; // maximum cumulative number of fields of this type and its inners (see updateMaxFieldCount())
public int declarationSourceStart;
public int declarationSourceEnd;
public int bodyStart;
@@ -199,7 +199,6 @@
localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType));
}
manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
- updateMaxFieldCount(); // propagate down the max field count
internalAnalyseCode(flowContext, flowInfo);
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
@@ -215,9 +214,7 @@
if (this.ignoreFurtherInvestigation)
return;
try {
- // propagate down the max field count
- updateMaxFieldCount();
- internalAnalyseCode(null, FlowInfo.initial(this.maxFieldCount));
+ internalAnalyseCode(null, FlowInfo.initial(this.scope.outerMostClassScope().referenceType().maxFieldCount));
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
@@ -237,7 +234,6 @@
localType.setConstantPoolName(currentScope.compilationUnitScope().computeConstantPoolName(localType));
}
manageEnclosingInstanceAccessIfNecessary(currentScope, flowInfo);
- updateMaxFieldCount(); // propagate down the max field count
internalAnalyseCode(flowContext, flowInfo);
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
@@ -654,6 +650,11 @@
staticInitializerContext.handledExceptions = Binding.ANY_EXCEPTION; // tolerate them all, and record them
/*}*/
staticFieldInfo = field.analyseCode(this.staticInitializerScope, staticInitializerContext, staticFieldInfo);
+ if (field.binding != null && this.scope.compilerOptions().includeFieldsInNullAnalysis
+ && ((field.binding.modifiers & ClassFileConstants.AccFinal) != 0)) {
+ // we won't reset null Info for constant fields
+ staticFieldInfo.updateConstantFieldsMask(field.binding);
+ }
// in case the initializer is not reachable, use a reinitialized flowInfo and enter a fake reachable
// branch, since the previous initializer already got the blame.
if (staticFieldInfo == FlowInfo.DEAD_END) {
@@ -690,7 +691,18 @@
}
if (this.methods != null) {
UnconditionalFlowInfo outerInfo = flowInfo.unconditionalFieldLessCopy();
- FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo);
+ UnconditionalFlowInfo staticFieldUnconditionalInfo = staticFieldInfo.unconditionalInits();
+ FlowInfo constructorInfo;
+ if (this.scope.compilerOptions().includeFieldsInNullAnalysis) {
+ flowInfo.addNullInfoFrom(staticFieldUnconditionalInfo.discardNonFieldInitializations());
+ flowInfo.addConstantFieldsMask(staticFieldUnconditionalInfo); // prevent resetting null info for constant fields inside methods
+ flowInfo.resetNullInfoForFields(); // only preserve null info for constant fields
+ constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(flowInfo);
+ constructorInfo.addConstantFieldsMask(staticFieldUnconditionalInfo); // prevent resetting null info for constant fields inside c'tor too
+ } else {
+ constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo);
+ }
+
for (int i = 0, count = this.methods.length; i < count; i++) {
AbstractMethodDeclaration method = this.methods[i];
if (method.ignoreFurtherInvestigation)
@@ -701,7 +713,7 @@
((Clinit)method).analyseCode(
this.scope,
staticInitializerContext,
- staticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo));
+ staticFieldUnconditionalInfo.addInitializationsFrom(outerInfo));
} else { // constructor
((ConstructorDeclaration)method).analyseCode(this.scope, initializerContext, constructorInfo.copy(), flowInfo.reachMode());
}
@@ -1045,8 +1057,6 @@
}
} while ((current = current.enclosingType()) != null);
}
- // this.maxFieldCount might already be set
- int localMaxFieldCount = 0;
int lastVisibleFieldID = -1;
boolean hasEnumConstants = false;
FieldDeclaration[] enumConstantsWithoutBody = null;
@@ -1056,6 +1066,38 @@
this.typeParameters[i].resolve(this.scope);
}
}
+ // field count from enclosing and supertypes should be included in maxFieldCount,
+ // to make field-ids unique among all fields in scope.
+ // 1.: enclosing:
+ TypeBinding original = sourceType.original();
+ int fieldAnalysisOffset = 0;
+ if (original instanceof NestedTypeBinding) {
+ // note: local types have no enclosingType in the AST but only in the binding:
+ fieldAnalysisOffset = ((NestedTypeBinding)original).enclosingType.cumulativeFieldCount;
+ }
+ // 2.: supers:
+ ReferenceBinding superClassBinding = sourceType.superclass;
+ while (superClassBinding != null) {
+ FieldBinding[] unResolvedFields = superClassBinding.unResolvedFields();
+ if (unResolvedFields != null) {
+ for (int i=unResolvedFields.length-1; i>=0; i--) {
+ // if the field is an initializer we do not want to update the count
+ switch (unResolvedFields[i].kind()) {
+ case AbstractVariableDeclaration.FIELD:
+ case AbstractVariableDeclaration.ENUM_CONSTANT:
+ fieldAnalysisOffset++;
+ }
+ }
+ }
+ fieldAnalysisOffset += findFieldCountFromSuperInterfaces(superClassBinding.superInterfaces());
+ superClassBinding = superClassBinding.superclass();
+ }
+ ReferenceBinding[] superInterfacesBinding = sourceType.superInterfaces;
+ fieldAnalysisOffset += findFieldCountFromSuperInterfaces(superInterfacesBinding);
+ sourceType.cumulativeFieldCount += fieldAnalysisOffset;
+ sourceType.fieldAnalysisOffset = fieldAnalysisOffset;
+ this.maxFieldCount = sourceType.cumulativeFieldCount;
+
if (this.memberTypes != null) {
for (int i = 0, count = this.memberTypes.length; i < count; i++) {
this.memberTypes[i].resolve(this.scope);
@@ -1087,7 +1129,6 @@
&& TypeBinding.LONG == fieldBinding.type) {
needSerialVersion = false;
}
- localMaxFieldCount++;
lastVisibleFieldID = field.binding.id;
break;
@@ -1098,9 +1139,6 @@
field.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope);
}
}
- if (this.maxFieldCount < localMaxFieldCount) {
- this.maxFieldCount = localMaxFieldCount;
- }
if (needSerialVersion) {
//check that the current type doesn't extend javax.rmi.CORBA.Stub
TypeBinding javaxRmiCorbaStub = this.scope.getType(TypeConstants.JAVAX_RMI_CORBA_STUB, 4);
@@ -1187,6 +1225,19 @@
}
}
+private int findFieldCountFromSuperInterfaces(ReferenceBinding[] superinterfaces) {
+ int numOfFields = 0;
+ if (superinterfaces == null)
+ return numOfFields ;
+ for (int i = 0; i < superinterfaces.length; i++) {
+ FieldBinding[] unResolvedFields = superinterfaces[i].unResolvedFields();
+ // no need to check field kinds as initializer cannot occur inside interfaces
+ numOfFields += unResolvedFields != null ? unResolvedFields.length : 0;
+ numOfFields += findFieldCountFromSuperInterfaces(superinterfaces[i].superInterfaces());
+ }
+ return numOfFields;
+}
+
/**
* Resolve a local type declaration
*/
@@ -1447,12 +1498,11 @@
/**
* MaxFieldCount's computation is necessary so as to reserve space for
* the flow info field portions. It corresponds to the maximum amount of
- * fields this class or one of its innertypes have.
+ * accumulated fields this class or one of its innertypes have.
*
- * During name resolution, types are traversed, and the max field count is recorded
- * on the outermost type. It is then propagated down during the flow analysis.
- *
- * This method is doing either up/down propagation.
+ * During buildFields() accumulative field counts are gather per class,
+ * which include fields of outer and super types.
+ * During resolve, the maximum of these counts is collected inside out.
*/
void updateMaxFieldCount() {
if (this.binding == null)
@@ -1460,8 +1510,6 @@
TypeDeclaration outerMostType = this.scope.outerMostClassScope().referenceType();
if (this.maxFieldCount > outerMostType.maxFieldCount) {
outerMostType.maxFieldCount = this.maxFieldCount; // up
- } else {
- this.maxFieldCount = outerMostType.maxFieldCount; // down
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
index 5cb4166..5128a63 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/ConditionalFlowInfo.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -13,6 +13,7 @@
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
/**
* Record conditional initialization status during definite assignment analysis
@@ -73,119 +74,118 @@
return this.initsWhenTrue;
}
-public boolean isDefinitelyAssigned(FieldBinding field) {
+public boolean isDefinitelyAssigned(VariableBinding var) {
- return this.initsWhenTrue.isDefinitelyAssigned(field)
- && this.initsWhenFalse.isDefinitelyAssigned(field);
+ return this.initsWhenTrue.isDefinitelyAssigned(var)
+ && this.initsWhenFalse.isDefinitelyAssigned(var);
}
-public boolean isDefinitelyAssigned(LocalVariableBinding local) {
-
- return this.initsWhenTrue.isDefinitelyAssigned(local)
- && this.initsWhenFalse.isDefinitelyAssigned(local);
-}
-
-public boolean isDefinitelyNonNull(LocalVariableBinding local) {
+public boolean isDefinitelyNonNull(VariableBinding local) {
return this.initsWhenTrue.isDefinitelyNonNull(local)
&& this.initsWhenFalse.isDefinitelyNonNull(local);
}
-public boolean isDefinitelyNull(LocalVariableBinding local) {
+public boolean isDefinitelyNull(VariableBinding local) {
return this.initsWhenTrue.isDefinitelyNull(local)
&& this.initsWhenFalse.isDefinitelyNull(local);
}
-public boolean isDefinitelyUnknown(LocalVariableBinding local) {
+public boolean isDefinitelyUnknown(VariableBinding local) {
return this.initsWhenTrue.isDefinitelyUnknown(local)
&& this.initsWhenFalse.isDefinitelyUnknown(local);
}
-public boolean isPotentiallyAssigned(FieldBinding field) {
- return this.initsWhenTrue.isPotentiallyAssigned(field)
- || this.initsWhenFalse.isPotentiallyAssigned(field);
+public boolean isPotentiallyAssigned(VariableBinding var) {
+ return this.initsWhenTrue.isPotentiallyAssigned(var)
+ || this.initsWhenFalse.isPotentiallyAssigned(var);
}
-public boolean isPotentiallyAssigned(LocalVariableBinding local) {
- return this.initsWhenTrue.isPotentiallyAssigned(local)
- || this.initsWhenFalse.isPotentiallyAssigned(local);
-}
-
-public boolean isPotentiallyNonNull(LocalVariableBinding local) {
+public boolean isPotentiallyNonNull(VariableBinding local) {
return this.initsWhenTrue.isPotentiallyNonNull(local)
|| this.initsWhenFalse.isPotentiallyNonNull(local);
}
-public boolean isPotentiallyNull(LocalVariableBinding local) {
+public boolean isPotentiallyNull(VariableBinding local) {
return this.initsWhenTrue.isPotentiallyNull(local)
|| this.initsWhenFalse.isPotentiallyNull(local);
}
-public boolean isPotentiallyUnknown(LocalVariableBinding local) {
+public boolean isPotentiallyUnknown(VariableBinding local) {
return this.initsWhenTrue.isPotentiallyUnknown(local)
|| this.initsWhenFalse.isPotentiallyUnknown(local);
}
-public boolean isProtectedNonNull(LocalVariableBinding local) {
+public boolean isProtectedNonNull(VariableBinding local) {
return this.initsWhenTrue.isProtectedNonNull(local)
&& this.initsWhenFalse.isProtectedNonNull(local);
}
-public boolean isProtectedNull(LocalVariableBinding local) {
+public boolean isProtectedNull(VariableBinding local) {
return this.initsWhenTrue.isProtectedNull(local)
&& this.initsWhenFalse.isProtectedNull(local);
}
-public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
+public void markAsComparedEqualToNonNull(VariableBinding local) {
this.initsWhenTrue.markAsComparedEqualToNonNull(local);
this.initsWhenFalse.markAsComparedEqualToNonNull(local);
}
-public void markAsComparedEqualToNull(LocalVariableBinding local) {
+public void markAsComparedEqualToNull(VariableBinding local) {
this.initsWhenTrue.markAsComparedEqualToNull(local);
this.initsWhenFalse.markAsComparedEqualToNull(local);
}
-public void markAsDefinitelyAssigned(FieldBinding field) {
- this.initsWhenTrue.markAsDefinitelyAssigned(field);
- this.initsWhenFalse.markAsDefinitelyAssigned(field);
+public void markAsDefinitelyAssigned(VariableBinding var) {
+ this.initsWhenTrue.markAsDefinitelyAssigned(var);
+ this.initsWhenFalse.markAsDefinitelyAssigned(var);
}
-public void markAsDefinitelyAssigned(LocalVariableBinding local) {
- this.initsWhenTrue.markAsDefinitelyAssigned(local);
- this.initsWhenFalse.markAsDefinitelyAssigned(local);
-}
-
-public void markAsDefinitelyNonNull(LocalVariableBinding local) {
+public void markAsDefinitelyNonNull(VariableBinding local) {
this.initsWhenTrue.markAsDefinitelyNonNull(local);
this.initsWhenFalse.markAsDefinitelyNonNull(local);
}
-public void markAsDefinitelyNull(LocalVariableBinding local) {
+public void markAsDefinitelyNull(VariableBinding local) {
this.initsWhenTrue.markAsDefinitelyNull(local);
this.initsWhenFalse.markAsDefinitelyNull(local);
}
-public void resetNullInfo(LocalVariableBinding local) {
+public void resetNullInfo(VariableBinding local) {
this.initsWhenTrue.resetNullInfo(local);
this.initsWhenFalse.resetNullInfo(local);
}
-public void markPotentiallyNullBit(LocalVariableBinding local) {
+public void resetNullInfoForFields() {
+ this.initsWhenTrue.resetNullInfoForFields();
+ this.initsWhenFalse.resetNullInfoForFields();
+}
+
+public void updateConstantFieldsMask(FieldBinding field) {
+ this.initsWhenTrue.updateConstantFieldsMask(field);
+ this.initsWhenFalse.updateConstantFieldsMask(field);
+}
+
+public void addConstantFieldsMask(UnconditionalFlowInfo other) {
+ this.initsWhenTrue.addConstantFieldsMask(other);
+ this.initsWhenFalse.addConstantFieldsMask(other);
+}
+
+public void markPotentiallyNullBit(VariableBinding local) {
this.initsWhenTrue.markPotentiallyNullBit(local);
this.initsWhenFalse.markPotentiallyNullBit(local);
}
-public void markPotentiallyNonNullBit(LocalVariableBinding local) {
+public void markPotentiallyNonNullBit(VariableBinding local) {
this.initsWhenTrue.markPotentiallyNonNullBit(local);
this.initsWhenFalse.markPotentiallyNonNullBit(local);
}
-public void markAsDefinitelyUnknown(LocalVariableBinding local) {
+public void markAsDefinitelyUnknown(VariableBinding local) {
this.initsWhenTrue.markAsDefinitelyUnknown(local);
this.initsWhenFalse.markAsDefinitelyUnknown(local);
}
-public void markPotentiallyUnknownBit(LocalVariableBinding local) {
+public void markPotentiallyUnknownBit(VariableBinding local) {
this.initsWhenTrue.markPotentiallyUnknownBit(local);
this.initsWhenFalse.markPotentiallyUnknownBit(local);
}
@@ -243,12 +243,12 @@
mergedWith(this.initsWhenFalse.unconditionalInits());
}
-public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) {
+public void markedAsNullOrNonNullInAssertExpression(VariableBinding local) {
this.initsWhenTrue.markedAsNullOrNonNullInAssertExpression(local);
this.initsWhenFalse.markedAsNullOrNonNullInAssertExpression(local);
}
-public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) {
+public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding local) {
return (this.initsWhenTrue.isMarkedAsNullOrNonNullInAssertExpression(local)
|| this.initsWhenFalse.isMarkedAsNullOrNonNullInAssertExpression(local));
}
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 12b5edc..3c62413 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
@@ -34,7 +34,7 @@
VariableBinding[] finalVariables;
int assignCount;
- LocalVariableBinding[] nullLocals;
+ VariableBinding[] nullVariables;
Expression[] nullReferences;
int[] nullCheckTypes;
int nullCount;
@@ -59,13 +59,13 @@
boolean complained = false; // remember if have complained on this final assignment
if (variable instanceof FieldBinding) {
// final field
- if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) {
+ if (flowInfo.isPotentiallyAssigned(variable)) {
complained = true;
scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, this.finalAssignments[i]);
}
} else {
// final local variable
- if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) {
+ if (flowInfo.isPotentiallyAssigned(variable)) {
complained = true;
scope.problemReporter().duplicateInitializationOfFinalLocal(
(LocalVariableBinding) variable,
@@ -90,29 +90,28 @@
for (int i = 0; i < this.nullCount; i++) {
if (this.nullCheckTypes[i] == ASSIGN_TO_NONNULL)
this.parent.recordNullityMismatch(scope, this.nullReferences[i],
- flowInfo.nullStatus(this.nullLocals[i]), this.expectedTypes[i]);
+ flowInfo.nullStatus(this.nullVariables[i]), this.expectedTypes[i]);
else
- this.parent.recordUsingNullReference(scope, this.nullLocals[i],
+ this.parent.recordUsingNullReference(scope, this.nullVariables[i],
this.nullReferences[i], this.nullCheckTypes[i], flowInfo);
-
}
}
else { // no enclosing loop, be as precise as possible right now
for (int i = 0; i < this.nullCount; i++) {
Expression expression = this.nullReferences[i];
// final local variable
- LocalVariableBinding local = this.nullLocals[i];
+ VariableBinding local = this.nullVariables[i];
switch (this.nullCheckTypes[i]) {
case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
if (flowInfo.isDefinitelyNonNull(local)) {
if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, expression);
+ scope.problemReporter().variableNonNullComparedToNull(local, expression);
}
}
continue;
@@ -126,27 +125,27 @@
switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNull(local, expression);
}
continue;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, expression);
+ scope.problemReporter().variableNullComparedToNonNull(local, expression);
}
continue;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, expression);
+ scope.problemReporter().variableRedundantNullAssignment(local, expression);
continue;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, expression);
+ scope.problemReporter().variableNullInstanceof(local, expression);
continue;
}
} else if (flowInfo.isPotentiallyNull(local)) {
@@ -154,14 +153,14 @@
case FlowContext.IN_COMPARISON_NULL:
this.nullReferences[i] = null;
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
this.nullReferences[i] = null;
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
@@ -170,11 +169,11 @@
break;
case MAY_NULL:
if (flowInfo.isDefinitelyNull(local)) {
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if (flowInfo.isPotentiallyNull(local)) {
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
}
break;
case ASSIGN_TO_NONNULL:
@@ -229,7 +228,7 @@
return true;
}
- public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
+ public void recordUsingNullReference(Scope scope, VariableBinding local,
Expression reference, int checkType, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 && !flowInfo.isDefinitelyUnknown(local)) {
if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative
@@ -243,14 +242,14 @@
if (flowInfo.cannotBeNull(local)) {
if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
} else if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, reference);
+ scope.problemReporter().variableNonNullComparedToNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -262,11 +261,11 @@
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -274,34 +273,34 @@
return;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
+ scope.problemReporter().variableNullComparedToNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
return;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
+ scope.problemReporter().variableRedundantNullAssignment(local, reference);
return;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, reference);
+ scope.problemReporter().variableNullInstanceof(local, reference);
return;
}
} else if (flowInfo.isPotentiallyNull(local)) {
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
@@ -313,7 +312,7 @@
return;
}
if (flowInfo.canOnlyBeNull(local)) {
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
break;
@@ -328,14 +327,14 @@
if (flowInfo.isDefinitelyNonNull(local)) {
if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, reference);
+ scope.problemReporter().variableNonNullComparedToNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -352,11 +351,11 @@
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -364,34 +363,34 @@
return;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
+ scope.problemReporter().variableNullComparedToNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
return;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
+ scope.problemReporter().variableRedundantNullAssignment(local, reference);
return;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, reference);
+ scope.problemReporter().variableNullInstanceof(local, reference);
return;
}
} else if (flowInfo.isPotentiallyNull(local)) {
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
@@ -400,11 +399,11 @@
break;
case MAY_NULL :
if (flowInfo.isDefinitelyNull(local)) {
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if (flowInfo.isPotentiallyNull(local)) {
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
if (flowInfo.isDefinitelyNonNull(local)) {
@@ -436,17 +435,17 @@
}
}
-protected void recordNullReference(LocalVariableBinding local,
+protected void recordNullReference(VariableBinding local,
Expression expression, int status) {
if (this.nullCount == 0) {
- this.nullLocals = new LocalVariableBinding[5];
+ this.nullVariables = new VariableBinding[5];
this.nullReferences = new Expression[5];
this.nullCheckTypes = new int[5];
}
- else if (this.nullCount == this.nullLocals.length) {
+ else if (this.nullCount == this.nullVariables.length) {
int newLength = this.nullCount * 2;
- System.arraycopy(this.nullLocals, 0,
- this.nullLocals = new LocalVariableBinding[newLength], 0,
+ System.arraycopy(this.nullVariables, 0,
+ this.nullVariables = new VariableBinding[newLength], 0,
this.nullCount);
System.arraycopy(this.nullReferences, 0,
this.nullReferences = new Expression[newLength], 0,
@@ -455,7 +454,7 @@
this.nullCheckTypes = new int[newLength], 0,
this.nullCount);
}
- this.nullLocals[this.nullCount] = local;
+ this.nullVariables[this.nullCount] = local;
this.nullReferences[this.nullCount] = expression;
this.nullCheckTypes[this.nullCount++] = status;
}
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 98caf30..33fd6c5 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
@@ -588,7 +588,7 @@
* combined with a context indicator (one of {@link #IN_COMPARISON_NULL},
* {@link #IN_COMPARISON_NON_NULL}, {@link #IN_ASSIGNMENT} or {@link #IN_INSTANCEOF})
*/
-protected void recordNullReference(LocalVariableBinding local,
+protected void recordNullReference(VariableBinding local,
Expression expression, int status) {
// default implementation: do nothing
}
@@ -631,7 +631,7 @@
* be known at the time of calling this method (they are influenced by
* code that follows the current point)
*/
-public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
+public void recordUsingNullReference(Scope scope, VariableBinding local,
Expression reference, int checkType, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
flowInfo.isDefinitelyUnknown(local)) {
@@ -643,14 +643,14 @@
if (flowInfo.isDefinitelyNonNull(local)) {
if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, reference);
+ scope.problemReporter().variableNonNullComparedToNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -670,11 +670,11 @@
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -682,34 +682,34 @@
return;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
+ scope.problemReporter().variableNullComparedToNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
return;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
+ scope.problemReporter().variableRedundantNullAssignment(local, reference);
return;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, reference);
+ scope.problemReporter().variableNullInstanceof(local, reference);
return;
}
} else if (flowInfo.isPotentiallyNull(local)) {
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
@@ -720,11 +720,11 @@
break;
case MAY_NULL :
if (flowInfo.isDefinitelyNull(local)) {
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if (flowInfo.isPotentiallyNull(local)) {
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
index b931372..422f0ad 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowInfo.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -17,6 +17,7 @@
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
public abstract class FlowInfo {
@@ -52,7 +53,7 @@
DEAD_END = new UnconditionalFlowInfo();
DEAD_END.tagBits = UNREACHABLE;
}
-
+
/**
* Add other inits to this flow info, then return this. The operation semantics
* are to match as closely as possible the application to this flow info of all
@@ -94,39 +95,42 @@
}
/**
- * Check whether a given local variable is known to be unable to gain a definite
+ * Check whether a given field or local variable is known to be unable to gain a definite
* non null or definite null status by the use of an enclosing flow info. The
* semantics are that if the current flow info marks the variable as potentially
* unknown or else as being both potentially null and potentially non null,
* then it won't ever be promoted as definitely null or definitely non null. (It
* could still get promoted to definite unknown).
- * @param local the variable to check
- * @return true iff this flow info prevents local from being promoted to
+ * @param binding the field or local variable to check
+ * @return true iff this flow info prevents field or local from being promoted to
* definite non null or definite null against an enclosing flow info
*/
-public boolean cannotBeDefinitelyNullOrNonNull(LocalVariableBinding local) {
- return isPotentiallyUnknown(local) ||
- isPotentiallyNonNull(local) && isPotentiallyNull(local);
+public boolean cannotBeDefinitelyNullOrNonNull(VariableBinding binding) {
+ return isPotentiallyUnknown(binding) ||
+ isPotentiallyNonNull(binding) && isPotentiallyNull(binding);
}
/**
- * Check whether a given local variable is known to be non null, either because
+ * Check whether a given field or local variable is known to be non null, either because
* it is definitely non null, or because is has been tested against non null.
- * @param local the variable to ckeck
- * @return true iff local cannot be null for this flow info
+ * @param binding the field or local to check
+ * @return true iff field or local cannot be null for this flow info
*/
-public boolean cannotBeNull(LocalVariableBinding local) {
- return isDefinitelyNonNull(local) || isProtectedNonNull(local);
+public boolean cannotBeNull(VariableBinding binding) {
+ return isDefinitelyNonNull(binding) || isProtectedNonNull(binding);
}
/**
- * Check whether a given local variable is known to be null, either because it
- * is definitely null, or because is has been tested against null.
- * @param local the variable to ckeck
- * @return true iff local can only be null for this flow info
+ * Check whether a given field or local variable is known to be null, either because it
+ * is definitely null, or because is has been tested against null. Note that for fields,
+ * this method only takes compile time analysis into account and there's no
+ * guarantee of the field being definitely null during runtime
+ * since it can be modified in some other thread.
+ * @param binding the field or local to check
+ * @return true iff field or local can only be null for this flow info
*/
-public boolean canOnlyBeNull(LocalVariableBinding local) {
- return isDefinitelyNull(local) || isProtectedNull(local);
+public boolean canOnlyBeNull(VariableBinding binding) {
+ return isDefinitelyNull(binding) || isProtectedNull(binding);
}
/**
@@ -138,6 +142,7 @@
public static UnconditionalFlowInfo initial(int maxFieldCount) {
UnconditionalFlowInfo info = new UnconditionalFlowInfo();
info.maxFieldCount = maxFieldCount;
+ info.constantFieldsMask = 0L;
return info;
}
@@ -164,196 +169,202 @@
abstract public FlowInfo initsWhenTrue();
/**
- * Check status of definite assignment for a field.
+ * Check status of definite assignment for a local or field.
*/
- abstract public boolean isDefinitelyAssigned(FieldBinding field);
+ abstract public boolean isDefinitelyAssigned(VariableBinding var);
+
+/**
+ * Check status of definite non-null value for a given field or local variable. Note that for fields, this method only
+ * takes compile time analysis into account and there's no guarantee of the field being definitely non null during runtime
+ * since it can be modified in some other thread.
+ * @param binding the field or local to check
+ * @return true iff field or local is definitely non null for this flow info
+ */
+ public abstract boolean isDefinitelyNonNull(VariableBinding binding);
+
+/**
+ * Check status of definite null value for a given field or local variable. Note that for fields, this method only
+ * takes compile time analysis into account and there's no guarantee of the field being definitely null during runtime
+ * since it can be modified in some other thread.
+ * @param binding the field or local to check
+ * @return true iff field or local is definitely null for this flow info
+ */
+public abstract boolean isDefinitelyNull(VariableBinding binding);
+
+/**
+ * Check status of definite unknown value for a given field or local variable.
+ * @param binding the field or local to check
+ * @return true iff field or local is definitely unknown for this flow info
+ */
+public abstract boolean isDefinitelyUnknown(VariableBinding binding);
/**
- * Check status of definite assignment for a local.
- */
- public abstract boolean isDefinitelyAssigned(LocalVariableBinding local);
-
-/**
- * Check status of definite non-null value for a given local variable.
- * @param local the variable to ckeck
- * @return true iff local is definitely non null for this flow info
- */
- public abstract boolean isDefinitelyNonNull(LocalVariableBinding local);
-
-/**
- * Check status of definite null value for a given local variable.
- * @param local the variable to ckeck
- * @return true iff local is definitely null for this flow info
- */
-public abstract boolean isDefinitelyNull(LocalVariableBinding local);
-
-/**
- * Check status of definite unknown value for a given local variable.
- * @param local the variable to ckeck
- * @return true iff local is definitely unknown for this flow info
- */
-public abstract boolean isDefinitelyUnknown(LocalVariableBinding local);
-
- /**
- * Check status of potential assignment for a field.
- */
- abstract public boolean isPotentiallyAssigned(FieldBinding field);
-
- /**
- * Check status of potential assignment for a local variable.
+ * Check status of potential assignment for a local variable or a field.
*/
- abstract public boolean isPotentiallyAssigned(LocalVariableBinding field);
+ abstract public boolean isPotentiallyAssigned(VariableBinding var);
/**
- * Check status of potential null assignment for a local. Return true if there
+ * Check status of potential null assignment for a field or local. Return true if there
* is a reasonable expectation that the variable be non null at this point.
- * @param local LocalVariableBinding - the binding for the checked local
- * @return true if there is a reasonable expectation that local be non null at
+ * @param binding VariableBinding - the binding for the checked field or local
+ * @return true if there is a reasonable expectation that the field or local be non null at
* this point
*/
-public abstract boolean isPotentiallyNonNull(LocalVariableBinding local);
+public abstract boolean isPotentiallyNonNull(VariableBinding binding);
/**
- * Check status of potential null assignment for a local. Return true if there
+ * Check status of potential null assignment for a field or local. Return true if there
* is a reasonable expectation that the variable be null at this point. This
* includes the protected null case, so as to augment diagnostics, but does not
* really check that someone deliberately assigned to null on any specific
* path
- * @param local LocalVariableBinding - the binding for the checked local
- * @return true if there is a reasonable expectation that local be null at
+ * @param binding VariableBinding - the binding for the checked field or local
+ * @return true if there is a reasonable expectation that the field or local be null at
* this point
*/
-public abstract boolean isPotentiallyNull(LocalVariableBinding local);
+public abstract boolean isPotentiallyNull(VariableBinding binding);
/**
- * Return true if the given local may have been assigned to an unknown value.
- * @param local the local to check
- * @return true if the given local may have been assigned to an unknown value
+ * Return true if the given field or local may have been assigned to an unknown value.
+ * @param binding the field or local to check
+ * @return true if the given field or local may have been assigned to an unknown value
*/
-public abstract boolean isPotentiallyUnknown(LocalVariableBinding local);
+public abstract boolean isPotentiallyUnknown(VariableBinding binding);
/**
- * Return true if the given local is protected by a test against a non null
+ * Return true if the given field or local is protected by a test against a non null
* value.
- * @param local the local to check
- * @return true if the given local is protected by a test against a non null
+ * @param binding the field or local to check
+ * @return true if the given field or local is protected by a test against a non null
*/
-public abstract boolean isProtectedNonNull(LocalVariableBinding local);
+public abstract boolean isProtectedNonNull(VariableBinding binding);
/**
- * Return true if the given local is protected by a test against null.
- * @param local the local to check
- * @return true if the given local is protected by a test against null
+ * Return true if the given field or local is protected by a test against null.
+ * @param binding the field or local to check
+ * @return true if the given field or local is protected by a test against null
*/
-public abstract boolean isProtectedNull(LocalVariableBinding local);
+public abstract boolean isProtectedNull(VariableBinding binding);
/**
- * Record that a local variable got checked to be non null.
- * @param local the checked local variable
+ * Record that a field or local variable got checked to be non null.
+ * @param binding the checked field or local variable
*/
-abstract public void markAsComparedEqualToNonNull(LocalVariableBinding local);
+abstract public void markAsComparedEqualToNonNull(VariableBinding binding);
/**
- * Record that a local variable got checked to be null.
- * @param local the checked local variable
+ * Record that a field or local variable got checked to be null.
+ * @param binding the checked field or local variable
*/
-abstract public void markAsComparedEqualToNull(LocalVariableBinding local);
+abstract public void markAsComparedEqualToNull(VariableBinding binding);
/**
- * Record a field got definitely assigned.
+ * Record a field or local got definitely assigned to a non-null value.
*/
- abstract public void markAsDefinitelyAssigned(FieldBinding field);
+ abstract public void markAsDefinitelyNonNull(VariableBinding binding);
/**
- * Record a local got definitely assigned to a non-null value.
+ * Record a field or local got definitely assigned to null.
*/
- abstract public void markAsDefinitelyNonNull(LocalVariableBinding local);
+ abstract public void markAsDefinitelyNull(VariableBinding binding);
/**
- * Record a local got definitely assigned to null.
+ * Reset all null-information about a given field or local.
*/
- abstract public void markAsDefinitelyNull(LocalVariableBinding local);
+ abstract public void resetNullInfo(VariableBinding binding);
/**
- * Reset all null-information about a given local.
+ * variant of {@link #resetNullInfo(VariableBinding)} for resetting null info for all fields
+ * Note that each fields status after the reset will become def. unknown i.e. 1001
+ * Also this method does not reset constant fields, which are identified by {@link #constantFieldsMask}
*/
- abstract public void resetNullInfo(LocalVariableBinding local);
+ abstract public void resetNullInfoForFields();
+
+ /**
+ * exclude a new field from being reset by {@link #resetNullInfoForFields()}
+ */
+ abstract public void updateConstantFieldsMask(FieldBinding field);
+
+ /**
+ * add the constant fields info from the other flow info
+ */
+ abstract public void addConstantFieldsMask(UnconditionalFlowInfo other);
+
+ /**
+ * Record a field or local may have got assigned to unknown (set the bit on existing info).
+ */
+ abstract public void markPotentiallyUnknownBit(VariableBinding binding);
/**
- * Record a local may have got assigned to unknown (set the bit on existing info).
+ * Record a field or local may have got assigned to null (set the bit on existing info).
*/
- abstract public void markPotentiallyUnknownBit(LocalVariableBinding local);
+ abstract public void markPotentiallyNullBit(VariableBinding binding);
/**
- * Record a local may have got assigned to null (set the bit on existing info).
+ * Record a field or local may have got assigned to non-null (set the bit on existing info).
*/
- abstract public void markPotentiallyNullBit(LocalVariableBinding local);
+ abstract public void markPotentiallyNonNullBit(VariableBinding binding);
/**
- * Record a local may have got assigned to non-null (set the bit on existing info).
+ * Record a local or field got definitely assigned.
*/
- abstract public void markPotentiallyNonNullBit(LocalVariableBinding local);
-
- /**
- * Record a local got definitely assigned.
- */
- abstract public void markAsDefinitelyAssigned(LocalVariableBinding local);
+ abstract public void markAsDefinitelyAssigned(VariableBinding var);
/**
- * Record a local got definitely assigned to an unknown value.
+ * Record a field or local got definitely assigned to an unknown value.
*/
-abstract public void markAsDefinitelyUnknown(LocalVariableBinding local);
+abstract public void markAsDefinitelyUnknown(VariableBinding binding);
/**
- * Mark the null status of the given local according to the given status
- * @param local
+ * Mark the null status of the given field or local according to the given status
+ * @param binding
* @param nullStatus bitset of FLowInfo.UNKNOWN ... FlowInfo.POTENTIALLY_NON_NULL
*/
-public void markNullStatus(LocalVariableBinding local, int nullStatus) {
+public void markNullStatus(VariableBinding binding, int nullStatus) {
switch(nullStatus) {
// definite status?
case FlowInfo.UNKNOWN :
- markAsDefinitelyUnknown(local);
+ markAsDefinitelyUnknown(binding);
break;
case FlowInfo.NULL :
- markAsDefinitelyNull(local);
+ markAsDefinitelyNull(binding);
break;
case FlowInfo.NON_NULL :
- markAsDefinitelyNonNull(local);
+ markAsDefinitelyNonNull(binding);
break;
default:
// collect potential status:
- resetNullInfo(local);
+ resetNullInfo(binding);
if ((nullStatus & FlowInfo.POTENTIALLY_UNKNOWN) != 0)
- markPotentiallyUnknownBit(local);
+ markPotentiallyUnknownBit(binding);
if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0)
- markPotentiallyNullBit(local);
+ markPotentiallyNullBit(binding);
if ((nullStatus & FlowInfo.POTENTIALLY_NON_NULL) != 0)
- markPotentiallyNonNullBit(local);
+ markPotentiallyNonNullBit(binding);
if ((nullStatus & (FlowInfo.POTENTIALLY_NULL|FlowInfo.POTENTIALLY_NON_NULL|FlowInfo.POTENTIALLY_UNKNOWN)) == 0)
- markAsDefinitelyUnknown(local);
+ markAsDefinitelyUnknown(binding);
}
}
/**
- * Answer the null status of the given local
- * @param local
+ * Answer the null status of the given field or local
+ * @param binding
* @return bitset of FlowInfo.UNKNOWN ... FlowInfo.POTENTIALLY_NON_NULL
*/
-public int nullStatus(LocalVariableBinding local) {
- if (isDefinitelyUnknown(local))
+public int nullStatus(VariableBinding binding) {
+ if (isDefinitelyUnknown(binding))
return FlowInfo.UNKNOWN;
- if (isDefinitelyNull(local))
+ if (isDefinitelyNull(binding))
return FlowInfo.NULL;
- if (isDefinitelyNonNull(local))
+ if (isDefinitelyNonNull(binding))
return FlowInfo.NON_NULL;
int status = 0;
- if (isPotentiallyUnknown(local))
+ if (isPotentiallyUnknown(binding))
status |= FlowInfo.POTENTIALLY_UNKNOWN;
- if (isPotentiallyNull(local))
+ if (isPotentiallyNull(binding))
status |= FlowInfo.POTENTIALLY_NULL;
- if (isPotentiallyNonNull(local))
+ if (isPotentiallyNonNull(binding))
status |= FlowInfo.POTENTIALLY_NON_NULL;
if (status > 0)
return status;
@@ -601,14 +612,14 @@
* where this variable is being checked against null
*/
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=303448
-abstract public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local);
+abstract public void markedAsNullOrNonNullInAssertExpression(VariableBinding binding);
/**
* Returns true if the local variable being checked for was marked as null or not null
* inside an assert expression due to comparison against null.
*/
//https://bugs.eclipse.org/bugs/show_bug.cgi?id=303448
-abstract public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local);
+abstract public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding binding);
/**
* Resets the definite and potential initialization info for the given local variable
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 cfab201..cbf63e5 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -48,7 +48,7 @@
VariableBinding finalVariables[];
int assignCount = 0;
- LocalVariableBinding[] nullLocals;
+ VariableBinding[] nullVariables;
Expression[] nullReferences;
int[] nullCheckTypes;
int nullCount;
@@ -101,14 +101,14 @@
if (variable == null) continue;
boolean complained = false; // remember if have complained on this final assignment
if (variable instanceof FieldBinding) {
- if (flowInfo.isPotentiallyAssigned((FieldBinding) variable)) {
+ if (flowInfo.isPotentiallyAssigned(variable)) {
complained = true;
scope.problemReporter().duplicateInitializationOfBlankFinalField(
(FieldBinding) variable,
this.finalAssignments[i]);
}
} else {
- if (flowInfo.isPotentiallyAssigned((LocalVariableBinding) variable)) {
+ if (flowInfo.isPotentiallyAssigned(variable)) {
complained = true;
scope.problemReporter().duplicateInitializationOfFinalLocal(
(LocalVariableBinding) variable,
@@ -145,7 +145,7 @@
if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) {
// check only immutable null checks on innermost looping context
for (int i = 0; i < this.nullCount; i++) {
- LocalVariableBinding local = this.nullLocals[i];
+ VariableBinding local = this.nullVariables[i];
Expression expression = this.nullReferences[i];
// final local variable
switch (this.nullCheckTypes[i]) {
@@ -155,11 +155,11 @@
this.nullReferences[i] = null;
if (this.nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, expression);
+ scope.problemReporter().variableNonNullComparedToNull(local, expression);
}
}
continue;
@@ -171,11 +171,11 @@
this.nullReferences[i] = null;
if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, expression);
+ scope.problemReporter().variableNonNullComparedToNull(local, expression);
}
}
continue;
@@ -184,11 +184,11 @@
this.nullReferences[i] = null;
if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNull(local, expression);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, expression);
+ scope.problemReporter().variableNullComparedToNonNull(local, expression);
}
}
continue;
@@ -203,27 +203,27 @@
switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNull(local, expression);
}
continue;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, expression);
+ scope.problemReporter().variableNullComparedToNonNull(local, expression);
}
continue;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, expression);
+ scope.problemReporter().variableRedundantNullAssignment(local, expression);
continue;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, expression);
+ scope.problemReporter().variableNullInstanceof(local, expression);
continue;
}
} else if (flowInfo.isPotentiallyNull(local)) {
@@ -231,14 +231,14 @@
case FlowContext.IN_COMPARISON_NULL:
this.nullReferences[i] = null;
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
this.nullReferences[i] = null;
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
@@ -248,7 +248,7 @@
case MAY_NULL:
if (flowInfo.isDefinitelyNull(local)) {
this.nullReferences[i] = null;
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
break;
@@ -267,7 +267,7 @@
for (int i = 0; i < this.nullCount; i++) {
Expression expression = this.nullReferences[i];
// final local variable
- LocalVariableBinding local = this.nullLocals[i];
+ VariableBinding local = this.nullVariables[i];
switch (this.nullCheckTypes[i]) {
case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
@@ -275,11 +275,11 @@
this.nullReferences[i] = null;
if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, expression);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, expression);
+ scope.problemReporter().variableNonNullComparedToNull(local, expression);
}
}
continue;
@@ -294,27 +294,27 @@
switch(this.nullCheckTypes[i] & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, expression);
+ scope.problemReporter().variableRedundantCheckOnNull(local, expression);
}
continue;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, expression);
+ scope.problemReporter().variableNullComparedToNonNull(local, expression);
}
continue;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, expression);
+ scope.problemReporter().variableRedundantNullAssignment(local, expression);
continue;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, expression);
+ scope.problemReporter().variableNullInstanceof(local, expression);
continue;
}
} else if (flowInfo.isPotentiallyNull(local)) {
@@ -322,14 +322,14 @@
case FlowContext.IN_COMPARISON_NULL:
this.nullReferences[i] = null;
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
this.nullReferences[i] = null;
if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
@@ -339,12 +339,12 @@
case MAY_NULL:
if (flowInfo.isDefinitelyNull(local)) {
this.nullReferences[i] = null;
- scope.problemReporter().localVariableNullReference(local, expression);
+ scope.problemReporter().variableNullReference(local, expression);
continue;
}
if (flowInfo.isPotentiallyNull(local)) {
this.nullReferences[i] = null;
- scope.problemReporter().localVariablePotentialNullReference(local, expression);
+ scope.problemReporter().variablePotentialNullReference(local, expression);
continue;
}
break;
@@ -476,27 +476,27 @@
return true;
}
-protected void recordNullReference(LocalVariableBinding local,
+protected void recordNullReference(VariableBinding local,
Expression expression, int status) {
if (this.nullCount == 0) {
- this.nullLocals = new LocalVariableBinding[5];
+ this.nullVariables = new VariableBinding[5];
this.nullReferences = new Expression[5];
this.nullCheckTypes = new int[5];
}
- else if (this.nullCount == this.nullLocals.length) {
- System.arraycopy(this.nullLocals, 0,
- this.nullLocals = new LocalVariableBinding[this.nullCount * 2], 0, this.nullCount);
+ else if (this.nullCount == this.nullVariables.length) {
+ System.arraycopy(this.nullVariables, 0,
+ this.nullVariables = new VariableBinding[this.nullCount * 2], 0, this.nullCount);
System.arraycopy(this.nullReferences, 0,
this.nullReferences = new Expression[this.nullCount * 2], 0, this.nullCount);
System.arraycopy(this.nullCheckTypes, 0,
this.nullCheckTypes = new int[this.nullCount * 2], 0, this.nullCount);
}
- this.nullLocals[this.nullCount] = local;
+ this.nullVariables[this.nullCount] = local;
this.nullReferences[this.nullCount] = expression;
this.nullCheckTypes[this.nullCount++] = status;
}
-public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
+public void recordUsingNullReference(Scope scope, VariableBinding local,
Expression reference, int checkType, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
flowInfo.isDefinitelyUnknown(local)) {
@@ -508,14 +508,14 @@
if (flowInfo.isDefinitelyNonNull(local)) {
if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNonNullComparedToNull(local, reference);
+ scope.problemReporter().variableNonNullComparedToNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -524,14 +524,14 @@
} else if (flowInfo.isDefinitelyNull(local)) {
if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
} else {
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
+ scope.problemReporter().variableNullComparedToNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -575,11 +575,11 @@
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
+ scope.problemReporter().variableRedundantCheckOnNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
@@ -587,34 +587,34 @@
return;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) {
- scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
+ scope.problemReporter().variableNullComparedToNonNull(local, reference);
}
if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) {
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
return;
case FlowContext.IN_ASSIGNMENT:
- scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
+ scope.problemReporter().variableRedundantNullAssignment(local, reference);
return;
case FlowContext.IN_INSTANCEOF:
- scope.problemReporter().localVariableNullInstanceof(local, reference);
+ scope.problemReporter().variableNullInstanceof(local, reference);
return;
}
} else if (flowInfo.isPotentiallyNull(local)) {
switch(checkType & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
break;
@@ -633,11 +633,11 @@
return;
}
if (flowInfo.isDefinitelyNull(local)) {
- scope.problemReporter().localVariableNullReference(local, reference);
+ scope.problemReporter().variableNullReference(local, reference);
return;
}
if (flowInfo.isPotentiallyNull(local)) {
- scope.problemReporter().localVariablePotentialNullReference(local, reference);
+ scope.problemReporter().variablePotentialNullReference(local, reference);
return;
}
recordNullReference(local, reference, checkType);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java
index 1470795..a70fc4d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/NullInfoRegistry.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2011 IBM Corporation and others.
+ * Copyright (c) 2006, 2012 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
@@ -11,7 +11,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
-import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
/**
* A degenerate form of UnconditionalFlowInfo explicitly meant to capture
@@ -40,6 +41,8 @@
*/
public NullInfoRegistry(UnconditionalFlowInfo upstream) {
this.maxFieldCount = upstream.maxFieldCount;
+ this.constantFieldsMask = upstream.constantFieldsMask;
+ this.extraConstantFieldMask = upstream.extraConstantFieldMask;
if ((upstream.tagBits & NULL_FLAG_MASK) != 0) {
long u1, u2, u3, u4, nu2, nu3, nu4;
this.nullBit2 = (u1 = upstream.nullBit1)
@@ -87,6 +90,8 @@
this.nullBit2 |= other.nullBit2;
this.nullBit3 |= other.nullBit3;
this.nullBit4 |= other.nullBit4;
+ this.maxFieldCount = other.maxFieldCount;
+ this.addConstantFieldsMask(other);
if (other.extra != null) {
if (this.extra == null) {
this.extra = new long[extraLength][];
@@ -116,13 +121,21 @@
return this;
}
-public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
+public void markAsComparedEqualToNonNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
+ if (local instanceof FieldBinding && ((local.modifiers & AccConstant) == AccConstant)) {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as definitely unknown to avoid deferring null check for these fields.
+ this.markAsDefinitelyUnknown(local);
+ return;
+ } else {
+ position = local.getAnalysisId(this.maxFieldCount);
+ }
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits
+ if (position < BitCacheSize) { // use bits
// set protected non null
this.nullBit1 |= (1L << position);
if (COVERAGE_TEST_FLAG) {
@@ -161,13 +174,20 @@
}
}
-public void markAsDefinitelyNonNull(LocalVariableBinding local) {
+public void markAsDefinitelyNonNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
- // position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits
+ if (local instanceof FieldBinding && ((local.modifiers & AccConstant) == AccConstant)) {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as definitely unknown to avoid deferring null check for these fields.
+ this.markAsDefinitelyUnknown(local);
+ return;
+ } else {
+ position = local.getAnalysisId(this.maxFieldCount);
+ }
+ if (position < BitCacheSize) { // use bits
// set assigned non null
this.nullBit3 |= (1L << position);
if (COVERAGE_TEST_FLAG) {
@@ -207,13 +227,21 @@
}
// PREMATURE consider ignoring extra 0 to 2 included - means a1 should not be used either
// PREMATURE project protected non null onto something else
-public void markAsDefinitelyNull(LocalVariableBinding local) {
+public void markAsDefinitelyNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
+ if (local instanceof FieldBinding && ((local.modifiers & AccConstant) == AccConstant)) {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as potential null.
+ this.markNullStatus(local, FlowInfo.POTENTIALLY_NULL);
+ return;
+ } else {
+ position = local.getAnalysisId(this.maxFieldCount);
+ }
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits
+ if (position < BitCacheSize) { // use bits
// set assigned null
this.nullBit2 |= (1L << position);
if (COVERAGE_TEST_FLAG) {
@@ -252,13 +280,13 @@
}
}
-public void markAsDefinitelyUnknown(LocalVariableBinding local) {
+public void markAsDefinitelyUnknown(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
+ int position = local.getAnalysisId(this.maxFieldCount);
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits
+ if (position < BitCacheSize) { // use bits
// set assigned unknown
this.nullBit4 |= (1L << position);
if (COVERAGE_TEST_FLAG) {
@@ -407,13 +435,13 @@
* Mark a local as potentially having been assigned to an unknown value.
* @param local the local to mark
*/
-public void markPotentiallyUnknownBit(LocalVariableBinding local) {
+public void markPotentiallyUnknownBit(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
mask = 1L << position;
isTrue((this.nullBit1 & mask) == 0, "Adding 'unknown' mark in unexpected state"); //$NON-NLS-1$
@@ -454,12 +482,12 @@
}
}
-public void markPotentiallyNullBit(LocalVariableBinding local) {
+public void markPotentiallyNullBit(VariableBinding local) {
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
- long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ long mask;
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
mask = 1L << position;
isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially null' mark in unexpected state"); //$NON-NLS-1$
@@ -500,12 +528,12 @@
}
}
-public void markPotentiallyNonNullBit(LocalVariableBinding local) {
+public void markPotentiallyNonNullBit(VariableBinding local) {
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
mask = 1L << position;
isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially non-null' mark in unexpected state"); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java
index ed1ae7a..4154cf4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/UnconditionalFlowInfo.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -18,11 +18,13 @@
package org.eclipse.jdt.internal.compiler.flow;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
/**
* Record initialization status during definite assignment analysis
@@ -80,17 +82,20 @@
// extra segments
public static final int extraLength = 6;
+ // extra bit fields for larger numbers of fields/variables
+ // extra[0] holds definiteInits values, extra[1] potentialInits, etc.
+ // lifecycle is extra == null or else all extra[]'s are allocated
+ // arrays which have the same size
public long extra[][];
- // extra bit fields for larger numbers of fields/variables
- // extra[0] holds definiteInits values, extra[1] potentialInits, etc.
- // lifecycle is extra == null or else all extra[]'s are allocated
- // arrays which have the same size
-
+
public int maxFieldCount; // limit between fields and locals
// Constants
public static final int BitCacheSize = 64; // 64 bits in a long.
public int[] nullStatusChangedInAssert; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=303448
+ public long constantFieldsMask; // record positions of constant fields so that they don't get reset in resetNullInfoForFields()
+ public long extraConstantFieldMask[]; // extra mask for larger number of fields
+ protected static final int AccConstant = ClassFileConstants.AccStatic|ClassFileConstants.AccFinal;
public FlowInfo addInitializationsFrom(FlowInfo inits) {
return addInfoFrom(inits, true);
@@ -520,13 +525,13 @@
return this;
}
-final public boolean cannotBeDefinitelyNullOrNonNull(LocalVariableBinding local) {
+final public boolean cannotBeDefinitelyNullOrNonNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
return (
(~this.nullBit1
@@ -551,13 +556,13 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean cannotBeNull(LocalVariableBinding local) {
+final public boolean cannotBeNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
return (this.nullBit1 & this.nullBit3
& ((this.nullBit2 & this.nullBit4) | ~this.nullBit2)
@@ -578,13 +583,13 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean canOnlyBeNull(LocalVariableBinding local) {
+final public boolean canOnlyBeNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
return (this.nullBit1 & this.nullBit2
& (~this.nullBit3 | ~this.nullBit4)
@@ -622,6 +627,8 @@
}
copy.tagBits = this.tagBits;
copy.maxFieldCount = this.maxFieldCount;
+ copy.constantFieldsMask = this.constantFieldsMask;
+ copy.extraConstantFieldMask = this.extraConstantFieldMask;
if (this.extra != null) {
int length;
copy.extra = new long[extraLength][];
@@ -739,7 +746,7 @@
if ((this.tagBits & UNREACHABLE_OR_DEAD) != 0) {
return true;
}
- return isDefinitelyAssigned(field.id);
+ return isDefinitelyAssigned(field.getAnalysisId(this.maxFieldCount));
}
final public boolean isDefinitelyAssigned(LocalVariableBinding local) {
@@ -750,7 +757,22 @@
return isDefinitelyAssigned(local.id + this.maxFieldCount);
}
-final public boolean isDefinitelyNonNull(LocalVariableBinding local) {
+final public boolean isDefinitelyAssigned(VariableBinding var) {
+ if (var instanceof FieldBinding) {
+ return this.isDefinitelyAssigned((FieldBinding)var);
+ } else {
+ return this.isDefinitelyAssigned((LocalVariableBinding)var);
+ }
+}
+
+final public boolean isDefinitelyNonNull(VariableBinding local) {
+ boolean isField = local instanceof FieldBinding;
+ if (isField && (this.tagBits & NULL_FLAG_MASK) == 0) {
+ // no local yet in scope. Came here because of a field being queried for non null
+ // will only happen for final fields, since they are assigned in a constructor or static block
+ // and we may currently be in some other method
+ this.tagBits |= NULL_FLAG_MASK;
+ }
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0) {
@@ -760,7 +782,7 @@
local.constant() != Constant.NotAConstant) { // String instances
return true;
}
- int position = local.id + this.maxFieldCount;
+ int position = local.getAnalysisId(this.maxFieldCount);
if (position < BitCacheSize) { // use bits
return ((this.nullBit1 & this.nullBit3 & (~this.nullBit2 | this.nullBit4))
& (1L << position)) != 0;
@@ -779,14 +801,21 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean isDefinitelyNull(LocalVariableBinding local) {
+final public boolean isDefinitelyNull(VariableBinding local) {
+ boolean isField = local instanceof FieldBinding;
+ if (isField && (this.tagBits & NULL_FLAG_MASK) == 0) {
+ // no local yet in scope. Came here because of a field being queried for non null
+ // will only happen for final fields, since they are assigned in a constructor or static block
+ // and we may currently be in some other method
+ this.tagBits |= NULL_FLAG_MASK;
+ }
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position = local.id + this.maxFieldCount;
+ int position = local.getAnalysisId(this.maxFieldCount);
if (position < BitCacheSize) { // use bits
return ((this.nullBit1 & this.nullBit2
& (~this.nullBit3 | ~this.nullBit4))
@@ -806,13 +835,13 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean isDefinitelyUnknown(LocalVariableBinding local) {
+final public boolean isDefinitelyUnknown(VariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0) {
return false;
}
- int position = local.id + this.maxFieldCount;
+ int position = local.getAnalysisId(this.maxFieldCount);
if (position < BitCacheSize) { // use bits
return ((this.nullBit1 & this.nullBit4
& ~this.nullBit2 & ~this.nullBit3) & (1L << position)) != 0;
@@ -854,7 +883,7 @@
}
final public boolean isPotentiallyAssigned(FieldBinding field) {
- return isPotentiallyAssigned(field.id);
+ return isPotentiallyAssigned(field.getAnalysisId(this.maxFieldCount));
}
final public boolean isPotentiallyAssigned(LocalVariableBinding local) {
@@ -865,14 +894,22 @@
return isPotentiallyAssigned(local.id + this.maxFieldCount);
}
+final public boolean isPotentiallyAssigned(VariableBinding var) {
+ if (var instanceof FieldBinding) {
+ return this.isPotentiallyAssigned((FieldBinding)var);
+ } else {
+ return this.isPotentiallyAssigned((LocalVariableBinding)var);
+ }
+}
+
// TODO (Ayush) Check why this method does not return true for protected non null (1111)
-final public boolean isPotentiallyNonNull(LocalVariableBinding local) {
+final public boolean isPotentiallyNonNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) { // use bits
// use bits
return ((this.nullBit3 & (~this.nullBit1 | ~this.nullBit2))
& (1L << position)) != 0;
@@ -892,13 +929,13 @@
}
// TODO (Ayush) Check why this method does not return true for protected null
-final public boolean isPotentiallyNull(LocalVariableBinding local) {
+final public boolean isPotentiallyNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
return ((this.nullBit2 & (~this.nullBit1 | ~this.nullBit3))
& (1L << position)) != 0;
@@ -917,13 +954,13 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean isPotentiallyUnknown(LocalVariableBinding local) {
+final public boolean isPotentiallyUnknown(VariableBinding local) {
// do not want to complain in unreachable code
if ((this.tagBits & UNREACHABLE) != 0 ||
(this.tagBits & NULL_FLAG_MASK) == 0) {
return false;
}
- int position = local.id + this.maxFieldCount;
+ int position = local.getAnalysisId(this.maxFieldCount);
if (position < BitCacheSize) { // use bits
return (this.nullBit4
& (~this.nullBit1 | ~this.nullBit2 & ~this.nullBit3)
@@ -944,14 +981,13 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean isProtectedNonNull(LocalVariableBinding local) {
+final public boolean isProtectedNonNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
- // use bits
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) { // use bits
return (this.nullBit1 & this.nullBit3 & this.nullBit4 & (1L << position)) != 0;
}
// use extra vector
@@ -969,13 +1005,13 @@
& (1L << (position % BitCacheSize))) != 0;
}
-final public boolean isProtectedNull(LocalVariableBinding local) {
+final public boolean isProtectedNull(VariableBinding local) {
if ((this.tagBits & NULL_FLAG_MASK) == 0 ||
(local.type.tagBits & TagBits.IsBaseType) != 0) {
return false;
}
- int position;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
return (this.nullBit1 & this.nullBit2
& (this.nullBit3 ^ this.nullBit4)
@@ -1008,15 +1044,27 @@
throw new AssertionFailedException("assertion failed: " + message); //$NON-NLS-1$
return expression;
}
-public void markAsComparedEqualToNonNull(LocalVariableBinding local) {
+public void markAsComparedEqualToNonNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
+ if (local instanceof FieldBinding) {
+ if ((local.modifiers & AccConstant) == AccConstant) {
+ position = local.getAnalysisId(this.maxFieldCount);
+ } else {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as definitely unknown to avoid deferring null check for these fields
+ this.markAsDefinitelyUnknown(local);
+ return;
+ }
+ } else {
+ position = local.id + this.maxFieldCount;
+ }
long mask;
long a1, a2, a3, a4, na2;
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ if (position < BitCacheSize) {
// use bits
if (((mask = 1L << position)
& (a1 = this.nullBit1)
@@ -1105,14 +1153,26 @@
}
}
-public void markAsComparedEqualToNull(LocalVariableBinding local) {
+public void markAsComparedEqualToNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
int position;
long mask;
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ if (local instanceof FieldBinding) {
+ if ((local.modifiers & AccConstant) == AccConstant) {
+ position = local.getAnalysisId(this.maxFieldCount);
+ } else {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as potential null.
+ this.markNullStatus(local, FlowInfo.POTENTIALLY_NULL);
+ return;
+ }
+ } else {
+ position = local.id + this.maxFieldCount;
+ }
+ if (position < BitCacheSize) {
// use bits
if (((mask = 1L << position) & this.nullBit1) != 0) {
if ((mask
@@ -1233,24 +1293,31 @@
}
}
-public void markAsDefinitelyAssigned(FieldBinding field) {
+public void markAsDefinitelyAssigned(VariableBinding var) {
if (this != DEAD_END)
- markAsDefinitelyAssigned(field.id);
+ markAsDefinitelyAssigned(var.getAnalysisId(this.maxFieldCount));
}
-public void markAsDefinitelyAssigned(LocalVariableBinding local) {
- if (this != DEAD_END)
- markAsDefinitelyAssigned(local.id + this.maxFieldCount);
-}
-
-public void markAsDefinitelyNonNull(LocalVariableBinding local) {
+public void markAsDefinitelyNonNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
long mask;
int position;
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits
+ if (local instanceof FieldBinding) {
+ if ((local.modifiers & AccConstant) == AccConstant) {
+ position = local.getAnalysisId(this.maxFieldCount);
+ } else {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as definitely unknown to avoid deferring null check for these fields.
+ this.markAsDefinitelyUnknown(local);
+ return;
+ }
+ } else {
+ position = local.id + this.maxFieldCount;
+ }
+ if (position < BitCacheSize) { // use bits
// set assigned non null
this.nullBit1 |= (mask = 1L << position);
this.nullBit3 |= mask;
@@ -1297,14 +1364,26 @@
}
}
-public void markAsDefinitelyNull(LocalVariableBinding local) {
+public void markAsDefinitelyNull(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
long mask;
int position;
// position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) { // use bits
+ if (local instanceof FieldBinding) {
+ if ((local.modifiers & AccConstant) == AccConstant) {
+ position = local.getAnalysisId(this.maxFieldCount);
+ } else {
+ // non-final fields may be modified in separate threads and we cannot be sure about their
+ // definite nullness. Hence, marking as potential null.
+ this.markNullStatus(local, FlowInfo.POTENTIALLY_NULL);
+ return;
+ }
+ } else {
+ position = local.id + this.maxFieldCount;
+ }
+ if (position < BitCacheSize) { // use bits
// mark assigned null
this.nullBit1 |= (mask = 1L << position);
this.nullBit2 |= mask;
@@ -1357,14 +1436,13 @@
*/
// PREMATURE may try to get closer to markAsDefinitelyAssigned, but not
// obvious
-public void markAsDefinitelyUnknown(LocalVariableBinding local) {
+public void markAsDefinitelyUnknown(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
long mask;
- int position;
- // position is zero-based
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
// mark assigned null
this.nullBit1 |= (mask = 1L << position);
@@ -1412,12 +1490,12 @@
}
}
-public void resetNullInfo(LocalVariableBinding local) {
+public void resetNullInfo(VariableBinding local) {
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
this.nullBit1 &= (mask = ~(1L << position));
this.nullBit2 &= mask;
@@ -1440,17 +1518,126 @@
}
}
+public void resetNullInfoForFields() {
+ if (this != DEAD_END) {
+ long mask = (-1L << this.maxFieldCount) | this.constantFieldsMask;
+ // first reset normal bits:
+ this.nullBit1 |= ~mask;
+ this.nullBit2 &= mask;
+ this.nullBit3 &= mask;
+ this.nullBit4 |= ~mask;
+ if (this.maxFieldCount >= BitCacheSize && this.extra != null) {
+ // use extra vector
+ int localsStartIndex = this.maxFieldCount/BitCacheSize - 1;
+ int localsStartOffset = this.maxFieldCount % BitCacheSize;
+ if (this.extraConstantFieldMask != null){
+ for (int vectorIndex = 0; vectorIndex < this.extra[2].length; vectorIndex++) {
+ if (vectorIndex >= this.extraConstantFieldMask.length) {
+ // no constant fields after this, just mask all fields
+ if (vectorIndex == localsStartIndex) {
+ // some locals, some fields at this vectorIndex
+ mask = -1L << localsStartOffset;
+ } else {
+ // all fields here
+ mask = 0L;
+ }
+ } else {
+ if (vectorIndex == localsStartIndex) {
+ // some locals, some fields at this vectorIndex
+ mask = ((-1 << localsStartOffset) | this.extraConstantFieldMask[vectorIndex]);
+ } else {
+ // all fields here
+ mask = 0L | this.extraConstantFieldMask[vectorIndex];
+ }
+
+ }
+ this.extra[2][vectorIndex]
+ |= ~mask;
+ this.extra[3][vectorIndex] &= mask;
+ this.extra[4][vectorIndex] &= mask;
+ this.extra[5][vectorIndex] |= ~mask;
+ }
+ } else {
+ // no constant fields
+ for (int vectorIndex = 0; vectorIndex < this.extra[2].length; vectorIndex++) {
+ if (vectorIndex == localsStartIndex) {
+ // some locals, some fields at this vectorIndex
+ mask = -1L << localsStartOffset;
+ } else {
+ // all fields here
+ mask = 0L;
+ }
+ this.extra[2][vectorIndex]
+ |= ~mask;
+ this.extra[3][vectorIndex] &= mask;
+ this.extra[4][vectorIndex] &= mask;
+ this.extra[5][vectorIndex] |= ~mask;
+ }
+ }
+ }
+ }
+}
+
+public void updateConstantFieldsMask(FieldBinding field) {
+ int position = field.getAnalysisId(this.maxFieldCount);
+ long mask = 1L << (position % BitCacheSize);
+ if (position < BitCacheSize) {
+ this.constantFieldsMask |= 1L << position; // exclude this field from being reset
+ } else {
+ // use extra vector
+ int vectorIndex = (position / BitCacheSize) - 1;
+ if (this.extraConstantFieldMask == null) {
+ // extra array not created. Create constant field mask bit streams.
+ int length = vectorIndex + 1;
+ this.extraConstantFieldMask = new long[length];
+ }
+ else {
+ int oldLength; // might need to grow the arrays
+ if (vectorIndex >= (oldLength = this.extraConstantFieldMask.length)) {
+ System.arraycopy(this.extraConstantFieldMask, 0, (this.extraConstantFieldMask = new long[vectorIndex + 1]), 0, oldLength);
+ }
+ }
+ this.extraConstantFieldMask[vectorIndex] |= mask; // exclude this field from resetNullInfoForFields
+ }
+}
+
+/**
+ * All the infos originate in TypeDeclaration.analyseCode(). So making sure that this method is called for every info that is sent into
+ * methods/constructors should be sufficient
+ */
+public void addConstantFieldsMask(UnconditionalFlowInfo other) {
+ this.constantFieldsMask |= other.constantFieldsMask;
+ if (other.extraConstantFieldMask != null) {
+ int oldLength = 0;
+ int otherLen = other.extraConstantFieldMask.length;
+ if (this.extraConstantFieldMask != null) {
+ oldLength = this.extraConstantFieldMask.length;
+ if (otherLen >= (oldLength = this.extraConstantFieldMask.length)) {
+ System.arraycopy(this.extraConstantFieldMask, 0, (this.extraConstantFieldMask = new long[otherLen]), 0, oldLength);
+ }
+ for (int i = 0; i < this.extraConstantFieldMask.length; i++) {
+ this.extraConstantFieldMask[i] |= other.extraConstantFieldMask[i];
+ }
+ } else {
+ this.extraConstantFieldMask = new long[otherLen];
+ }
+ for (int i = oldLength; i < otherLen; i++) {
+ this.extraConstantFieldMask[i] = other.extraConstantFieldMask[i];
+ }
+ }
+}
+
/**
* Mark a local as potentially having been assigned to an unknown value.
* @param local the local to mark
*/
-public void markPotentiallyUnknownBit(LocalVariableBinding local) {
+public void markPotentiallyUnknownBit(VariableBinding local) {
// protected from non-object locals in calling methods
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
mask = 1L << position;
isTrue((this.nullBit1 & mask) == 0, "Adding 'unknown' mark in unexpected state"); //$NON-NLS-1$
@@ -1492,12 +1679,12 @@
}
}
-public void markPotentiallyNullBit(LocalVariableBinding local) {
+public void markPotentiallyNullBit(VariableBinding local) {
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
mask = 1L << position;
isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially null' mark in unexpected state"); //$NON-NLS-1$
@@ -1539,12 +1726,12 @@
}
}
-public void markPotentiallyNonNullBit(LocalVariableBinding local) {
+public void markPotentiallyNonNullBit(VariableBinding local) {
if (this != DEAD_END) {
this.tagBits |= NULL_FLAG_MASK;
- int position;
long mask;
- if ((position = local.id + this.maxFieldCount) < BitCacheSize) {
+ int position = local.getAnalysisId(this.maxFieldCount);
+ if (position < BitCacheSize) {
// use bits
mask = 1L << position;
isTrue((this.nullBit1 & mask) == 0, "Adding 'potentially non-null' mark in unexpected state"); //$NON-NLS-1$
@@ -1901,6 +2088,8 @@
copy.tagBits = this.tagBits & ~NULL_FLAG_MASK;
copy.maxFieldCount = this.maxFieldCount;
copy.nullStatusChangedInAssert = this.nullStatusChangedInAssert;
+ copy.constantFieldsMask = this.constantFieldsMask;
+ copy.extraConstantFieldMask = this.extraConstantFieldMask;
if (this.extra != null) {
int length;
copy.extra = new long[extraLength][];
@@ -2023,6 +2212,8 @@
UnconditionalFlowInfo copy = new UnconditionalFlowInfo();
copy.tagBits = this.tagBits;
copy.maxFieldCount = this.maxFieldCount;
+ copy.constantFieldsMask = this.constantFieldsMask;
+ copy.extraConstantFieldMask = this.extraConstantFieldMask;
int limit = this.maxFieldCount;
if (limit < BitCacheSize) {
long mask;
@@ -2077,8 +2268,8 @@
return this;
}
-public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) {
- int position = local.id + this.maxFieldCount;
+public void markedAsNullOrNonNullInAssertExpression(VariableBinding binding) {
+ int position = binding.getAnalysisId(this.maxFieldCount);
int oldLength;
if (this.nullStatusChangedInAssert == null) {
this.nullStatusChangedInAssert = new int[position + 1];
@@ -2091,8 +2282,8 @@
this.nullStatusChangedInAssert[position] = 1;
}
-public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) {
- int position = local.id + this.maxFieldCount;
+public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding binding) {
+ int position = binding.getAnalysisId(this.maxFieldCount);
if(this.nullStatusChangedInAssert == null || position >= this.nullStatusChangedInAssert.length) {
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
index bd0c98d..1d6cf3f 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -138,6 +138,7 @@
public static final String OPTION_ReportTasks = "org.eclipse.jdt.core.compiler.problem.tasks"; //$NON-NLS-1$
public static final String OPTION_ReportUnusedObjectAllocation = "org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation"; //$NON-NLS-1$
public static final String OPTION_IncludeNullInfoFromAsserts = "org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts"; //$NON-NLS-1$
+ public static final String OPTION_IncludeFieldsInNullAnalysis = "org.eclipse.jdt.core.compiler.problem.includeFieldsInNullAnalysis"; //$NON-NLS-1$
public static final String OPTION_ReportMethodCanBeStatic = "org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic"; //$NON-NLS-1$
public static final String OPTION_ReportMethodCanBePotentiallyStatic = "org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic"; //$NON-NLS-1$
public static final String OPTION_ReportRedundantSpecificationOfTypeArguments = "org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments"; //$NON-NLS-1$
@@ -386,6 +387,8 @@
public boolean ignoreMethodBodies;
/** Raise null related warnings for variables tainted inside an assert statement (java 1.4 and above)*/
public boolean includeNullInfoFromAsserts;
+ /** Specify if fields are to be included in null analysis */
+ public boolean includeFieldsInNullAnalysis;
/** Controls whether forced generic type problems get reported */
public boolean reportUnavoidableGenericTypeProblems;
@@ -1058,6 +1061,7 @@
optionsMap.put(OPTION_ReportTasks, getSeverityString(Tasks));
optionsMap.put(OPTION_ReportUnusedObjectAllocation, getSeverityString(UnusedObjectAllocation));
optionsMap.put(OPTION_IncludeNullInfoFromAsserts, this.includeNullInfoFromAsserts ? ENABLED : DISABLED);
+ optionsMap.put(OPTION_IncludeFieldsInNullAnalysis, this.includeFieldsInNullAnalysis ? ENABLED : DISABLED);
optionsMap.put(OPTION_ReportMethodCanBeStatic, getSeverityString(MethodCanBeStatic));
optionsMap.put(OPTION_ReportMethodCanBePotentiallyStatic, getSeverityString(MethodCanBePotentiallyStatic));
optionsMap.put(OPTION_ReportRedundantSpecificationOfTypeArguments, getSeverityString(RedundantSpecificationOfTypeArguments));
@@ -1227,6 +1231,9 @@
// allow null info from asserts to be considered downstream by default
this.includeNullInfoFromAsserts = false;
+ // null analysis for fields
+ this.includeFieldsInNullAnalysis = false;
+
this.isAnnotationBasedNullAnalysisEnabled = false;
this.nullableAnnotationName = DEFAULT_NULLABLE_ANNOTATION_NAME;
this.nonNullAnnotationName = DEFAULT_NONNULL_ANNOTATION_NAME;
@@ -1452,6 +1459,13 @@
this.includeNullInfoFromAsserts = false;
}
}
+ if ((optionValue = optionsMap.get(OPTION_IncludeFieldsInNullAnalysis)) != null) {
+ if (ENABLED.equals(optionValue)) {
+ this.includeFieldsInNullAnalysis = true;
+ } else if (DISABLED.equals(optionValue)) {
+ this.includeFieldsInNullAnalysis = false;
+ }
+ }
if ((optionValue = optionsMap.get(OPTION_ReportMethodWithConstructorName)) != null) updateSeverity(MethodWithConstructorName, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportOverridingPackageDefaultMethod)) != null) updateSeverity(OverriddenPackageDefaultMethod, optionValue);
if ((optionValue = optionsMap.get(OPTION_ReportDeprecation)) != null) updateSeverity(UsingDeprecatedAPI, optionValue);
@@ -1742,6 +1756,7 @@
buf.append("\n\t- missing @Deprecated annotation: ").append(getSeverityString(MissingDeprecatedAnnotation)); //$NON-NLS-1$
buf.append("\n\t- incomplete enum switch: ").append(getSeverityString(IncompleteEnumSwitch)); //$NON-NLS-1$
buf.append("\n\t- raise null related warnings for variables tainted in assert statements: ").append(this.includeNullInfoFromAsserts ? ENABLED : DISABLED); //$NON-NLS-1$
+ buf.append("\n\t- include fields in null analysis: ").append(this.includeFieldsInNullAnalysis ? ENABLED : DISABLED); //$NON-NLS-1$
buf.append("\n\t- suppress warnings: ").append(this.suppressWarnings ? ENABLED : DISABLED); //$NON-NLS-1$
buf.append("\n\t- suppress optional errors: ").append(this.suppressOptionalErrors ? ENABLED : DISABLED); //$NON-NLS-1$
buf.append("\n\t- unhandled warning token: ").append(getSeverityString(UnhandledWarningToken)); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
index 8778c6b..4d5f38e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
@@ -40,7 +40,6 @@
public TypeDeclaration referenceContext;
public TypeReference superTypeReference;
java.util.ArrayList deferredBoundChecks;
-
public ClassScope(Scope parent, TypeDeclaration context) {
super(Scope.CLASS_SCOPE, parent);
this.referenceContext = context;
@@ -81,6 +80,7 @@
}
}
}
+ this.referenceContext.binding.cumulativeFieldCount += outerMostMethodScope().analysisIndex;
connectMemberTypes();
buildFieldsAndMethods();
anonymousType.faultInTypesForFieldsAndMethods();
@@ -145,6 +145,7 @@
// remove duplicate fields
if (count != fieldBindings.length)
System.arraycopy(fieldBindings, 0, fieldBindings = new FieldBinding[count], 0, count);
+ sourceType.cumulativeFieldCount += count;
sourceType.tagBits &= ~(TagBits.AreFieldsSorted|TagBits.AreFieldsComplete); // in case some static imports reached already into this type
sourceType.setFields(fieldBindings);
}
@@ -227,6 +228,7 @@
checkParameterizedTypeBounds();
checkParameterizedSuperTypeCollisions();
}
+ this.referenceContext.binding.cumulativeFieldCount += outerMostMethodScope().analysisIndex;
buildFieldsAndMethods();
localType.faultInTypesForFieldsAndMethods();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
index 2c44d5a..4c9aa8d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/FieldBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -275,6 +275,13 @@
return originalField.tagBits;
}
+public int getAnalysisId(int maxFieldCount) {
+ TypeBinding original = this.declaringClass.original();
+ if (original instanceof SourceTypeBinding)
+ return ((SourceTypeBinding)original).fieldAnalysisOffset + this.id;
+ return this.id;
+}
+
public final boolean isDefault() {
return !isPublic() && !isProtected() && !isPrivate();
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index c7ad46f..83981f5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -44,6 +44,8 @@
public TypeVariableBinding[] typeVariables;
public ClassScope scope;
+ public int fieldAnalysisOffset; // an offset for ids of fields of this class (id to be used for flow analysis)
+ public int cumulativeFieldCount; // cumulative field count from all enclosing types, used to build unique field id's for member types.
// Synthetics are separated into 4 categories: methods, super methods, fields, class literals and bridge methods
// if a new category is added, also increment MAX_SYNTHETICS
@@ -68,6 +70,7 @@
this.modifiers = scope.referenceContext.modifiers;
this.sourceName = scope.referenceContext.name;
this.scope = scope;
+ this.cumulativeFieldCount = 0;
// expect the fields & methods to be initialized correctly later
this.fields = Binding.UNINITIALIZED_FIELDS;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java
index dc39e36..273f2af 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java
@@ -37,6 +37,10 @@
return this.constant;
}
+ public int getAnalysisId(int maxFieldCount) {
+ return this.id + maxFieldCount;
+ }
+
public abstract AnnotationBinding[] getAnnotations();
public final boolean isBlankFinal(){
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index 49bac9d..1df2202 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -109,6 +109,7 @@
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
+import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.WildcardBinding;
import org.eclipse.jdt.internal.compiler.parser.JavadocTagConstants;
import org.eclipse.jdt.internal.compiler.parser.Parser;
@@ -287,18 +288,26 @@
return CompilerOptions.VarargsArgumentNeedCast;
case IProblem.NullLocalVariableReference:
+ case IProblem.NullFieldReference:
return CompilerOptions.NullReference;
case IProblem.PotentialNullLocalVariableReference:
case IProblem.PotentialNullMessageSendReference:
+ case IProblem.PotentialNullFieldReference:
return CompilerOptions.PotentialNullReference;
case IProblem.RedundantLocalVariableNullAssignment:
+ case IProblem.RedundantFieldNullAssignment:
case IProblem.RedundantNullCheckOnNonNullLocalVariable:
case IProblem.RedundantNullCheckOnNullLocalVariable:
case IProblem.NonNullLocalVariableComparisonYieldsFalse:
case IProblem.NullLocalVariableComparisonYieldsFalse:
case IProblem.NullLocalVariableInstanceofYieldsFalse:
+ case IProblem.NullFieldInstanceofYieldsFalse:
+ case IProblem.RedundantNullCheckOnNonNullField:
+ case IProblem.RedundantNullCheckOnNullField:
+ case IProblem.NonNullFieldComparisonYieldsFalse:
+ case IProblem.NullFieldComparisonYieldsFalse:
case IProblem.RedundantNullCheckOnNonNullMessageSend:
return CompilerOptions.RedundantNullCheck;
@@ -5073,110 +5082,158 @@
}
}
-public void localVariableNonNullComparedToNull(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.NonNullLocalVariableComparisonYieldsFalse);
+public void variableNonNullComparedToNull(VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.NonNullFieldComparisonYieldsFalse;
+ } else {
+ problem = IProblem.NonNullLocalVariableComparisonYieldsFalse;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.NonNullLocalVariableComparisonYieldsFalse,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariableNullComparedToNonNull(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.NullLocalVariableComparisonYieldsFalse);
+public void variableNullComparedToNonNull(VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.NullFieldComparisonYieldsFalse;
+ } else {
+ problem = IProblem.NullLocalVariableComparisonYieldsFalse;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.NullLocalVariableComparisonYieldsFalse,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariableNullInstanceof(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.NullLocalVariableInstanceofYieldsFalse);
+public void variableNullInstanceof(VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.NullFieldInstanceofYieldsFalse;
+ } else {
+ problem = IProblem.NullLocalVariableInstanceofYieldsFalse;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.NullLocalVariableInstanceofYieldsFalse,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariableNullReference(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.NullLocalVariableReference);
+public void variableNullReference(VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.NullFieldReference;
+ } else {
+ problem = IProblem.NullLocalVariableReference;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.NullLocalVariableReference,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariablePotentialNullReference(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.PotentialNullLocalVariableReference);
+public void variablePotentialNullReference(VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.PotentialNullFieldReference;
+ } else {
+ problem = IProblem.PotentialNullLocalVariableReference;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name)};
+ String[] arguments = new String[] {new String(variable.name)};
this.handle(
- IProblem.PotentialNullLocalVariableReference,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariableRedundantCheckOnNonNull(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.RedundantNullCheckOnNonNullLocalVariable);
+public void variableRedundantCheckOnNonNull(VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.RedundantNullCheckOnNonNullField;
+ } else {
+ problem = IProblem.RedundantNullCheckOnNonNullLocalVariable;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.RedundantNullCheckOnNonNullLocalVariable,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariableRedundantCheckOnNull(LocalVariableBinding local, ASTNode location) {
- int severity = computeSeverity(IProblem.RedundantNullCheckOnNullLocalVariable);
+public void variableRedundantCheckOnNull (VariableBinding variable, ASTNode location) {
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.RedundantNullCheckOnNullField;
+ } else {
+ problem = IProblem.RedundantNullCheckOnNullLocalVariable;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.RedundantNullCheckOnNullLocalVariable,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
-public void localVariableRedundantNullAssignment(LocalVariableBinding local, ASTNode location) {
+public void variableRedundantNullAssignment (VariableBinding variable, ASTNode location) {
if ((location.bits & ASTNode.FirstAssignmentToLocal) != 0) // https://bugs.eclipse.org/338303 - Warning about Redundant assignment conflicts with definite assignment
return;
- int severity = computeSeverity(IProblem.RedundantLocalVariableNullAssignment);
+ int problem;
+ if (variable instanceof FieldBinding) {
+ problem = IProblem.RedundantFieldNullAssignment;
+ } else {
+ problem = IProblem.RedundantLocalVariableNullAssignment;
+ }
+ int severity = computeSeverity(problem);
if (severity == ProblemSeverities.Ignore) return;
- String[] arguments = new String[] {new String(local.name) };
+ String[] arguments = new String[] {new String(variable.name) };
this.handle(
- IProblem.RedundantLocalVariableNullAssignment,
+ problem,
arguments,
arguments,
severity,
- nodeSourceStart(local, location),
- nodeSourceEnd(local, location));
+ nodeSourceStart(variable, location),
+ nodeSourceEnd(variable, location));
}
public void methodMustOverride(AbstractMethodDeclaration method, long complianceLevel) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index d05ebd2..dba1762 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -581,6 +581,16 @@
### MORE GENERICS
660 = Unused type arguments for the non generic constructor {0}({1}) of type {2}; it should not be parameterized with arguments <{3}>
+### NULL ANALYSIS FOR FIELDS
+670 = Null pointer access: The field {0} can only be null at this location
+671 = Potential null pointer access: The field {0} may be null at this location
+672 = Redundant null check: The field {0} can only be null at this location
+673 = Null comparison always yields false: The field {0} can only be null at this location
+674 = Redundant null check: The field {0} cannot be null at this location
+675 = Null comparison always yields false: The field {0} cannot be null at this location
+676 = Redundant assignment: The field {0} can only be null at this location
+677 = instanceof always yields false: The field {0} can only be null at this location
+
### CORRUPTED BINARIES
700 = The class file {0} contains a signature ''{1}'' ill-formed at position {2}
diff --git a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java
index 52c943d..4bdcc4d 100644
--- a/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java
+++ b/org.eclipse.jdt.core/eval/org/eclipse/jdt/internal/eval/CodeSnippetClassFile.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2010 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -126,6 +126,7 @@
}
// retrieve the enclosing one guaranteed to be the one matching the propagated flow info
// 1FF9ZBU: LFCOM:ALL - Local variable attributes busted (Sanity check)
+ // see also https://bugs.eclipse.org/bugs/show_bug.cgi?id=247564#c65
this.codeStream.maxFieldCount = aType.scope.outerMostClassScope().referenceType().maxFieldCount;
}
/**
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
index 5898902..8b2e896 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2012 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
@@ -1780,6 +1780,20 @@
*/
public static final String COMPILER_PB_INCLUDE_ASSERTS_IN_NULL_ANALYSIS = PLUGIN_ID + ".compiler.problem.includeNullInfoFromAsserts"; //$NON-NLS-1$
/**
+ * Compiler option ID: Raise null related errors or warnings on fields.
+ * <p>When enabled, the compiler will flag all null related errors or warnings that have been enabled by the user
+ * on fields, in addition to local variables.</p>
+ * <p>When disabled, the compiler will not flag null related errors or warnings on fields.</p>
+ * <dl>
+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.includeFieldsInNullAnalysis"</code></dd>
+ * <dt>Possible values:</dt><dd><code>{ "enabled", "disabled" }</code></dd>
+ * <dt>Default:</dt><dd><code>"disabled"</code></dd>
+ * </dl>
+ * @since 3.8
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_PB_INCLUDE_FIELDS_IN_NULL_ANALYSIS = PLUGIN_ID + ".compiler.problem.includeFieldsInNullAnalysis"; //$NON-NLS-1$
+ /**
* Compiler option ID: Further Determining the Effect of <code>@SuppressWarnings</code> if also
* {@link #COMPILER_PB_SUPPRESS_WARNINGS} is enabled.
* <p>When enabled, the <code>@SuppressWarnings</code> annotation can additionally be used to suppress