Skip to main content
aboutsummaryrefslogtreecommitdiffstats

Back to the top

e.git/diff/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java?h=BETA_JAVA8&id=8a1621e802c664e59aba36b8a87f59ae57902e37'>org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeDeclaration.java14
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java15
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java52
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java101
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java128
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java32
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java117
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java9
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java25
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/UnresolvedReferenceBinding.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/VariableBinding.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java156
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties19
44 files changed, 1203 insertions, 488 deletions
diff --git a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java
index dc0cbaddd3..cf7c616d76 100644
--- a/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java
+++ b/org.eclipse.jdt.core.tests.builder/src/org/eclipse/jdt/core/tests/builder/AnnotationDependencyTests.java
@@ -8,7 +8,9 @@
* Contributors:
* Walter Harley (eclipse@cafewalter.com) - initial implementation
* IBM Corporation - initial API and implementation
- * Stephan Herrmann - Contribution for bug 365992 - [builder] [null] Change of nullness for a parameter doesn't trigger a build for the files that call the method
+ * Stephan Herrmann - Contributions for
+ * bug 365992 - [builder] [null] Change of nullness for a parameter doesn't trigger a build for the files that call the method
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.core.tests.builder;
@@ -28,6 +30,7 @@ import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.tests.util.Util;
+import org.osgi.framework.Bundle;
/**
* Tests to verify that annotation changes cause recompilation of dependent types.
@@ -140,7 +143,8 @@ public class AnnotationDependencyTests extends BuilderTests {
void setupProjectForNullAnnotations() throws IOException, JavaModelException {
// add the org.eclipse.jdt.annotation library (bin/ folder or jar) to the project:
- File bundleFile = FileLocator.getBundleFile(Platform.getBundle("org.eclipse.jdt.annotation"));
+ Bundle[] bundles = Platform.getBundles("org.eclipse.jdt.annotation","[1.1.0,2.0.0)");
+ File bundleFile = FileLocator.getBundleFile(bundles[0]);
String annotationsLib = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath();
IJavaProject javaProject = env.getJavaProject(this.projectPath);
IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
@@ -1481,7 +1485,7 @@ public class AnnotationDependencyTests extends BuilderTests {
env.addClass( this.srcRoot, "p1", "Test2", test2CodeB );
incrementalBuild( this.projectPath );
expectingProblemsFor(test1Path,
- "Problem : Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable [ resource : </Project/src/p1/Test1.java> range : <126,143> category : <90> severity : <2>]");
+ "Problem : Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable [ resource : </Project/src/p1/Test1.java> range : <126,143> category : <90> severity : <2>]");
// verify that Test1 was recompiled
expectingUniqueCompiledClasses(new String[] { "p1.Test1", "p1.Test2" });
@@ -1494,7 +1498,7 @@ public class AnnotationDependencyTests extends BuilderTests {
env.addClass( this.srcRoot, "p1", "Test2", test2CodeC );
incrementalBuild( this.projectPath );
expectingProblemsFor(test1Path,
- "Problem : Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\' [ resource : </Project/src/p1/Test1.java> range : <126,143> category : <90> severity : <1>]");
+ "Problem : Null type safety: The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\' [ resource : </Project/src/p1/Test1.java> range : <126,143> category : <90> severity : <1>]");
// verify that Test1 was recompiled
expectingUniqueCompiledClasses(new String[] { "p1.Test1", "p1.Test2" });
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java
index 852d91bb74..eefd2ebae5 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AbstractNullAnnotationTest.java
@@ -76,7 +76,7 @@ public abstract class AbstractNullAnnotationTest extends AbstractComparableTest
int len = defaultLibs.length;
this.LIBS = new String[len+1];
System.arraycopy(defaultLibs, 0, this.LIBS, 0, len);
- String version = this.complianceLevel >= ClassFileConstants.JDK1_8 ? "2.0.0" : "1.1.0";
+ String version = this.complianceLevel >= ClassFileConstants.JDK1_8 ? "[2.0.0,3.0.0)" : "[1.1.0,2.0.0)";
Bundle[] bundles = Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", version);
File bundleFile = FileLocator.getBundleFile(bundles[0]);
if (bundleFile.isDirectory())
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java
index 000ac13aef..35d82b67d2 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/AnnotationTest.java
@@ -19,6 +19,7 @@
* bug 386356 - Type mismatch error with annotations and generics
* bug 331649 - [compiler][null] consider null annotations for fields
* bug 376590 - Private fields with @Inject are ignored by unused field validation
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller - Contributions for
* bug 384567 - [1.5][compiler] Compiler accepts illegal modifiers on package declaration
*******************************************************************************/
@@ -10336,7 +10337,7 @@ public void testBug365437d() {
customOptions.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.ERROR);
customOptions.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.ERROR);
customOptions.put(CompilerOptions.OPTION_AnnotationBasedNullAnalysis, CompilerOptions.ENABLED);
- customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "p.NonNull");
+ customOptions.put(CompilerOptions.OPTION_NonNullByDefaultAnnotationName, "p.NonNullByDefault");
this.runNegativeTest(
true,
new String[] {
@@ -10359,7 +10360,7 @@ public void testBug365437d() {
" public E2(long l) {}\n" +
"}\n" +
"class E3 {\n" +
- " @p.NonNull\n" +
+ " @p.NonNullByDefault\n" +
" private E3() {}\n" +
" public E3(long l) {}\n" +
"}\n" +
@@ -10369,12 +10370,12 @@ public void testBug365437d() {
" private E4() {}\n" +
" public E4(long l) {}\n" +
"}\n",
- "p/NonNull.java",
+ "p/NonNullByDefault.java",
"package p;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
- "@Target({TYPE, METHOD,PARAMETER,CONSTRUCTOR})\n" +
- "public @interface NonNull {\n" +
+ "@Target({TYPE, METHOD,CONSTRUCTOR})\n" +
+ "public @interface NonNullByDefault {\n" +
"}",
"p/Annot.java",
"package p;\n" +
@@ -10487,7 +10488,7 @@ public void testBug365437f() {
customOptions.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.ERROR);
customOptions.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.ERROR);
customOptions.put(CompilerOptions.OPTION_AnnotationBasedNullAnalysis, CompilerOptions.ENABLED);
- customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "p.NonNull");
+ customOptions.put(CompilerOptions.OPTION_NonNullByDefaultAnnotationName, "p.NonNullByDefault");
this.runNegativeTest(
true,
new String[] {
@@ -10505,7 +10506,7 @@ public void testBug365437f() {
" private class E22{}\n" +
"}\n" +
"class E3 {\n" +
- " @p.NonNull\n" +
+ " @p.NonNullByDefault\n" +
" private class E33{}\n" +
"}\n" +
"class E4 {\n" +
@@ -10513,12 +10514,12 @@ public void testBug365437f() {
" @p.Annot\n" +
" private class E44{}\n" +
"}\n",
- "p/NonNull.java",
+ "p/NonNullByDefault.java",
"package p;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
- "@Target({TYPE, METHOD,PARAMETER,LOCAL_VARIABLE})\n" +
- "public @interface NonNull {\n" +
+ "@Target({TYPE, METHOD,PARAMETER})\n" +
+ "public @interface NonNullByDefault {\n" +
"}",
"p/Annot.java",
"package p;\n" +
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
index 49295ecf20..101647665c 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
@@ -27,6 +27,7 @@
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 381443 - [compiler][null] Allow parameter widening from @NonNull to unannotated
* bug 383368 - [compiler][null] syntactic null analysis for field references
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper Steen Moller - Contributions for
* bug 404146 - [1.7][compiler] nested try-catch-finally-blocks leads to unrunnable Java byte code
* bug 407297 - [1.8][compiler] Control generation of parameter names by option
@@ -12640,12 +12641,12 @@ public void test313_warn_options() {
"2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing nullable annotation: inherited method from X declares this parameter as @Nullable\n" +
+ "Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing non-null annotation: inherited method from X declares this parameter as @NonNull\n" +
+ "Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"3 problems (3 warnings)",
true);
@@ -12689,12 +12690,12 @@ public void test314_warn_options() {
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing nullable annotation: inherited method from X declares this parameter as @Nullable\n" +
+ "Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing non-null annotation: inherited method from X declares this parameter as @NonNull\n" +
+ "Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"3 problems (3 errors)",
true);
@@ -13452,7 +13453,8 @@ public void testBug375409e() {
"import java.lang.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" +
- " return new X().bar();\n" +
+ " Object o3 = new X().bar();\n" + // need a local to involve flow analysis
+ " return o3;\n" +
" }\n" +
" @Nullable Object bar() {\n" +
" return null;\n" +
@@ -13680,12 +13682,12 @@ public void testBug375366c() throws IOException {
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing nullable annotation: inherited method from X declares this parameter as @Nullable\n" +
+ "Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing non-null annotation: inherited method from X declares this parameter as @NonNull\n" +
+ "Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"3 problems (2 errors, 1 warning)",
false/*don't flush*/);
@@ -13734,7 +13736,7 @@ public void testBug375366d() throws IOException {
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
- "Missing nullable annotation: inherited method from X declares this parameter as @Nullable\n" +
+ "Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"2 problems (2 errors)",
false/*don't flush*/);
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 54020f76dd..07da838ef8 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
@@ -30,6 +30,7 @@
* bug 401796 - [1.8][compiler] don't treat default methods as overriding an independent inherited abstract method
* bug 404649 - [1.8][compiler] detect illegal reference to indirect or redundant super
* bug 400761 - [compiler][null] null may be return as boolean without a diagnostic
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller - Contributions for
* bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression
* bug 382721 - [1.8][compiler] Effectively final variables needs special treatment
@@ -488,6 +489,7 @@ public void test011_problem_categories() {
expectedProblemAttributes.put("ExternalProblemNotFixable", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
expectedProblemAttributes.put("ExplicitAnnotationTargetRequired", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("FallthroughCase", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("FieldComparisonYieldsFalse", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("FieldHidingField", new ProblemAttributes(CategorizedProblem.CAT_NAME_SHADOWING_CONFLICT));
expectedProblemAttributes.put("FieldHidingLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_NAME_SHADOWING_CONFLICT));
expectedProblemAttributes.put("FieldMissingDeprecatedAnnotation", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE));
@@ -816,12 +818,13 @@ public void test011_problem_categories() {
expectedProblemAttributes.put("NotVisibleMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER));
expectedProblemAttributes.put("NotVisibleType", new ProblemAttributes(CategorizedProblem.CAT_TYPE));
expectedProblemAttributes.put("NullableFieldReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("NullAnnotationUnsupportedLocation", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
expectedProblemAttributes.put("NullExpressionReference", 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));
expectedProblemAttributes.put("NullityMismatchingTypeAnnotation", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
- expectedProblemAttributes.put("NullityMismatchingTypeAnnotationUnchecked", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("NullityUncheckedTypeAnnotationDetail", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NullSourceString", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
expectedProblemAttributes.put("NullUnboxing", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("NumericValueOutOfRange", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL));
@@ -877,6 +880,8 @@ public void test011_problem_categories() {
expectedProblemAttributes.put("RedundantSpecificationOfTypeArguments", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullAnnotation", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
+ expectedProblemAttributes.put("RedundantNullCheck", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
+ expectedProblemAttributes.put("RedundantNullCheckOnField", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullExpression", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullSpecdField", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
@@ -1272,6 +1277,7 @@ public void test012_compiler_problems_tuning() {
expectedProblemAttributes.put("ExternalProblemNotFixable", SKIP);
expectedProblemAttributes.put("ExplicitAnnotationTargetRequired", SKIP);
expectedProblemAttributes.put("FallthroughCase", new ProblemAttributes(JavaCore.COMPILER_PB_FALLTHROUGH_CASE));
+ expectedProblemAttributes.put("FieldComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("FieldHidingField", new ProblemAttributes(JavaCore.COMPILER_PB_FIELD_HIDING));
expectedProblemAttributes.put("FieldHidingLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_FIELD_HIDING));
expectedProblemAttributes.put("FieldMissingDeprecatedAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_DEPRECATED_ANNOTATION));
@@ -1600,8 +1606,9 @@ public void test012_compiler_problems_tuning() {
expectedProblemAttributes.put("NotVisibleMethod", SKIP);
expectedProblemAttributes.put("NotVisibleType", SKIP);
expectedProblemAttributes.put("NullableFieldReference", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_REFERENCE));
+ expectedProblemAttributes.put("NullAnnotationUnsupportedLocation", SKIP);
expectedProblemAttributes.put("NullityMismatchingTypeAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION));
- expectedProblemAttributes.put("NullityMismatchingTypeAnnotationUnchecked", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_UNCHECKED_CONVERSION));
+ expectedProblemAttributes.put("NullityUncheckedTypeAnnotationDetail", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_UNCHECKED_CONVERSION));
expectedProblemAttributes.put("NullExpressionReference", 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));
@@ -1661,6 +1668,8 @@ public void test012_compiler_problems_tuning() {
expectedProblemAttributes.put("RedundantSpecificationOfTypeArguments", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_TYPE_ARGUMENTS));
expectedProblemAttributes.put("RedundantLocalVariableNullAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION));
+ expectedProblemAttributes.put("RedundantNullCheck", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
+ expectedProblemAttributes.put("RedundantNullCheckOnField", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullExpression", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullSpecdField", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
expectedProblemAttributes.put("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK));
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest8.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest8.java
index 3742640459..f50670fcb0 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest8.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/FlowAnalysisTest8.java
@@ -64,12 +64,12 @@ public void testLambda_01() {
"1. WARNING in X.java (at line 3)\n" +
" ISAM printer = (p,o) -> p.concat(o.toString());\n" +
" ^^^^^^^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ "Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n" +
"2. ERROR in X.java (at line 3)\n" +
" ISAM printer = (p,o) -> p.concat(o.toString());\n" +
" ^\n" +
- "Potential null pointer access: The variable o may be null at this location\n" +
+ "Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n");
}
@@ -139,17 +139,17 @@ public void testLambda_03() {
"2. ERROR in X.java (at line 7)\n" +
" -> System.out.println(o1.toString()+o2.toString()+o3.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The variable o1 may be null at this location\n" +
+ "Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n" +
"3. ERROR in X.java (at line 7)\n" +
" -> System.out.println(o1.toString()+o2.toString()+o3.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The variable o2 may be null at this location\n" +
+ "Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n" +
"4. ERROR in X.java (at line 7)\n" +
" -> System.out.println(o1.toString()+o2.toString()+o3.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The variable o3 may be null at this location\n" +
+ "Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n");
}
@@ -175,7 +175,7 @@ public void testLambda_04() {
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" ISAM printer1 = (@NonNull int i) \n" +
- " ^^^^^^^^^^^^\n" +
+ " ^^^^^^^^\n" +
"The nullness annotation @NonNull is not applicable for the primitive type int\n" +
"----------\n");
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
index 59ec933977..732889c57e 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
@@ -15,7 +15,6 @@ import java.io.File;
import java.util.Map;
import junit.framework.Test;
-import junit.framework.TestSuite;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
@@ -23,6 +22,8 @@ import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
// see bug 186342 - [compiler][null] Using annotations for null checking
public class NullAnnotationTest extends AbstractNullAnnotationTest {
+private String TEST_JAR_SUFFIX = ".jar";
+
public NullAnnotationTest(String name) {
super(name);
}
@@ -30,27 +31,87 @@ public NullAnnotationTest(String name) {
// Static initializer to specify tests subset using TESTS_* static variables
// All specified tests which do not belong to the class are skipped...
static {
-// TESTS_NAMES = new String[] { "test_nullable_field_10c" };
+// TESTS_NAMES = new String[] { "test_nonnull_var_in_constrol_structure_1" };
// TESTS_NUMBERS = new int[] { 561 };
// TESTS_RANGE = new int[] { 1, 2049 };
}
public static Test suite() {
-// return buildComparableTestSuite(testClass());
- // see also removal in org.eclipse.jdt.core.tests.compiler.regression.TestAll
- Class evaluationTestClass = testClass();
- TestSuite suite = new TestSuite(evaluationTestClass.getName());
- suite.addTest(buildUniqueComplianceTestSuite(evaluationTestClass, ClassFileConstants.JDK1_5));
- suite.addTest(buildUniqueComplianceTestSuite(evaluationTestClass, ClassFileConstants.JDK1_6));
- suite.addTest(buildUniqueComplianceTestSuite(evaluationTestClass, ClassFileConstants.JDK1_7));
- // currently not for JDK1_8
- return suite;
+ return buildComparableTestSuite(testClass());
}
public static Class testClass() {
return NullAnnotationTest.class;
}
+String mismatch_NonNull_Nullable(String type) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Null type mismatch: required \'@NonNull "+type+"\' but the provided value is specified as @Nullable\n"
+ : "Null type mismatch (type annotations): required '@NonNull "+type+"' but this expression has type '@Nullable "+type+"'\n";
+}
+String nullTypeSafety() {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Null type safety: "
+ : "Null type safety (type annotations): ";
+}
+String mismatch_NonNull_Null(String type) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Null type mismatch: required \'@NonNull "+type+"\' but the provided value is null\n"
+ : "Null type mismatch: required \'@NonNull "+type+"\' but the provided value is null\n";
+}
+String variableMayBeNull(String var) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Potential null pointer access: The variable "+var+" may be null at this location\n"
+ : "Potential null pointer access: this expression has a '@Nullable' type\n";
+}
+String redundant_check_nonnull(String expr, String type) {
+ return this.complianceLevel < ClassFileConstants.JDK1_8
+ ? "Redundant null check: "+expr+" is specified as @NonNull\n"
+ : "Redundant null check: comparing '"+type+"' against null\n";
+}
+String redundantCheck_method_cannot_return_null(String method, String type) {
+ return this.complianceLevel < ClassFileConstants.JDK1_8
+ ? "Redundant null check: The method "+method+" cannot return null\n"
+ : "Redundant null check: comparing '@NonNull "+type+"' against null\n";
+}
+String checkAlwaysFalse_method_cannot_return_null(String method, String type) {
+ return this.complianceLevel < ClassFileConstants.JDK1_8
+ ? "Null comparison always yields false: The method "+method+" cannot return null\n"
+ : "Redundant null check: comparing '@NonNull "+type+"' against null\n";
+}
+String redundant_check_canonlynull(String expr, String type) {
+ return this.complianceLevel < ClassFileConstants.JDK1_8
+ ? "Redundant null check: "+expr+" can only be null at this location\n"
+ : "Redundant null check: comparing '@NonNull "+type+"' against null\n";
+}
+
+String checkAlwaysFalse_nonnull(String expr, String type) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Null comparison always yields false: "+expr+" is specified as @NonNull\n"
+ : "Redundant null check: comparing '@NonNull "+type+"' against null\n";
+}
+String potNPE_nullable(String expr) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Potential null pointer access: "+expr+" is specified as @Nullable\n"
+ : "Potential null pointer access: this expression has a '@Nullable' type\n";
+}
+String potNPE_nullable_maybenull(String expr) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Potential null pointer access: "+expr+" may be null at this location\n"
+ : "Potential null pointer access: this expression has a '@Nullable' type\n";
+}
+String nonNullArrayOf(String string) {
+ return (this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "@NonNull Object[]"
+ : "Object @NonNull[]";
+}
+
+protected void setUp() throws Exception {
+ super.setUp();
+ if (this.complianceLevel >= ClassFileConstants.JDK1_8)
+ this.TEST_JAR_SUFFIX = "_1.8.jar";
+}
+
// a nullable argument is dereferenced without a check
public void test_nullable_paramter_001() {
runNegativeTest(
@@ -66,7 +127,7 @@ public void test_nullable_paramter_001() {
"1. ERROR in X.java (at line 4)\n" +
" System.out.print(o.toString());\n" +
" ^\n" +
- "Potential null pointer access: The variable o may be null at this location\n" +
+ variableMayBeNull("o") +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
@@ -108,7 +169,7 @@ public void test_nonnull_parameter_001() {
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
- "Redundant null check: The variable o is specified as @NonNull\n" +
+ redundant_check_nonnull("The variable o", "@NonNull Object") +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
@@ -219,7 +280,7 @@ public void test_nonnull_parameter_005() {
"1. WARNING in X.java (at line 3)\n" +
" l.setObject(o);\n" +
" ^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n");
}
// a ternary non-null expression is passed to a nonnull parameter
@@ -264,7 +325,7 @@ public void test_nonnull_parameter_007() {
"1. ERROR in XSub.java (at line 4)\n" +
" super(b);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n");
}
// a nullable value is passed to a non-null parameter in an allocation expression
@@ -286,7 +347,7 @@ public void test_nonnull_parameter_008() {
"1. ERROR in X.java (at line 5)\n" +
" return new X(b);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" /* compiler output */);
}
// a nullable value is passed to a non-null parameter in a qualified allocation expression
@@ -310,7 +371,7 @@ public void test_nonnull_parameter_009() {
"1. ERROR in X.java (at line 7)\n" +
" return this.new Local(b);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" /* compiler output */);
}
// null is passed to a non-null parameter in a qualified allocation expression, across CUs
@@ -427,7 +488,7 @@ public void test_nonnull_parameter_012() {
"2. ERROR in X.java (at line 4)\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" ^^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
+ mismatch_NonNull_Null("Object") +
"----------\n" /* compiler output */);
}
// a method of a local class has a non-null parameter, client passes null
@@ -460,7 +521,9 @@ public void test_nonnull_parameter_015() {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
- " void foo(@NonNull Object ... o) {\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? " void foo(@NonNull Object ... o) {\n"
+ : " void foo(Object @NonNull... o) {\n") +
" if (o != null)\n" +
" System.out.print(o.toString());\n" +
" }\n" +
@@ -482,12 +545,12 @@ public void test_nonnull_parameter_015() {
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
- "Redundant null check: The variable o is specified as @NonNull\n" +
+ redundant_check_nonnull("The variable o", "Object @NonNull[]") +
"----------\n" +
"2. ERROR in X.java (at line 14)\n" +
" foo(objs);\n" +
" ^^^^\n" +
- "Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
+ "Null type mismatch: required \'"+nonNullArrayOf("Object")+"\' but the provided value is null\n" +
"----------\n" +
"3. WARNING in X.java (at line 18)\n" +
" foo2(2, null);\n" +
@@ -497,7 +560,7 @@ public void test_nonnull_parameter_015() {
"4. ERROR in X.java (at line 18)\n" +
" foo2(2, null);\n" +
" ^^^^\n" +
- "Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
+ "Null type mismatch: required \'"+nonNullArrayOf("Object")+"\' but the provided value is null\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
@@ -509,15 +572,21 @@ public void test_nonnull_parameter_016() {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
- " X(@NonNull Object ... o) {\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? " X(@NonNull Object ... o) {\n"
+ : " X(Object @NonNull... o) {\n") +
" if (o != null)\n" +
" System.out.print(o.toString());\n" +
" }\n" +
" class Y extends X {\n" +
- " Y(int i, @NonNull Object ... o) {\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? " Y(int i, @NonNull Object ... o) {\n"
+ : " Y(int i, Object @NonNull... o) {\n") +
" super(i, (Object)null);\n" +
" }\n" +
- " Y(char c, @NonNull Object ... o) {\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? " Y(char c, @NonNull Object ... o) {\n"
+ : " Y(char c, Object @NonNull... o) {\n") +
" this(1, new Object(), null);\n" +
" }\n" +
" }\n" +
@@ -534,17 +603,17 @@ public void test_nonnull_parameter_016() {
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
- "Redundant null check: The variable o is specified as @NonNull\n" +
+ redundant_check_nonnull("The variable o", "Object @NonNull[]") +
"----------\n" +
"2. ERROR in X.java (at line 16)\n" +
" new X((Object[])null);\n" +
" ^^^^^^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
+ "Null type mismatch: required \'"+nonNullArrayOf("Object")+"\' but the provided value is null\n" +
"----------\n" +
"3. ERROR in X.java (at line 21)\n" +
" this.new Y(2, (Object[])null);\n" +
" ^^^^^^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
+ "Null type mismatch: required \'"+nonNullArrayOf("Object")+"\' but the provided value is null\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
@@ -618,8 +687,8 @@ public void test_nonnull_parameter_014() {
"----------\n" +
"1. ERROR in B.java (at line 8)\n" +
" l.callMe(getNull());\n" +
- " ^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
+ " ^^^^^^^^^\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
// assigning potential null to a nonnull local variable
@@ -650,7 +719,7 @@ public void test_nonnull_local_001() {
"3. WARNING in X.java (at line 7)\n" +
" @NonNull Object o3 = p;\n" +
" ^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
@@ -687,7 +756,7 @@ public void test_nonnull_local_002() {
"3. WARNING in X.java (at line 10)\n" +
" o3 = p;\n" +
" ^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
@@ -760,7 +829,7 @@ public void test_parameter_specification_inheritance_002() {
"1. ERROR in X.java (at line 3)\n" +
" void foo(Object o) {\n" +
" ^^^^^^\n" +
- "Missing nullable annotation: inherited method from Lib declares this parameter as @Nullable\n" +
+ "Missing nullable annotation: inherited method from Lib specifies this parameter as @Nullable\n" +
"----------\n");
}
// a method relaxes the parameter null specification, super interface declares parameter o as @NonNull
@@ -958,18 +1027,18 @@ public void test_parameter_specification_inheritance_008() {
"1. WARNING in XSub.java (at line 3)\n" +
" public void printObject(Object o) { super.printObject(o); }\n" +
" ^^^^^^\n" +
- "Missing non-null annotation: inherited method from X declares this parameter as @NonNull\n" +
+ "Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"2. ERROR in XSub.java (at line 3)\n" +
" public void printObject(Object o) { super.printObject(o); }\n" +
" ^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n" +
"----------\n" +
"1. ERROR in M.java (at line 3)\n" +
" x.printObject(o);\n" +
" ^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n");
}
// a static method has a more relaxed null contract than a like method in the super class, but no overriding.
@@ -1259,7 +1328,7 @@ public void test_parameter_specification_inheritance_016() {
"1. ERROR in XSub.java (at line 3)\n" +
" public void foo(String s) { if (s != null) super.foo(s); }\n" +
" ^^^^^^\n" +
- "Missing non-null annotation: inherited method from X declares this parameter as @NonNull\n" +
+ "Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n");
}
@@ -1290,7 +1359,7 @@ public void test_parameter_specification_inheritance_017() {
"1. WARNING in XSub.java (at line 1)\n" +
" public class XSub extends X implements IX {\n" +
" ^^^^\n" +
- "Missing non-null annotation: inherited method from IX declares this parameter as @NonNull\n" +
+ "Missing non-null annotation: inherited method from IX specifies this parameter as @NonNull\n" +
"----------\n");
}
@@ -1404,7 +1473,7 @@ public void test_nonnull_return_001() {
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
- "Redundant null check: The variable o cannot be null at this location\n" +
+ "Redundant null check: The variable o cannot be null at this location\n" + // no immediate type annotation
"----------\n");
}
// a non-null method returns null
@@ -1445,7 +1514,7 @@ public void test_nonnull_return_004() {
"1. ERROR in X.java (at line 4)\n" +
" return o;\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
// a non-null method returns its non-null argument
@@ -1479,7 +1548,7 @@ public void test_nonnull_return_006() {
"1. WARNING in X.java (at line 4)\n" +
" return o;\n" +
" ^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n");
}
// a result from a nullable method is directly dereferenced
@@ -1527,7 +1596,7 @@ public void test_nonnull_return_008() {
"1. ERROR in X.java (at line 7)\n" +
" if (getObject() == null)\n" +
" ^^^^^^^^^^^\n" +
- "Null comparison always yields false: The method getObject() cannot return null\n" +
+ checkAlwaysFalse_method_cannot_return_null("getObject()", "Object") +
"----------\n" +
"2. WARNING in X.java (at line 8)\n" +
" throw new RuntimeException();\n" +
@@ -1564,7 +1633,7 @@ public void test_nonnull_return_009() {
"2. ERROR in X.java (at line 8)\n" +
" if (left != getObject())\n" +
" ^^^^^^^^^^^\n" +
- "Redundant null check: The method getObject() cannot return null\n" +
+ redundantCheck_method_cannot_return_null("getObject()", "Object") +
"----------\n");
}
// a result from a nonnull method is directly checked for null (from local): not redundant due to loop
@@ -1621,7 +1690,7 @@ public void _test_nonnull_return_009b() {
"1. ERROR in X.java (at line 9)\n" +
" if (left != getObject())\n" +
" ^^^^\n" +
- "Redundant null check: The variable left can only be null at this location\n" +
+ redundant_check_canonlynull("The variable left", "Object") +
"----------\n" +
"2. ERROR in X.java (at line 9)\n" +
" if (left != getObject())\n" +
@@ -1681,7 +1750,9 @@ public void test_nonnull_return_011() {
"1. ERROR in X.java (at line 5)\n" +
" if (dubious == null)\n" +
" ^^^^^^^\n" +
- "Null comparison always yields false: The variable dubious is specified as @NonNull\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "Null comparison always yields false: The variable dubious is specified as @NonNull\n"
+ : "Redundant null check: comparing '@NonNull Object' against null\n" ) +
"----------\n" +
"2. WARNING in X.java (at line 6)\n" +
" return dubious;\n" +
@@ -1887,7 +1958,7 @@ public void test_annotation_import_005() {
"1. ERROR in X.java (at line 4)\n" +
" return l.getObject();\n" +
" ^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type Object needs unchecked conversion to conform to \'@MustNotBeNull Object\'\n" +
+ nullTypeSafety() + "The expression of type 'Object' needs unchecked conversion to conform to \'@MustNotBeNull Object\'\n" +
"----------\n",
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
@@ -1940,7 +2011,9 @@ public void test_illegal_annotation_001() {
"1. ERROR in X.java (at line 2)\n" +
" @NonNull public class X {\n" +
" ^^^^^^^^\n" +
- "The annotation @NonNull is disallowed for this location\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "The annotation @NonNull is disallowed for this location\n"
+ : "The nullness annotation 'NonNull' is not applicable at this location\n") +
"----------\n",
this.LIBS,
false/*shouldFlush*/);
@@ -1982,8 +2055,30 @@ public void test_illegal_annotation_003() {
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" @NonNull void foo() {}\n" +
- " ^^^^^^^^^^^^^\n" +
- "The nullness annotation @NonNull is not applicable for the primitive type void\n" +
+ " ^^^^^^^^\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "The nullness annotation @NonNull is not applicable for the primitive type void\n"
+ : "Type annotation is illegal for a method that returns void\n") +
+ "----------\n",
+ this.LIBS,
+ false/*shouldFlush*/);
+}
+
+// a null annotation is illegally used on an int method:
+public void test_illegal_annotation_003b() {
+ runNegativeTest(
+ new String[] {
+ "X.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class X {\n" +
+ " @NonNull int foo() { return 1; }\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 3)\n" +
+ " @NonNull int foo() { return 1; }\n" +
+ " ^^^^^^^^\n" +
+ "The nullness annotation @NonNull is not applicable for the primitive type int\n" +
"----------\n",
this.LIBS,
false/*shouldFlush*/);
@@ -2002,7 +2097,7 @@ public void test_illegal_annotation_004() {
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" void foo(@Nullable int i) {}\n" +
- " ^^^^^^^^^^^^^\n" +
+ " ^^^^^^^^^\n" +
"The nullness annotation @Nullable is not applicable for the primitive type int\n" +
"----------\n",
this.LIBS,
@@ -2025,7 +2120,7 @@ public void test_illegal_annotation_005() {
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" @Nullable int i = 3;\n" +
- " ^^^^^^^^^^^^^\n" +
+ " ^^^^^^^^^\n" +
"The nullness annotation @Nullable is not applicable for the primitive type int\n" +
"----------\n",
this.LIBS,
@@ -2100,6 +2195,28 @@ public void test_illegal_annotation_007() {
"----------\n");
}
+// a null annotation is illegally used on a constructor:
+public void test_illegal_annotation_008() {
+ runNegativeTest(
+ new String[] {
+ "X.java",
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class X {\n" +
+ " @NonNull X() {}\n" +
+ "}\n"
+ },
+ "----------\n" +
+ "1. ERROR in X.java (at line 3)\n" +
+ " @NonNull X() {}\n" +
+ " ^^^^^^^^\n" +
+ ((this.complianceLevel < ClassFileConstants.JDK1_8)
+ ? "The annotation @NonNull is disallowed for this location\n"
+ : "The nullness annotation 'NonNull' is not applicable at this location\n" ) +
+ "----------\n",
+ this.LIBS,
+ false/*shouldFlush*/);
+}
+
public void test_default_nullness_002() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
@@ -2180,7 +2297,7 @@ public void test_default_nullness_003() {
"2. ERROR in p2\\Y.java (at line 6)\n" +
" bar(o);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
// package level default is consumed from package-info.class, similarly for type level default
@@ -2232,12 +2349,12 @@ public void test_default_nullness_003a() {
"2. ERROR in p2\\Y.java (at line 6)\n" +
" bar(o);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n" +
"3. ERROR in p2\\Y.java (at line 7)\n" +
" accept(o);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
// same as test_default_nullness_003a, but default-induced annotations are combined with explicit ones (not null related)
@@ -2295,12 +2412,12 @@ public void test_default_nullness_003b() {
"2. ERROR in p2\\Y.java (at line 6)\n" +
" bar(o);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n" +
"3. ERROR in p2\\Y.java (at line 7)\n" +
" accept(o);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
// don't apply type-level default to non-reference type
@@ -2722,7 +2839,7 @@ public void test_default_nullness_016() {
"4. ERROR in X.java (at line 11)\n" +
" iFoo = arg;\n" +
" ^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
@@ -3145,7 +3262,7 @@ public void test_nonnull_var_in_constrol_structure_1() {
"2. ERROR in X.java (at line 10)\n" +
" print(s);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"3. ERROR in X.java (at line 15)\n" +
" print(s);\n" +
@@ -3197,7 +3314,7 @@ public void test_nonnull_var_in_constrol_structure_2() {
"1. ERROR in X.java (at line 10)\n" +
" print(s);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"2. ERROR in X.java (at line 16)\n" +
" print(s);\n" +
@@ -3244,7 +3361,7 @@ public void test_nonnull_var_in_constrol_structure_3() {
"1. ERROR in X.java (at line 12)\n" +
" print(s);\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"2. ERROR in X.java (at line 19)\n" +
" print(s);\n" +
@@ -3358,8 +3475,8 @@ public void test_message_send_in_control_structure_02() {
"----------\n" +
"1. WARNING in Bug370930.java (at line 5)\n" +
" for(@NonNull String s: list) { // warning here: insufficient info on elements\n" +
- " ^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ " ^^^^\n" +
+ nullTypeSafety() + "The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n");
}
//Bug 370930 - NonNull annotation not considered for enhanced for loops over array
@@ -3382,7 +3499,7 @@ public void test_message_send_in_control_structure_02a() {
"1. WARNING in Bug370930.java (at line 4)\n" +
" for(@NonNull String s: array) { // warning here: insufficient info on elements\n" +
" ^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ nullTypeSafety() + "The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n");
}
//Bug 370930 - NonNull annotation not considered for enhanced for loops
@@ -3406,7 +3523,7 @@ public void test_message_send_in_control_structure_03() {
"1. ERROR in Bug370930.java (at line 6)\n" +
" expectNonNull(s); // warning here\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n");
}
public void test_assignment_expression_1() {
@@ -3485,7 +3602,7 @@ public void test_nesting_1() {
"1. ERROR in X.java (at line 16)\n" +
" print(s2);\n" +
" ^^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"2. ERROR in X.java (at line 25)\n" +
" @NonNull String s3R = s3;\n" +
@@ -3847,12 +3964,12 @@ public void test_nonnull_field_5() {
"1. ERROR in X.java (at line 5)\n" +
" return o == null;\n" +
" ^\n" +
- "Null comparison always yields false: The field o is declared as @NonNull\n" +
+ checkAlwaysFalse_nonnull("The field o", "Object") +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" return this.o != null;\n" +
- " ^\n" +
- "Redundant null check: The field o is declared as @NonNull\n" +
+ " ^\n" +
+ redundant_check_nonnull("The field o", "@NonNull Object") +
"----------\n");
}
@@ -3878,12 +3995,12 @@ public void test_nonnull_field_6() {
"1. ERROR in X.java (at line 5)\n" +
" if (o != null)\n" +
" ^\n" +
- "Redundant null check: The field o is declared as @NonNull\n" +
+ redundant_check_nonnull("The field o", "@NonNull Object") +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" return this.o == null;\n" +
" ^\n" +
- "Null comparison always yields false: The field o is declared as @NonNull\n" +
+ checkAlwaysFalse_nonnull("The field o", "Object") +
"----------\n");
}
@@ -3920,12 +4037,12 @@ public void test_nonnull_field_7() {
"1. ERROR in X.java (at line 7)\n" +
" if (Objects.o != null) // redundant\n" +
" ^\n" +
- "Redundant null check: The field o is declared as @NonNull\n" +
+ redundant_check_nonnull("The field o", "@NonNull Object") +
"----------\n" +
"2. ERROR in X.java (at line 13)\n" +
" if (null != Objects.o) // redundant\n" +
" ^\n" +
- "Redundant null check: The field o is declared as @NonNull\n" +
+ redundant_check_nonnull("The field o", "@NonNull Object") +
"----------\n");
}
@@ -3956,7 +4073,7 @@ public void test_nonnull_field_8() {
"1. ERROR in X.java (at line 7)\n" +
" if (objs.o == null) // always false\n" +
" ^\n" +
- "Null comparison always yields false: The field o is declared as @NonNull\n" +
+ checkAlwaysFalse_nonnull("The field o", "Object") +
"----------\n" +
"2. WARNING in X.java (at line 8)\n" +
" System.out.print(\"not null\");\n" +
@@ -3996,12 +4113,12 @@ public void test_nonnull_field_9() {
"1. ERROR in X.java (at line 8)\n" +
" if (this.objs.o != null) // redundant\n" +
" ^\n" +
- "Redundant null check: The field o is declared as @NonNull\n" +
+ redundant_check_nonnull("The field o", "@NonNull Object") +
"----------\n" +
"2. ERROR in X.java (at line 11)\n" +
" if (getObjs().o != null) // redundant\n" +
- " ^\n" +
- "Redundant null check: The field o is declared as @NonNull\n" +
+ " ^\n" +
+ redundant_check_nonnull("The field o", "@NonNull Object") +
"----------\n");
}
@@ -4029,7 +4146,7 @@ public void test_nonnull_field_11() {
"1. ERROR in X.java (at line 5)\n" +
" o = x;\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n" +
"2. ERROR in X.java (at line 10)\n" +
" objs.o = null;\n" +
@@ -4053,7 +4170,7 @@ public void test_nonnull_field_12() {
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" @NonNull int o = 1;\n" +
- " ^^^^^^^^^^^^\n" +
+ " ^^^^^^^^\n" +
"The nullness annotation @NonNull is not applicable for the primitive type int\n" +
"----------\n");
}
@@ -4244,8 +4361,8 @@ public void test_nullable_field_1() {
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" return this.o.toString();\n" +
- " ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ " ^\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
// access to a nullable field - single name reference
@@ -4267,7 +4384,7 @@ public void test_nullable_field_2() {
"1. ERROR in X.java (at line 5)\n" +
" return o.toString();\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
// access to a nullable field - qualified name reference
@@ -4290,12 +4407,12 @@ public void test_nullable_field_3() {
"1. ERROR in X.java (at line 6)\n" +
" return other.o.toString();\n" +
" ^^^^^\n" +
- "Potential null pointer access: The field other is declared as @Nullable\n" +
+ potNPE_nullable("The field other") +
"----------\n" +
"2. ERROR in X.java (at line 6)\n" +
" return other.o.toString();\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
// access to a nullable field - qualified name reference - multiple segments
@@ -4318,17 +4435,17 @@ public void test_nullable_field_3m() {
"1. ERROR in X.java (at line 6)\n" +
" return other.other.o.toString();\n" +
" ^^^^^\n" +
- "Potential null pointer access: The field other is declared as @Nullable\n" +
+ potNPE_nullable("The field other") +
"----------\n" +
"2. ERROR in X.java (at line 6)\n" +
" return other.other.o.toString();\n" +
" ^^^^^\n" +
- "Potential null pointer access: The field other is declared as @Nullable\n" +
+ potNPE_nullable("The field other") +
"----------\n" +
"3. ERROR in X.java (at line 6)\n" +
" return other.other.o.toString();\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
// access to a nullable field - dereference after check
@@ -4366,12 +4483,12 @@ public void test_nullable_field_4() {
"1. ERROR in X.java (at line 12)\n" +
" String local = o.toString();\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n" +
"2. ERROR in X.java (at line 15)\n" +
" return this.o.toString(); // warn here\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
@@ -4403,8 +4520,8 @@ public void test_nullable_field_5() {
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" return y.z.o.toString(); // pot.NPE on z\n" +
- " ^\n" +
- "Potential null pointer access: The field z is declared as @Nullable\n" +
+ " ^\n" +
+ potNPE_nullable("The field z") +
"----------\n");
}
@@ -4438,12 +4555,12 @@ public void test_nullable_field_6() {
"1. ERROR in X.java (at line 5)\n" +
" return y.z.o.toString(); // pot.NPE on y and o\n" +
" ^\n" +
- "Potential null pointer access: The field y is declared as @Nullable\n" +
+ potNPE_nullable("The field y") +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" return y.z.o.toString(); // pot.NPE on y and o\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
@@ -4470,13 +4587,13 @@ public void test_nullable_field_7() {
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" return this.y.o.toString(); // pot.NPE on y and o\n" +
- " ^\n" +
- "Potential null pointer access: The field y is declared as @Nullable\n" +
+ " ^\n" +
+ potNPE_nullable("The field y") +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" return this.y.o.toString(); // pot.NPE on y and o\n" +
- " ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ " ^\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
@@ -4515,7 +4632,7 @@ public void test_nullable_field_9() {
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" @Nullable int i;\n" +
- " ^^^^^^^^^^^^^\n" +
+ " ^^^^^^^^^\n" +
"The nullness annotation @Nullable is not applicable for the primitive type int\n" +
"----------\n");
}
@@ -4589,37 +4706,37 @@ public void test_nullable_field_10b() {
"1. ERROR in X.java (at line 7)\n" +
" System.out.println(other.o1.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n" +
"2. ERROR in X.java (at line 10)\n" +
" System.out.println(o2.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"3. ERROR in X.java (at line 12)\n" +
" System.out.println(this.o2.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"4. ERROR in X.java (at line 13)\n" +
" System.out.println (null != o3 ? o3.toString() : \"nothing\");\n" +
" ^^\n" +
- "Potential null pointer access: The field o3 is declared as @Nullable\n" +
+ potNPE_nullable("The field o3") +
"----------\n" +
"5. ERROR in X.java (at line 15)\n" +
" System.out.println(x.o1.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n" +
"6. ERROR in X.java (at line 17)\n" +
" System.out.println(this.x.o1.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n" +
"7. ERROR in X.java (at line 19)\n" +
" System.out.println(this.x.o1.toString());\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n");
}
@@ -4657,22 +4774,22 @@ public void test_nullable_field_10c() {
"1. ERROR in X.java (at line 8)\n" +
" System.out.println(o2.toString()); // warn here: disjunktion is no protection\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"2. ERROR in X.java (at line 10)\n" +
" System.out.println(o1.toString()); // warn here: negated inequality is no protection\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n" +
"3. ERROR in X.java (at line 14)\n" +
" System.out.println(o2.toString()); // warn here: negated conjunction is no protection\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"4. ERROR in X.java (at line 16)\n" +
" System.out.println(o1.toString()); // warn here: double negation is no protection\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n");
}
@@ -4705,7 +4822,7 @@ public void test_nullable_field_10d() {
"1. ERROR in X.java (at line 12)\n" +
" System.out.println(o1.toString()); // info is expired\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n");
}
@@ -4746,7 +4863,7 @@ public void test_nullable_field_10e() {
"2. ERROR in X.java (at line 6)\n" +
" System.out.println(this.o2.toString()); // field access is not protected\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"3. WARNING in X.java (at line 12)\n" +
" Y o1 = new Y();\n" +
@@ -4756,12 +4873,12 @@ public void test_nullable_field_10e() {
"4. ERROR in X.java (at line 14)\n" +
" System.out.println(this.o1.o2.toString()); // field access via other field not protected\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"5. ERROR in X.java (at line 16)\n" +
" System.out.println(o1.o2.toString()); // field access via local not protected\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n");
}
@@ -4790,12 +4907,12 @@ public void test_nullable_field_10f() {
"1. ERROR in X.java (at line 5)\n" +
" if (o1 != null && o1 != null) // second term is redundant\n" +
" ^^\n" +
- "Redundant null check: this expression cannot be null\n" +
+ "Redundant null check: The field o1 cannot be null at this location (ignoring concurrency)\n" +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" if (o1 != null) // this if is redundant\n" +
" ^^\n" +
- "Redundant null check: this expression cannot be null\n" +
+ "Redundant null check: The field o1 cannot be null at this location (ignoring concurrency)\n" +
"----------\n");
}
@@ -4905,22 +5022,22 @@ public void test_nullable_field_12() {
"1. ERROR in X.java (at line 6)\n" +
" System.out.println(goo()+other.o1.toString()); // warn here: expired by call to goo()\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n" +
"2. ERROR in X.java (at line 9)\n" +
" System.out.println(o2.toString()); // warn here: not protected\n" +
" ^^\n" +
- "Potential null pointer access: The field o2 is declared as @Nullable\n" +
+ potNPE_nullable("The field o2") +
"----------\n" +
"3. ERROR in X.java (at line 11)\n" +
" System.out.println(o3.toString()); // warn here: expired by empty statement\n" +
" ^^\n" +
- "Potential null pointer access: The field o3 is declared as @Nullable\n" +
+ potNPE_nullable("The field o3") +
"----------\n" +
"4. ERROR in X.java (at line 13)\n" +
" System.out.println(o4.toString()); // warn here: expired by call to hoo()\n" +
" ^^\n" +
- "Potential null pointer access: The field o4 is declared as @Nullable\n" +
+ potNPE_nullable("The field o4") +
"----------\n");
}
@@ -4948,7 +5065,7 @@ public void test_nullable_field_13() {
"1. ERROR in X.java (at line 7)\n" +
" this.o2 = other.o1; // warn here: assign @Nullable to @NonNull\n" +
" ^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("Object") +
"----------\n");
}
@@ -4997,7 +5114,7 @@ public void test_nullable_field_14a() {
"1. ERROR in X.java (at line 6)\n" +
" return this.o.toString(); // warn here, check has no effect\n" +
" ^\n" +
- "Potential null pointer access: The field o is declared as @Nullable\n" +
+ potNPE_nullable("The field o") +
"----------\n");
}
@@ -5026,17 +5143,17 @@ public void test_nullable_field_15() {
"1. ERROR in X.java (at line 8)\n" +
" ((Number)nullable).intValue(); // A\n" +
" ^^^^^^^^\n" +
- "Potential null pointer access: The field nullable is declared as @Nullable\n" +
+ potNPE_nullable("The field nullable") +
"----------\n" +
"2. ERROR in X.java (at line 11)\n" +
" nullable.toString(); // B\n" +
" ^^^^^^^^\n" +
- "Potential null pointer access: The field nullable is declared as @Nullable\n" +
+ potNPE_nullable("The field nullable") +
"----------\n" +
"3. ERROR in X.java (at line 13)\n" +
" nullable.toString(); // C\n" +
" ^^^^^^^^\n" +
- "Potential null pointer access: The field nullable is declared as @Nullable\n" +
+ potNPE_nullable("The field nullable") +
"----------\n");
}
// an enum is declared within the scope of a null-default
@@ -5137,7 +5254,7 @@ public void testBug372011() {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=374129 - more tests for bug 372011
// Test whether @NonNullByDefault on a binary package or an enclosing type is respected from enclosed elements.
public void testBug374129() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test374129.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test374129"+this.TEST_JAR_SUFFIX;
/* content of Test372129.jar:
p1bin/package-info.java:
@org.eclipse.jdt.annotation.NonNullByDefault
@@ -5221,27 +5338,27 @@ public void testBug374129() {
"1. ERROR in bug374129\\Test.java (at line 22)\n" +
" s = c1.getId(n, n); // error on first arg (package default)\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"2. ERROR in bug374129\\Test.java (at line 23)\n" +
" s = c1i.getId(n, n); // error on first arg (package default propagated into inner)\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"3. ERROR in bug374129\\Test.java (at line 24)\n" +
" s = c2.getId(n, n); // error on first arg (type default)\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n" +
"4. WARNING in bug374129\\Test.java (at line 25)\n" +
" s = c2i.getId(n, n); // no arg error (canceled default), return requires unchecked conversion\n" +
" ^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ nullTypeSafety() + "The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n" +
"5. ERROR in bug374129\\Test.java (at line 26)\n" +
" s = c3.getId(n, n); // error on first arg (method default)\n" +
" ^\n" +
- "Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
+ mismatch_NonNull_Nullable("String") +
"----------\n",
libs,
true /* shouldFlush*/);
@@ -5353,7 +5470,7 @@ public void testBug388630_2() {
"1. ERROR in X.java (at line 7)\n" +
" System.out.println(a.toString());\n" +
" ^\n" +
- "Potential null pointer access: The variable a may be null at this location\n" +
+ variableMayBeNull("a") +
"----------\n");
}
@@ -5422,7 +5539,7 @@ public class C2 implements i2.I2 {
// Test whether null annotations from a super interface are respected
// Class and its super interface both read from binary
public void testBug388281_01() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281"+this.TEST_JAR_SUFFIX;
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
@@ -5459,7 +5576,7 @@ public void testBug388281_01() {
// Test whether null annotations from a super interface are respected
// Class from source, its supers (class + super interface) from binary
public void testBug388281_02() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281"+this.TEST_JAR_SUFFIX;
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
@@ -5493,8 +5610,8 @@ public void testBug388281_02() {
"----------\n" +
"1. ERROR in ctest\\C.java (at line 5)\n" +
" System.out.println(a1.toString()); // (1)\n" +
- " ^^\n" +
- "Potential null pointer access: The variable a1 may be null at this location\n" +
+ " ^^\n" +
+ potNPE_nullable_maybenull("The variable a1") +
"----------\n" +
"2. ERROR in ctest\\C.java (at line 6)\n" +
" return null; // (2)\n" +
@@ -5521,7 +5638,7 @@ public void testBug388281_02() {
// Test whether null annotations from a super interface trigger an error against the overriding implementation
// Class from source, its super interface from binary
public void testBug388281_03() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281"+this.TEST_JAR_SUFFIX;
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
@@ -5549,8 +5666,8 @@ public void testBug388281_03() {
"----------\n" +
"1. ERROR in ctest\\C.java (at line 4)\n" +
" System.out.println(a1.toString()); // (1)\n" +
- " ^^\n" +
- "Potential null pointer access: The variable a1 may be null at this location\n" +
+ " ^^\n" +
+ potNPE_nullable_maybenull("The variable a1") +
"----------\n" +
"2. ERROR in ctest\\C.java (at line 5)\n" +
" return null; // (2)\n" +
@@ -5559,8 +5676,8 @@ public void testBug388281_03() {
"----------\n" +
"3. ERROR in ctest\\C.java (at line 12)\n" +
" System.out.println(a1.toString()); // (3)\n" +
- " ^^\n" +
- "Potential null pointer access: The variable a1 may be null at this location\n" +
+ " ^^\n" +
+ potNPE_nullable_maybenull("The variable a1") +
"----------\n",
libs,
true /* shouldFlush*/,
@@ -5602,7 +5719,7 @@ public void testBug388281_04() {
"1. ERROR in ctest\\C.java (at line 5)\n" +
" System.out.println(s2.toString()); // (1)\n" +
" ^^\n" +
- "Potential null pointer access: The variable s2 may be null at this location\n" +
+ variableMayBeNull("s2") +
"----------\n" +
"2. ERROR in ctest\\C.java (at line 6)\n" +
" return null; // (2)\n" +
@@ -5612,7 +5729,7 @@ public void testBug388281_04() {
"3. ERROR in ctest\\C.java (at line 9)\n" +
" System.out.println(s1.toString()); // (3)\n" +
" ^^\n" +
- "Potential null pointer access: The variable s1 may be null at this location\n" +
+ variableMayBeNull("s1") +
"----------\n");
}
@@ -5621,7 +5738,7 @@ public void testBug388281_04() {
// Class from source, its super interface from binary
// Super interface subject to package level @NonNullByDefault
public void testBug388281_05() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281"+this.TEST_JAR_SUFFIX;
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
@@ -5674,7 +5791,7 @@ public void testBug388281_05() {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
// Conflicting annotations from several indirect super interfaces must be detected
public void testBug388281_06() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281"+this.TEST_JAR_SUFFIX;
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
@@ -5754,7 +5871,7 @@ public void testBug388281_07() {
"3. ERROR in p2\\Sub.java (at line 7)\n" +
" System.out.println(arg.toString()); // (1)\n" +
" ^^^\n" +
- "Potential null pointer access: The variable arg may be null at this location\n" +
+ variableMayBeNull("arg") +
"----------\n" +
"----------\n" +
"1. ERROR in Client.java (at line 4)\n" +
@@ -5767,7 +5884,7 @@ public void testBug388281_07() {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=388281
// report conflict between inheritance and default - binary types
public void testBug388281_08() {
- String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281.jar";
+ String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test388281"+this.TEST_JAR_SUFFIX;
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
@@ -5811,8 +5928,8 @@ public void testBug388281_08() {
"----------\n" +
"3. ERROR in ctest\\Ctest.java (at line 12)\n" +
" System.out.println(o1.toString()); // (1) inherited @Nullable\n" +
- " ^^\n" +
- "Potential null pointer access: The variable o1 may be null at this location\n" +
+ " ^^\n" +
+ potNPE_nullable_maybenull("The variable o1") +
"----------\n" +
"4. ERROR in ctest\\Ctest.java (at line 13)\n" +
" return null; // (2) @NonNullByDefault in i2.II\n" +
@@ -6003,7 +6120,7 @@ public void testBug382069_k() {
"1. ERROR in X.java (at line 11)\n" +
" return o1.length(); // no longer protected\n" +
" ^^\n" +
- "Potential null pointer access: The field o1 is declared as @Nullable\n" +
+ potNPE_nullable("The field o1") +
"----------\n");
}
//https://bugs.eclipse.org/400761: [compiler][null] null may be return as boolean without a diagnostic
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
index efec9527b0..4f09a58015 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
@@ -50,7 +50,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
// Static initializer to specify tests subset using TESTS_* static variables
// All specified tests which do not belong to the class are skipped...
static {
-// TESTS_NAMES = new String[] { "testBinary" };
+// TESTS_NAMES = new String[] { "testArrayType_10" };
// TESTS_NUMBERS = new int[] { 561 };
// TESTS_RANGE = new int[] { 1, 2049 };
}
@@ -184,7 +184,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"1. ERROR in X.java (at line 5)\n" +
" System.out.print(l.get(0).toString()); // problem: l may be null\n" +
" ^\n" +
- "Potential null pointer access: The variable l may be null at this location\n" +
+ "Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n" +
"2. ERROR in X.java (at line 6)\n" +
" l.add(null); // problem: cannot insert \'null\' into this list\n" +
@@ -194,7 +194,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"3. ERROR in X.java (at line 9)\n" +
" System.out.print(l.get(0).toString()); // problem: l may be null\n" +
" ^\n" +
- "Potential null pointer access: The variable l may be null at this location\n" +
+ "Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n" +
"4. ERROR in X.java (at line 10)\n" +
" l.add(0, null); // problem: cannot insert \'null\' into this list\n" +
@@ -236,7 +236,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"1. ERROR in A.java (at line 9)\n" +
" @NonNull Object o = i.foo(null); // problems: argument and assignment violate null contracts\n" +
" ^^^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
+ "Null type mismatch (type annotations): required '@NonNull Object' but this expression has type '@Nullable Object'\n" +
"----------\n" +
"2. ERROR in A.java (at line 9)\n" +
" @NonNull Object o = i.foo(null); // problems: argument and assignment violate null contracts\n" +
@@ -292,7 +292,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"2. ERROR in B.java (at line 7)\n" +
" return idY(null);\n" +
" ^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
+ "Null type mismatch (type annotations): required '@NonNull Object' but this expression has type '@Nullable String'\n" +
"----------\n",
null,
true, /* shouldFlush*/
@@ -526,36 +526,44 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
" array = maybeStringArray; // problem: array can be null\n" +
" maybeStringArray = null; // no problem\n" +
" }\n" +
- " void leaf(String @NonNull[] realStringArray, String @Nullable[] maybeStringArray) {\n" +
+ " void leaf(String @NonNull[] realStringArray, String @Nullable[] maybeStringArray, boolean b) {\n" +
" @NonNull String string;\n" +
" string = realStringArray[0]; // problem: unchecked conversion\n" +
" realStringArray[0] = null; // no problem\n" +
- " string = maybeStringArray[0]; // problems: indexing nullable array & unchecked conversion\n" +
- " maybeStringArray[0] = null; // problem: indexing nullable array\n" +
+ " if (b)\n" +
+ " string = maybeStringArray[0]; // problems: indexing nullable array & unchecked conversion\n" +
+ " else\n" +
+ " maybeStringArray[0] = null; // problem: indexing nullable array\n" +
+ " maybeStringArray[0] = null; // problem protected by previous dereference\n" +
" }\n" +
"}\n"},
"----------\n" +
- "1. ERROR in A.java (at line 7)\n" +
+ "1. ERROR in A.java (at line 6)\n" +
+ " realStringArray = null; // problem: cannot assign null as @NonNull array\n" +
+ " ^^^^\n" +
+ "Null type mismatch: required \'String @NonNull[]\' but the provided value is null\n" +
+ "----------\n" +
+ "2. ERROR in A.java (at line 7)\n" +
" array = maybeStringArray; // problem: array can be null\n" +
" ^^^^^^^^^^^^^^^^\n" +
- "Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
+ "Null type mismatch (type annotations): required '@NonNull Object' but this expression has type 'String @Nullable[]'\n" +
"----------\n" +
- "2. WARNING in A.java (at line 12)\n" +
+ "3. WARNING in A.java (at line 12)\n" +
" string = realStringArray[0]; // problem: unchecked conversion\n" +
" ^^^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ "Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n" +
- "3. ERROR in A.java (at line 14)\n" +
+ "4. ERROR in A.java (at line 15)\n" +
" string = maybeStringArray[0]; // problems: indexing nullable array & unchecked conversion\n" +
" ^^^^^^^^^^^^^^^^\n" +
"Potential null pointer access: this expression has a '@Nullable' type\n" +
"----------\n" +
- "4. WARNING in A.java (at line 14)\n" +
+ "5. WARNING in A.java (at line 15)\n" +
" string = maybeStringArray[0]; // problems: indexing nullable array & unchecked conversion\n" +
" ^^^^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ "Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n" +
- "5. ERROR in A.java (at line 15)\n" +
+ "6. ERROR in A.java (at line 17)\n" +
" maybeStringArray[0] = null; // problem: indexing nullable array\n" +
" ^^^^^^^^^^^^^^^^\n" +
"Potential null pointer access: this expression has a '@Nullable' type\n" +
@@ -608,12 +616,12 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"1. WARNING in A.java (at line 5)\n" +
" array = realArrays; // problem: unchecked conversion\n" +
" ^^^^^^^^^^\n" +
- "Null type safety: The expression of type String[][] needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ "Null type safety (type annotations): The expression of type 'String [] @NonNull[]' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n" +
"2. WARNING in A.java (at line 7)\n" +
" array = maybeArrays; // problem: unchecked conversion\n" +
" ^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String[][] needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ "Null type safety (type annotations): The expression of type 'String [] @Nullable[]' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n" +
"3. ERROR in A.java (at line 13)\n" +
" realArrays[0] = null; // problem: cannot assign null to @NonNull array\n" +
@@ -623,12 +631,12 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"4. ERROR in A.java (at line 14)\n" +
" array = maybeArrays[0]; // problem: element can be null\n" +
" ^^^^^^^^^^^^^^\n" +
- "Null type mismatch: required '@NonNull Object' but the provided value is inferred as @Nullable\n" +
+ "Null type mismatch (type annotations): required '@NonNull Object' but this expression has type 'String @Nullable[]'\n" +
"----------\n" +
"5. WARNING in A.java (at line 19)\n" +
" array = realArrays[0][0]; // problem: unchecked conversion\n" +
" ^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ "Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n" +
"6. ERROR in A.java (at line 21)\n" +
" array = maybeArrays[0][0]; // problems: indexing nullable array & unchecked conversion\n" +
@@ -638,7 +646,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"7. WARNING in A.java (at line 21)\n" +
" array = maybeArrays[0][0]; // problems: indexing nullable array & unchecked conversion\n" +
" ^^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull Object\'\n" +
+ "Null type safety (type annotations): The expression of type 'String' needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n" +
"8. ERROR in A.java (at line 22)\n" +
" maybeArrays[0][0] = null; // problem: indexing nullable array\n" +
@@ -688,7 +696,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"2. WARNING in A.java (at line 5)\n" +
" realArrays[0] = unknownArrays[0]; // problems: inner array is unspecified, outer can be null\n" +
" ^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String[] needs unchecked conversion to conform to \'String @NonNull[]\'\n" +
+ "Null type safety (type annotations): The expression of type 'String []' needs unchecked conversion to conform to \'String @NonNull[]\'\n" +
"----------\n" +
"3. ERROR in A.java (at line 5)\n" +
" realArrays[0] = unknownArrays[0]; // problems: inner array is unspecified, outer can be null\n" +
@@ -703,7 +711,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"5. WARNING in A.java (at line 9)\n" +
" s = unknownStrings;\n" +
" ^^^^^^^^^^^^^^\n" +
- "Null type mismatch (type annotations): the expression of type \'String[]\' needs unchecked conversion to conform to \'String @NonNull[]\'\n" +
+ "Null type safety (type annotations): The expression of type \'String[]\' needs unchecked conversion to conform to \'String @NonNull[]\'\n" +
"----------\n" +
"6. ERROR in A.java (at line 10)\n" +
" consume(maybeStrings);\n" +
@@ -713,7 +721,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"7. WARNING in A.java (at line 11)\n" +
" consume(unknownStrings);\n" +
" ^^^^^^^^^^^^^^\n" +
- "Null type mismatch (type annotations): the expression of type \'String[]\' needs unchecked conversion to conform to \'String @NonNull[]\'\n" +
+ "Null type safety (type annotations): The expression of type \'String[]\' needs unchecked conversion to conform to \'String @NonNull[]\'\n" +
"----------\n",
null,
true, /* shouldFlush*/
@@ -773,12 +781,12 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"5. WARNING in A.java (at line 8)\n" +
" realArrays = mixedArrays; // problem on inner\n" +
" ^^^^^^^^^^^\n" +
- "Null type mismatch (type annotations): the expression of type \'String @NonNull[] []\' needs unchecked conversion to conform to \'String @NonNull[] @NonNull[]\'\n" +
+ "Null type safety (type annotations): The expression of type \'String @NonNull[] []\' needs unchecked conversion to conform to \'String @NonNull[] @NonNull[]\'\n" +
"----------\n" +
"6. WARNING in A.java (at line 9)\n" +
" maybeArrays = mixedArrays; // problem on inner\n" +
" ^^^^^^^^^^^\n" +
- "Null type mismatch (type annotations): the expression of type \'String @NonNull[] []\' needs unchecked conversion to conform to \'String @NonNull[] @Nullable[]\'\n" +
+ "Null type safety (type annotations): The expression of type \'String @NonNull[] []\' needs unchecked conversion to conform to \'String @NonNull[] @Nullable[]\'\n" +
"----------\n" +
"7. ERROR in A.java (at line 10)\n" +
" consume(maybeArrays, mixedArrays, maybeArrays);\n" +
@@ -788,7 +796,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"8. WARNING in A.java (at line 10)\n" +
" consume(maybeArrays, mixedArrays, maybeArrays);\n" +
" ^^^^^^^^^^^\n" +
- "Null type mismatch (type annotations): the expression of type \'String @NonNull[] []\' needs unchecked conversion to conform to \'String @NonNull[] @Nullable[]\'\n" +
+ "Null type safety (type annotations): The expression of type \'String @NonNull[] []\' needs unchecked conversion to conform to \'String @NonNull[] @Nullable[]\'\n" +
"----------\n" +
"9. ERROR in A.java (at line 10)\n" +
" consume(maybeArrays, mixedArrays, maybeArrays);\n" +
@@ -956,7 +964,7 @@ public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
"1. ERROR in Y.java (at line 6)\n" +
" x.setAllStrings(-1, ss);\n" +
" ^^^^^^^^^^^^^\n" +
- "The method setAllStrings(int, java.util.List<java.lang.@NonNull String>) in the type X is not applicable for the arguments (int, java.util.List<java.lang.@Nullable String>)\n" +
+ "The method setAllStrings(int, List<@NonNull String>) in the type X is not applicable for the arguments (int, List<@Nullable String>)\n" +
"----------\n"
);
}
diff --git a/org.eclipse.jdt.core.tests.compiler/workspace/Test374129_1.8.jar b/org.eclipse.jdt.core.tests.compiler/workspace/Test374129_1.8.jar
new file mode 100644
index 0000000000..fbb65fb1bc
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/workspace/Test374129_1.8.jar
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.compiler/workspace/Test388281_1.8.jar b/org.eclipse.jdt.core.tests.compiler/workspace/Test388281_1.8.jar
new file mode 100644
index 0000000000..9d36658697
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.compiler/workspace/Test388281_1.8.jar
Binary files differ
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java
index 840e36f00d..481a370241 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java
@@ -5,6 +5,10 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
@@ -113,7 +117,7 @@ public class NullAnnotationModelTests extends ReconcilerTests {
"1. WARNING in /P/p2/C2.java (at line 8)\n" +
" return arg == null ? null : arg.toString();\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ "Null type safety: The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n");
} finally {
deleteProject("P");
@@ -155,7 +159,7 @@ public class NullAnnotationModelTests extends ReconcilerTests {
"1. WARNING in /P/p2/C2.java (at line 8)\n" +
" return arg == null ? null : arg.toString();\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
- "Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
+ "Null type safety: The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n");
} finally {
deleteProject("P");
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnArgumentName.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnArgumentName.java
index 4abb8fb720..62b583b527 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnArgumentName.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/complete/CompletionOnArgumentName.java
@@ -5,8 +5,14 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist.complete;
@@ -30,7 +36,7 @@ public class CompletionOnArgumentName extends Argument {
this.realName = name;
}
- public void bind(MethodScope scope, TypeBinding typeBinding, boolean used) {
+ public TypeBinding bind(MethodScope scope, TypeBinding typeBinding, boolean used) {
super.bind(scope, typeBinding, used);
throw new CompletionNodeFound(this, scope);
diff --git a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnArgumentName.java b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnArgumentName.java
index bbbce409b1..98d8147578 100644
--- a/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnArgumentName.java
+++ b/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionOnArgumentName.java
@@ -5,8 +5,14 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
+ * Stephan Herrmann - Contribution for
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.codeassist.select;
@@ -23,7 +29,7 @@ public class SelectionOnArgumentName extends Argument {
super(name, posNom, tr, modifiers);
}
- public void bind(MethodScope scope, TypeBinding typeBinding, boolean used) {
+ public TypeBinding bind(MethodScope scope, TypeBinding typeBinding, boolean used) {
super.bind(scope, typeBinding, used);
throw new SelectionNodeFound(this.binding);
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 178a636d99..3eff6957e6 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
@@ -161,7 +161,7 @@
* ArrayReferencePotentialNullReference
* DereferencingNullableExpression
* NullityMismatchingTypeAnnotation
- * NullityMismatchingTypeAnnotationUnchecked
+ * NullityUncheckedTypeAnnotationDetail
* NullableFieldReference
* UninitializedNonNullField
* UninitializedNonNullFieldHintMissingDefault
@@ -184,6 +184,8 @@
* NullUnboxing
* NullExpressionReference
* PotentialNullExpressionReference
+ * RedundantNullCheckAgainstNonNullType
+ * NullAnnotationUnsupportedLocation
* Jesper S Moller - added the following constants
* TargetTypeNotAFunctionalInterface
* OuterLocalMustBeEffectivelyFinal
@@ -1667,6 +1669,10 @@ void setSourceStart(int sourceStart);
int ConflictingNullAnnotations = MethodRelated + 939;
/** @since 3.9 */
int ConflictingInheritedNullAnnotations = MethodRelated + 940;
+ /** @since 3.9 BETA_JAVA8 */
+ int RedundantNullCheckOnField = Internal + 941;
+ /** @since 3.9 BETA_JAVA8 */
+ int FieldComparisonYieldsFalse = Internal + 942;
/** @since 3.9 BETA_JAVA8 */
int ArrayReferencePotentialNullReference = Internal + 951;
@@ -1675,7 +1681,7 @@ void setSourceStart(int sourceStart);
/** @since 3.9 BETA_JAVA8 */
int NullityMismatchingTypeAnnotation = Internal + 953;
/** @since 3.9 BETA_JAVA8 */
- int NullityMismatchingTypeAnnotationUnchecked = Internal + 954;
+ int NullityUncheckedTypeAnnotationDetail = Internal + 954;
/** @since 3.9 BETA_JAVA8 */
int ReferenceExpressionParameterMismatchPromisedNullable = MethodRelated + 955;
/** @since 3.9 BETA_JAVA8 */
@@ -1684,6 +1690,10 @@ void setSourceStart(int sourceStart);
int ReferenceExpressionReturnNullRedef = MethodRelated + 957;
/** @since 3.9 BETA_JAVA8 */
int ReferenceExpressionReturnNullRedefUnchecked = MethodRelated + 958;
+ /** @since 3.9 BETA_JAVA8 */
+ int RedundantNullCheckAgainstNonNullType = Internal + 959;
+ /** @since 3.9 BETA_JAVA8 */
+ int NullAnnotationUnsupportedLocation = Internal + 960;
// Java 8 work
/** @since 3.9 BETA_JAVA8 */
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
index 2e3dd07fc1..970e842bf2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
@@ -20,14 +20,13 @@
* bug 374605 - Unreasonable warning for enum-based switch statements
* bug 384870 - [compiler] @Deprecated annotation not detected if preceded by other annotation
* bug 393719 - [compiler] inconsistent warnings on iteration variables
- * bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller - Contributions for
* bug 382721 - [1.8][compiler] Effectively final variables needs special treatment
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.core.compiler.CharOperation;
-import org.eclipse.jdt.internal.compiler.ast.Annotation.TypeUseBinding;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.lookup.*;
@@ -708,7 +707,7 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
Annotation annotation = sourceAnnotations[i];
final Binding annotationRecipient = annotation.recipient;
if (annotationRecipient != null && recipient != null) {
- // only local, field and type_use can share annnotations
+ // only local and field can share annnotations
switch (recipient.kind()) {
case Binding.FIELD :
FieldBinding field = (FieldBinding) recipient;
@@ -756,9 +755,6 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
}
}
break;
- case Binding.TYPE_USE :
- TypeUseBinding typeUse = (TypeUseBinding) recipient;
- typeUse.tagBits = ((TypeUseBinding) annotationRecipient).tagBits;
}
return;
} else {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
index a048315035..137282aa2e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AbstractMethodDeclaration.java
@@ -20,6 +20,7 @@
* bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 401030 - [1.8][null] Null analysis support for lambda methods.
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -94,17 +95,15 @@ public abstract class AbstractMethodDeclaration
}
// version for invocation from LambdaExpression:
static void createArgumentBindings(Argument[] arguments, MethodBinding binding, MethodScope scope) {
+ boolean useTypeAnnotations = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8;
if (arguments != null && binding != null) {
for (int i = 0, length = arguments.length; i < length; i++) {
Argument argument = arguments[i];
- argument.createBinding(scope, binding.parameters[i]);
+ binding.parameters[i] = argument.createBinding(scope, binding.parameters[i]);
+ if (useTypeAnnotations)
+ continue; // no business with SE7 null annotations in the 1.8 case.
// createBinding() has resolved annotations, now transfer nullness info from the argument to the method:
- // prefer type annotation:
- long argTypeTagBits = (argument.type.resolvedType.tagBits & TagBits.AnnotationNullMASK);
- // if none found try SE7 annotation:
- if (argTypeTagBits == 0) {
- argTypeTagBits = (argument.binding.tagBits & TagBits.AnnotationNullMASK);
- }
+ long argTypeTagBits = (argument.binding.tagBits & TagBits.AnnotationNullMASK);
if (argTypeTagBits != 0) {
if (binding.parameterNonNullness == null) {
binding.parameterNonNullness = new Boolean[arguments.length];
@@ -133,7 +132,7 @@ public abstract class AbstractMethodDeclaration
AnnotationBinding[][] paramAnnotations = null;
for (int i = 0, length = this.arguments.length; i < length; i++) {
Argument argument = this.arguments[i];
- argument.bind(this.scope, this.binding.parameters[i], used);
+ this.binding.parameters[i] = argument.bind(this.scope, this.binding.parameters[i], used);
if (argument.annotations != null) {
if (paramAnnotations == null) {
paramAnnotations = new AnnotationBinding[length][];
@@ -214,6 +213,27 @@ public abstract class AbstractMethodDeclaration
}
}
+ /**
+ * Feed null information from argument annotations into the analysis and mark arguments as assigned.
+ * Variant for Java 8 using type annotations
+ */
+ static void analyseArguments18(FlowInfo flowInfo, Argument[] methodArguments, MethodBinding methodBinding) {
+ if (methodArguments != null) {
+ int length = Math.min(methodBinding.parameters.length, methodArguments.length);
+ for (int i = 0; i < length; i++) {
+ // leverage null type annotations:
+ long tagBits = methodBinding.parameters[i].tagBits & TagBits.AnnotationNullMASK;
+ if (tagBits == TagBits.AnnotationNonNull)
+ flowInfo.markAsDefinitelyNonNull(methodArguments[i].binding);
+ else if (tagBits == TagBits.AnnotationNullable)
+ flowInfo.markPotentiallyNullBit(methodArguments[i].binding);
+
+ // tag parameters as being set:
+ flowInfo.markAsDefinitelyAssigned(methodArguments[i].binding);
+ }
+ }
+ }
+
public CompilationResult compilationResult() {
return this.compilationResult;
@@ -501,13 +521,16 @@ public abstract class AbstractMethodDeclaration
bindThrownExceptions();
resolveJavadoc();
resolveAnnotations(this.scope, this.annotations, this.binding);
- validateNullAnnotations();
+
+ long sourceLevel = this.scope.compilerOptions().sourceLevel;
+ validateNullAnnotations(sourceLevel);
+
resolveStatements();
// check @Deprecated annotation presence
if (this.binding != null
&& (this.binding.getAnnotationTagBits() & TagBits.AnnotationDeprecated) == 0
&& (this.binding.modifiers & ClassFileConstants.AccDeprecated) != 0
- && this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) {
+ && sourceLevel >= ClassFileConstants.JDK1_5) {
this.scope.problemReporter().missingDeprecatedAnnotationForMethod(this);
}
} catch (AbortMethod e) {
@@ -618,17 +641,26 @@ public abstract class AbstractMethodDeclaration
return null;
}
- void validateNullAnnotations() {
+ void validateNullAnnotations(long sourceLevel) {
+ if (this.binding == null) return;
// null annotations on parameters?
- if (this.binding != null && this.binding.parameterNonNullness != null) {
- int length = this.binding.parameters.length;
- for (int i=0; i<length; i++) {
- if (this.binding.parameterNonNullness[i] != null) {
- long nullAnnotationTagBit = this.binding.parameterNonNullness[i].booleanValue()
- ? TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
- this.scope.validateNullAnnotation(nullAnnotationTagBit, this.arguments[i].type, this.arguments[i].annotations);
+ if (sourceLevel < ClassFileConstants.JDK1_8) {
+ if (this.binding.parameterNonNullness != null) {
+ int length = this.binding.parameters.length;
+ for (int i=0; i<length; i++) {
+ if (this.binding.parameterNonNullness[i] != null) {
+ long nullAnnotationTagBit = this.binding.parameterNonNullness[i].booleanValue()
+ ? TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
+ this.scope.validateNullAnnotation(nullAnnotationTagBit, this.arguments[i].type, this.arguments[i].annotations);
+ }
}
}
+ } else {
+ int length = this.binding.parameters.length;
+ for (int i=0; i<length; i++) {
+ long nullAnnotationTagBit = this.binding.parameters[i].tagBits & TagBits.AnnotationNullMASK;
+ this.scope.validateNullAnnotation(nullAnnotationTagBit, this.arguments[i].type, this.arguments[i].annotations);
+ }
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
index 1bfac7b8d7..baa970484e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
@@ -15,6 +15,7 @@
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365662 - [compiler][null] warn on contradictory and redundant null annotations
* bug 331649 - [compiler][null] consider null annotations for fields
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
*******************************************************************************/
@@ -894,9 +895,16 @@ public abstract class Annotation extends Expression {
AbstractMethodDeclaration methodDeclaration = sourceType.scope.referenceContext.declarationOf(sourceMethod);
recordSuppressWarnings(scope, methodDeclaration.declarationSourceStart, methodDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
}
- if ((sourceMethod.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
+ long nullBits = sourceMethod.tagBits & TagBits.AnnotationNullMASK;
+ if (nullBits == TagBits.AnnotationNullMASK) {
scope.problemReporter().contradictoryNullAnnotations(this);
- sourceMethod.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems
+ sourceMethod.tagBits &= ~TagBits.AnnotationNullMASK; // avoid secondary problems
+ }
+ if (nullBits != 0 && sourceMethod.isConstructor()) {
+ if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8)
+ scope.problemReporter().nullAnnotationUnsupportedLocation(this);
+ // for declaration annotations the inapplicability will be reported below
+ sourceMethod.tagBits &= ~TagBits.AnnotationNullMASK;
}
break;
case Binding.FIELD :
@@ -907,6 +915,8 @@ public abstract class Annotation extends Expression {
FieldDeclaration fieldDeclaration = sourceType.scope.referenceContext.declarationOf(sourceField);
recordSuppressWarnings(scope, fieldDeclaration.declarationSourceStart, fieldDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
}
+ // fields don't yet have their type resolved, in 1.8 null annotations
+ // will be transfered from the field to its type during STB.resolveTypeFor().
if ((sourceField.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
scope.problemReporter().contradictoryNullAnnotations(this);
sourceField.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems
@@ -914,15 +924,44 @@ public abstract class Annotation extends Expression {
break;
case Binding.LOCAL :
LocalVariableBinding variable = (LocalVariableBinding) this.recipient;
- variable.tagBits |= tagBits;
+ if ((annotationType.tagBits & TagBits.AnnotationTargetMASK) == TagBits.AnnotationForTypeUse) {
+ if (variable.type != null) {
+ if (variable.type.isBaseType()) {
+ scope.problemReporter().illegalAnnotationForBaseType(this, variable.type);
+ } else {
+ long nullTagBits = tagBits & TagBits.AnnotationNullMASK;
+ variable.type = scope.environment().createAnnotatedType(variable.type, nullTagBits);
+ if ((variable.type.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
+ scope.problemReporter().contradictoryNullAnnotations(this);
+ variable.type.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems
+ }
+ }
+ }
+ } else {
+ if (scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) {
+ variable.tagBits |= tagBits;
+ if ((variable.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
+ scope.problemReporter().contradictoryNullAnnotations(this);
+ variable.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems
+ }
+ } else if (variable.type != null) {
+ // bits not relating to null analysis go into the variable:
+ variable.tagBits |= tagBits & ~TagBits.AnnotationNullMASK;
+ // null bits go into the type:
+ long nullTagBits = tagBits & TagBits.AnnotationNullMASK;
+ if (nullTagBits != 0) {
+ variable.type = scope.environment().pushAnnotationIntoType(variable.type, variable.declaration.type, nullTagBits);
+ if ((variable.type.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
+ scope.problemReporter().contradictoryNullAnnotations(this);
+ variable.type = variable.type.unannotated();
+ }
+ }
+ }
+ }
if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
LocalDeclaration localDeclaration = variable.declaration;
recordSuppressWarnings(scope, localDeclaration.declarationSourceStart, localDeclaration.declarationSourceEnd, scope.compilerOptions().suppressWarnings);
}
- if ((variable.tagBits & TAGBITS_NULLABLE_OR_NONNULL) == TAGBITS_NULLABLE_OR_NONNULL) {
- scope.problemReporter().contradictoryNullAnnotations(this);
- variable.tagBits &= ~TAGBITS_NULLABLE_OR_NONNULL; // avoid secondary problems
- }
break;
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java
index 5e08fc19ce..65c3482672 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Argument.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 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
@@ -14,6 +14,7 @@
* Stephan Herrmann - Contributions for
* bug 186342 - [compiler][null] Using annotations for null checking
* bug 365519 - editorial cleanup after bug 186342 and bug 365387
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -52,7 +53,7 @@ public class Argument extends LocalDeclaration {
this.bits |= (IsLocalDeclarationReachable | IsArgument | IsTypeElided);
}
- public void createBinding(MethodScope scope, TypeBinding typeBinding) {
+ public TypeBinding createBinding(MethodScope scope, TypeBinding typeBinding) {
if (this.binding == null) {
// for default constructors and fake implementation of abstract methods
this.binding = new LocalVariableBinding(this, typeBinding, this.modifiers, true /*isArgument*/);
@@ -67,10 +68,11 @@ public class Argument extends LocalDeclaration {
}
resolveAnnotations(scope, this.annotations, this.binding);
this.binding.declaration = this;
+ return this.binding.type; // might have been updated during resolveAnnotations (for typeAnnotations)
}
- public void bind(MethodScope scope, TypeBinding typeBinding, boolean used) {
- createBinding(scope, typeBinding); // basically a no-op if createBinding() was called before
+ public TypeBinding bind(MethodScope scope, TypeBinding typeBinding, boolean used) {
+ TypeBinding newTypeBinding = createBinding(scope, typeBinding); // basically a no-op if createBinding() was called before
// record the resolved type into the type reference
Binding existingVariable = scope.getBinding(this.name, Binding.VARIABLE, this, false /*do not resolve hidden field*/);
@@ -97,6 +99,7 @@ public class Argument extends LocalDeclaration {
}
scope.addLocalVariable(this.binding);
this.binding.useFlag = used ? LocalVariableBinding.USED : LocalVariableBinding.UNUSED;
+ return newTypeBinding;
}
/**
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
index c892b9d823..8d21847b6c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
@@ -20,6 +20,7 @@
* bug 345305 - [compiler][null] Compiler misidentifies a case of "variable can only be null"
* bug 383368 - [compiler][null] syntactic null analysis for field references
* bug 400761 - [compiler][null] null may be return as boolean without a diagnostic
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -469,6 +470,14 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
TypeBinding valueIfTrueType = this.originalValueIfTrueType;
TypeBinding valueIfFalseType = this.originalValueIfFalseType;
+ if (use18specifics && valueIfTrueType != null && valueIfFalseType != null) {
+ if (valueIfTrueType.isAnnotatedTypeWithoutArguments() != valueIfFalseType.isAnnotatedTypeWithoutArguments()) {
+ if (valueIfTrueType.isAnnotatedTypeWithoutArguments()) // FIXME(stephan) mixed scenarios: null tag bits & type arguments
+ valueIfTrueType = valueIfTrueType.original();
+ else
+ valueIfFalseType = valueIfFalseType.original();
+ }
+ }
if (use15specifics && valueIfTrueType != valueIfFalseType) {
if (valueIfTrueType.isBaseType()) {
if (valueIfFalseType.isBaseType()) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
index 1338801712..2f93e73ed4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConstructorDeclaration.java
@@ -17,6 +17,7 @@
* bug 331649 - [compiler][null] consider null annotations for fields
* bug 383368 - [compiler][null] syntactic null analysis for field references
* bug 400421 - [compiler] Null analysis for fields does not take @com.google.inject.Inject into account
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -130,7 +131,10 @@ public void analyseCode(ClassScope classScope, InitializationFlowContext initial
}
// nullity and mark as assigned
- analyseArguments(flowInfo, this.arguments, this.binding);
+ if (classScope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8)
+ analyseArguments(flowInfo, this.arguments, this.binding);
+ else
+ analyseArguments18(flowInfo, this.arguments, this.binding);
// propagate to constructor call
if (this.constructorCall != null) {
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 8b6189a9ea..2f69346237 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
@@ -20,6 +20,7 @@
* bug 400761 - [compiler][null] null may be return as boolean without a diagnostic
* bug 402993 - [null] Follow up of bug 401088: Missing warning about redundant null check
* bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -560,19 +561,19 @@ public final boolean checkCastTypesCompatibility(Scope scope, TypeBinding castTy
* @return could this expression be checked by the current implementation?
*/
public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ boolean isNullable = false;
if (this.resolvedType != null) {
+ // 1. priority: @NonNull
if ((this.resolvedType.tagBits & TagBits.AnnotationNonNull) != 0) {
return true; // no danger
} else if ((this.resolvedType.tagBits & TagBits.AnnotationNullable) != 0) {
- scope.problemReporter().dereferencingNullableExpression(this, scope.environment());
- return true; // danger is definite.
- // stopping analysis at this point requires that the above error is not suppressable
- // unless suppressing all null warnings (otherwise we'd miss a stronger warning below).
+ isNullable = true;
}
}
LocalVariableBinding local = localVariableBinding();
if (local != null &&
(local.type.tagBits & TagBits.IsBaseType) == 0) {
+ // 2. priority: local with flow analysis (via the FlowContext)
if ((this.bits & ASTNode.IsNonNull) == 0) {
flowContext.recordUsingNullReference(scope, local, this,
FlowContext.MAY_NULL, flowInfo);
@@ -585,6 +586,10 @@ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flow
// from thereon it is set
flowContext.markFinallyNullStatus(local, FlowInfo.NON_NULL);
return true;
+ } else if (isNullable) {
+ // 3. priority: @Nullable without a local
+ scope.problemReporter().dereferencingNullableExpression(this);
+ return true;
}
return false; // not checked
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index 2ce4373aba..ce86994548 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -16,6 +16,7 @@
* bug 382721 - [1.8][compiler] Effectively final variables needs special treatment
* Stephan Herrmann - Contribution for
* bug 401030 - [1.8][null] Null analysis support for lambda methods.
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -206,15 +207,14 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
if ((parameterType.tagBits & TagBits.HasMissingType) != 0) {
this.binding.tagBits |= TagBits.HasMissingType;
}
- if (haveDescriptor && expectedParameterType != null && parameterType.isValidBinding() && parameterType != expectedParameterType) {
+ if (haveDescriptor && expectedParameterType != null && parameterType.isValidBinding() && parameterType.unannotated() != expectedParameterType.unannotated()) {
this.scope.problemReporter().lambdaParameterTypeMismatched(argument, argument.type, expectedParameterType);
}
TypeBinding leafType = parameterType.leafComponentType();
if (leafType instanceof ReferenceBinding && (((ReferenceBinding) leafType).modifiers & ExtraCompilerModifiers.AccGenericSignature) != 0)
this.binding.modifiers |= ExtraCompilerModifiers.AccGenericSignature;
- newParameters[i] = parameterType;
- argument.bind(this.scope, parameterType, false);
+ newParameters[i] = argument.bind(this.scope, parameterType, false);
if (argument.annotations != null) {
this.binding.tagBits |= TagBits.HasParameterAnnotations;
if (parameterAnnotations == null) {
@@ -309,7 +309,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
// nullity and mark as assigned
MethodBinding methodWithParameterDeclaration = argumentsTypeElided() ? this.descriptor : this.binding;
- AbstractMethodDeclaration.analyseArguments(lambdaInfo, this.arguments, methodWithParameterDeclaration);
+ AbstractMethodDeclaration.analyseArguments18(lambdaInfo, this.arguments, methodWithParameterDeclaration);
if (this.arguments != null) {
for (int i = 0, count = this.arguments.length; i < count; i++) {
@@ -346,14 +346,11 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
// pre: !argumentTypeElided()
void validateNullAnnotations() {
// null annotations on parameters?
- if (this.binding != null && this.binding.parameterNonNullness != null) {
+ if (this.binding != null) {
int length = this.binding.parameters.length;
for (int i=0; i<length; i++) {
- if (this.binding.parameterNonNullness[i] != null) {
- long nullAnnotationTagBit = this.binding.parameterNonNullness[i].booleanValue()
- ? TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
- this.scope.validateNullAnnotation(nullAnnotationTagBit, this.arguments[i].type, this.arguments[i].annotations);
- }
+ long nullAnnotationTagBit = this.binding.returnType.tagBits & TagBits.AnnotationNullMASK;
+ this.scope.validateNullAnnotation(nullAnnotationTagBit, this.arguments[i].type, this.arguments[i].annotations);
}
}
}
@@ -361,23 +358,20 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
// pre: !argumentTypeElided()
// try to merge null annotations from descriptor into binding, complaining about any incompatibilities found
private void mergeParameterNullAnnotations(BlockScope currentScope) {
- if (this.descriptor.parameterNonNullness == null)
- return;
- if (this.binding.parameterNonNullness == null) {
- this.binding.parameterNonNullness = this.descriptor.parameterNonNullness;
- return;
- }
LookupEnvironment env = currentScope.environment();
- Boolean[] ourNonNullness = this.binding.parameterNonNullness;
- Boolean[] descNonNullness = this.descriptor.parameterNonNullness;
- int len = Math.min(ourNonNullness.length, descNonNullness.length);
+ TypeBinding[] ourParameters = this.binding.parameters;
+ TypeBinding[] descParameters = this.descriptor.parameters;
+ int len = Math.min(ourParameters.length, descParameters.length);
for (int i = 0; i < len; i++) {
- if (ourNonNullness[i] == null) {
- ourNonNullness[i] = descNonNullness[i];
- } else if (ourNonNullness[i] != descNonNullness[i]) {
- if (ourNonNullness[i] == Boolean.TRUE) { // requested @NonNull not provided
+ long ourTagBits = ourParameters[i].tagBits & TagBits.AnnotationNullMASK;
+ long descTagBits = descParameters[i].tagBits & TagBits.AnnotationNullMASK;
+ if (ourTagBits == 0L) {
+ if (descTagBits != 0L && !ourParameters[i].isBaseType())
+ ourParameters[i] = env.createAnnotatedType(ourParameters[i], descTagBits);
+ } else if (ourTagBits != descTagBits) {
+ if (ourTagBits == TagBits.AnnotationNonNull) { // requested @NonNull not provided
char[][] inheritedAnnotationName = null;
- if (descNonNullness[i] == Boolean.FALSE)
+ if (descTagBits == TagBits.AnnotationNullable)
inheritedAnnotationName = env.getNullableAnnotationName();
currentScope.problemReporter().illegalRedefinitionToNonNullParameter(this.arguments[i], this.descriptor.declaringClass, inheritedAnnotationName);
}
@@ -390,7 +384,7 @@ public class LambdaExpression extends FunctionalExpression implements ReferenceC
if (nullStatus != FlowInfo.NON_NULL) {
// if we can't prove non-null check against declared null-ness of the descriptor method:
// Note that this.binding never has a return type declaration, always inherit null-ness from the descriptor
- if ((this.descriptor.tagBits & TagBits.AnnotationNonNull) != 0) {
+ if ((this.descriptor.returnType.tagBits & TagBits.AnnotationNonNull) != 0) {
flowContext.recordNullityMismatch(this.scope, expression, expression.resolvedType, this.descriptor.returnType, nullStatus);
}
}
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 25d7cd1805..1792a88ab8 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
@@ -33,6 +33,7 @@
* bug 404649 - [1.8][compiler] detect illegal reference to indirect or redundant super
* bug 403086 - [compiler][null] include the effect of 'assert' in syntactic null analysis for fields
* bug 403147 - [compiler][null] FUP of bug 400761: consolidate interaction between unboxing, NPE, and deferred checking
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller - Contributions for
* Bug 378674 - "The method can be declared as static" is wrong
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
@@ -490,7 +491,9 @@ public void manageSyntheticAccessIfNecessary(BlockScope currentScope, FlowInfo f
public int nullStatus(FlowInfo flowInfo, FlowContext flowContext) {
if (this.binding.isValidBinding()) {
// try to retrieve null status of this message send from an annotation of the called method:
- long tagBits = this.binding.tagBits;
+ long tagBits = this.binding.tagBits & TagBits.AnnotationNullMASK;
+ if (tagBits == 0L) // alternatively look for type annotation (will only be present in 1.8+):
+ tagBits = this.binding.returnType.tagBits & TagBits.AnnotationNullMASK;
if ((tagBits & TagBits.AnnotationNonNull) != 0)
return FlowInfo.NON_NULL;
if ((tagBits & TagBits.AnnotationNullable) != 0)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
index f6aacc792e..0e5cd7d2ac 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MethodDeclaration.java
@@ -18,6 +18,7 @@
* bug 368546 - [compiler][resource] Avoid remaining false positives found when compiling the Eclipse SDK
* bug 382353 - [1.8][compiler] Implementation property modifiers should be accepted on default methods.
* bug 383368 - [compiler][null] syntactic null analysis for field references
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller <jesper@selskabet.org> - Contributions for
* bug 378674 - "The method can be declared as static" is wrong
*******************************************************************************/
@@ -106,7 +107,10 @@ public class MethodDeclaration extends AbstractMethodDeclaration {
FlowInfo.DEAD_END);
// nullity and mark as assigned
- analyseArguments(flowInfo, this.arguments, this.binding);
+ if (classScope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8)
+ analyseArguments(flowInfo, this.arguments, this.binding);
+ else
+ analyseArguments18(flowInfo, this.arguments, this.binding);
if (this.binding.declaringClass instanceof MemberTypeBinding && !this.binding.declaringClass.isStatic()) {
// method of a non-static member type can't be static.
@@ -351,10 +355,12 @@ public class MethodDeclaration extends AbstractMethodDeclaration {
return this.typeParameters;
}
- void validateNullAnnotations() {
- super.validateNullAnnotations();
+ void validateNullAnnotations(long sourceLevel) {
+ super.validateNullAnnotations(sourceLevel);
// null-annotations on the return type?
- if (this.binding != null)
- this.scope.validateNullAnnotation(this.binding.tagBits, this.returnType, this.annotations);
+ if (this.binding != null && this.binding.returnType != null) {
+ long tagBits = (sourceLevel < ClassFileConstants.JDK1_8) ? this.binding.tagBits : this.binding.returnType.tagBits;
+ this.scope.validateNullAnnotation(tagBits, this.returnType, this.annotations);
+ }
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
index 7aaef04b91..5c37a0e2f9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
@@ -17,6 +17,7 @@
* bug 331649 - [compiler][null] consider null annotations for fields
* bug 383368 - [compiler][null] syntactic null analysis for field references
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -56,14 +57,15 @@ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flow
}
protected boolean checkNullableFieldDereference(Scope scope, FieldBinding field, long sourcePosition) {
- if ((field.tagBits & TagBits.AnnotationNullable) != 0) {
- scope.problemReporter().nullableFieldDereference(field, sourcePosition);
- return true;
- }
+ // preference to type annotations if we have any
if ((field.type.tagBits & TagBits.AnnotationNullable) != 0) {
scope.problemReporter().dereferencingNullableExpression(sourcePosition, scope.environment());
return true;
}
+ if ((field.tagBits & TagBits.AnnotationNullable) != 0) {
+ scope.problemReporter().nullableFieldDereference(field, sourcePosition);
+ return true;
+ }
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
index b3b4588623..4268c66054 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReferenceExpression.java
@@ -17,6 +17,7 @@
* Stephan Herrmann - Contribution for
* bug 402028 - [1.8][compiler] null analysis for reference expressions
* bug 404649 - [1.8][compiler] detect illegal reference to indirect or redundant super via I.super.m() syntax
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contribution for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
*******************************************************************************/
@@ -443,25 +444,25 @@ public class ReferenceExpression extends FunctionalExpression implements Invocat
if (scope.compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
int len = this.descriptor.parameters.length;
for (int i = 0; i < len; i++) {
- Boolean declared = this.descriptor.parameterNonNullness == null ? null : this.descriptor.parameterNonNullness[i];
- Boolean implemented = this.binding.parameterNonNullness == null ? null : this.binding.parameterNonNullness[i];
- if (declared == Boolean.FALSE) { // promise to accept null
- if (implemented != Boolean.FALSE) {
- char[][] requiredAnnot = implemented == null ? null : scope.environment().getNonNullAnnotationName();
+ long declared = this.descriptor.parameters[i].tagBits & TagBits.AnnotationNullMASK;
+ long implemented = this.binding.parameters[i].tagBits & TagBits.AnnotationNullMASK;
+ if (declared == TagBits.AnnotationNullable) { // promise to accept null
+ if (implemented != TagBits.AnnotationNullable) {
+ char[][] requiredAnnot = implemented == 0L ? null : scope.environment().getNonNullAnnotationName();
scope.problemReporter().parameterLackingNullableAnnotation(this, this.descriptor, i,
scope.environment().getNullableAnnotationName(),
requiredAnnot, this.binding.parameters[i]);
}
- } else if (declared == null) {
- if (implemented == Boolean.TRUE) {
+ } else if (declared == 0L) {
+ if (implemented == TagBits.AnnotationNonNull) {
scope.problemReporter().parameterRequiresNonnull(this, this.descriptor, i,
scope.environment().getNonNullAnnotationName(), this.binding.parameters[i]);
}
}
}
- if ((this.descriptor.tagBits & TagBits.AnnotationNonNull) != 0) {
- if ((this.binding.tagBits & TagBits.AnnotationNonNull) == 0) {
- char[][] providedAnnotationName = ((this.binding.tagBits & TagBits.AnnotationNullable) != 0) ?
+ if ((this.descriptor.returnType.tagBits & TagBits.AnnotationNonNull) != 0) {
+ if ((this.binding.returnType.tagBits & TagBits.AnnotationNonNull) == 0) {
+ char[][] providedAnnotationName = ((this.binding.returnType.tagBits & TagBits.AnnotationNullable) != 0) ?
scope.environment().getNullableAnnotationName() : null;
scope.problemReporter().illegalReturnRedefinition(this, this.descriptor,
scope.environment().getNonNullAnnotationName(),
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
index 8145e39f2a..4054efd363 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ReturnStatement.java
@@ -27,13 +27,15 @@
* bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
* bug 383368 - [compiler][null] syntactic null analysis for field references
* bug 400761 - [compiler][null] null may be return as boolean without a diagnostic
- * bug 401030 - [1.8][null] Null analysis support for lambda methods.
+ * bug 401030 - [1.8][null] Null analysis support for lambda methods.
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller - Contributions for
* bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.*;
import org.eclipse.jdt.internal.compiler.flow.*;
import org.eclipse.jdt.internal.compiler.impl.Constant;
@@ -159,17 +161,21 @@ void checkAgainstNullAnnotation(BlockScope scope, FlowContext flowContext, int n
if (nullStatus != FlowInfo.NON_NULL) {
// if we can't prove non-null check against declared null-ness of the enclosing method:
long tagBits;
- MethodBinding methodBinding;
+ MethodBinding methodBinding = null;
+ boolean useTypeAnnotations = scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8;
try {
methodBinding = scope.methodScope().referenceMethodBinding();
- tagBits = methodBinding.tagBits;
+ tagBits = (useTypeAnnotations) ? methodBinding.returnType.tagBits : methodBinding.tagBits;
} catch (NullPointerException npe) {
// chain of references in try-block has several potential nulls;
// any null means we cannot perform the following check
return;
}
if ((tagBits & TagBits.AnnotationNonNull) != 0) {
- flowContext.recordNullityMismatch(scope, this.expression, this.expression.resolvedType, methodBinding.returnType, nullStatus);
+ if (useTypeAnnotations && (this.expression.resolvedType.tagBits & TagBits.AnnotationNullMASK) != 0) // TODO(stephan) more detailed checking
+ scope.problemReporter().nullityMismatchingTypeAnnotation(this.expression, this.expression.resolvedType, methodBinding.returnType, 3);
+ else
+ flowContext.recordNullityMismatch(scope, this.expression, this.expression.resolvedType, methodBinding.returnType, nullStatus);
}
}
}
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 9cd61ce970..8dfccafebe 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
@@ -22,6 +22,7 @@
* bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types
* bug 331649 - [compiler][null] consider null annotations for fields
* bug 383368 - [compiler][null] syntactic null analysis for field references
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Andy Clement - Contributions for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
*******************************************************************************/
@@ -84,14 +85,15 @@ protected void analyseArguments(BlockScope currentScope, FlowContext flowContext
&& compilerOptions.isAnnotationBasedNullAnalysisEnabled;
boolean hasJDK15NullAnnotations = methodBinding.parameterNonNullness != null;
int numParamsToCheck = methodBinding.parameters.length;
+ TypeBinding varArgsType = null;
+ boolean passThrough = false;
if (considerTypeAnnotations || hasJDK15NullAnnotations) {
// check if varargs need special treatment:
- boolean passThrough = false;
if (methodBinding.isVarargs()) {
int varArgPos = numParamsToCheck-1;
// this if-block essentially copied from generateArguments(..):
if (numParamsToCheck == arguments.length) {
- TypeBinding varArgsType = methodBinding.parameters[varArgPos];
+ varArgsType = methodBinding.parameters[varArgPos];
TypeBinding lastType = arguments[varArgPos].resolvedType;
if (lastType == TypeBinding.NULL
|| (varArgsType.dimensions() == lastType.dimensions()
@@ -105,17 +107,12 @@ protected void analyseArguments(BlockScope currentScope, FlowContext flowContext
if (considerTypeAnnotations) {
for (int i=0; i<numParamsToCheck; i++) {
TypeBinding expectedType = methodBinding.parameters[i];
- Expression argument = arguments[i];
- // prefer check based on type annotations:
- int severity = findNullTypeAnnotationMismatch(expectedType, argument.resolvedType);
- if (severity > 0) {
- // immediate reporting:
- currentScope.problemReporter().nullityMismatchingTypeAnnotation(argument, argument.resolvedType, expectedType, severity==1, currentScope.environment());
- // next check flow-based null status against null JDK15-style annotations:
- } else if (hasJDK15NullAnnotations && methodBinding.parameterNonNullness[i] == Boolean.TRUE) {
- int nullStatus = argument.nullStatus(flowInfo, flowContext); // slight loss of precision: should also use the null info from the receiver.
- if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided
- flowContext.recordNullityMismatch(currentScope, argument, argument.resolvedType, expectedType, nullStatus);
+ analyseOneArgument18(currentScope, flowContext, flowInfo, expectedType, arguments[i]);
+ }
+ if (!passThrough && varArgsType instanceof ArrayBinding) {
+ TypeBinding expectedType = ((ArrayBinding) varArgsType).elementsType();
+ for (int i = numParamsToCheck; i < arguments.length; i++) {
+ analyseOneArgument18(currentScope, flowContext, flowInfo, expectedType, arguments[i]);
}
}
} else if (hasJDK15NullAnnotations) {
@@ -131,25 +128,52 @@ protected void analyseArguments(BlockScope currentScope, FlowContext flowContext
}
}
}
+void analyseOneArgument18(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo,
+ TypeBinding expectedType, Expression argument) {
+ int nullStatus = argument.nullStatus(flowInfo, flowContext); // slight loss of precision: should also use the null info from the receiver.
+ int severity = findNullTypeAnnotationMismatch(expectedType, argument.resolvedType, nullStatus);
+ switch (severity) {
+ case 3:
+ // immediate reporting:
+ currentScope.problemReporter().nullityMismatchingTypeAnnotation(argument, argument.resolvedType, expectedType, severity);
+ break;
+ case 2:
+ case 1:
+ flowContext.recordNullityMismatch(currentScope, argument, argument.resolvedType, expectedType, nullStatus);
+ break;
+ }
+}
/** Check null-ness of 'var' against a possible null annotation */
protected int checkAssignmentAgainstNullAnnotation(BlockScope currentScope, FlowContext flowContext,
VariableBinding var, int nullStatus, Expression expression, TypeBinding providedType)
{
- int severity = 0;
- if ((var.tagBits & TagBits.AnnotationNonNull) != 0
- && nullStatus != FlowInfo.NON_NULL) {
- flowContext.recordNullityMismatch(currentScope, expression, providedType, var.type, nullStatus);
+ long lhsTagBits = 0L;
+ boolean hasReported = false;
+ if (currentScope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8) {
+ lhsTagBits = var.tagBits & TagBits.AnnotationNullMASK;
+ } else {
+ lhsTagBits = var.type.tagBits & TagBits.AnnotationNullMASK;
+ int severity = findNullTypeAnnotationMismatch(var.type, providedType, nullStatus);
+ if (severity == 3) {
+ currentScope.problemReporter().nullityMismatchingTypeAnnotation(expression, providedType, var.type, severity);
+ hasReported = true;
+ } else if (severity == 2) {
+ flowContext.recordNullityMismatch(currentScope, expression, providedType, var.type, nullStatus);
+ hasReported = true;
+ }
+ }
+ if (lhsTagBits == TagBits.AnnotationNonNull && nullStatus != FlowInfo.NON_NULL) {
+ if (!hasReported)
+ flowContext.recordNullityMismatch(currentScope, expression, providedType, var.type, nullStatus);
return FlowInfo.NON_NULL;
- } else if ((severity = findNullTypeAnnotationMismatch(var.type, providedType)) > 0) {
- currentScope.problemReporter().nullityMismatchingTypeAnnotation(expression, providedType, var.type, severity==1, currentScope.environment());
- } else if ((var.tagBits & TagBits.AnnotationNullable) != 0
- && nullStatus == FlowInfo.UNKNOWN) { // provided a legacy type?
+ } else if (lhsTagBits == TagBits.AnnotationNullable && nullStatus == FlowInfo.UNKNOWN) { // provided a legacy type?
return FlowInfo.POTENTIALLY_NULL; // -> use more specific info from the annotation
}
return nullStatus;
}
-protected int findNullTypeAnnotationMismatch(TypeBinding requiredType, TypeBinding providedType) {
+// return: severity: 0 = no problem; 1 = flow related problem; 2 = unchecked wrt type detail; 3 = conflicting annotations
+protected int findNullTypeAnnotationMismatch(TypeBinding requiredType, TypeBinding providedType, int nullStatus) {
int severity = 0;
if (requiredType instanceof ArrayBinding) {
long[] requiredDimsTagBits = ((ArrayBinding)requiredType).nullTagBitsPerDimension;
@@ -163,19 +187,50 @@ protected int findNullTypeAnnotationMismatch(TypeBinding requiredType, TypeBindi
for (int i=0; i<dims; i++) {
long requiredBits = requiredDimsTagBits[i] & TagBits.AnnotationNullMASK;
long providedBits = providedDimsTagBits[i] & TagBits.AnnotationNullMASK;
- if (requiredBits != 0 && requiredBits != providedBits) {
- if (providedBits == 0)
- severity = 1; // need unchecked conversion regarding type detail
- else
- return 2; // mismatching annotations
- }
+ if (i > 0)
+ nullStatus = 0; // don't use beyond the outermost dimension
+ severity = Math.max(severity, computeNullProblemSeverity(requiredBits, providedBits, nullStatus));
+ if (severity == 3)
+ return severity;
}
}
+ } else if (providedType.id == TypeIds.T_null) {
+ if (dims > 0 && requiredDimsTagBits[0] == TagBits.AnnotationNonNull)
+ return 1;
}
}
+ } else if (requiredType instanceof ParameterizedTypeBinding) {
+ long requiredBits = requiredType.tagBits & TagBits.AnnotationNullMASK;
+ if (requiredBits == TagBits.AnnotationNullable)
+ return 0; // accepting anything
+ long providedBits = providedType.tagBits & TagBits.AnnotationNullMASK;
+ severity = computeNullProblemSeverity(requiredBits, providedBits, nullStatus);
+ // TODO(stephan): descend into type parameters
}
return severity;
}
+static int computeNullProblemSeverity(long requiredBits, long providedBits, int nullStatus) {
+ if (requiredBits != 0 && requiredBits != providedBits) {
+ if (providedBits != 0) {
+ return 3; // mismatching annotations
+ } else {
+ if (requiredBits == TagBits.AnnotationNonNull) {
+ if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) {
+ return 1; // @NonNull vs. inferred @Nullable
+ } else if (nullStatus == FlowInfo.NULL) {
+ return 1; // @NonNull vs. null
+ } else if (nullStatus == FlowInfo.NON_NULL) {
+ return 0;
+ } else {
+ return 2; // need unchecked conversion regarding type detail
+ }
+ } else {
+ return 2; // need unchecked conversion regarding type detail
+ }
+ }
+ }
+ return 0;
+}
/**
* INTERNAL USE ONLY.
* This is used to redirect inter-statements jumps.
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 b1aa52695e..c8f5d2e68e 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
@@ -14,6 +14,7 @@
* Stephan Herrmann - Contributions for
* Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop)
* Bug 388630 - @NonNull diagnostics at line 0
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Keigo Imai - Contribution for bug 388903 - Cannot extend inner class as an anonymous class when it extends the outer class
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -423,6 +424,7 @@ public MethodBinding createDefaultConstructorWithBinding(MethodBinding inherited
System.arraycopy(inheritedConstructorBinding.parameterNonNullness, 0,
constructor.binding.parameterNonNullness = new Boolean[len], 0, len);
}
+ // TODO(stephan): do argument types already carry sufficient info about type annotations?
constructor.scope = new MethodScope(this.scope, constructor, true);
constructor.bindArguments();
@@ -1016,6 +1018,18 @@ public void resolve() {
this.scope.problemReporter().notAFunctionalInterface(this);
}
}
+ if (this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_8) {
+ if ((annotationTagBits & TagBits.AnnotationNullMASK) != 0) {
+ for (int i = 0; i < this.annotations.length; i++) {
+ ReferenceBinding annotationType = this.annotations[i].getCompilerAnnotation().getAnnotationType();
+ if (annotationType != null) {
+ if (annotationType.id == TypeIds.T_ConfiguredAnnotationNonNull
+ || annotationType.id == TypeIds.T_ConfiguredAnnotationNullable)
+ this.scope.problemReporter().nullAnnotationUnsupportedLocation(this.annotations[i]);
+ }
+ }
+ }
+ }
if ((this.bits & ASTNode.UndocumentedEmptyBlock) != 0) {
this.scope.problemReporter().undocumentedEmptyBlock(this.bodyStart-1, this.bodyEnd);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
index fb95d5c6ee..ec34352363 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
@@ -15,6 +15,7 @@
* bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Andy Clement (GoPivotal, Inc) aclement@gopivotal.com - Contributions for
* Bug 383624 - [1.8][compiler] Revive code generation support for type annotations (from Olivier's work)
* Bug 409236 - [1.8][compiler] Type annotations on intersection cast types dropped by code generator
@@ -602,8 +603,8 @@ protected void resolveAnnotations(Scope scope) {
}
} else {
if (tagBits != 0) {
- if (this.resolvedType instanceof ReferenceBinding) {
- this.resolvedType = scope.environment().createAnnotatedType((ReferenceBinding) this.resolvedType, tagBits);
+ if (this.resolvedType.isBaseType()) {
+ this.resolvedType = scope.environment().createAnnotatedType(this.resolvedType, tagBits);
} else {
// TODO(stephan) report null annotation on non-reference type
}
@@ -646,6 +647,6 @@ protected TypeBinding captureTypeAnnotations(Scope scope, ReferenceBinding enclo
}
if (annotationBits == 0L)
return argType;
- return scope.environment().createAnnotatedType((ReferenceBinding) argType, annotationBits);
+ return scope.environment().createAnnotatedType(argType, annotationBits);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
index dc12d31bc8..72071c8762 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
@@ -15,6 +15,7 @@
* bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types
* bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -22,6 +23,7 @@ import java.util.List;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
public final class ArrayBinding extends TypeBinding {
@@ -155,8 +157,8 @@ public TypeBinding elementsType() {
System.arraycopy(this.nullTagBitsPerDimension, 1, nullTagBitsSub = new long[len], 0, len);
}
if (this.dimensions == 1) {
- if (nullTagBitsSub != null && nullTagBitsSub[0] != 0L && this.leafComponentType instanceof ReferenceBinding)
- return this.environment.createAnnotatedType((ReferenceBinding) this.leafComponentType, nullTagBitsSub[0]);
+ if (nullTagBitsSub != null && nullTagBitsSub[0] != 0L)
+ return this.environment.createAnnotatedType(this.leafComponentType, nullTagBitsSub[0]);
return this.leafComponentType;
}
return this.environment.createArrayType(this.leafComponentType, this.dimensions - 1, nullTagBitsSub);
@@ -246,7 +248,7 @@ public TypeBinding leafComponentType(){
return this.leafComponentType;
}
-public char[] nullAnnotatedReadableName(LookupEnvironment env, boolean shortNames) /* java.lang.Object @o.e.j.a.NonNull[] */ {
+public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) /* java.lang.Object @o.e.j.a.NonNull[] */ {
if (this.nullTagBitsPerDimension == null)
return shortNames ? shortReadableName() : readableName();
char[][] brackets = new char[this.dimensions][];
@@ -254,9 +256,9 @@ public char[] nullAnnotatedReadableName(LookupEnvironment env, boolean shortName
if ((this.nullTagBitsPerDimension[i] & TagBits.AnnotationNullMASK) != 0) {
char[][] fqAnnotationName;
if ((this.nullTagBitsPerDimension[i] & TagBits.AnnotationNonNull) != 0)
- fqAnnotationName = env.getNonNullAnnotationName();
+ fqAnnotationName = options.nonNullAnnotationName;
else
- fqAnnotationName = env.getNullableAnnotationName();
+ fqAnnotationName = options.nullableAnnotationName;
char[] annotationName = shortNames
? fqAnnotationName[fqAnnotationName.length-1]
: CharOperation.concatWith(fqAnnotationName, '.');
@@ -329,4 +331,7 @@ public void swapUnresolved(UnresolvedReferenceBinding unresolvedType, ReferenceB
public String toString() {
return this.leafComponentType != null ? debugName() : "NULL TYPE ARRAY"; //$NON-NLS-1$
}
+public TypeBinding unannotated() {
+ return this.environment.createArrayType(this.leafComponentType, this.dimensions);
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index 3ca943d716..a9bf7fff11 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -22,6 +22,7 @@
* bug 388281 - [compiler][null] inheritance of null annotations as an option
* bug 331649 - [compiler][null] consider null annotations for fields
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -1203,8 +1204,13 @@ SimpleLookupTable storedAnnotations(boolean forceInitialize) {
void scanFieldForNullAnnotation(IBinaryField field, FieldBinding fieldBinding) {
if (this.environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8) {
-// FIXME(stephan): the following code could be used as a stop-gap measure to hook type annotation tagBits into our existing analysis:
-// fieldBinding.tagBits |= (fieldBinding.type.tagBits & TagBits.AnnotationNullMASK);
+ TypeBinding fieldType = fieldBinding.type;
+ if (fieldType != null
+ && !fieldType.isBaseType()
+ && (fieldType.tagBits & TagBits.AnnotationNullMASK) == 0
+ && (this.tagBits & TagBits.AnnotationNonNullByDefault) != 0) {
+ fieldBinding.type = this.environment.createAnnotatedType(fieldType, TagBits.AnnotationNonNull);
+ }
return; // not using fieldBinding.tagBits when we have type annotations.
}
@@ -1245,27 +1251,8 @@ void scanFieldForNullAnnotation(IBinaryField field, FieldBinding fieldBinding) {
void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding methodBinding) {
if (!this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled)
return;
- if (this.environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8) {
- // FIXME(stephan): the following code could be used as a stop-gap measure to hook type annotation tagBits into our existing analysis:
-// methodBinding.tagBits |= (methodBinding.returnType.tagBits & TagBits.AnnotationNullMASK);
-// TypeBinding[] parameters = methodBinding.parameters;
-// int numVisibleParams = parameters.length;
-// for (int j = 0; j < numVisibleParams; j++) {
-// if ((parameters[j].tagBits & TagBits.AnnotationNonNull) != 0) {
-// if (methodBinding.parameterNonNullness == null)
-// methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
-// methodBinding.parameterNonNullness[j] = Boolean.TRUE;
-// break;
-// } else if ((parameters[j].tagBits & TagBits.AnnotationNullable) != 0) {
-// if (methodBinding.parameterNonNullness == null)
-// methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
-// methodBinding.parameterNonNullness[j] = Boolean.FALSE;
-// break;
-// }
-// }
- // END
- return; // not using method.tagBits and parameterNonNullness when we have type annotations.
- }
+ boolean useTypeAnnotations = this.environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8;
+ // in 1.8 we only need @NonNullByDefault, see below and exit further down.
char[][] nullableAnnotationName = this.environment.getNullableAnnotationName();
char[][] nonNullAnnotationName = this.environment.getNonNullAnnotationName();
char[][] nonNullByDefaultAnnotationName = this.environment.getNonNullByDefaultAnnotationName();
@@ -1284,17 +1271,22 @@ void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding methodBindi
if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) {
methodBinding.tagBits |= TagBits.AnnotationNonNullByDefault;
}
- if (!explicitNullness && CharOperation.equals(typeName, nonNullAnnotationName)) {
- methodBinding.tagBits |= TagBits.AnnotationNonNull;
- explicitNullness = true;
- }
- if (!explicitNullness && CharOperation.equals(typeName, nullableAnnotationName)) {
- methodBinding.tagBits |= TagBits.AnnotationNullable;
- explicitNullness = true;
+ if (!useTypeAnnotations) {
+ if (!explicitNullness && CharOperation.equals(typeName, nonNullAnnotationName)) {
+ methodBinding.tagBits |= TagBits.AnnotationNonNull;
+ explicitNullness = true;
+ }
+ if (!explicitNullness && CharOperation.equals(typeName, nullableAnnotationName)) {
+ methodBinding.tagBits |= TagBits.AnnotationNullable;
+ explicitNullness = true;
+ }
}
}
}
+ if (useTypeAnnotations)
+ return;
+
// parameters:
TypeBinding[] parameters = methodBinding.parameters;
int numVisibleParams = parameters.length;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
index ca73e4712e..e596cbc88c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
@@ -5,6 +5,10 @@
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
@@ -19,6 +23,7 @@ import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
+import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
/**
@@ -97,27 +102,44 @@ public class ImplicitNullAnnotationVerifier {
checkNullSpecInheritance(currentMethod, srcMethod, needToApplyNonNullDefault, complain, currentSuper, scope, inheritedNonNullnessInfos);
needToApplyNonNullDefault = false;
}
+ long sourceLevel = scope.compilerOptions().sourceLevel;
// transfer collected information into currentMethod:
InheritedNonNullnessInfo info = inheritedNonNullnessInfos[0];
if (!info.complained) {
+ long tagBits = 0;
if (info.inheritedNonNullness == Boolean.TRUE) {
- currentMethod.tagBits |= TagBits.AnnotationNonNull;
+ tagBits = TagBits.AnnotationNonNull;
} else if (info.inheritedNonNullness == Boolean.FALSE) {
- currentMethod.tagBits |= TagBits.AnnotationNullable;
+ tagBits = TagBits.AnnotationNullable;
+ }
+ if (tagBits != 0) {
+ if (sourceLevel < ClassFileConstants.JDK1_8) {
+ currentMethod.tagBits |= tagBits;
+ } else {
+ if (!currentMethod.returnType.isBaseType())
+ currentMethod.returnType = scope.environment()
+ .createAnnotatedType(currentMethod.returnType, tagBits);
+ }
}
}
for (int i=0; i<paramLen; i++) {
info = inheritedNonNullnessInfos[i+1];
if (!info.complained && info.inheritedNonNullness != null) {
Argument currentArg = srcMethod == null ? null : srcMethod.arguments[i];
- recordArgNonNullness(currentMethod, paramLen, i, currentArg, info.inheritedNonNullness);
+ if (sourceLevel < ClassFileConstants.JDK1_8)
+ recordArgNonNullness(currentMethod, paramLen, i, currentArg, info.inheritedNonNullness);
+ else
+ recordArgNonNullness18(currentMethod, i, currentArg, info.inheritedNonNullness, scope.environment());
}
}
}
if (needToApplyNonNullDefault) {
- currentMethod.fillInDefaultNonNullness(srcMethod);
+ if (scope.compilerOptions().sourceLevel < ClassFileConstants.JDK1_8)
+ currentMethod.fillInDefaultNonNullness(srcMethod);
+ else
+ currentMethod.fillInDefaultNonNullness18(srcMethod, scope.environment());
}
} finally {
currentMethod.tagBits |= TagBits.IsNullnessKnown;
@@ -197,12 +219,11 @@ public class ImplicitNullAnnotationVerifier {
// TODO (stephan): even here we may need to report problems? How to discriminate?
this.buddyImplicitNullAnnotationsVerifier.checkImplicitNullAnnotations(inheritedMethod, null, false, scope);
}
- long inheritedBits = inheritedMethod.tagBits;
- long inheritedNullnessBits = inheritedBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable);
- long currentBits = currentMethod.tagBits;
- long currentNullnessBits = currentBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable);
-
LookupEnvironment environment = scope.environment();
+ boolean useTypeAnnotations = environment.globalOptions.sourceLevel >= ClassFileConstants.JDK1_8;
+ long inheritedNullnessBits = getReturnTypeNullnessTagBits(inheritedMethod, useTypeAnnotations);
+ long currentNullnessBits = getReturnTypeNullnessTagBits(currentMethod, useTypeAnnotations);
+
boolean shouldInherit = this.inheritNullAnnotations;
// return type:
@@ -224,13 +245,14 @@ public class ImplicitNullAnnotationVerifier {
inheritedMethod, Boolean.valueOf(inheritedNullnessBits == TagBits.AnnotationNonNull), inheritedNonNullnessInfos[0]);
} else {
// no need to defer, record this info now:
- currentMethod.tagBits |= inheritedNullnessBits;
+ applyReturnNullBits(currentMethod, inheritedNullnessBits, environment);
}
break returnType; // compatible by construction, skip complain phase below
}
}
if (hasNonNullDefault) { // conflict with inheritance already checked
- currentMethod.tagBits |= (currentNullnessBits = TagBits.AnnotationNonNull);
+ currentNullnessBits = TagBits.AnnotationNonNull;
+ applyReturnNullBits(currentMethod, currentNullnessBits, environment);
}
}
if (shouldComplain) {
@@ -254,6 +276,8 @@ public class ImplicitNullAnnotationVerifier {
int length = 0;
if (currentArguments != null)
length = currentArguments.length;
+ if (useTypeAnnotations) // need to look for type annotations on all parameters:
+ length = currentMethod.parameters.length;
else if (inheritedMethod.parameterNonNullness != null)
length = inheritedMethod.parameterNonNullness.length;
else if (currentMethod.parameterNonNullness != null)
@@ -264,10 +288,8 @@ public class ImplicitNullAnnotationVerifier {
Argument currentArgument = currentArguments == null
? null : currentArguments[i];
- Boolean inheritedNonNullNess = (inheritedMethod.parameterNonNullness == null)
- ? null : inheritedMethod.parameterNonNullness[i];
- Boolean currentNonNullNess = (currentMethod.parameterNonNullness == null)
- ? null : currentMethod.parameterNonNullness[i];
+ Boolean inheritedNonNullNess = getParameterNonNullness(inheritedMethod, i, useTypeAnnotations);
+ Boolean currentNonNullNess = getParameterNonNullness(currentMethod, i, useTypeAnnotations);
if (currentNonNullNess == null) {
// unspecified, may fill in either from super or from default
@@ -288,14 +310,20 @@ public class ImplicitNullAnnotationVerifier {
inheritedMethod, inheritedNonNullNess, inheritedNonNullnessInfos[i+1]);
} else {
// no need to defer, record this info now:
- recordArgNonNullness(currentMethod, length, i, currentArgument, inheritedNonNullNess);
+ if (!useTypeAnnotations)
+ recordArgNonNullness(currentMethod, length, i, currentArgument, inheritedNonNullNess);
+ else
+ recordArgNonNullness18(currentMethod, i, currentArgument, inheritedNonNullNess, environment);
}
continue; // compatible by construction, skip complain phase below
}
}
if (hasNonNullDefault) { // conflict with inheritance already checked
currentNonNullNess = Boolean.TRUE;
- recordArgNonNullness(currentMethod, length, i, currentArgument, Boolean.TRUE);
+ if (!useTypeAnnotations)
+ recordArgNonNullness(currentMethod, length, i, currentArgument, Boolean.TRUE);
+ else
+ recordArgNonNullness18(currentMethod, i, currentArgument, Boolean.TRUE, environment);
}
}
if (shouldComplain) {
@@ -341,6 +369,38 @@ public class ImplicitNullAnnotationVerifier {
}
}
+ void applyReturnNullBits(MethodBinding method, long nullnessBits, LookupEnvironment environment) {
+ if (environment.globalOptions.sourceLevel < ClassFileConstants.JDK1_8) {
+ method.tagBits |= nullnessBits;
+ } else {
+ if (!method.returnType.isBaseType())
+ method.returnType = environment.createAnnotatedType(method.returnType, nullnessBits);
+ }
+ }
+
+ private Boolean getParameterNonNullness(MethodBinding method, int i, boolean useTypeAnnotations) {
+ if (useTypeAnnotations) {
+ TypeBinding parameter = method.parameters[i];
+ if (parameter != null) {
+ long nullBits = parameter.tagBits & TagBits.AnnotationNullMASK;
+ if (nullBits != 0L)
+ return Boolean.valueOf(nullBits == TagBits.AnnotationNonNull);
+ }
+ return null;
+ }
+ return (method.parameterNonNullness == null)
+ ? null : method.parameterNonNullness[i];
+ }
+
+ private long getReturnTypeNullnessTagBits(MethodBinding method, boolean useTypeAnnotations) {
+ if (useTypeAnnotations) {
+ if (method.returnType == null)
+ return 0L;
+ return method.returnType.tagBits & TagBits.AnnotationNullMASK;
+ }
+ return method.tagBits & TagBits.AnnotationNullMASK;
+ }
+
/* check for conflicting annotations and record here the info 'inheritedNonNullness' found in 'inheritedMethod'. */
protected void recordDeferredInheritedNullness(Scope scope, ASTNode location,
MethodBinding inheritedMethod, Boolean inheritedNonNullness,
@@ -368,6 +428,13 @@ public class ImplicitNullAnnotationVerifier {
TagBits.AnnotationNonNull : TagBits.AnnotationNullable;
}
}
+ void recordArgNonNullness18(MethodBinding method, int paramIdx, Argument currentArgument, Boolean nonNullNess, LookupEnvironment env) {
+ method.parameters[paramIdx] = env.createAnnotatedType(method.parameters[paramIdx],
+ nonNullNess.booleanValue() ? TagBits.AnnotationNonNull : TagBits.AnnotationNullable);
+ if (currentArgument != null) {
+ currentArgument.binding.type = method.parameters[paramIdx];
+ }
+ }
// ==== minimal set of utility methods previously from MethodVerifier15: ====
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
index d5baa5e29f..70591051bf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2012 IBM Corporation and others.
+ * Copyright (c) 2000, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -18,6 +18,7 @@
* bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* bug 392862 - [1.8][compiler][null] Evaluate null annotations on array types
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -31,6 +32,8 @@ import java.util.Set;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ClassFilePool;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
+import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
+import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.TypeAnnotationWalker;
@@ -996,13 +999,66 @@ public ParameterizedTypeBinding createParameterizedType(ReferenceBinding generic
return createParameterizedType(genericType, typeArguments, 0L, enclosingType);
}
/**
- * Create a ParameterizedTypeBinding which represents the same structure as the given genericType but with type annotations,
- * as given by 'annotationBits' (TagBits.AnnotationNonNull or TagBits.AnnotationNullable).
+ * Create a ParameterizedTypeBinding or ArrayBinding which represents the same structure as the given genericType,
+ * but with type annotations as given by 'annotationBits' (TagBits.AnnotationNonNull or TagBits.AnnotationNullable).
*/
-public ParameterizedTypeBinding createAnnotatedType(ReferenceBinding genericType, long annotationBits) {
- TypeBinding[] typeArguments = genericType.isParameterizedType() ? ((ParameterizedTypeBinding) genericType).arguments : null;
- return createParameterizedType(genericType, typeArguments, annotationBits, genericType.enclosingType());
-}
+public TypeBinding createAnnotatedType(TypeBinding genericType, long annotationBits) {
+ if (genericType instanceof UnresolvedReferenceBinding) {
+ // clone so we don't interfere with future lookups:
+ return new UnresolvedReferenceBinding((UnresolvedReferenceBinding)genericType, annotationBits);
+ }
+ if (genericType instanceof ReferenceBinding) {
+ TypeBinding[] typeArguments = genericType.isParameterizedType() ? ((ParameterizedTypeBinding) genericType).arguments : null;
+ ParameterizedTypeBinding parameterizedType = createParameterizedType((ReferenceBinding) genericType, typeArguments,
+ annotationBits, genericType.enclosingType());
+ parameterizedType.id = genericType.id; // for well-known types shared the id (only here since those types are not generic, are they?)
+ return parameterizedType;
+ } else if (genericType instanceof ArrayBinding) {
+ long[] tagBitsPerDims = ((ArrayBinding) genericType).nullTagBitsPerDimension;
+ if (tagBitsPerDims == null)
+ tagBitsPerDims = new long[genericType.dimensions()+1];
+ if (tagBitsPerDims[0] != annotationBits) {
+ tagBitsPerDims[0] = annotationBits;
+ return createArrayType(genericType.leafComponentType(), genericType.dimensions(), tagBitsPerDims);
+ }
+ }
+ // TODO(stephan): PolyTypeBinding
+ return genericType;
+}
+
+/**
+ * Create an annotated type from 'type' by applying 'annotationBits' to its outermost enclosing type.
+ * This is used for those locations where a null annotations was parsed as a declaration annotation
+ * and later must be pushed into the type.
+ * @param type
+ * @param annotationBits
+ */
+public TypeBinding pushAnnotationIntoType(TypeBinding type, TypeReference typeRef, long annotationBits) {
+ TypeBinding outermostType = type;
+ if (typeRef instanceof QualifiedTypeReference) {
+ int depth = typeRef.getAnnotatableLevels();
+ while (--depth > 0)
+ outermostType = outermostType.enclosingType();
+ }
+ if ((outermostType.tagBits & TagBits.AnnotationNullMASK) != annotationBits) {
+ if (type == outermostType)
+ return createAnnotatedType(type, annotationBits);
+ // types with true enclosingType() must be ReferenceBindings
+ return reWrap((ReferenceBinding) type, outermostType, (ReferenceBinding)createAnnotatedType(outermostType, annotationBits));
+ }
+ return type;
+}
+
+private ReferenceBinding reWrap(ReferenceBinding inner, TypeBinding outer, ReferenceBinding annotatedOuter) {
+ ReferenceBinding annotatedEnclosing = (inner.enclosingType() == outer)
+ ? annotatedOuter
+ : reWrap(inner.enclosingType(), outer, annotatedOuter);
+ TypeBinding[] arguments = (inner instanceof ParameterizedTypeBinding)
+ ? ((ParameterizedTypeBinding) inner).arguments
+ : Binding.NO_TYPES;
+ return createParameterizedType((ReferenceBinding)inner.original(), arguments, inner.tagBits, annotatedEnclosing);
+}
+
/**
* Note: annotationBits are exactly those tagBits from annotations on type parameters that are interpreted by the compiler, currently: null annotations.
* typeArguments should never be Binding.NO_TYPES, but rather: null, if no type arguments are present (and only annotationBits are the reason for coming here).
@@ -1349,19 +1405,7 @@ TypeBinding getTypeFromSignature(char[] signature, int start, int end, boolean i
// null annotations on dimensions?
long[] annotationTagBitsOnDimensions = null;
if (dimension > 0 && walker != TypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) {
- TypeAnnotationWalker dimensionsWalker = null;
- for (int i = 0; i < dimension; i++) {
- if (dimensionsWalker == null)
- dimensionsWalker = walker; // outermost dimension == main type
- else
- dimensionsWalker = dimensionsWalker.toNextArrayDimension();
- long tagBits = typeAnnotationsToTagBits(dimensionsWalker.getAnnotationsAtCursor());
- if (tagBits != 0L) {
- if (annotationTagBitsOnDimensions == null)
- annotationTagBitsOnDimensions = new long[dimension+1]; // leave room for leaf type
- annotationTagBitsOnDimensions[i] = tagBits;
- }
- }
+ annotationTagBitsOnDimensions = getAnnotationTagBitsOnDimensions(dimension, walker);
}
if (end == -1)
@@ -1411,12 +1455,7 @@ TypeBinding getTypeFromSignature(char[] signature, int start, int end, boolean i
}
long tagBits = typeAnnotationsToTagBits(walker.getAnnotationsAtCursor());
if (tagBits != 0 && annotationTagBitsOnDimensions == null) {
- if (refType instanceof UnresolvedReferenceBinding) {
- // clone so we don't interfere with future lookups:
- binding = new UnresolvedReferenceBinding(refType, tagBits);
- } else {
- binding = createAnnotatedType(refType, tagBits);
- }
+ binding = createAnnotatedType(refType, tagBits);
} else {
if (annotationTagBitsOnDimensions != null)
annotationTagBitsOnDimensions[dimension] = tagBits; // insert leaf type into array
@@ -1431,6 +1470,24 @@ TypeBinding getTypeFromSignature(char[] signature, int start, int end, boolean i
return createArrayType(binding, dimension);
}
+private long[] getAnnotationTagBitsOnDimensions(int dimension, TypeAnnotationWalker walker) {
+ TypeAnnotationWalker dimensionsWalker = null;
+ long[] annotationTagBitsOnDimensions = null;
+ for (int i = 0; i < dimension; i++) {
+ if (dimensionsWalker == null)
+ dimensionsWalker = walker; // outermost dimension == main type
+ else
+ dimensionsWalker = dimensionsWalker.toNextArrayDimension();
+ long tagBits = typeAnnotationsToTagBits(dimensionsWalker.getAnnotationsAtCursor());
+ if (tagBits != 0L) {
+ if (annotationTagBitsOnDimensions == null)
+ annotationTagBitsOnDimensions = new long[dimension+1]; // leave room for leaf type
+ annotationTagBitsOnDimensions[i] = tagBits;
+ }
+ }
+ return annotationTagBitsOnDimensions;
+}
+
public long typeAnnotationsToTagBits(IBinaryAnnotation[] annotations) {
long tagBits = 0;
for (int i = 0; i < annotations.length; i++) {
@@ -1478,7 +1535,7 @@ public TypeBinding getTypeFromTypeSignature(SignatureWrapper wrapper, TypeVariab
int varEnd = wrapper.computeEnd();
for (int i = staticVariables.length; --i >= 0;)
if (CharOperation.equals(staticVariables[i].sourceName, wrapper.signature, varStart, varEnd))
- return dimension == 0 ? (TypeBinding) staticVariables[i] : createArrayType(staticVariables[i], dimension);
+ return typeFromTypeVariable(staticVariables[i], dimension, walker);
ReferenceBinding initialType = enclosingType;
do {
TypeVariableBinding[] enclosingTypeVariables;
@@ -1489,7 +1546,7 @@ public TypeBinding getTypeFromTypeSignature(SignatureWrapper wrapper, TypeVariab
}
for (int i = enclosingTypeVariables.length; --i >= 0;)
if (CharOperation.equals(enclosingTypeVariables[i].sourceName, wrapper.signature, varStart, varEnd))
- return dimension == 0 ? (TypeBinding) enclosingTypeVariables[i] : createArrayType(enclosingTypeVariables[i], dimension);
+ return typeFromTypeVariable(enclosingTypeVariables[i], dimension, walker);
} while ((enclosingType = enclosingType.enclosingType()) != null);
this.problemReporter.undefinedTypeVariableSignature(CharOperation.subarray(wrapper.signature, varStart, varEnd), initialType);
return null; // cannot reach this, since previous problem will abort compilation
@@ -1533,6 +1590,21 @@ public TypeBinding getTypeFromTypeSignature(SignatureWrapper wrapper, TypeVariab
return dimension == 0 ? (TypeBinding) parameterizedType : createArrayType(parameterizedType, dimension);
}
+private TypeBinding typeFromTypeVariable(TypeVariableBinding typeVariableBinding, int dimension, TypeAnnotationWalker walker) {
+ long tagBits = typeAnnotationsToTagBits(walker.getAnnotationsAtCursor());
+ if (dimension == 0) {
+ if (tagBits != 0L)
+ return createAnnotatedType(typeVariableBinding, tagBits);
+ return typeVariableBinding;
+ } else {
+ long[] annotationTagBitsOnDimensions = null;
+ if (walker != TypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) {
+ annotationTagBitsOnDimensions = getAnnotationTagBitsOnDimensions(dimension, walker);
+ }
+ return createArrayType(typeVariableBinding, dimension, annotationTagBitsOnDimensions);
+ }
+}
+
TypeBinding getTypeFromVariantTypeSignature(
SignatureWrapper wrapper,
TypeVariableBinding[] staticVariables,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
index 025b07804d..5d1aa26890 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
@@ -18,6 +18,7 @@
* bug 365662 - [compiler][null] warn on contradictory and redundant null annotations
* bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults
* bug 388281 - [compiler][null] inheritance of null annotations as an option
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -501,6 +502,37 @@ protected void fillInDefaultNonNullness(AbstractMethodDeclaration sourceMethod)
}
}
+protected void fillInDefaultNonNullness18(AbstractMethodDeclaration sourceMethod, LookupEnvironment env) {
+ boolean added = false;
+ int length = this.parameters.length;
+ for (int i = 0; i < length; i++) {
+ TypeBinding parameter = this.parameters[i];
+ if (parameter.isBaseType())
+ continue;
+ long existing = parameter.tagBits & TagBits.AnnotationNullMASK;
+ if (existing == 0L) {
+ added = true;
+ if (!parameter.isBaseType()) {
+ this.parameters[i] = env.createAnnotatedType(parameter, TagBits.AnnotationNonNull);
+ if (sourceMethod != null)
+ sourceMethod.arguments[i].binding.type = this.parameters[i];
+ }
+ } else if (sourceMethod != null && (parameter.tagBits & TagBits.AnnotationNonNull) != 0) {
+ sourceMethod.scope.problemReporter().nullAnnotationIsRedundant(sourceMethod, i);
+ }
+ }
+ if (added)
+ this.tagBits |= TagBits.HasParameterAnnotations;
+ if ( this.returnType != null
+ && !this.returnType.isBaseType()
+ && (this.returnType.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0)
+ {
+ this.returnType = env.createAnnotatedType(this.returnType, TagBits.AnnotationNonNull);
+ } else if (sourceMethod != null && (this.returnType.tagBits & TagBits.AnnotationNonNull) != 0) {
+ sourceMethod.scope.problemReporter().nullAnnotationIsRedundant(sourceMethod, -1/*signifies method return*/);
+ }
+}
+
public MethodBinding findOriginalInheritedMethod(MethodBinding inheritedMethod) {
MethodBinding inheritedOriginal = inheritedMethod.original();
TypeBinding superType = this.declaringClass.findSuperTypeOriginatingFrom(inheritedOriginal.declaringClass);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
index 758a12535b..d114e4f750 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
@@ -16,6 +16,7 @@
* bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* bug 395002 - Self bound generic class doesn't resolve bounds properly for wildcards for certain parametrisation.
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -24,6 +25,7 @@ import java.util.List;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
/**
* A parameterized type encapsulates a type with type arguments,
@@ -781,6 +783,12 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
return true;
}
+ public TypeBinding unannotated() {
+ if (isAnnotatedTypeWithoutArguments())
+ return this.type;
+ return this.environment.createParameterizedType(this.type, this.arguments, this.enclosingType);
+ }
+
public int kind() {
return PARAMETERIZED_TYPE;
}
@@ -862,19 +870,9 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
public char[] readableName() {
StringBuffer nameBuffer = new StringBuffer(10);
if (isMemberType()) {
- nameBuffer.append(enclosingType().readableName());
- appendNullAnnotation(nameBuffer);
- nameBuffer.append('.');
- nameBuffer.append(this.sourceName);
+ nameBuffer.append(CharOperation.concat(enclosingType().readableName(), this.sourceName, '.'));
} else {
- int i;
- int l=this.type.compoundName.length;
- for (i=0; i<l-1; i++) {
- nameBuffer.append(this.type.compoundName[i]);
- nameBuffer.append('.');
- }
- appendNullAnnotation(nameBuffer);
- nameBuffer.append(this.type.compoundName[i]);
+ nameBuffer.append(CharOperation.concatWith(this.type.compoundName, '.'));
}
if (this.arguments != null && this.arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations
nameBuffer.append('<');
@@ -890,19 +888,6 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
return readableName;
}
- private void appendNullAnnotation(StringBuffer nameBuffer) {
- if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
- // restore applied null annotation from tagBits:
- if ((this.tagBits & TagBits.AnnotationNonNull) != 0) {
- char[][] nonNullAnnotationName = environment().getNonNullAnnotationName();
- nameBuffer.append('@').append(nonNullAnnotationName[nonNullAnnotationName.length-1]).append(' ');
- } else if ((this.tagBits & TagBits.AnnotationNullable) != 0) {
- char[][] nullableAnnotationName = environment().getNullableAnnotationName();
- nameBuffer.append('@').append(nullableAnnotationName[nullableAnnotationName.length-1]).append(' ');
- }
- }
- }
-
ReferenceBinding resolve() {
if ((this.tagBits & TagBits.HasUnresolvedTypeVariables) == 0)
return this;
@@ -975,6 +960,88 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
}
/**
+ * @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#nullAnnotatedReadableName(CompilerOptions,boolean)
+ */
+ public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) {
+ if (shortNames)
+ return nullAnnotatedShortReadableName(options);
+ return nullAnnotatedReadableName(options);
+ }
+
+ char[] nullAnnotatedReadableName(CompilerOptions options) {
+ StringBuffer nameBuffer = new StringBuffer(10);
+ if (isMemberType()) {
+ nameBuffer.append(enclosingType().readableName());
+ nameBuffer.append('.');
+ appendNullAnnotation(nameBuffer);
+ nameBuffer.append(this.sourceName);
+ } else if (this.type.compoundName != null) {
+ int i;
+ int l=this.type.compoundName.length;
+ for (i=0; i<l-1; i++) {
+ nameBuffer.append(this.type.compoundName[i]);
+ nameBuffer.append('.');
+ }
+ appendNullAnnotation(nameBuffer);
+ nameBuffer.append(this.type.compoundName[i]);
+ } else {
+ // case of TypeVariableBinding with nullAnnotationTagBits:
+ appendNullAnnotation(nameBuffer);
+ nameBuffer.append(this.type.sourceName);
+ }
+ if (this.arguments != null && this.arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations
+ nameBuffer.append('<');
+ for (int i = 0, length = this.arguments.length; i < length; i++) {
+ if (i > 0) nameBuffer.append(',');
+ nameBuffer.append(this.arguments[i].nullAnnotatedReadableName(options, false));
+ }
+ nameBuffer.append('>');
+ }
+ int nameLength = nameBuffer.length();
+ char[] readableName = new char[nameLength];
+ nameBuffer.getChars(0, nameLength, readableName, 0);
+ return readableName;
+ }
+
+ char[] nullAnnotatedShortReadableName(CompilerOptions options) {
+ StringBuffer nameBuffer = new StringBuffer(10);
+ if (isMemberType()) {
+ nameBuffer.append(enclosingType().shortReadableName());
+ nameBuffer.append('.');
+ appendNullAnnotation(nameBuffer);
+ nameBuffer.append(this.sourceName);
+ } else {
+ appendNullAnnotation(nameBuffer);
+ nameBuffer.append(this.type.sourceName);
+ }
+ if (this.arguments != null && this.arguments.length > 0) { // empty arguments array happens when PTB has been created just to capture type annotations
+ nameBuffer.append('<');
+ for (int i = 0, length = this.arguments.length; i < length; i++) {
+ if (i > 0) nameBuffer.append(',');
+ nameBuffer.append(this.arguments[i].nullAnnotatedReadableName(options, true));
+ }
+ nameBuffer.append('>');
+ }
+ int nameLength = nameBuffer.length();
+ char[] shortReadableName = new char[nameLength];
+ nameBuffer.getChars(0, nameLength, shortReadableName, 0);
+ return shortReadableName;
+ }
+
+ private void appendNullAnnotation(StringBuffer nameBuffer) {
+ if (this.environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
+ // restore applied null annotation from tagBits:
+ if ((this.tagBits & TagBits.AnnotationNonNull) != 0) {
+ char[][] nonNullAnnotationName = environment().getNonNullAnnotationName();
+ nameBuffer.append('@').append(nonNullAnnotationName[nonNullAnnotationName.length-1]).append(' ');
+ } else if ((this.tagBits & TagBits.AnnotationNullable) != 0) {
+ char[][] nullableAnnotationName = environment().getNullableAnnotationName();
+ nameBuffer.append('@').append(nullableAnnotationName[nullableAnnotationName.length-1]).append(' ');
+ }
+ }
+ }
+
+ /**
* @see org.eclipse.jdt.internal.compiler.lookup.TypeBinding#signature()
*/
public char[] signature() {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 0d473d379a..d5e21637cb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -23,6 +23,7 @@
* bug 400421 - [compiler] Null analysis for fields does not take @com.google.inject.Inject into account
* bug 382069 - [null] Make the null analysis consider JUnit's assertNotNull similarly to assertions
* bug 392384 - [1.8][compiler][null] Restore nullness info from type annotations in class files
+ * Bug 392099 - [1.8][compiler][null] Apply null annotation on types for null analysis
* Jesper S Moller - Contributions for
* bug 382701 - [1.8][compiler] Implement semantic analysis of Lambda expressions & Reference expression
*******************************************************************************/
@@ -35,6 +36,7 @@ import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
/*
@@ -1445,15 +1447,16 @@ public MethodBinding[] methods() {
return Binding.NO_METHODS;
}
-public char[] nullAnnotatedReadableName(LookupEnvironment env, boolean shortNames) /* java.lang.Object @o.e.j.a.NonNull[] */ {
+public char[] nullAnnotatedReadableName(CompilerOptions options, boolean shortNames) /* java.lang.Object @o.e.j.a.NonNull[] */ {
+ // TODO(stephan): respect annotatable enclosing types!
char[] typeName = shortNames ? shortReadableName() : readableName();
if ((this.tagBits & TagBits.AnnotationNullMASK) == 0)
return typeName;
char[][] fqAnnotationName;
if ((this.tagBits & TagBits.AnnotationNonNull) != 0)
- fqAnnotationName = env.getNonNullAnnotationName();
+ fqAnnotationName = options.nonNullAnnotationName;
else
- fqAnnotationName = env.getNullableAnnotationName();
+ fqAnnotationName = options.nullableAnnotationName;
char[] annotationName = shortNames
? fqAnnotationName[fqAnnotationName.length-1]
: CharOperation.concatWith(fqAnnotationName, '.');
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java