From a1c831accbe1d013566d961396c6adfb5c4fb365 Mon Sep 17 00:00:00 2001 From: Ayushman Jain Date: Thu, 15 Dec 2011 00:20:13 +0100 Subject: Fix v1.2.1 --- .../regression/CompilerInvocationTests.java | 16 + .../regression/NullReferenceImplTests.java | 32 +- .../compiler/regression/NullReferenceTest.java | 608 +++++++++++++++++++-- .../org/eclipse/jdt/core/compiler/IProblem.java | 20 + .../jdt/internal/compiler/ast/Assignment.java | 26 +- .../eclipse/jdt/internal/compiler/ast/Block.java | 2 + .../jdt/internal/compiler/ast/CastExpression.java | 8 + .../jdt/internal/compiler/ast/EqualExpression.java | 6 +- .../jdt/internal/compiler/ast/Expression.java | 15 +- .../internal/compiler/ast/FieldDeclaration.java | 7 + .../jdt/internal/compiler/ast/FieldReference.java | 14 + .../compiler/ast/InstanceOfExpression.java | 10 +- .../jdt/internal/compiler/ast/MessageSend.java | 3 + .../internal/compiler/ast/SingleNameReference.java | 16 +- .../jdt/internal/compiler/ast/Statement.java | 9 +- .../jdt/internal/compiler/ast/TypeDeclaration.java | 50 +- .../compiler/flow/ConditionalFlowInfo.java | 44 +- .../internal/compiler/flow/FinallyFlowContext.java | 93 ++-- .../jdt/internal/compiler/flow/FlowContext.java | 28 +- .../jdt/internal/compiler/flow/FlowInfo.java | 194 ++++--- .../internal/compiler/flow/LoopingFlowContext.java | 102 ++-- .../internal/compiler/flow/NullInfoRegistry.java | 66 ++- .../compiler/flow/UnconditionalFlowInfo.java | 260 +++++++-- .../jdt/internal/compiler/lookup/ClassScope.java | 28 +- .../jdt/internal/compiler/lookup/FieldBinding.java | 12 + .../internal/compiler/problem/ProblemReporter.java | 153 ++++-- .../internal/compiler/problem/messages.properties | 10 + .../jdt/internal/eval/CodeSnippetClassFile.java | 2 +- 28 files changed, 1421 insertions(+), 413 deletions(-) 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 95fa1b11c3..670e436152 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 @@ public void test011_problem_categories() { 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 @@ public void test011_problem_categories() { 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 @@ public void test011_problem_categories() { 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 @@ public void test011_problem_categories() { 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 @@ public void test012_compiler_problems_tuning() { 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 @@ public void test012_compiler_problems_tuning() { 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 @@ public void test012_compiler_problems_tuning() { 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 @@ public void test012_compiler_problems_tuning() { 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/NullReferenceImplTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullReferenceImplTests.java index 95af131fd8..047f5f5f7c 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 @@ -34,9 +34,11 @@ import org.eclipse.jdt.internal.compiler.flow.NullInfoRegistry; import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo; import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo.AssertionFailedException; 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.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 +1085,36 @@ public FlowInfo copy() { return copy; } -public void markAsDefinitelyNonNull(LocalVariableBinding local) { - grow(local.id + this.maxFieldCount); +public void markAsDefinitelyNonNull(VariableBinding local) { + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + grow(position); super.markAsDefinitelyNonNull(local); } -public void markAsDefinitelyNull(LocalVariableBinding local) { - grow(local.id + this.maxFieldCount); +public void markAsDefinitelyNull(VariableBinding local) { + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + grow(position); super.markAsDefinitelyNull(local); } -public void markAsDefinitelyUnknown(LocalVariableBinding local) { - grow(local.id + this.maxFieldCount); +public void markAsDefinitelyUnknown(VariableBinding local) { + int position; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 2349a4b7fb..4997fb0d32 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 @@ -108,13 +108,12 @@ public void test0002_simple_field() { " 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" ); } @@ -345,13 +344,12 @@ public void test0014_field_with_explicit_this_access() { " 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" ); } @@ -367,13 +365,12 @@ public void test0015_field_with_explicit_this_access() { " 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" ); } @@ -423,13 +420,12 @@ public void test0018_field_of_enclosing_object() { " }\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" ); } @@ -448,13 +444,12 @@ public void test0019_field_synchronized() { " }\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" ); } @@ -15425,4 +15420,547 @@ public void testBug360328d() { "",/* expected error */ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); } + +// 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 static final field +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" + + "Redundant null check: The field o can only be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 7)\n" + + " if (o != null) {}\n" + + " ^\n" + + "Null comparison always yields false: The field o can only be null at this location\n" + + "----------\n" + + "4. WARNING in X.java (at line 7)\n" + + " if (o != null) {}\n" + + " ^^\n" + + "Dead code\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" + + "Redundant null check: The field o can only be null at this location\n" + + "----------\n" + + "3. ERROR in X.java (at line 11)\n" + + " if (o != null) {}\n" + + " ^\n" + + "Null comparison always yields false: The field o can only be null at this location\n" + + "----------\n" + + "4. WARNING in X.java (at line 11)\n" + + " if (o != null) {}\n" + + " ^^\n" + + "Dead code\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 -- 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 -- 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 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 37)\n" + + " if (this.field97.hashCode() == 0) {}\n" + + " ^^^^^^^\n" + + "Potential null pointer access: The field field97 may be null at this location\n" + + "----------\n" + + "3. 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" + ); +} } \ No newline at end of file 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 6b2f414126..d31e46667e 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 @@ void setSourceStart(int sourceStart); /** @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/ast/Assignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java index 970fc2432c..f527decdec 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 @@ -42,14 +42,15 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl // 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(); if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) { this.expression.checkNPE(currentScope, flowContext, flowInfo); } flowInfo = ((Reference) this.lhs) .analyseAssignment(currentScope, flowContext, flowInfo, this, false) .unconditionalInits(); - if (local != null) { + if (var instanceof LocalVariableBinding) { + LocalVariableBinding local = (LocalVariableBinding) var; LocalVariableBinding previousTrackerBinding = null; if (local.closeTracker != null) { // Assigning to a variable already holding an AutoCloseable, has it been closed before? @@ -60,17 +61,23 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl FakedTrackingVariable.handleResourceAssignment(flowInfo, this, this.expression, local, previousTrackerBinding); } 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); + if (var instanceof FieldBinding && var.isFinal() && ((FieldBinding) var).isStatic()) { + // static final field being assigned. Record its null status for future reference + // since the flowInfo from a constructor or static block wont be available in a method + FieldBinding fieldBinding = (FieldBinding) var; + fieldBinding.setNullStatusForStaticFinalField(nullStatus); + } } return flowInfo; } @@ -224,4 +231,7 @@ public void traverse(ASTVisitor visitor, BlockScope scope) { public LocalVariableBinding localVariableBinding() { return this.lhs.localVariableBinding(); } +public VariableBinding variableBinding() { + return this.lhs.variableBinding(); +} } 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 90b2f961ed..daf54e41f6 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 @@ -37,6 +37,8 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl flowInfo = stat.analyseCode(this.scope, flowContext, flowInfo); } } + // don't let the flow info collected for fields from this block persist. + flowInfo.resetNullInfoForFields(); 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 396276aa46..2be8077de5 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 @@ -33,6 +33,7 @@ import org.eclipse.jdt.internal.compiler.lookup.Scope; 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 @@ public LocalVariableBinding localVariableBinding() { return this.expression.localVariableBinding(); } +/** + * @see org.eclipse.jdt.internal.compiler.ast.Expression#variableBinding() + */ +public VariableBinding variableBinding() { + return this.expression.variableBinding(); +} + 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 e521d40fd1..ae49e8c8d5 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 @@ -39,16 +39,16 @@ public class EqualExpression extends BinaryExpression { // TODO: handle all kinds of expressions (cf. also https://bugs.eclipse.org/364326) } - LocalVariableBinding local = this.left.localVariableBinding(); + VariableBinding local = this.left.variableBinding(); 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(); 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 df21615a67..7512e3f837 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 @@ -36,6 +36,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.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 @@ public final boolean checkCastTypesCompatibility(Scope scope, TypeBinding castTy * @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(); 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 @@ public int nullStatus(FlowInfo flowInfo) { this.constant != null && this.constant != Constant.NotAConstant) return FlowInfo.NON_NULL; // constant expression cannot be null - LocalVariableBinding local = localVariableBinding(); + VariableBinding local = variableBinding(); if (local != null) return flowInfo.nullStatus(local); return FlowInfo.NON_NULL; @@ -1112,4 +1113,12 @@ public void traverse(ASTVisitor visitor, BlockScope scope) { 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... + */ +public VariableBinding variableBinding() { + 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 30ab54eb92..57d58e3be0 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 @@ -74,6 +74,13 @@ public FlowInfo analyseCode(MethodScope initializationScope, FlowContext flowCon .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 + + 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 66f16b6742..1377d7882b 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 @@ -35,6 +35,7 @@ import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; 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 +669,17 @@ public void traverse(ASTVisitor visitor, BlockScope scope) { } visitor.endVisit(this, scope); } + +public VariableBinding variableBinding() { + if (this.receiver.isThis() || this.binding.isStatic()) { + if (this.receiver instanceof MessageSend) { + if (((MessageSend) this.receiver).actualReceiverType == this.receiver.resolvedType) { + return this.binding; + } + } else { + return this.binding; + } + } + return super.variableBinding(); +} } 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 3382ebbb0a..55450db385 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 @@ -31,16 +31,16 @@ public InstanceOfExpression(Expression expression, TypeReference type) { } 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(); + 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 46d4540c51..c97fab26d7 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 @@ -115,6 +115,9 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl // 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. + 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/SingleNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java index d625d5b307..5fda9bf308 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 @@ -806,6 +806,15 @@ public LocalVariableBinding localVariableBinding() { return null; } +public VariableBinding variableBinding() { + switch (this.bits & ASTNode.RestrictiveFlagMASK) { + case Binding.FIELD : + // reading a field + 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 +867,10 @@ public int nullStatus(FlowInfo flowInfo) { } 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 8f68b7cc3a..66c17f5075 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 abstract FlowInfo analyseCode(BlockScope currentScope, FlowContext flowCo 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 @@ protected void analyseArguments(BlockScope currentScope, FlowContext flowContext /** 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 f5d41ea501..b6566bf85d 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 @@ -651,7 +651,7 @@ private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { // branch, since the previous initializer already got the blame. if (staticFieldInfo == FlowInfo.DEAD_END) { this.staticInitializerScope.problemReporter().initializerMustCompleteNormally(field); - staticFieldInfo = FlowInfo.initial(this.maxFieldCount).setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); + staticFieldInfo = FlowInfo.initial(this.scope.cumulativeFieldCount).setReachMode(FlowInfo.UNREACHABLE); } } else { if ((nonStaticFieldInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0) @@ -667,7 +667,7 @@ private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { // branch, since the previous initializer already got the blame. if (nonStaticFieldInfo == FlowInfo.DEAD_END) { this.initializerScope.problemReporter().initializerMustCompleteNormally(field); - nonStaticFieldInfo = FlowInfo.initial(this.maxFieldCount).setReachMode(FlowInfo.UNREACHABLE_OR_DEAD); + nonStaticFieldInfo = FlowInfo.initial(this.scope.cumulativeFieldCount).setReachMode(FlowInfo.UNREACHABLE); } } } @@ -683,6 +683,8 @@ private void internalAnalyseCode(FlowContext flowContext, FlowInfo flowInfo) { } if (this.methods != null) { UnconditionalFlowInfo outerInfo = flowInfo.unconditionalFieldLessCopy(); + //int fieldOffset = isLocal ? this.scope.cumulativeFieldCount : this.maxFieldCount; + //int fieldStart = isLocal ? this.scope.localTypeFieldIdStart : 0; FlowInfo constructorInfo = nonStaticFieldInfo.unconditionalInits().discardNonFieldInitializations().addInitializationsFrom(outerInfo); for (int i = 0, count = this.methods.length; i < count; i++) { AbstractMethodDeclaration method = this.methods[i]; @@ -1038,8 +1040,6 @@ public void resolve() { } } while ((current = current.enclosingType()) != null); } - // this.maxFieldCount might already be set - int localMaxFieldCount = 0; int lastVisibleFieldID = -1; boolean hasEnumConstants = false; FieldDeclaration[] enumConstantsWithoutBody = null; @@ -1049,11 +1049,22 @@ public void resolve() { this.typeParameters[i].resolve(this.scope); } } - if (this.memberTypes != null) { - for (int i = 0, count = this.memberTypes.length; i < count; i++) { - this.memberTypes[i].resolve(this.scope); + // field count from supertypes should be included in maxFieldCount, + // so that a field from supertype doesn't end up with same id as a local variable + // in a method being analyzed. + int superFieldsCount = 0; + ReferenceBinding superClassBinding = sourceType.superclass; + while (superClassBinding != null) { + FieldBinding[] unResolvedFields = superClassBinding.unResolvedFields(); + if (unResolvedFields != null) { + superFieldsCount += unResolvedFields.length; } + superFieldsCount += findFieldCountFromSuperInterfaces(superClassBinding.superInterfaces()); + superClassBinding = superClassBinding.superclass(); } + ReferenceBinding[] superInterfacesBinding = this.binding.superInterfaces; + superFieldsCount += findFieldCountFromSuperInterfaces(superInterfacesBinding); + this.scope.cumulativeFieldCount += superFieldsCount; if (this.fields != null) { for (int i = 0, count = this.fields.length; i < count; i++) { FieldDeclaration field = this.fields[i]; @@ -1074,13 +1085,13 @@ public void resolve() { this.ignoreFurtherInvestigation = true; continue; } + field.binding.id += superFieldsCount; if (needSerialVersion && ((fieldBinding.modifiers & (ClassFileConstants.AccStatic | ClassFileConstants.AccFinal)) == (ClassFileConstants.AccStatic | ClassFileConstants.AccFinal)) && CharOperation.equals(TypeConstants.SERIALVERSIONUID, fieldBinding.name) && TypeBinding.LONG == fieldBinding.type) { needSerialVersion = false; } - localMaxFieldCount++; lastVisibleFieldID = field.binding.id; break; @@ -1090,9 +1101,15 @@ public void resolve() { } field.resolve(field.isStatic() ? this.staticInitializerScope : this.initializerScope); } - } - if (this.maxFieldCount < localMaxFieldCount) { - this.maxFieldCount = localMaxFieldCount; + } + // if (this.maxFieldCount < localMaxFieldCount) { + // this.maxFieldCount = localMaxFieldCount; + // } + this.maxFieldCount = this.scope.cumulativeFieldCount; + if (this.memberTypes != null) { + for (int i = 0, count = this.memberTypes.length; i < count; i++) { + this.memberTypes[i].resolve(this.scope); + } } if (needSerialVersion) { //check that the current type doesn't extend javax.rmi.CORBA.Stub @@ -1180,6 +1197,17 @@ public void resolve() { } } +private int findFieldCountFromSuperInterfaces(ReferenceBinding[] superinterfaces) { + int numOfFields = 0; + if (superinterfaces == null) + return numOfFields ; + for (int i = 0; i < superinterfaces.length; i++) { + numOfFields += superinterfaces[i].fieldCount(); + numOfFields += findFieldCountFromSuperInterfaces(superinterfaces[i].superInterfaces()); + } + return numOfFields; +} + /** * Resolve a local type declaration */ 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 5cb4166561..718f23a94d 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 @@ -13,6 +13,7 @@ package org.eclipse.jdt.internal.compiler.flow; 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 @@ -85,17 +86,17 @@ public boolean isDefinitelyAssigned(LocalVariableBinding 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); } @@ -110,37 +111,37 @@ public boolean isPotentiallyAssigned(LocalVariableBinding 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); } @@ -155,37 +156,42 @@ public void markAsDefinitelyAssigned(LocalVariableBinding 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 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 +249,12 @@ public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { 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 d148222482..d9234efdac 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 @@ -32,7 +32,7 @@ public class FinallyFlowContext extends FlowContext { VariableBinding[] finalVariables; int assignCount; - LocalVariableBinding[] nullLocals; + VariableBinding[] nullVariables; Expression[] nullReferences; int[] nullCheckTypes; int nullCount; @@ -87,29 +87,28 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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; @@ -123,27 +122,27 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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)) { @@ -151,14 +150,14 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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; @@ -167,11 +166,11 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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: @@ -226,7 +225,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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 @@ -240,14 +239,14 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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); @@ -259,11 +258,11 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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); @@ -271,34 +270,34 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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; @@ -310,7 +309,7 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { return; } if (flowInfo.canOnlyBeNull(local)) { - scope.problemReporter().localVariableNullReference(local, reference); + scope.problemReporter().variableNullReference(local, reference); return; } break; @@ -325,14 +324,14 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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); @@ -349,11 +348,11 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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); @@ -361,34 +360,34 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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; @@ -397,11 +396,11 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { 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)) { @@ -433,17 +432,17 @@ public void complainOnDeferredChecks(FlowInfo flowInfo, BlockScope scope) { } } -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, @@ -452,7 +451,7 @@ protected void recordNullReference(LocalVariableBinding local, 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 98caf30e05..33fd6c5f58 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 @@ protected boolean recordFinalAssignment(VariableBinding variable, Reference fina * 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 @@ public void recordSettingFinal(VariableBinding variable, Reference finalReferenc * 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 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 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 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding 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().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 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 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 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 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 b931372d0d..cdab292d19 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 @@ -17,6 +17,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; 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 { @@ -94,39 +95,42 @@ abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits); } /** - * 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); } /** @@ -174,25 +178,29 @@ abstract public FlowInfo initsWhenFalse(); 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 + * 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(LocalVariableBinding local); + public abstract boolean isDefinitelyNonNull(VariableBinding binding); /** - * 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 + * 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(LocalVariableBinding local); +public abstract boolean isDefinitelyNull(VariableBinding binding); /** - * 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 + * 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(LocalVariableBinding local); +public abstract boolean isDefinitelyUnknown(VariableBinding binding); /** * Check status of potential assignment for a field. @@ -206,59 +214,59 @@ public abstract boolean isDefinitelyUnknown(LocalVariableBinding local); abstract public boolean isPotentiallyAssigned(LocalVariableBinding field); /** - * 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. @@ -266,34 +274,38 @@ abstract public void markAsComparedEqualToNull(LocalVariableBinding local); abstract public void markAsDefinitelyAssigned(FieldBinding field); /** - * Record a local got definitely assigned to a non-null value. + * Record a field or local got definitely assigned to a non-null value. */ - abstract public void markAsDefinitelyNonNull(LocalVariableBinding local); + abstract public void markAsDefinitelyNonNull(VariableBinding binding); /** - * Record a local got definitely assigned to null. + * Record a field or local got definitely assigned to null. */ - abstract public void markAsDefinitelyNull(LocalVariableBinding local); + abstract public void markAsDefinitelyNull(VariableBinding binding); /** - * Reset all null-information about a given local. + * Reset all null-information about a given field or local. */ - abstract public void resetNullInfo(LocalVariableBinding local); + abstract public void resetNullInfo(VariableBinding binding); /** - * Record a local may have got assigned to unknown (set the bit on existing info). + * variant of {@link #resetNullInfo(VariableBinding)} for resetting null info for all fields */ - abstract public void markPotentiallyUnknownBit(LocalVariableBinding local); + abstract public void resetNullInfoForFields(); + /** + * 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 null (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 markPotentiallyNullBit(LocalVariableBinding local); + abstract public void markPotentiallyNullBit(VariableBinding binding); /** - * Record a local may have got assigned to non-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 markPotentiallyNonNullBit(LocalVariableBinding local); + abstract public void markPotentiallyNonNullBit(VariableBinding binding); /** * Record a local got definitely assigned. @@ -301,59 +313,59 @@ abstract public void markAsComparedEqualToNull(LocalVariableBinding local); abstract public void markAsDefinitelyAssigned(LocalVariableBinding local); /** - * 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 +613,14 @@ abstract public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect(); * 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 38295c8cb7..6013dd0445 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 @@ -47,7 +47,7 @@ public class LoopingFlowContext extends SwitchFlowContext { VariableBinding finalVariables[]; int assignCount = 0; - LocalVariableBinding[] nullLocals; + VariableBinding[] nullVariables; Expression[] nullReferences; int[] nullCheckTypes; int nullCount; @@ -143,7 +143,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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]) { @@ -153,11 +153,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -169,11 +169,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -182,11 +182,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -201,27 +201,27 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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)) { @@ -229,14 +229,14 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -246,7 +246,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn case MAY_NULL: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; - scope.problemReporter().localVariableNullReference(local, expression); + scope.problemReporter().variableNullReference(local, expression); continue; } break; @@ -265,7 +265,7 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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: @@ -273,11 +273,11 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -292,27 +292,27 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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)) { @@ -320,14 +320,14 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -337,12 +337,12 @@ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowIn 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; @@ -474,27 +474,27 @@ public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) 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)) { @@ -506,14 +506,14 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 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); @@ -522,14 +522,14 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, } 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); @@ -573,11 +573,11 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding 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().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); @@ -585,34 +585,34 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 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; @@ -631,11 +631,11 @@ public void recordUsingNullReference(Scope scope, LocalVariableBinding local, 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 14707955b9..296b7e3ba0 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 @@ -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 @@ -116,13 +117,18 @@ public NullInfoRegistry add(NullInfoRegistry other) { 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) { + position = local.id; + } else { + position = local.id + 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 +167,18 @@ public void markAsComparedEqualToNonNull(LocalVariableBinding local) { } } -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) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits // set assigned non null this.nullBit3 |= (1L << position); if (COVERAGE_TEST_FLAG) { @@ -207,13 +218,18 @@ public void markAsDefinitelyNonNull(LocalVariableBinding local) { } // 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) { + position = local.id; + } else { + position = local.id + 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 +268,18 @@ public void markAsDefinitelyNull(LocalVariableBinding local) { } } -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; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 +428,18 @@ public String toString(){ * 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 +480,17 @@ public void markPotentiallyUnknownBit(LocalVariableBinding local) { } } -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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 +531,17 @@ public void markPotentiallyNullBit(LocalVariableBinding local) { } } -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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 ed1ae7ad83..74eecbf7e8 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 @@ -23,6 +23,7 @@ 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 @@ -520,13 +521,18 @@ public UnconditionalFlowInfo addPotentialNullInfoFrom( 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return ( (~this.nullBit1 @@ -551,13 +557,18 @@ final public boolean cannotBeDefinitelyNullOrNonNull(LocalVariableBinding local) & (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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit3 & ((this.nullBit2 & this.nullBit4) | ~this.nullBit2) @@ -578,13 +589,18 @@ final public boolean cannotBeNull(LocalVariableBinding local) { & (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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit2 & (~this.nullBit3 | ~this.nullBit4) @@ -750,7 +766,13 @@ final public boolean isDefinitelyAssigned(LocalVariableBinding local) { return isDefinitelyAssigned(local.id + this.maxFieldCount); } -final public boolean isDefinitelyNonNull(LocalVariableBinding local) { +final public boolean isDefinitelyNonNull(VariableBinding local) { + if (local instanceof FieldBinding && (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,16 @@ final public boolean isDefinitelyNonNull(LocalVariableBinding local) { local.constant() != Constant.NotAConstant) { // String instances return true; } - int position = local.id + this.maxFieldCount; + int position; + if (local instanceof FieldBinding) { + if (local.isFinal() && ((FieldBinding)local).isStatic()) { + // static final field's null status may not be in the flow info + return (((FieldBinding) local).getNullStatusForStaticFinalField() == FlowInfo.NON_NULL); + } + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return ((this.nullBit1 & this.nullBit3 & (~this.nullBit2 | this.nullBit4)) & (1L << position)) != 0; @@ -779,14 +810,29 @@ final public boolean isDefinitelyNonNull(LocalVariableBinding local) { & (1L << (position % BitCacheSize))) != 0; } -final public boolean isDefinitelyNull(LocalVariableBinding local) { +final public boolean isDefinitelyNull(VariableBinding local) { + if (local instanceof FieldBinding && (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; + if (local instanceof FieldBinding) { + if (local.isFinal() && ((FieldBinding)local).isStatic()) { + // static final field's null status may not be in the flow info + return (((FieldBinding) local).getNullStatusForStaticFinalField() == FlowInfo.NULL); + } + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return ((this.nullBit1 & this.nullBit2 & (~this.nullBit3 | ~this.nullBit4)) @@ -806,13 +852,18 @@ final public boolean isDefinitelyNull(LocalVariableBinding local) { & (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; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return ((this.nullBit1 & this.nullBit4 & ~this.nullBit2 & ~this.nullBit3) & (1L << position)) != 0; @@ -866,13 +917,18 @@ final public boolean isPotentiallyAssigned(LocalVariableBinding local) { } // 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits // use bits return ((this.nullBit3 & (~this.nullBit1 | ~this.nullBit2)) & (1L << position)) != 0; @@ -892,13 +948,22 @@ final public boolean isPotentiallyNonNull(LocalVariableBinding local) { } // 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) { + if (local instanceof FieldBinding) { + if (local.isFinal() && ((FieldBinding)local).isStatic()) { + // static final field's null status may not be in the flow info + return (((FieldBinding) local).getNullStatusForStaticFinalField() == FlowInfo.POTENTIALLY_NULL); + } + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return ((this.nullBit2 & (~this.nullBit1 | ~this.nullBit3)) & (1L << position)) != 0; @@ -917,13 +982,18 @@ final public boolean isPotentiallyNull(LocalVariableBinding local) { & (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; + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } if (position < BitCacheSize) { // use bits return (this.nullBit4 & (~this.nullBit1 | ~this.nullBit2 & ~this.nullBit3) @@ -944,14 +1014,18 @@ final public boolean isPotentiallyUnknown(LocalVariableBinding local) { & (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 + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit3 & this.nullBit4 & (1L << position)) != 0; } // use extra vector @@ -969,13 +1043,18 @@ final public boolean isProtectedNonNull(LocalVariableBinding local) { & (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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits return (this.nullBit1 & this.nullBit2 & (this.nullBit3 ^ this.nullBit4) @@ -1008,15 +1087,21 @@ protected static boolean isTrue(boolean expression, String message) { 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) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NON_NULL); + 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 +1190,20 @@ public void markAsComparedEqualToNonNull(LocalVariableBinding local) { } } -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) { + 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 @@ -1243,14 +1334,20 @@ public void markAsDefinitelyAssigned(LocalVariableBinding local) { 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) { + this.markNullStatus(local, FlowInfo.POTENTIALLY_NON_NULL); + 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 +1394,20 @@ public void markAsDefinitelyNonNull(LocalVariableBinding local) { } } -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) { + 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 +1460,19 @@ public void markAsDefinitelyNull(LocalVariableBinding local) { */ // 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits // mark assigned null this.nullBit1 |= (mask = 1L << position); @@ -1412,12 +1520,17 @@ public void markAsDefinitelyUnknown(LocalVariableBinding local) { } } -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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + this.maxFieldCount; + } + if (position < BitCacheSize) { // use bits this.nullBit1 &= (mask = ~(1L << position)); this.nullBit2 &= mask; @@ -1440,17 +1553,54 @@ public void resetNullInfo(LocalVariableBinding local) { } } +public void resetNullInfoForFields() { + if (this != DEAD_END) { + long mask; + if (this.maxFieldCount < BitCacheSize) { + // use bits + this.nullBit1 &= (mask = -1L << this.maxFieldCount); + this.nullBit2 &= mask; + this.nullBit3 &= mask; + this.nullBit4 &= mask; + } + else { + this.nullBit1 &= (mask = 0L); + this.nullBit2 &= mask; + this.nullBit3 &= mask; + this.nullBit4 &= mask; + if (this.extra != null){ + for (int position = BitCacheSize; position < this.maxFieldCount; position++) { + // use extra vector + int vectorIndex = (position / BitCacheSize) - 1; + if (vectorIndex >= this.extra[2].length) + break; // No null info about fields beyond this point in the extra vector + this.extra[2][vectorIndex] + &= (mask = ~(1L << (position % BitCacheSize))); + this.extra[3][vectorIndex] &= mask; + this.extra[4][vectorIndex] &= mask; + this.extra[5][vectorIndex] &= mask; + } + } + } + } +} + /** * 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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 +1642,17 @@ public void markPotentiallyUnknownBit(LocalVariableBinding local) { } } -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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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 +1694,17 @@ public void markPotentiallyNullBit(LocalVariableBinding local) { } } -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) { + if (local instanceof FieldBinding) { + position = local.id; + } else { + position = local.id + 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$ @@ -2077,8 +2237,13 @@ public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect() { return this; } -public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) { - int position = local.id + this.maxFieldCount; +public void markedAsNullOrNonNullInAssertExpression(VariableBinding binding) { + int position; + if (binding instanceof FieldBinding) { + position = binding.id; + } else { + position = binding.id + this.maxFieldCount; + } int oldLength; if (this.nullStatusChangedInAssert == null) { this.nullStatusChangedInAssert = new int[position + 1]; @@ -2091,8 +2256,13 @@ public void markedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) this.nullStatusChangedInAssert[position] = 1; } -public boolean isMarkedAsNullOrNonNullInAssertExpression(LocalVariableBinding local) { - int position = local.id + this.maxFieldCount; +public boolean isMarkedAsNullOrNonNullInAssertExpression(VariableBinding binding) { + int position; + if (binding instanceof FieldBinding) { + position = binding.id; + } else { + position = binding.id + 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/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java index 3eb3cca8fc..16c3b5c68e 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 @@ -39,11 +39,13 @@ public class ClassScope extends Scope { public TypeDeclaration referenceContext; public TypeReference superTypeReference; java.util.ArrayList deferredBoundChecks; - + public int cumulativeFieldCount; // cumulative field count from all enclosing types, used to build unique field id's for member types. + public int localTypeFieldIdStart; public ClassScope(Scope parent, TypeDeclaration context) { super(Scope.CLASS_SCOPE, parent); this.referenceContext = context; this.deferredBoundChecks = null; // initialized if required + this.localTypeFieldIdStart = this.cumulativeFieldCount = 0; } void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding supertype) { @@ -80,6 +82,8 @@ public class ClassScope extends Scope { } } } + this.cumulativeFieldCount += outerMostMethodScope().analysisIndex; + this.localTypeFieldIdStart = outerMostMethodScope().analysisIndex; connectMemberTypes(); buildFieldsAndMethods(); anonymousType.faultInTypesForFieldsAndMethods(); @@ -109,6 +113,23 @@ public class ClassScope extends Scope { FieldBinding[] fieldBindings = new FieldBinding[count]; HashtableOfObject knownFieldNames = new HashtableOfObject(count); count = 0; + ClassScope enclosingClass = this.enclosingClassScope(); + if (enclosingClass != null) { + this.cumulativeFieldCount += enclosingClass.cumulativeFieldCount; + } +// SourceTypeBinding enclosingSourceType = this.enclosingSourceType(); +// if (enclosingSourceType != null) { +// ReferenceBinding superClassBinding = sourceType.superclass; +// while (superClassBinding != null) { +// FieldBinding[] unResolvedFields = superClassBinding.unResolvedFields(); +// if (unResolvedFields != null) { +// this.cumulativeFieldCount += unResolvedFields.length; +// } +// superClassBinding = superClassBinding.superclass(); +// } +// ReferenceBinding[] superInterfacesBinding = enclosingSourceType.superInterfaces; +// this.cumulativeFieldCount += findFieldCountFromSuperInterfaces(superInterfacesBinding); +// } for (int i = 0; i < size; i++) { FieldDeclaration field = fields[i]; if (field.getKind() == AbstractVariableDeclaration.INITIALIZER) { @@ -116,7 +137,7 @@ public class ClassScope extends Scope { // now this error reporting is moved into the parser itself. See https://bugs.eclipse.org/bugs/show_bug.cgi?id=212713 } else { FieldBinding fieldBinding = new FieldBinding(field, null, field.modifiers | ExtraCompilerModifiers.AccUnresolved, sourceType); - fieldBinding.id = count; + fieldBinding.id = count + this.cumulativeFieldCount; // field's type will be resolved when needed for top level types checkAndSetModifiersForField(fieldBinding, field); @@ -144,6 +165,7 @@ public class ClassScope extends Scope { // remove duplicate fields if (count != fieldBindings.length) System.arraycopy(fieldBindings, 0, fieldBindings = new FieldBinding[count], 0, count); + this.cumulativeFieldCount += count; sourceType.tagBits &= ~(TagBits.AreFieldsSorted|TagBits.AreFieldsComplete); // in case some static imports reached already into this type sourceType.setFields(fieldBindings); } @@ -226,6 +248,8 @@ public class ClassScope extends Scope { checkParameterizedTypeBounds(); checkParameterizedSuperTypeCollisions(); } + this.cumulativeFieldCount += outerMostMethodScope().analysisIndex; + this.localTypeFieldIdStart = 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 2c44d5a67f..dc36d918a1 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 @@ -16,11 +16,13 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; +import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.impl.Constant; public class FieldBinding extends VariableBinding { public ReferenceBinding declaringClass; public int compoundUseFlag = 0; // number or accesses via postIncrement or compoundAssignment + private int nullStatus; protected FieldBinding() { super(null, null, 0, null); @@ -29,12 +31,14 @@ protected FieldBinding() { public FieldBinding(char[] name, TypeBinding type, int modifiers, ReferenceBinding declaringClass, Constant constant) { super(name, type, modifiers, constant); this.declaringClass = declaringClass; + this.nullStatus = FlowInfo.UNKNOWN; } // special API used to change field declaring class for runtime visibility check public FieldBinding(FieldBinding initialFieldBinding, ReferenceBinding declaringClass) { super(initialFieldBinding.name, initialFieldBinding.type, initialFieldBinding.modifiers, initialFieldBinding.constant()); this.declaringClass = declaringClass; this.id = initialFieldBinding.id; + this.nullStatus = FlowInfo.UNKNOWN; setAnnotations(initialFieldBinding.getAnnotations()); } /* API @@ -386,4 +390,12 @@ public FieldDeclaration sourceField() { } return null; } + +public int getNullStatusForStaticFinalField() { + return this.nullStatus; +} + +public void setNullStatusForStaticFinalField(int nullStatusToMark) { + this.nullStatus = nullStatusToMark; +} } 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 9b83933a30..10cdb109e9 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 @@ -108,6 +108,7 @@ import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; 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; @@ -286,18 +287,26 @@ public static int getIrritant(int problemID) { 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; @@ -5072,110 +5081,158 @@ public void localVariableHiding(LocalDeclaration local, Binding hiddenVariable, } } -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 d05ebd231a..dba17627fe 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 52c943db8f..20a3d223d6 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, 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 -- cgit v1.2.3