Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 207270 Details for
Bug 186342
[compiler][null] Using annotations for null checking
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
test & implementation v12
Bug_186342_v12.patch (text/plain), 255.83 KB, created by
Stephan Herrmann
on 2011-11-19 16:35:38 EST
(
hide
)
Description:
test & implementation v12
Filename:
MIME Type:
Creator:
Stephan Herrmann
Created:
2011-11-19 16:35:38 EST
Size:
255.83 KB
patch
obsolete
>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 48667a7..8aa2bf0 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 >@@ -14,6 +14,7 @@ > * bug 185682 - Increment/decrement operators mark local variables as read > * bug 349326 - [1.7] new warning for missing try-with-resources > * bug 359721 - [options] add command line option for new warning token "resource" >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.core.tests.compiler.regression; > >@@ -1799,6 +1800,11 @@ > " <argument value=\"---OUTPUT_DIR_PLACEHOLDER---\"/>\n" + > " </command_line>\n" + > " <options>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnull\" value=\"org.eclipse.jdt.annotation.NonNull\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault\" value=\"org.eclipse.jdt.annotation.NonNullByDefault\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullisdefault\" value=\"disabled\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullable\" value=\"org.eclipse.jdt.annotation.Nullable\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullanalysis\" value=\"disabled\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"disabled\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.codegen.targetPlatform\" value=\"1.5\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.codegen.unusedLocal\" value=\"optimize out\"/>\n" + >@@ -1857,14 +1863,18 @@ > " <option key=\"org.eclipse.jdt.core.compiler.problem.noEffectAssignment\" value=\"warning\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion\" value=\"warning\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral\" value=\"ignore\"/>\n" + >- " <option key=\"org.eclipse.jdt.core.compiler.problem.nullReference\" value=\"warning\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.problem.nullReference\" value=\"warning\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo\" value=\"warning\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.problem.nullSpecViolation\" value=\"error\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.overridingMethodWithoutSuperInvocation\" value=\"ignore\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod\" value=\"warning\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.parameterAssignment\" value=\"ignore\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment\" value=\"ignore\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.potentialNullReference\" value=\"ignore\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation\" value=\"error\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable\" value=\"ignore\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.rawTypeReference\" value=\"warning\"/>\n" + >+ " <option key=\"org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation\" value=\"warning\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.redundantNullCheck\" value=\"ignore\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments\" value=\"ignore\"/>\n" + > " <option key=\"org.eclipse.jdt.core.compiler.problem.redundantSuperinterface\" value=\"ignore\"/>\n" + >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 0b6cf1d..1a77477 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 >@@ -11,6 +11,7 @@ > * Stephan Herrmann - Contributions for > * bug 236385: [compiler] Warn for potential programming problem if an object is created but not used > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.core.tests.compiler.regression; > >@@ -380,6 +381,7 @@ > expectedProblemAttributes.put("CannotDefineStaticInitializerInLocalType", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); > expectedProblemAttributes.put("CannotExtendEnum", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("CannotHideAnInstanceMethodWithAStaticMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); >+ expectedProblemAttributes.put("CannotImplementIncompatibleNullness", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("CannotImportPackage", new ProblemAttributes(CategorizedProblem.CAT_IMPORT)); > expectedProblemAttributes.put("CannotInferElidedTypes", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("CannotInvokeSuperConstructorInEnum", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); >@@ -470,8 +472,10 @@ > expectedProblemAttributes.put("HierarchyHasProblems", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("IllegalAbstractModifierCombinationForMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); > expectedProblemAttributes.put("IllegalAccessFromTypeVariable", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); >+ expectedProblemAttributes.put("IllegalAnnotationForBaseType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("IllegalCast", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("IllegalClassLiteralForTypeVariable", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); >+ expectedProblemAttributes.put("IllegalDefinitionToNonNullParameter", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("IllegalDimension", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); > expectedProblemAttributes.put("IllegalEnclosingInstanceSpecification", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("IllegalExtendedDimensions", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); >@@ -506,6 +510,8 @@ > expectedProblemAttributes.put("IllegalPrimitiveOrArrayTypeForEnclosingInstance", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("IllegalQualifiedEnumConstantLabel", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); > expectedProblemAttributes.put("IllegalQualifiedParameterizedTypeAllocation", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); >+ expectedProblemAttributes.put("IllegalReturnNullityRedefinition", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); >+ expectedProblemAttributes.put("IllegalRedefinitionToNonNullParameter", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("IllegalStaticModifierForMemberType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("IllegalTypeVariableSuperReference", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); > expectedProblemAttributes.put("IllegalUnderscorePosition", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX)); >@@ -690,6 +696,7 @@ > expectedProblemAttributes.put("MissingEnclosingInstanceForConstructorCall", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("MissingEnumConstantCase", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("MissingOverrideAnnotation", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE)); >+ expectedProblemAttributes.put("MissingNullAnnotationType", new ProblemAttributes(CategorizedProblem.CAT_BUILDPATH)); > expectedProblemAttributes.put("MissingOverrideAnnotationForInterfaceMethodImplementation", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE)); > expectedProblemAttributes.put("MissingReturnType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("MissingSemiColon", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX)); >@@ -729,6 +736,7 @@ > expectedProblemAttributes.put("NotVisibleField", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); > expectedProblemAttributes.put("NotVisibleMethod", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); > expectedProblemAttributes.put("NotVisibleType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); >+ expectedProblemAttributes.put("NullAnnotationNameMustBeQualified", new ProblemAttributes(CategorizedProblem.CAT_BUILDPATH)); > 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)); >@@ -745,6 +753,8 @@ > expectedProblemAttributes.put("PackageCollidesWithType", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("PackageIsNotExpectedPackage", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); > expectedProblemAttributes.put("ParameterAssignment", new ProblemAttributes(CategorizedProblem.CAT_CODE_STYLE)); >+ expectedProblemAttributes.put("ParameterLackingNonNullAnnotation", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); >+ expectedProblemAttributes.put("ParameterLackingNullableAnnotation", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("ParameterMismatch", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); > expectedProblemAttributes.put("ParameterizedConstructorArgumentTypeMismatch", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("ParameterizedMethodArgumentTypeMismatch", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); >@@ -771,6 +781,7 @@ > expectedProblemAttributes.put("PotentiallyUnclosedCloseable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("PotentiallyUnclosedCloseableAtExit", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("PotentialNullLocalVariableReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); >+ expectedProblemAttributes.put("PotentialNullMessageSendReference", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("PublicClassMustMatchFileName", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("RawMemberTypeCannotBeParameterized", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("RawTypeReference", new ProblemAttributes(CategorizedProblem.CAT_UNCHECKED_RAW)); >@@ -779,10 +790,15 @@ > expectedProblemAttributes.put("RedefinedLocal", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); > 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("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); >+ expectedProblemAttributes.put("RedundantNullCheckOnNonNullMessageSend", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("RedundantSuperinterface", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE)); > expectedProblemAttributes.put("ReferenceToForwardField", new ProblemAttributes(CategorizedProblem.CAT_MEMBER)); >+ expectedProblemAttributes.put("RequiredNonNullButProvidedNull", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); >+ expectedProblemAttributes.put("RequiredNonNullButProvidedPotentialNull", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); >+ expectedProblemAttributes.put("RequiredNonNullButProvidedUnknown", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM)); > expectedProblemAttributes.put("ReferenceToForwardTypeVariable", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("ResourceHasToImplementAutoCloseable", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); > expectedProblemAttributes.put("ReturnTypeAmbiguous", DEPRECATED); >@@ -1056,6 +1072,7 @@ > expectedProblemAttributes.put("CannotExtendEnum", SKIP); > expectedProblemAttributes.put("CannotHideAnInstanceMethodWithAStaticMethod", SKIP); > expectedProblemAttributes.put("CannotImportPackage", SKIP); >+ expectedProblemAttributes.put("CannotImplementIncompatibleNullness", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); > expectedProblemAttributes.put("CannotInferElidedTypes", SKIP); > expectedProblemAttributes.put("CannotInvokeSuperConstructorInEnum", SKIP); > expectedProblemAttributes.put("CannotOverrideAStaticMethodWithAnInstanceMethod", SKIP); >@@ -1145,8 +1162,10 @@ > expectedProblemAttributes.put("HierarchyHasProblems", SKIP); > expectedProblemAttributes.put("IllegalAbstractModifierCombinationForMethod", SKIP); > expectedProblemAttributes.put("IllegalAccessFromTypeVariable", SKIP); >+ expectedProblemAttributes.put("IllegalAnnotationForBaseType", SKIP); > expectedProblemAttributes.put("IllegalCast", SKIP); > expectedProblemAttributes.put("IllegalClassLiteralForTypeVariable", SKIP); >+ expectedProblemAttributes.put("IllegalDefinitionToNonNullParameter", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); > expectedProblemAttributes.put("IllegalDimension", SKIP); > expectedProblemAttributes.put("IllegalEnclosingInstanceSpecification", SKIP); > expectedProblemAttributes.put("IllegalExtendedDimensions", SKIP); >@@ -1181,6 +1200,8 @@ > expectedProblemAttributes.put("IllegalPrimitiveOrArrayTypeForEnclosingInstance", SKIP); > expectedProblemAttributes.put("IllegalQualifiedEnumConstantLabel", SKIP); > expectedProblemAttributes.put("IllegalQualifiedParameterizedTypeAllocation", SKIP); >+ expectedProblemAttributes.put("IllegalRedefinitionToNonNullParameter", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); >+ expectedProblemAttributes.put("IllegalReturnNullityRedefinition", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); > expectedProblemAttributes.put("IllegalStaticModifierForMemberType", SKIP); > expectedProblemAttributes.put("IllegalTypeVariableSuperReference", SKIP); > expectedProblemAttributes.put("IllegalUnderscorePosition", SKIP); >@@ -1364,6 +1385,7 @@ > expectedProblemAttributes.put("MissingEnclosingInstance", SKIP); > expectedProblemAttributes.put("MissingEnclosingInstanceForConstructorCall", SKIP); > expectedProblemAttributes.put("MissingEnumConstantCase", new ProblemAttributes(JavaCore.COMPILER_PB_INCOMPLETE_ENUM_SWITCH)); >+ expectedProblemAttributes.put("MissingNullAnnotationType", SKIP); > expectedProblemAttributes.put("MissingOverrideAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION)); > expectedProblemAttributes.put("MissingOverrideAnnotationForInterfaceMethodImplementation", new ProblemAttributes(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION)); > expectedProblemAttributes.put("MissingReturnType", SKIP); >@@ -1404,6 +1426,7 @@ > expectedProblemAttributes.put("NotVisibleField", SKIP); > expectedProblemAttributes.put("NotVisibleMethod", SKIP); > expectedProblemAttributes.put("NotVisibleType", SKIP); >+ expectedProblemAttributes.put("NullAnnotationNameMustBeQualified", SKIP); > expectedProblemAttributes.put("NullLocalVariableComparisonYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); > expectedProblemAttributes.put("NullLocalVariableInstanceofYieldsFalse", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); > expectedProblemAttributes.put("NullLocalVariableReference", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_REFERENCE)); >@@ -1420,6 +1443,8 @@ > expectedProblemAttributes.put("PackageCollidesWithType", SKIP); > expectedProblemAttributes.put("PackageIsNotExpectedPackage", SKIP); > expectedProblemAttributes.put("ParameterAssignment", new ProblemAttributes(JavaCore.COMPILER_PB_PARAMETER_ASSIGNMENT)); >+ expectedProblemAttributes.put("ParameterLackingNonNullAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); >+ expectedProblemAttributes.put("ParameterLackingNullableAnnotation", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); > expectedProblemAttributes.put("ParameterMismatch", SKIP); > expectedProblemAttributes.put("ParameterizedConstructorArgumentTypeMismatch", SKIP); > expectedProblemAttributes.put("ParameterizedMethodArgumentTypeMismatch", SKIP); >@@ -1446,6 +1471,7 @@ > expectedProblemAttributes.put("PotentiallyUnclosedCloseable", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE)); > expectedProblemAttributes.put("PotentiallyUnclosedCloseableAtExit", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE)); > expectedProblemAttributes.put("PotentialNullLocalVariableReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE)); >+ expectedProblemAttributes.put("PotentialNullMessageSendReference", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE)); > expectedProblemAttributes.put("PublicClassMustMatchFileName", SKIP); > expectedProblemAttributes.put("RawMemberTypeCannotBeParameterized", SKIP); > expectedProblemAttributes.put("RawTypeReference", new ProblemAttributes(JavaCore.COMPILER_PB_RAW_TYPE_REFERENCE)); >@@ -1454,11 +1480,16 @@ > expectedProblemAttributes.put("RedefinedLocal", SKIP); > 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("RedundantNullCheckOnNonNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); >+ expectedProblemAttributes.put("RedundantNullCheckOnNonNullMessageSend", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); > expectedProblemAttributes.put("RedundantNullCheckOnNullLocalVariable", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK)); > expectedProblemAttributes.put("RedundantSuperinterface", new ProblemAttributes(JavaCore.COMPILER_PB_REDUNDANT_SUPERINTERFACE)); > expectedProblemAttributes.put("ReferenceToForwardField", SKIP); > expectedProblemAttributes.put("ReferenceToForwardTypeVariable", SKIP); >+ expectedProblemAttributes.put("RequiredNonNullButProvidedNull", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION)); >+ expectedProblemAttributes.put("RequiredNonNullButProvidedPotentialNull", new ProblemAttributes(JavaCore.COMPILER_PB_POTENTIAL_NULL_SPECIFICATION_VIOLATION)); >+ expectedProblemAttributes.put("RequiredNonNullButProvidedUnknown", new ProblemAttributes(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO)); > expectedProblemAttributes.put("ResourceHasToImplementAutoCloseable", SKIP); > expectedProblemAttributes.put("ReturnTypeAmbiguous", SKIP); > expectedProblemAttributes.put("ReturnTypeCannotBeVoidArray", SKIP); >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 >new file mode 100644 >index 0000000..e0df1fa >--- /dev/null >+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java >@@ -0,0 +1,2213 @@ >+/******************************************************************************* >+ * Copyright (c) 2010, 2011 GK Software AG 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 >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Stephan Herrmann - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.jdt.core.tests.compiler.regression; >+ >+ >+import java.io.File; >+import java.util.Map; >+ >+import junit.framework.Test; >+ >+import org.eclipse.core.runtime.FileLocator; >+import org.eclipse.core.runtime.Platform; >+import org.eclipse.jdt.core.JavaCore; >+ >+// see bug 186342 - [compiler][null] Using annotations for null checking >+public class NullAnnotationTest extends AbstractComparableTest { >+ >+// class libraries including our default null annotation types: >+String[] LIBS; >+ >+// names and content of custom annotations used in a few tests: >+private static final String CUSTOM_NONNULL_NAME = "org/foo/NonNull.java"; >+private static final String CUSTOM_NONNULL_CONTENT = >+ "package org.foo;\n" + >+ "import static java.lang.annotation.ElementType.*;\n" + >+ "import java.lang.annotation.*;\n" + >+ "@Retention(RetentionPolicy.CLASS)\n" + >+ "@Target({METHOD,PARAMETER,LOCAL_VARIABLE})\n" + >+ "public @interface NonNull {\n" + >+ "}\n"; >+private static final String CUSTOM_NULLABLE_NAME = "org/foo/Nullable.java"; >+private static final String CUSTOM_NULLABLE_CONTENT = "package org.foo;\n" + >+ "import static java.lang.annotation.ElementType.*;\n" + >+ "import java.lang.annotation.*;\n" + >+ "@Retention(RetentionPolicy.CLASS)\n" + >+ "@Target({METHOD,PARAMETER,LOCAL_VARIABLE})\n" + >+ "public @interface Nullable {\n" + >+ "}\n"; >+ >+public NullAnnotationTest(String name) { >+ super(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_illegal_annotation_00" }; >+// TESTS_NUMBERS = new int[] { 561 }; >+// TESTS_RANGE = new int[] { 1, 2049 }; >+} >+ >+public static Test suite() { >+ return buildComparableTestSuite(testClass()); >+} >+ >+public static Class testClass() { >+ return NullAnnotationTest.class; >+} >+ >+protected void setUp() throws Exception { >+ super.setUp(); >+ if (this.LIBS == null) { >+ String[] defaultLibs = getDefaultClassPaths(); >+ int len = defaultLibs.length; >+ this.LIBS = new String[len+1]; >+ System.arraycopy(defaultLibs, 0, this.LIBS, 0, len); >+ File bundleFile = FileLocator.getBundleFile(Platform.getBundle("org.eclipse.jdt.annotation.null")); >+ if (bundleFile.isDirectory()) >+ this.LIBS[len] = bundleFile.getPath()+"/bin"; >+ else >+ this.LIBS[len] = bundleFile.getPath(); >+ } >+} >+// Conditionally augment problem detection settings >+static boolean setNullRelatedOptions = true; >+protected Map getCompilerOptions() { >+ Map defaultOptions = super.getCompilerOptions(); >+ if (setNullRelatedOptions) { >+ defaultOptions.put(JavaCore.COMPILER_PB_NULL_REFERENCE, JavaCore.ERROR); >+ defaultOptions.put(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE, JavaCore.ERROR); >+ defaultOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR); >+ defaultOptions.put(JavaCore.COMPILER_PB_INCLUDE_ASSERTS_IN_NULL_ANALYSIS, JavaCore.ENABLED); >+ >+ defaultOptions.put(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION_FOR_INTERFACE_METHOD_IMPLEMENTATION, JavaCore.DISABLED); >+ >+ // enable null annotations: >+ defaultOptions.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ // leave other new options at these defaults: >+// defaultOptions.put(CompilerOptions.OPTION_ReportNullContractViolation, JavaCore.ERROR); >+// defaultOptions.put(CompilerOptions.OPTION_ReportPotentialNullContractViolation, JavaCore.ERROR); >+// defaultOptions.put(CompilerOptions.OPTION_ReportNullContractInsufficientInfo, CompilerOptions.WARNING); >+ >+// defaultOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.eclipse.jdt.annotation.Nullable"); >+// defaultOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "org.eclipse.jdt.annotation.NonNull"); >+ } >+ return defaultOptions; >+} >+void runNegativeTestWithLibs(String[] testFiles, String expectedErrorLog) { >+ runNegativeTest( >+ testFiles, >+ expectedErrorLog, >+ this.LIBS, >+ false /*shouldFlush*/); >+} >+void runNegativeTestWithLibs(boolean shouldFlushOutputDirectory, String[] testFiles, Map customOptions, String expectedErrorLog) { >+ runNegativeTest( >+ shouldFlushOutputDirectory, >+ testFiles, >+ this.LIBS, >+ customOptions, >+ expectedErrorLog, >+ // runtime options >+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); >+} >+void runNegativeTestWithLibs(String[] testFiles, Map customOptions, String expectedErrorLog) { >+ runNegativeTestWithLibs(false /* flush output directory */, testFiles, customOptions, expectedErrorLog); >+} >+void runConformTestWithLibs(String[] testFiles, Map customOptions, String expectedCompilerLog) { >+ runConformTestWithLibs(false /* flush output directory */, testFiles, customOptions, expectedCompilerLog); >+} >+void runConformTestWithLibs(boolean shouldFlushOutputDirectory, String[] testFiles, Map customOptions, String expectedCompilerLog) { >+ runConformTest( >+ shouldFlushOutputDirectory, >+ testFiles, >+ this.LIBS, >+ customOptions, >+ expectedCompilerLog, >+ "",/* expected output */ >+ "",/* expected error */ >+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); >+} >+void runConformTest(String[] testFiles, Map customOptions, String expectedOutputString) { >+ runConformTest( >+ testFiles, >+ expectedOutputString, >+ null /*classLibraries*/, >+ true /*shouldFlushOutputDirectory*/, >+ null /*vmArguments*/, >+ customOptions, >+ null /*customRequestor*/); >+ >+} >+// a nullable argument is dereferenced without a check >+public void test_nullable_paramter_001() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void foo(@Nullable Object o) {\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ "}\n"}, >+ "----------\n" + >+ "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" + >+ "----------\n", >+ this.LIBS, >+ true /* shouldFlush*/); >+} >+ >+// a null value is passed to a nullable argument >+public void test_nullable_paramter_002() { >+ runConformTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void foo(@Nullable Object o) {\n" + >+ " // nop\n" + >+ " }\n" + >+ " void bar() {\n" + >+ " foo(null);\n" + >+ " }\n" + >+ "}\n"}, >+ "", >+ this.LIBS, >+ false/*shouldFlush*/, >+ null/*vmArgs*/); >+} >+ >+// a non-null argument is checked for null >+public void test_nonnull_parameter_001() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void foo(@NonNull Object o) {\n" + >+ " if (o != null)\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ "}\n"}, >+ "----------\n" + >+ "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" + >+ "----------\n", >+ this.LIBS, >+ true /* shouldFlush*/); >+} >+// a non-null argument is dereferenced without a check >+public void test_nonnull_parameter_002() { >+ runConformTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void foo(@NonNull Object o) {\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ " public static void main(String... args) {\n" + >+ " new X().foo(\"OK\");\n" + >+ " }\n" + >+ "}\n"}, >+ "OK", >+ this.LIBS, >+ false/*shouldFlush*/, >+ null/*vmArgs*/); >+} >+// passing null to nonnull parameter - many fields in enclosing class >+public void test_nonnull_parameter_003() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " int i00, i01, i02, i03, i04, i05, i06, i07, i08, i09;" + >+ " int i10, i11, i12, i13, i14, i15, i16, i17, i18, i19;" + >+ " int i20, i21, i22, i23, i24, i25, i26, i27, i28, i29;" + >+ " int i30, i31, i32, i33, i34, i35, i36, i37, i38, i39;" + >+ " int i40, i41, i42, i43, i44, i45, i46, i47, i48, i49;" + >+ " int i50, i51, i52, i53, i54, i55, i56, i57, i58, i59;" + >+ " int i60, i61, i62, i63, i64, i65, i66, i67, i68, i69;" + >+ " void foo(@NonNull Object o) {\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ " void bar() {\n" + >+ " foo(null);\n" + >+ " }\n" + >+ "}\n"}, >+ "----------\n" + >+ "1. ERROR in X.java (at line 7)\n" + >+ " foo(null);\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n", >+ this.LIBS, >+ true /* shouldFlush*/); >+} >+// passing potential null to nonnull parameter - target method is consumed from .class >+public void test_nonnull_parameter_004() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " void setObject(@NonNull Object o) { }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ " void bar(Lib l, boolean b) {\n" + >+ " Object o = null;\n" + >+ " if (b) o = new Object();\n" + >+ " l.setObject(o);\n" + >+ " }\n" + >+ "}\n"}, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 5)\n" + >+ " l.setObject(o);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// passing unknown value to nonnull parameter - target method is consumed from .class >+public void test_nonnull_parameter_005() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " void setObject(@NonNull Object o) { }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runConformTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ " void bar(Lib l, Object o) {\n" + >+ " l.setObject(o);\n" + >+ " }\n" + >+ "}\n"}, >+ null /* options */, >+ "----------\n" + >+ "1. WARNING in X.java (at line 3)\n" + >+ " l.setObject(o);\n" + >+ " ^\n" + >+ "Potential type mismatch: required \'@NonNull Object\' but nullness of the provided value is unknown\n" + >+ "----------\n"); >+} >+// a ternary non-null expression is passed to a nonnull parameter >+public void test_nonnull_parameter_006() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void m1(@NonNull String a) {}\n" + >+ " void m2(@Nullable String b) {\n" + >+ " m1(b == null ? \"\" : b);\n" + >+ " }\n" + >+ "}\n"}, >+ customOptions, >+ "" /* compiler output */); >+} >+// nullable value passed to a non-null parameter in a super-call >+public void test_nonnull_parameter_007() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "XSub.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class XSub extends XSuper {\n" + >+ " XSub(@Nullable String b) {\n" + >+ " super(b);\n" + >+ " }\n" + >+ "}\n", >+ "XSuper.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class XSuper {\n" + >+ " XSuper(@NonNull String b) {\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in XSub.java (at line 4)\n" + >+ " super(b);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// a nullable value is passed to a non-null parameter in an allocation expression >+public void test_nonnull_parameter_008() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " X(@NonNull String a) {}\n" + >+ " static X create(@Nullable String b) {\n" + >+ " return new X(b);\n" + >+ " }\n" + >+ "}\n"}, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 5)\n" + >+ " return new X(b);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" /* compiler output */); >+} >+// a nullable value is passed to a non-null parameter in a qualified allocation expression >+public void test_nonnull_parameter_009() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " class Local {\n" + >+ " Local(@NonNull String a) {}\n" + >+ " }\n" + >+ " Local create(@Nullable String b) {\n" + >+ " return this.new Local(b);\n" + >+ " }\n" + >+ "}\n"}, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 7)\n" + >+ " return this.new Local(b);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" /* compiler output */); >+} >+// assigning potential null to a nonnull local variable >+public void test_nonnull_local_001() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void foo(boolean b, Object p) {\n" + >+ " @NonNull Object o1 = b ? null : new Object();\n" + >+ " @NonNull String o2 = \"\";\n" + >+ " o2 = null;\n" + >+ " @NonNull Object o3 = p;\n" + >+ " }\n" + >+ "}\n"}, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " @NonNull Object o1 = b ? null : new Object();\n" + >+ " ^^^^^^^^^^^^^^^^^^^^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 6)\n" + >+ " o2 = null;\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value is null\n" + >+ "----------\n" + >+ "3. WARNING in X.java (at line 7)\n" + >+ " @NonNull Object o3 = p;\n" + >+ " ^\n" + >+ "Potential type mismatch: required \'@NonNull Object\' but nullness of the provided value is unknown\n" + >+ "----------\n", >+ this.LIBS, >+ true /* shouldFlush*/); >+} >+ >+// a method tries to tighten the type specification, super declares parameter o as @Nullable >+// other parameters: s is redefined from not constrained to @Nullable which is OK >+// third is redefined from not constrained to @NonNull which is bad, too >+public void test_parameter_specification_inheritance_001() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " void foo(String s, @Nullable Object o, Object third) { }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X extends Lib {\n" + >+ " @Override\n" + >+ " void foo(@Nullable String s, @NonNull Object o, @NonNull Object third) { System.out.print(o.toString()); }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " void foo(@Nullable String s, @NonNull Object o, @NonNull Object third) { System.out.print(o.toString()); }\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "Illegal redefinition of parameter o, inherited method from Lib declares this parameter as @Nullable\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 4)\n" + >+ " void foo(@Nullable String s, @NonNull Object o, @NonNull Object third) { System.out.print(o.toString()); }\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "Illegal redefinition of parameter third, inherited method from Lib does not constrain this parameter\n" + >+ "----------\n"); >+} >+// a method body fails to redeclare the inherited null annotation, super declares parameter as @Nullable >+public void test_parameter_specification_inheritance_002() { >+ runConformTest( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " void foo(@Nullable Object o) { }\n" + >+ "}\n" >+ }, >+ "", >+ this.LIBS, >+ false/*shouldFlush*/, >+ null/*vmArgs*/); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X extends Lib {\n" + >+ " @Override\n" + >+ " void foo(Object o) {\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "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" + >+ "----------\n"); >+} >+// a method relaxes the parameter null specification, super interface declares parameter o as @NonNull >+// other (first) parameter just repeats the inherited @NonNull >+public void test_parameter_specification_inheritance_003() { >+ runConformTest( >+ new String[] { >+ "IX.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public interface IX {\n" + >+ " void foo(@NonNull String s, @NonNull Object o);\n" + >+ "}\n", >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X implements IX {\n" + >+ " public void foo(@NonNull String s, @Nullable Object o) { ; }\n" + >+ " void bar() { foo(\"OK\", null); }\n" + >+ "}\n" >+ }, >+ "", >+ this.LIBS, >+ false/*shouldFlush*/, >+ null/*vmArgs*/); >+} >+// a method adds a @NonNull annotation, super interface has no null annotation >+// changing other from unconstrained to @Nullable is OK >+public void test_parameter_specification_inheritance_004() { >+ runConformTest( >+ new String[] { >+ "IX.java", >+ "public interface IX {\n" + >+ " void foo(Object o, Object other);\n" + >+ "}\n" >+ }); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X implements IX {\n" + >+ " public void foo(@NonNull Object o, @Nullable Object other) { System.out.print(o.toString()); }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 3)\n" + >+ " public void foo(@NonNull Object o, @Nullable Object other) { System.out.print(o.toString()); }\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "Illegal redefinition of parameter o, inherited method from IX does not constrain this parameter\n" + >+ "----------\n"); >+} >+// a method tries to relax the null contract, super declares @NonNull return >+public void test_parameter_specification_inheritance_005() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " @NonNull Object getObject() { return new Object(); }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, //dont' flush >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X extends Lib {\n" + >+ " @Override\n" + >+ " @Nullable Object getObject() { return null; }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " @Nullable Object getObject() { return null; }\n" + >+ " ^^^^^^^^^^^^^^^^\n" + >+ "The return type is incompatible with the @NonNull return from Lib.getObject()\n" + >+ "----------\n"); >+} >+ >+// super has no constraint for return, sub method confirms the null contract as @Nullable >+public void test_parameter_specification_inheritance_006() { >+ runConformTest( >+ new String[] { >+ "Lib.java", >+ "public class Lib {\n" + >+ " Object getObject() { return null; }\n" + >+ "}\n" >+ }); >+ runConformTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X extends Lib {\n" + >+ " @Override\n" + >+ " @Nullable Object getObject() { return null; }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+} >+// a method body violates the inherited null specification, super declares @NonNull return, missing redeclaration >+public void test_parameter_specification_inheritance_007() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " @NonNull Object getObject() { return new Object(); }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X extends Lib {\n" + >+ " @Override\n" + >+ " Object getObject() { return null; }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 3)\n" + >+ " Object getObject() { return null; }\n" + >+ " ^^^^^^\n" + >+ "The return type is incompatible with the @NonNull return from Lib.getObject()\n" + >+ "----------\n"); >+} >+//a method body violates the @NonNull return specification (repeated from super) >+public void test_parameter_specification_inheritance_007a() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " @NonNull Object getObject() { return new Object(); }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X extends Lib {\n" + >+ " @Override\n" + >+ " @NonNull Object getObject() { return null; }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " @NonNull Object getObject() { return null; }\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n"); >+} >+// a client potentially violates the inherited null specification, super interface declares @NonNull parameter >+public void test_parameter_specification_inheritance_008() { >+ Map options = getCompilerOptions(); >+ options.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "IX.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public interface IX {\n" + >+ " void printObject(@NonNull Object o);\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X implements IX {\n" + >+ " public void printObject(Object o) { System.out.print(o.toString()); }\n" + >+ "}\n", >+ "M.java", >+ "public class M{\n" + >+ " void foo(IX x, Object o) {\n" + >+ " x.printObject(o);\n" + >+ " }\n" + >+ "}\n" >+ }, >+ options, >+ "----------\n" + >+ // additional error: >+ "1. ERROR in X.java (at line 2)\n" + >+ " public void printObject(Object o) { System.out.print(o.toString()); }\n" + >+ " ^^^^^^\n" + >+ "Missing non-null annotation: inherited method from IX declares this parameter as @NonNull\n" + >+ "----------\n" + >+ // main error: >+ "----------\n" + >+ "1. ERROR in M.java (at line 3)\n" + >+ " x.printObject(o);\n" + >+ " ^\n" + >+ "Potential type mismatch: required \'@NonNull Object\' but nullness of the provided value is unknown\n" + >+ "----------\n"); >+} >+// a static method has a more relaxed null contract than a like method in the super class, but no overriding. >+public void test_parameter_specification_inheritance_009() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " @NonNull static Object getObject() { return new Object(); }\n" + >+ "}\n", >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X extends Lib {\n" + >+ " @Nullable static Object getObject() { return null; }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+} >+// class default is nonnull, method and its super both use the default >+public void test_parameter_specification_inheritance_010() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " protected String getString(String s) {\n" + >+ " if (Character.isLowerCase(s.charAt(0)))\n" + >+ " return getString(s);\n" + >+ " return s;\n" + >+ " }\n" + >+ "}\n", >+ "p1/Y.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class Y extends X {\n" + >+ " @Override\n" + >+ " protected String getString(String s) {\n" + >+ " return super.getString(s);\n" + >+ " }\n" + >+ "}\n", >+ }, >+ customOptions, >+ ""); >+} >+// class default is nonnull, method and its super both use the default, super-call passes null >+public void test_parameter_specification_inheritance_011() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " protected String getString(String s) {\n" + >+ " if (Character.isLowerCase(s.charAt(0)))\n" + >+ " return getString(s);\n" + >+ " return s;\n" + >+ " }\n" + >+ "}\n", >+ "p1/Y.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class Y extends X {\n" + >+ " @Override\n" + >+ " protected String getString(String s) {\n" + >+ " return super.getString(null);\n" + >+ " }\n" + >+ "}\n", >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p1\\Y.java (at line 7)\n" + >+ " return super.getString(null);\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value is null\n" + >+ "----------\n"); >+} >+// methods from two super types have different null contracts. >+// sub-class merges both using the weakest common contract >+public void test_parameter_specification_inheritance_012() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " public @Nullable String getString(String s1, @Nullable String s2, @NonNull String s3) {\n" + >+ " return s1;\n" + >+ " }\n" + >+ "}\n", >+ "p1/IY.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public interface IY {\n" + >+ " @NonNull String getString(@NonNull String s1, @NonNull String s2, @Nullable String s3);\n" + >+ "}\n", >+ "p1/Y.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Y extends X implements IY {\n" + >+ " @Override\n" + >+ " public @NonNull String getString(@Nullable String s1, @Nullable String s2, @Nullable String s3) {\n" + >+ " return \"\";\n" + >+ " }\n" + >+ "}\n", >+ }, >+ customOptions, >+ ""); >+} >+// methods from two super types have different null contracts. >+// sub-class overrides this method in non-conforming ways >+public void test_parameter_specification_inheritance_013() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " public @Nullable String getString(String s1, @Nullable String s2, @NonNull String s3) {\n" + >+ " return s1;\n" + >+ " }\n" + >+ "}\n", >+ "p1/IY.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public interface IY {\n" + >+ " @NonNull String getString(@NonNull String s1, @NonNull String s2, @Nullable String s3);\n" + >+ "}\n", >+ "p1/Y.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Y extends X implements IY {\n" + >+ " @Override\n" + >+ " public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" + >+ " return \"\";\n" + >+ " }\n" + >+ "}\n", >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p1\\Y.java (at line 5)\n" + >+ " public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" + >+ " ^^^^^^^^^^^^^^^^\n" + >+ "The return type is incompatible with the @NonNull return from IY.getString(String, String, String)\n" + >+ "----------\n" + >+ "2. ERROR in p1\\Y.java (at line 5)\n" + >+ " public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" + >+ " ^^^^^^\n" + >+ "Missing non-null annotation: inherited method from IY declares this parameter as @NonNull\n" + >+ "----------\n" + >+ "3. ERROR in p1\\Y.java (at line 5)\n" + >+ " public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "Illegal redefinition of parameter s2, inherited method from X declares this parameter as @Nullable\n" + >+ "----------\n" + >+ "4. ERROR in p1\\Y.java (at line 5)\n" + >+ " public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "Illegal redefinition of parameter s3, inherited method from IY declares this parameter as @Nullable\n" + >+ "----------\n"); >+} >+// methods from two super types have different null contracts. >+// sub-class does not override, but should to bridge the incompatibility >+public void test_parameter_specification_inheritance_014() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/IY.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public interface IY {\n" + >+ " public @NonNull String getString1(String s);\n" + >+ " public @NonNull String getString2(String s);\n" + >+ " public String getString3(@Nullable String s);\n" + >+ " public @NonNull String getString4(@Nullable String s);\n" + >+ " public @NonNull String getString5(@Nullable String s);\n" + >+ " public @Nullable String getString6(@NonNull String s);\n" + >+ "}\n", >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " public @Nullable String getString1(String s) {\n" + // incomp. return >+ " return s;\n" + >+ " }\n" + >+ " public String getString2(String s) {\n" + // incomp. return >+ " return s;\n" + >+ " }\n" + >+ " public String getString3(String s) {\n" + // incomp. arg >+ " return \"\";\n" + >+ " }\n" + >+ " public @NonNull String getString4(@Nullable String s) {\n" + >+ " return \"\";\n" + >+ " }\n" + >+ " public @NonNull String getString5(@NonNull String s) {\n" + // incomp. arg >+ " return s;\n" + >+ " }\n" + >+ " public @NonNull String getString6(@Nullable String s) {\n" + >+ " return \"\";\n" + >+ " }\n" + >+ "}\n", >+ "p1/Y.java", >+ "package p1;\n" + >+ "public class Y extends X implements IY {\n" + >+ "}\n", >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p1\\Y.java (at line 2)\n" + >+ " public class Y extends X implements IY {\n" + >+ " ^\n" + >+ "The method getString1(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" + >+ "----------\n" + >+ "2. ERROR in p1\\Y.java (at line 2)\n" + >+ " public class Y extends X implements IY {\n" + >+ " ^\n" + >+ "The method getString2(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" + >+ "----------\n" + >+ "3. ERROR in p1\\Y.java (at line 2)\n" + >+ " public class Y extends X implements IY {\n" + >+ " ^\n" + >+ "The method getString5(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" + >+ "----------\n" + >+ "4. ERROR in p1\\Y.java (at line 2)\n" + >+ " public class Y extends X implements IY {\n" + >+ " ^\n" + >+ "The method getString3(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" + >+ "----------\n"); >+} >+// a nullable return value is dereferenced without a check >+public void test_nullable_return_001() { >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @Nullable Object getObject() { return null; }\n" + >+ " void foo() {\n" + >+ " Object o = getObject();\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 6)\n" + >+ " System.out.print(o.toString());\n" + >+ " ^\n" + >+ "Potential null pointer access: The variable o may be null at this location\n" + >+ "----------\n"); >+} >+// a nullable return value is dereferenced without a check, method is read from .class file >+public void test_nullable_return_002() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " @Nullable Object getObject() { return null; }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ " void foo(Lib l) {\n" + >+ " Object o = l.getObject();\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "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" + >+ "----------\n"); >+} >+// a non-null return value is checked for null, method is read from .class file >+public void test_nonnull_return_001() { >+ runConformTestWithLibs( >+ new String[] { >+ "Lib.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Lib {\n" + >+ " @NonNull Object getObject() { return new Object(); }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ ""); >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ " void foo(Lib l) {\n" + >+ " Object o = l.getObject();\n" + >+ " if (o != null)\n" + >+ " System.out.print(o.toString());\n" + >+ " }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "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" + >+ "----------\n"); >+} >+// a non-null method returns null >+public void test_nonnull_return_003() { >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject(boolean b) {\n" + >+ " if (b)\n" + >+ " return null;\n" + // definite specification violation despite enclosing "if" >+ " return new Object();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 5)\n" + >+ " return null;\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n"); >+} >+// a non-null method potentially returns null >+public void test_nonnull_return_004() { >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject(@Nullable Object o) {\n" + >+ " return o;\n" + // 'o' is only potentially null >+ " }\n" + >+ "}\n" >+ }, >+ null /*customOptions*/, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " return o;\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// a non-null method returns its non-null argument >+public void test_nonnull_return_005() { >+ runConformTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject(@NonNull Object o) {\n" + >+ " return o;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ null, // options >+ ""); >+} >+//a non-null method has insufficient nullness info for its return value >+public void test_nonnull_return_006() { >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject(Object o) {\n" + >+ " return o;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. WARNING in X.java (at line 4)\n" + >+ " return o;\n" + >+ " ^\n" + >+ "Potential type mismatch: required \'@NonNull Object\' but nullness of the provided value is unknown\n" + >+ "----------\n"); >+} >+// a result from a nullable method is directly dereferenced >+public void test_nonnull_return_007() { >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @Nullable Object getObject() {\n" + >+ " return null;\n" + >+ " }\n" + >+ " void test() {\n" + >+ " getObject().toString();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 7)\n" + >+ " getObject().toString();\n" + >+ " ^^^^^^^^^^^\n" + >+ "Potential null pointer access: The method getObject() may return null\n" + >+ "----------\n"); >+} >+// a result from a nonnull method is directly checked for null: redundant >+public void test_nonnull_return_008() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject() {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ " void test() {\n" + >+ " if (getObject() == null)\n" + >+ " throw new RuntimeException();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 7)\n" + >+ " if (getObject() == null)\n" + >+ " ^^^^^^^^^^^\n" + >+ "Redundant null check: The method getObject() cannot return null\n" + >+ "----------\n"); >+} >+// a result from a nonnull method is directly checked for null (from local): redundant >+public void test_nonnull_return_009() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject() {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ " void test() {\n" + >+ " Object left = null;\n" + >+ " if (left != getObject())\n" + >+ " throw new RuntimeException();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 8)\n" + >+ " if (left != getObject())\n" + >+ " ^^^^\n" + >+ "Redundant null check: The variable left can only be null at this location\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 8)\n" + >+ " if (left != getObject())\n" + >+ " ^^^^^^^^^^^\n" + >+ "Redundant null check: The method getObject() cannot return null\n" + >+ "----------\n"); >+} >+// a result from a nullable method is assigned and checked for null (from local): not redundant >+// see also Bug 336428 - [compiler][null] bogus warning "redundant null check" in condition of do {} while() loop >+public void test_nonnull_return_010() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @Nullable X getX() {\n" + >+ " return new X();\n" + >+ " }\n" + >+ " void test() {\n" + >+ " X left = this;\n" + >+ " do {\n" + >+ " if (left == null) \n" + >+ " throw new RuntimeException();\n" + >+ " } while ((left = left.getX()) != null);\n" + // no warning/error here! >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 9)\n" + >+ " if (left == null) \n" + >+ " ^^^^\n" + >+ "Null comparison always yields false: The variable left cannot be null at this location\n" + >+ "----------\n"); >+} >+// a non-null method returns a checked-for null value, but that branch is dead code >+public void test_nonnull_return_011() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " Object getObject(Object dubious) {\n" + >+ " if (dubious == null)\n" + // redundant >+ " return dubious;\n" + // definitely null, but not reported inside dead code >+ " return new Object();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 5)\n" + >+ " if (dubious == null)\n" + >+ " ^^^^^^^\n" + >+ "Null comparison always yields false: The variable dubious cannot be null at this location\n" + >+ "----------\n" + >+ "2. WARNING in X.java (at line 6)\n" + >+ " return dubious;\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "Dead code\n" + >+ "----------\n"); >+} >+// a non-null method returns a definite null from a conditional expression >+// requires the fix for Bug 354554 - [null] conditional with redundant condition yields weak error message >+// TODO(SH): ENABLE! >+public void _test_nonnull_return_012() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " Object getObject(Object dubious) {\n" + >+ " return dubious == null ? dubious : null;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 5)\n" + >+ " return dubious == null ? dubious : null;\n" + >+ " ^^^^^^^\n" + >+ "Null comparison always yields false: The variable dubious cannot be null at this location\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 5)\n" + >+ " return dubious == null ? dubious : null;\n" + >+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n"); >+} >+// don't apply any default annotations to return void >+public void test_nonnull_return_013() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " void getObject() {}\n" + >+ "}\n", >+ "Y.java", >+ "public class Y extends X {\n" + >+ " @Override\n" + >+ " void getObject() {}\n" + // don't complain, void takes no (default) annotation >+ "}\n" >+ }, >+ customOptions, >+ ""); >+} >+//suppress an error regarding null-spec violation >+public void test_suppress_001() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS, JavaCore.ENABLED); >+ runConformTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @SuppressWarnings(\"null\")\n" + >+ " @NonNull Object getObject(@Nullable Object o) {\n" + >+ " return o;\n" + // 'o' is only potentially null >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ ""); >+} >+// mixed use of fully qualified name / explicit import >+public void test_annotation_import_001() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.Nullable"); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull"); >+ runConformTestWithLibs( >+ new String[] { >+ CUSTOM_NULLABLE_NAME, >+ CUSTOM_NULLABLE_CONTENT, >+ CUSTOM_NONNULL_NAME, >+ CUSTOM_NONNULL_CONTENT, >+ "Lib.java", >+ "public class Lib {\n" + >+ " @org.foo.NonNull Object getObject() { return new Object(); }\n" + // FQN >+ "}\n", >+ "X.java", >+ "import org.foo.NonNull;\n" + // explicit import >+ "public class X {\n" + >+ " @NonNull Object getObject(@NonNull Lib l) {\n" + >+ " return l.getObject();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ ""); >+} >+ >+// use of explicit imports throughout >+public void test_annotation_import_002() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.Nullable"); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull"); >+ runConformTest( >+ new String[] { >+ CUSTOM_NULLABLE_NAME, >+ CUSTOM_NULLABLE_CONTENT, >+ CUSTOM_NONNULL_NAME, >+ CUSTOM_NONNULL_CONTENT, >+ "Lib.java", >+ "import org.foo.NonNull;\n" + >+ "public class Lib {\n" + >+ " @NonNull Object getObject() { return new Object(); }\n" + >+ "}\n", >+ "X.java", >+ "import org.foo.NonNull;\n" + >+ "public class X {\n" + >+ " @NonNull Object getObject(@org.foo.Nullable String dummy, @NonNull Lib l) {\n" + >+ " Object o = l.getObject();" + >+ " return o;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ ""); >+} >+// explicit import of existing annotation types >+// using a Lib without null specifications >+public void test_annotation_import_005() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.MayBeNull"); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.MustNotBeNull"); >+ runNegativeTest( >+ true/*shouldFlushOutputDirectory*/, >+ new String[] { >+ "org/foo/MayBeNull.java", >+ "package org.foo;\n" + >+ "import java.lang.annotation.*;\n" + >+ "@Retention(RetentionPolicy.CLASS)\n" + >+ "public @interface MayBeNull {}\n", >+ >+ "org/foo/MustNotBeNull.java", >+ "package org.foo;\n" + >+ "import java.lang.annotation.*;\n" + >+ "@Retention(RetentionPolicy.CLASS)\n" + >+ "public @interface MustNotBeNull {}\n", >+ >+ "Lib.java", >+ "public class Lib {\n" + >+ " Object getObject() { return new Object(); }\n" + >+ "}\n", >+ "X.java", >+ "import org.foo.*;\n" + >+ "public class X {\n" + >+ " @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" + >+ " return l.getObject();\n" + >+ " }\n" + >+ "}\n", >+ >+ }, >+ null /*no libs*/, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " return l.getObject();\n" + >+ " ^^^^^^^^^^^^^\n" + >+ "Potential type mismatch: required \'@MustNotBeNull Object\' but nullness of the provided value is unknown\n" + >+ "----------\n", >+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); >+} >+// a non-null method returns a value obtained from an unannotated method, missing annotation types >+public void test_annotation_import_006() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.MayBeNull"); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.MustNotBeNull"); >+ runNegativeTest( >+ true/*shouldFlushOutputDirectory*/, >+ new String[] { >+ "Lib.java", >+ "public class Lib {\n" + >+ " Object getObject() { return new Object(); }\n" + >+ "}\n", >+ "X.java", >+ "public class X {\n" + >+ " @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" + >+ " return l.getObject();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ null /* no libs */, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 2)\n" + >+ " @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" + >+ " ^^^^^^^^^^^^^\n" + >+ "MustNotBeNull cannot be resolved to a type\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 2)\n" + >+ " @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" + >+ " ^^^^^^^^^^^^^\n" + >+ "MustNotBeNull cannot be resolved to a type\n" + >+ "----------\n", >+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); >+} >+// using nullness defaulting to nonnull, missing annotation types >+public void test_annotation_import_007() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.MayBeNull"); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.MustNotBeNull"); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ runNegativeTest( >+ true/*shouldFlushOutputDirectory*/, >+ new String[] { >+ "Lib.java", >+ "public class Lib {\n" + >+ " Object getObject() { return new Object(); }\n" + >+ "}\n", >+ "X.java", >+ "public class X {\n" + >+ " Object getObject(Lib l) {\n" + >+ " return l.getObject();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ this.LIBS, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in Lib.java (at line 1)\n" + >+ " public class Lib {\n" + >+ " ^\n" + >+ "Buildpath problem: the type org.foo.MustNotBeNull, which is configured as a null annotation type, cannot be resolved\n" + >+ "----------\n", >+ JavacTestOptions.Excuse.EclipseWarningConfiguredAsError); >+} >+ >+// a null annotation is illegally used on a class: >+public void test_illegal_annotation_001() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNull public class X {\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 2)\n" + >+ " @NonNull public class X {\n" + >+ " ^^^^^^^^\n" + >+ "The annotation @NonNull is disallowed for this location\n" + >+ "----------\n", >+ this.LIBS, >+ false/*shouldFlush*/); >+} >+// this test has been removed: >+// setting default to nullable, default applies to a parameter >+// public void test_default_nullness_001() >+ >+// a null annotation is illegally defined by its simple name >+public void test_illegal_annotation_002() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "NichtNull"); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 1)\n" + >+ " public class X {\n" + >+ " ^\n" + >+ "Cannot use the unqualified name \'NichtNull\' as an annotation name for null specification\n" + >+ "----------\n"); >+} >+ >+// a null annotation is illegally used on a void method: >+public void test_illegal_annotation_003() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @NonNull void foo() {}\n" + >+ "}\n" >+ }, >+ "----------\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.LIBS, >+ false/*shouldFlush*/); >+} >+ >+// a null annotation is illegally used on a primitive type parameter >+public void test_illegal_annotation_004() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void foo(@Nullable int i) {}\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 3)\n" + >+ " void foo(@Nullable int i) {}\n" + >+ " ^^^^^^^^^^^^^\n" + >+ "The nullness annotation @Nullable is not applicable for the primitive type int\n" + >+ "----------\n", >+ this.LIBS, >+ false/*shouldFlush*/); >+} >+ >+// a null annotation is illegally used on a primitive type local var >+public void test_illegal_annotation_005() { >+ runNegativeTest( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " int foo() {\n" + >+ " @Nullable int i = 3;\n" + >+ " return i;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ "----------\n" + >+ "1. ERROR in X.java (at line 4)\n" + >+ " @Nullable int i = 3;\n" + >+ " ^^^^^^^^^^^^^\n" + >+ "The nullness annotation @Nullable is not applicable for the primitive type int\n" + >+ "----------\n", >+ this.LIBS, >+ false/*shouldFlush*/); >+} >+ >+// a configured annotation type does not exist >+// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=186342#c133 >+public void test_illegal_annotation_006() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "nullAnn.Nullable"); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p/Test.java", >+ "package p;\n" + >+ "import nullAnn.*; // 1 \n" + >+ "\n" + >+ "public class Test { \n" + >+ "\n" + >+ " void foo(@nullAnn.Nullable Object o) { // 2\n" + >+ " o.toString(); \n" + >+ " }\n" + >+ "}" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p\\Test.java (at line 2)\n" + >+ " import nullAnn.*; // 1 \n" + >+ " ^^^^^^^\n" + >+ "The import nullAnn cannot be resolved\n" + >+ "----------\n" + >+ "2. ERROR in p\\Test.java (at line 6)\n" + >+ " void foo(@nullAnn.Nullable Object o) { // 2\n" + >+ " ^^^^^^^\n" + >+ "nullAnn cannot be resolved to a type\n" + >+ "----------\n"); >+} >+ >+public void test_default_nullness_002() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " Object getObject(@Nullable Object o) {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ "}\n", >+ "Y.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Y extends X {\n" + >+ " @Override\n" + >+ " @Nullable Object getObject(Object o) {\n" + // complain illegal return redef and inherited annot is not repeated >+ " return o;\n" + >+ " }\n" + >+ "}\n", >+ }, >+ customOptions, >+ // main error: >+ "----------\n" + >+ "1. ERROR in Y.java (at line 4)\n" + >+ " @Nullable Object getObject(Object o) {\n" + >+ " ^^^^^^^^^^^^^^^^\n" + >+ "The return type is incompatible with the @NonNull return from X.getObject(Object)\n" + >+ "----------\n" + >+ // additional error: >+ "2. ERROR in Y.java (at line 4)\n" + >+ " @Nullable Object getObject(Object o) {\n" + >+ " ^^^^^^\n" + >+ "Illegal redefinition of parameter o, inherited method from X declares this parameter as @Nullable\n" + >+ "----------\n"); >+} >+// package default is non-null >+public void test_default_nullness_003() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " protected Object getObject(@Nullable Object o) {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ "}\n", >+ "p2/package-info.java", >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "package p2;\n", >+ "p2/Y.java", >+ "package p2;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Y extends p1.X {\n" + >+ " @Override\n" + >+ " protected @Nullable Object getObject(@Nullable Object o) {\n" + >+ " bar(o);\n" + >+ " return o;\n" + >+ " }\n" + >+ " void bar(Object o2) { }\n" + // parameter is nonnull per package default >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p2\\Y.java (at line 5)\n" + >+ " protected @Nullable Object getObject(@Nullable Object o) {\n" + >+ " ^^^^^^^^^^^^^^^^\n" + >+ "The return type is incompatible with the @NonNull return from X.getObject(Object)\n" + >+ "----------\n" + >+ "2. ERROR in p2\\Y.java (at line 6)\n" + >+ " bar(o);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// package level default is consumed from package-info.class >+public void test_default_nullness_003a() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " protected Object getObject(@Nullable Object o) {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ " protected void bar(Object o2) { }\n" + // parameter is nonnull per type default >+ "}\n", >+ "p2/package-info.java", >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "package p2;\n", >+ }, >+ customOptions, >+ ""); >+ // check if default is visible from package-info.class. >+ runNegativeTestWithLibs( >+ false, // don't flush >+ new String[] { >+ "p2/Y.java", >+ "package p2;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Y extends p1.X {\n" + >+ " @Override\n" + >+ " protected @Nullable Object getObject(@Nullable Object o) {\n" + // can't override inherited default nonnull >+ " bar(o);\n" + // parameter is nonnull in super class's .class file >+ " accept(o);\n" + >+ " return o;\n" + >+ " }\n" + >+ " void accept(Object a) {}\n" + // governed by package level default >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p2\\Y.java (at line 5)\n" + >+ " protected @Nullable Object getObject(@Nullable Object o) {\n" + >+ " ^^^^^^^^^^^^^^^^\n" + >+ "The return type is incompatible with the @NonNull return from X.getObject(Object)\n" + >+ "----------\n" + >+ "2. ERROR in p2\\Y.java (at line 6)\n" + >+ " bar(o);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n" + >+ "3. ERROR in p2\\Y.java (at line 7)\n" + >+ " accept(o);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// don't apply type-level default to non-reference type >+public void test_default_nullness_004() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " protected Object getObject(boolean o) {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ "}\n", >+ "p2/Y.java", >+ "package p2;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class Y extends p1.X {\n" + >+ " @Override\n" + >+ " protected @NonNull Object getObject(boolean o) {\n" + >+ " return o ? this : new Object();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ ""); >+} >+// package default is non-null >+// see also Bug 354536 - compiling package-info.java still depends on the order of compilation units >+public void test_default_nullness_005() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull"); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "public class X {\n" + >+ " class Inner {" + >+ " protected Object getObject(String s) {\n" + >+ " return null;\n" + >+ " }\n" + >+ " }\n" + >+ "}\n", >+ "p1/package-info.java", >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "package p1;\n", >+ CUSTOM_NONNULL_NAME, >+ CUSTOM_NONNULL_CONTENT >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p1\\X.java (at line 4)\n" + >+ " return null;\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n"); >+} >+// package default is non-null, package-info.java read before the annotation type >+// compile order: beginToCompile(X.Inner) triggers reading of package-info.java before the annotation type was read >+public void test_default_nullness_006() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull"); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/package-info.java", >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "package p1;\n", >+ "p1/X.java", >+ "package p1;\n" + >+ "public class X {\n" + >+ " class Inner {" + >+ " protected Object getObject(String s) {\n" + >+ " return null;\n" + >+ " }\n" + >+ " }\n" + >+ "}\n", >+ CUSTOM_NONNULL_NAME, >+ CUSTOM_NONNULL_CONTENT >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p1\\X.java (at line 4)\n" + >+ " return null;\n" + >+ " ^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n"); >+} >+// global default nonnull, but return may be null >+public void test_default_nullness_007() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " @Nullable Object dangerous() {\n" + >+ " return null;\n" + >+ " }\n" + >+ " Object broken() {\n" + >+ " return dangerous();\n" + >+ " }\n" + >+ "}\n", >+ >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 7)\n" + >+ " return dangerous();\n" + >+ " ^^^^^^^^^^^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value can be null\n" + >+ "----------\n"); >+} >+ >+// cancel type level default to comply with super specification >+public void test_default_nullness_008() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "public class X {\n" + >+ " protected Object getObject(Object o) {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ "}\n", >+ "p2/Y.java", >+ "package p2;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class Y extends p1.X {\n" + >+ " @Override\n" + >+ " @NonNullByDefault(false)\n" + >+ " protected Object getObject(Object o) {\n" + >+ " if (o.toString().length() == 0)\n" + // dereference without a warning >+ " return null;\n" + // return null without a warning >+ " return o.toString();\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ ""); >+} >+ >+// cancel outer type level default to comply with super specification >+public void test_default_nullness_009() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ runNegativeTestWithLibs( >+ new String[] { >+ "p1/X.java", >+ "package p1;\n" + >+ "public class X {\n" + >+ " protected Object getObject(Object o) {\n" + >+ " return new Object();\n" + >+ " }\n" + >+ "}\n", >+ "p2/Y.java", >+ "package p2;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class Y { \n" + >+ " @NonNullByDefault(false)\n" + >+ " static class Z extends p1.X {\n" + >+ " @Override\n" + >+ " protected Object getObject(Object o) {\n" + >+ " if (o.toString().length() == 0) {\n" + >+ " o = null;\n" + // assign null without a warning >+ " bar(o); // error: arg is declared @NonNull\n" + >+ " return null;\n" + >+ " }\n" + >+ " return o.toString();\n" + >+ " }\n" + >+ " String bar(@NonNull Object o) {\n" + >+ " return getObject(o).toString();" + >+ " }\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in p2\\Y.java (at line 11)\n" + >+ " bar(o); // error: arg is declared @NonNull\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull Object\' but the provided value is null\n" + >+ "----------\n"); >+} >+// non-null declarations are redundant within a default scope. >+public void test_default_nullness_010() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ runConformTestWithLibs( >+ new String[] { >+ "p2/Y.java", >+ "package p2;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class Y {\n" + >+ " protected @NonNull Object getObject(@NonNull Object o) {\n" + >+ " return o;\n" + >+ " }\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. WARNING in p2\\Y.java (at line 5)\n" + >+ " protected @NonNull Object getObject(@NonNull Object o) {\n" + >+ " ^^^^^^^^^^^^^^^\n" + >+ "The nullness annotation is redundant with a default that applies to this location\n" + >+ "----------\n" + >+ "2. WARNING in p2\\Y.java (at line 5)\n" + >+ " protected @NonNull Object getObject(@NonNull Object o) {\n" + >+ " ^^^^^^^^^^^^^^^^^\n" + >+ "The nullness annotation is redundant with a default that applies to this location\n" + >+ "----------\n"); >+} >+// a nonnull variable is dereferenced in a loop >+public void test_nonnull_var_in_constrol_structure_1() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void print4(@NonNull String s) {\n" + >+ " for (int i=0; i<4; i++)\n" + >+ " print(s);\n" + >+ " }\n" + >+ " void print5(@Nullable String s) {\n" + >+ " for (int i=0; i<5; i++)\n" + >+ " print(s);\n" + >+ " }\n" + >+ " void print6(boolean b) {\n" + >+ " String s = b ? null : \"\";\n" + >+ " for (int i=0; i<5; i++)\n" + >+ " print(s);\n" + >+ " }\n" + >+ " void print(@NonNull String s) {\n" + >+ " System.out.print(s);\n" + >+ " }\n" + >+ "}\n", >+ >+ }, >+ customOptions, >+ "----------\n" + >+ "1. WARNING in X.java (at line 3)\n" + >+ " void print4(@NonNull String s) {\n" + >+ " ^^^^^^^^^^^^^^^^^\n" + >+ "The nullness annotation is redundant with a default that applies to this location\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 9)\n" + >+ " print(s);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" + >+ "3. ERROR in X.java (at line 14)\n" + >+ " print(s);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" + >+ "4. WARNING in X.java (at line 16)\n" + >+ " void print(@NonNull String s) {\n" + >+ " ^^^^^^^^^^^^^^^^^\n" + >+ "The nullness annotation is redundant with a default that applies to this location\n" + >+ "----------\n"); >+} >+// a nonnull variable is dereferenced in a finally block >+public void test_nonnull_var_in_constrol_structure_2() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void print4(String s) {\n" + >+ " try { /*empty*/ } finally {\n" + >+ " print(s);\n" + >+ " }\n" + >+ " }\n" + >+ " void print5(@Nullable String s) {\n" + >+ " try { /*empty*/ } finally {\n" + >+ " print(s);\n" + >+ " }\n" + >+ " }\n" + >+ " void print6(boolean b) {\n" + >+ " String s = b ? null : \"\";\n" + >+ " try { /*empty*/ } finally {\n" + >+ " print(s);\n" + >+ " }\n" + >+ " }\n" + >+ " void print(String s) {\n" + >+ " System.out.print(s);\n" + >+ " }\n" + >+ "}\n", >+ >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 10)\n" + >+ " print(s);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 16)\n" + >+ " print(s);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// a nonnull variable is dereferenced in a finally block inside a loop >+public void test_nonnull_var_in_constrol_structure_3() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION, JavaCore.IGNORE); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class X {\n" + >+ " void print4(@NonNull String s) {\n" + >+ " for (int i=0; i<4; i++)\n" + >+ " try { /*empty*/ } finally {\n" + >+ " print(s);\n" + >+ " }\n" + >+ " }\n" + >+ " void print5(@Nullable String s) {\n" + >+ " for (int i=0; i<5; i++)\n" + >+ " try { /*empty*/ } finally {\n" + >+ " print(s);\n" + >+ " }\n" + >+ " }\n" + >+ " void print6(boolean b) {\n" + >+ " String s = b ? null : \"\";\n" + >+ " for (int i=0; i<4; i++)\n" + >+ " try { /*empty*/ } finally {\n" + >+ " print(s);\n" + >+ " }\n" + >+ " }\n" + >+ " void print(@NonNull String s) {\n" + >+ " System.out.print(s);\n" + >+ " }\n" + >+ "}\n", >+ >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 12)\n" + >+ " print(s);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 19)\n" + >+ " print(s);\n" + >+ " ^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// a nonnull variable is dereferenced method of a nested type >+public void test_nesting_1() { >+ Map customOptions = getCompilerOptions(); >+// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR); >+ customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "@NonNullByDefault\n" + >+ "public class X {\n" + >+ " void print4(final String s1) {\n" + >+ " for (int i=0; i<3; i++)\n" + >+ " new Runnable() {\n" + >+ " public void run() {\n" + >+ " print(s1);\n" + >+ " }\n" + >+ " }.run();\n" + >+ " }\n" + >+ " void print8(final @Nullable String s2) {\n" + >+ " for (int i=0; i<3; i++)\n" + >+ " new Runnable() {\n" + >+ " public void run() {\n" + >+ " print(s2);\n" + >+ " }\n" + >+ " }.run();\n" + >+ " }\n" + >+ " void print16(boolean b) {\n" + >+ " final String s3 = b ? null : \"\";\n" + >+ " for (int i=0; i<3; i++)\n" + >+ " new Runnable() {\n" + >+ " public void run() {\n" + >+ " @NonNull String s3R = s3;\n" + >+ " }\n" + >+ " }.run();\n" + >+ " }\n" + >+ " void print(String s) {\n" + >+ " System.out.print(s);\n" + >+ " }\n" + >+ "}\n", >+ >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 16)\n" + >+ " print(s2);\n" + >+ " ^^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n" + >+ "2. ERROR in X.java (at line 25)\n" + >+ " @NonNull String s3R = s3;\n" + >+ " ^^\n" + >+ "Type mismatch: required \'@NonNull String\' but the provided value can be null\n" + >+ "----------\n"); >+} >+// Test a regression incurred to the OT/J based implementation >+// by the fix in Bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop) >+public void test_constructor_with_nested_class() { >+ runConformTest( >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ " final Object o1;\n" + >+ " final Object o2;\n" + >+ " public X() {\n" + >+ " this.o1 = new Object() {\n" + >+ " public String toString() { return \"O1\"; }\n" + >+ " };\n" + >+ " this.o2 = new Object();" + >+ " }\n" + >+ "}\n" >+ }, >+ ""); >+} >+} >diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java >index 28a772e..4dbf8ba 100644 >--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java >+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/TestAll.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.core.tests.compiler.regression; > >@@ -96,6 +97,7 @@ > since_1_5.add(InnerEmulationTest_1_5.class); > since_1_5.add(AssignmentTest_1_5.class); > since_1_5.add(InnerClass15Test.class); >+ since_1_5.add(NullAnnotationTest.class); > > // Tests to run when compliance is greater than 1.5 > ArrayList since_1_6 = new ArrayList(); >diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15JLS4Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15JLS4Test.java >index 00678f7..1e06c11 100644 >--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15JLS4Test.java >+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15JLS4Test.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.core.tests.dom; > >@@ -7494,7 +7495,10 @@ > buffer.append(typeBinding.getAnnotations().length); > typeBinding= typeBinding.getSuperclass(); > } >- assertEquals("Wrong number of annotations", "000", String.valueOf(buffer)); >+ // initially, this test expected "000", but after https://bugs.eclipse.org/186342 >+ // annotations are resolved more eagerly, which makes the annotations on Test2 show up, >+ // which is actually the right outcome. >+ assertEquals("Wrong number of annotations", "020", String.valueOf(buffer)); > } > } > >diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15Test.java >index 2af2c81..e89ba8f 100644 >--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15Test.java >+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter15Test.java >@@ -10,6 +10,7 @@ > * Stephan Herrmann - Contributions for > * Bug 342671 - ClassCastException: org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding cannot be cast to org.eclipse.jdt.internal.compiler.lookup.ArrayBinding > * Bug 353474 - type converters should include more annotations >+ * Bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.core.tests.dom; > >@@ -7497,7 +7498,10 @@ > buffer.append(typeBinding.getAnnotations().length); > typeBinding= typeBinding.getSuperclass(); > } >- assertEquals("Wrong number of annotations", "000", String.valueOf(buffer)); >+ // initially, this test expected "000", but after https://bugs.eclipse.org/186342 >+ // annotations are resolved more eagerly, which makes the annotations on Test2 show up, >+ // which is actually the right outcome. >+ assertEquals("Wrong number of annotations", "020", String.valueOf(buffer)); > } > } > >diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java >index d3e6b2b..537f730 100644 >--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java >+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/AllJavaModelTests.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2010 IBM Corporation and others. >+ * Copyright (c) 2000, 2011 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -185,6 +185,9 @@ > UtilTests.class, > > JavaCoreOptionsTests.class, >+ >+ // Tests regarding null-annotations: >+ NullAnnotationModelTests.class, > }; > > Class[] deprecatedClasses = getDeprecatedJDOMTestClasses(); >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 >new file mode 100644 >index 0000000..da0622a >--- /dev/null >+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java >@@ -0,0 +1,410 @@ >+/******************************************************************************* >+ * Copyright (c) 2011 GK Software AG 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 >+ * http://www.eclipse.org/legal/epl-v10.html >+ * >+ * Contributors: >+ * Stephan Herrmann - initial API and implementation >+ *******************************************************************************/ >+package org.eclipse.jdt.core.tests.model; >+ >+import java.io.File; >+import java.io.IOException; >+import java.io.StringBufferInputStream; >+import java.net.URL; >+import java.util.Hashtable; >+ >+import junit.framework.Test; >+ >+import org.eclipse.core.resources.IFile; >+import org.eclipse.core.resources.IMarker; >+import org.eclipse.core.resources.IResource; >+import org.eclipse.core.resources.IncrementalProjectBuilder; >+import org.eclipse.core.runtime.CoreException; >+import org.eclipse.core.runtime.FileLocator; >+import org.eclipse.core.runtime.Platform; >+import org.eclipse.jdt.core.ICompilationUnit; >+import org.eclipse.jdt.core.IJavaModelMarker; >+import org.eclipse.jdt.core.IJavaProject; >+import org.eclipse.jdt.core.JavaCore; >+import org.eclipse.jdt.core.compiler.IProblem; >+import org.eclipse.jdt.core.dom.AST; >+import org.eclipse.jdt.core.dom.ASTParser; >+import org.eclipse.jdt.core.dom.CompilationUnit; >+ >+public class NullAnnotationModelTests extends ReconcilerTests { >+ >+ String ANNOTATION_LIB; >+ >+ public static Test suite() { >+ return buildModelTestSuite(NullAnnotationModelTests.class); >+ } >+ >+ public NullAnnotationModelTests(String name) { >+ super(name); >+ } >+ >+ static { >+// TESTS_NAMES = new String[] { "testMissingAnnotation5" }; >+ } >+ >+ public void setUp() throws Exception { >+ super.setUp(); >+ File bundleFile = FileLocator.getBundleFile(Platform.getBundle("org.eclipse.jdt.annotation.null")); >+ this.ANNOTATION_LIB = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); >+ } >+ >+ protected String testJarPath(String jarName) throws IOException { >+ URL libEntry = Platform.getBundle("org.eclipse.jdt.core.tests.model").getEntry("/workspace/NullAnnotations/lib/"+jarName); >+ return FileLocator.toFileURL(libEntry).getPath(); >+ } >+ >+ >+ public void testConvertedSourceType1() throws CoreException, InterruptedException { >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ p.setOption(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ >+ this.createFolder("/P/p1"); >+ String c1SourceString = >+ "package p1;\n" + >+ "import org.eclipse.jdt.annotation.*;\n" + >+ "public class C1 {\n" + >+ " public String foo(@Nullable Object arg) {\n" + // this is consumed via SourceTypeConverter >+ " return arg == null ? \"\" : arg.toString();\n" + >+ " }\n" + >+ "}\n"; >+ this.createFile( >+ "/P/p1/C1.java", >+ c1SourceString); >+ >+ this.createFolder("/P/p2"); >+ String c2SourceString = >+ "package p2;\n" + >+ "public class C2 {\n" + >+ " String bar(p1.C1 c, C2 c2) {;\n" + >+ " return c.foo(null);\n" + // don't complain despite default nonnull, foo has explicit @Nullable >+ " }\n" + >+ " String foo(Object arg) {\n" + >+ " return arg == null ? null : arg.toString();\n" + >+ " }\n" + >+ "}\n"; >+ this.createFile( >+ "/P/p2/C2.java", >+ c2SourceString); >+ >+ char[] c2SourceChars = c2SourceString.toCharArray(); >+ this.problemRequestor.initialize(c2SourceChars); >+ >+ getCompilationUnit("/P/p2/C2.java").getWorkingCopy(this.wcOwner, null); >+ >+ assertProblems("Unexpected problems", "----------\n" + >+ "1. WARNING in /P/p2/C2.java (at line 7)\n" + >+ " return arg == null ? null : arg.toString();\n" + >+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + >+ "Potential type mismatch: required \'@NonNull String\' but nullness of the provided value is unknown\n" + >+ "----------\n"); >+ } finally { >+ deleteProject("P"); >+ } >+ } >+ >+ public void testBinaryType1() throws CoreException, InterruptedException, IOException { >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, >+ new String[] {"JCL15_LIB", this.ANNOTATION_LIB, testJarPath("example.jar")}, >+ "bin", "1.5"); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ p.setOption(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED); >+ >+ // example.jar contains p1/C1.java just like testConvertedSourceType1() >+ >+ this.createFolder("/P/p2"); >+ String c2SourceString = >+ "package p2;\n" + >+ "public class C2 {\n" + >+ " String bar(p1.C1 c) {;\n" + >+ " return c.foo(null);\n" + // don't complain despite default nonnull, foo has explicit @Nullable >+ " }\n" + >+ " String foo(Object arg) {\n" + >+ " return arg == null ? null : arg.toString();\n" + >+ " }\n" + >+ "}\n"; >+ this.createFile( >+ "/P/p2/C2.java", >+ c2SourceString); >+ >+ char[] c2SourceChars = c2SourceString.toCharArray(); >+ this.problemRequestor.initialize(c2SourceChars); >+ >+ getCompilationUnit("/P/p2/C2.java").getWorkingCopy(this.wcOwner, null); >+ >+ assertProblems("Unexpected problems", "----------\n" + >+ "1. WARNING in /P/p2/C2.java (at line 7)\n" + >+ " return arg == null ? null : arg.toString();\n" + >+ " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + >+ "Potential type mismatch: required \'@NonNull String\' but nullness of the provided value is unknown\n" + >+ "----------\n"); >+ } finally { >+ deleteProject("P"); >+ } >+ } >+ >+ public void testMissingAnnotation1() throws CoreException { >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "in.valid"); >+ >+ this.createFolder("/P/p1"); >+ String c1SourceString = >+ "package p1;\n" + >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "public class C1 {\n" + >+ " public String foo(Object arg) {\n" + >+ " return arg == null ? \"\" : arg.toString();\n" + >+ " }\n" + >+ "}\n"; >+ this.createFile( >+ "/P/p1/C1.java", >+ c1SourceString); >+ >+ this.problemRequestor.initialize(c1SourceString.toCharArray()); >+ >+ getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); >+ >+ assertProblems("Unexpected problems", >+ "----------\n" + >+ "1. ERROR in /P/p1/C1.java (at line 1)\n" + >+ " package p1;\n" + >+ " ^\n" + >+ "Buildpath problem: the type in.valid, which is configured as a null annotation type, cannot be resolved\n" + >+ "----------\n"); >+ } finally { >+ deleteProject("P"); >+ } >+ } >+ >+ public void testMissingAnnotation2() throws CoreException { >+ Hashtable javaOptions = JavaCore.getOptions(); >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); >+ IFile settings = (IFile) p.getProject().findMember(".settings/org.eclipse.jdt.core.prefs"); >+ settings.appendContents(new StringBufferInputStream("\norg.eclipse.jdt.core.compiler.annotation.nonnull=not.valid\n"), 0, null); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ >+ this.createFolder("/P/p1"); >+ String c1SourceString = >+ "package p1;\n" + >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "public class C1 {\n" + >+ " public String foo(Object arg) {\n" + >+ " return arg == null ? \"\" : arg.toString();\n" + >+ " }\n" + >+ "}\n"; >+ this.createFile( >+ "/P/p1/C1.java", >+ c1SourceString); >+ >+ this.problemRequestor.initialize(c1SourceString.toCharArray()); >+ >+ getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); >+ >+ assertProblems("Unexpected problems", >+ "----------\n" + >+ "1. ERROR in /P/p1/C1.java (at line 1)\n" + >+ " package p1;\n" + >+ " ^\n" + >+ "Buildpath problem: the type not.valid, which is configured as a null annotation type, cannot be resolved\n" + >+ "----------\n"); >+ } finally { >+ deleteProject("P"); >+ JavaCore.setOptions(javaOptions); >+ // work against side-effect of JavaRuntime listening to change of prefs-file. >+ // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=302850#c25 >+ } >+ } >+ >+ // Bug 363858 - [dom] early throwing of AbortCompilation causes NPE in CompilationUnitResolver >+ public void testMissingAnnotation3() throws CoreException { >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "invalid"); >+ >+ this.createFolder("/P/p1"); >+ String c1SourceString = >+ "package p1;\n" + >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "public class C1 {\n" + >+ " public String foo(Object arg) {\n" + >+ " return arg == null ? \"\" : arg.toString();\n" + >+ " }\n" + >+ "}\n"; >+ this.createFile( >+ "/P/p1/C1.java", >+ c1SourceString); >+ >+ this.problemRequestor.initialize(c1SourceString.toCharArray()); >+ >+ final ICompilationUnit unit = getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); >+ assertProblems("Unexpected problems", >+ "----------\n" + >+ "1. ERROR in /P/p1/C1.java (at line 0)\n" + >+ " package p1;\n" + >+ " ^\n" + >+ "Cannot use the unqualified name \'invalid\' as an annotation name for null specification\n" + >+ "----------\n"); >+ >+ ASTParser parser = ASTParser.newParser(AST.JLS4); >+ parser.setProject(p); >+ parser.setResolveBindings(true); >+ parser.setSource(unit); >+ CompilationUnit ast = (CompilationUnit) parser.createAST(null); >+ assertNotNull("ast should not be null", ast); >+ this.problemRequestor.reset(); >+ this.problemRequestor.beginReporting(); >+ IProblem[] problems = ast.getProblems(); >+ for (int i=0; i<problems.length; i++) >+ this.problemRequestor.acceptProblem(problems[i]); >+ assertProblems("Unexpected problems (2)", >+ "----------\n" + >+ "1. ERROR in /P/p1/C1.java (at line 0)\n" + >+ " package p1;\n" + >+ " ^\n" + >+ "Cannot use the unqualified name \'invalid\' as an annotation name for null specification\n" + >+ "----------\n"); >+ } finally { >+ deleteProject("P"); >+ } >+ } >+ >+ // initialization of null annotations is triggered from package-info.java: illegal simple name >+ public void testMissingAnnotation4() throws CoreException { >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "invalid"); >+ >+ this.createFolder("/P/p1"); >+ String piSourceString = >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "package p1;\n"; >+ this.createFile( >+ "/P/p1/package-info.java", >+ piSourceString); >+ >+ this.problemRequestor.initialize(piSourceString.toCharArray()); >+ >+ // Challenge CompilationUnitProblemFinder: >+ final ICompilationUnit unit = getCompilationUnit("/P/p1/package-info.java").getWorkingCopy(this.wcOwner, null); >+ String expectedError = "----------\n" + >+ "1. ERROR in /P/p1/package-info.java (at line 0)\n" + >+ " @org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ " ^\n" + >+ "Cannot use the unqualified name \'invalid\' as an annotation name for null specification\n" + >+ "----------\n"; >+ assertProblems("Unexpected problems from CompilationUnitProblemFinder", expectedError); >+ >+ // Challenge JavaBuilder: >+ p.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); >+ IMarker[] markers = p.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); >+ assertMarkers("Unexpected markers", >+ "Cannot use the unqualified name 'invalid' as an annotation name for null specification", >+ markers); >+ assertEquals("Unexpected marker path", "/P", markers[0].getResource().getFullPath().toString()); >+ >+ // Challenge CompilationUnitResolver: >+ ASTParser parser = ASTParser.newParser(AST.JLS4); >+ parser.setProject(p); >+ parser.setResolveBindings(true); >+ parser.setSource(unit); >+ CompilationUnit ast = (CompilationUnit) parser.createAST(null); >+ assertNotNull("ast should not be null", ast); >+ this.problemRequestor.reset(); >+ this.problemRequestor.beginReporting(); >+ IProblem[] problems = ast.getProblems(); >+ for (int i=0; i<problems.length; i++) >+ this.problemRequestor.acceptProblem(problems[i]); >+ assertProblems("Unexpected problems from CompilationUnitResolver", expectedError); >+ } finally { >+ deleteProject("P"); >+ } >+ } >+ >+ // initialization of null annotations is >+ // - triggered from resolveTypesFor(MethodBinding) >+ // - default is defined in package-info.java: >+ // must detect missing non-null annotation and report against the project >+ public void testMissingAnnotation5() throws CoreException, InterruptedException { >+ try { >+ // Resources creation >+ IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); >+ p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); >+ p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "pack.Missing"); >+ >+ this.createFolder("/P/p1"); >+ String piSourceString = >+ "@org.eclipse.jdt.annotation.NonNullByDefault\n" + >+ "package p1;\n"; >+ this.createFile("/P/p1/package-info.java", piSourceString); >+ >+ String c1SourceString = >+ "package p1;\n" + >+ "public class C1 {\n" + >+ " String foo(String arg) { return arg; }\n" + >+ "}\n"; >+ this.createFile("/P/p1/C1.java", c1SourceString); >+ >+ this.problemRequestor.initialize(piSourceString.toCharArray()); >+ >+ // Challenge CompilationUnitProblemFinder: >+ assertNoProblem(piSourceString.toCharArray(), getCompilationUnit("/P/p1/package-info.java")); >+ >+ this.problemRequestor.initialize(c1SourceString.toCharArray()); >+ >+ // Challenge CompilationUnitProblemFinder: >+ ICompilationUnit unit = getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); >+ String expectedError = "----------\n" + >+ "1. ERROR in /P/p1/C1.java (at line 1)\n" + >+ " package p1;\n" + >+ " ^\n" + >+ "Buildpath problem: the type pack.Missing, which is configured as a null annotation type, cannot be resolved\n" + >+ "----------\n"; >+ assertProblems("Unexpected problems", expectedError); >+ >+ // Challenge JavaBuilder: >+ p.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); >+ IMarker[] markers = p.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); >+ assertMarkers("Unexpected markers", >+ "Buildpath problem: the type pack.Missing, which is configured as a null annotation type, cannot be resolved", >+ markers); >+ assertEquals("Unexpected marker path", "/P", markers[0].getResource().getFullPath().toString()); >+ >+ // Challenge CompilationUnitResolver: >+ ASTParser parser = ASTParser.newParser(AST.JLS4); >+ parser.setProject(p); >+ parser.setResolveBindings(true); >+ parser.setSource(unit); >+ CompilationUnit ast = (CompilationUnit) parser.createAST(null); >+ assertNotNull("ast should not be null", ast); >+ this.problemRequestor.reset(); >+ this.problemRequestor.beginReporting(); >+ IProblem[] problems = ast.getProblems(); >+ for (int i=0; i<problems.length; i++) >+ this.problemRequestor.acceptProblem(problems[i]); >+ assertProblems("Unexpected problems (2)", expectedError); >+ } finally { >+ deleteProject("P"); >+ } >+ } >+} >diff --git a/org.eclipse.jdt.core.tests.model/workspace/NullAnnotations/lib/example.jar b/org.eclipse.jdt.core.tests.model/workspace/NullAnnotations/lib/example.jar >new file mode 100644 >index 0000000..4f9b0ca >--- /dev/null >+++ b/org.eclipse.jdt.core.tests.model/workspace/NullAnnotations/lib/example.jar >Binary files differ >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 1a4724d..fce317a 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 >@@ -122,6 +122,20 @@ > * UnclosedCloseable > * UnclosedCloseableAtExit > * ExplicitlyClosedAutoCloseable >+ * RequiredNonNullButProvidedNull >+ * RequiredNonNullButProvidedPotentialNull >+ * RequiredNonNullButProvidedUnknown >+ * MissingNullAnnotationType >+ * NullAnnotationNameMustBeQualified >+ * IllegalReturnNullityRedefinition >+ * IllegalRedefinitionToNonNullParameter >+ * IllegalDefinitionToNonNullParameter >+ * ParameterLackingNonNullAnnotation >+ * ParameterLackingNullableAnnotation >+ * PotentialNullMessageSendReference >+ * RedundantNullCheckOnNonNullMessageSend >+ * CannotImplementIncompatibleNullness >+ * RedundantNullAnnotation > *******************************************************************************/ > package org.eclipse.jdt.core.compiler; > >@@ -1414,6 +1428,40 @@ > /** @since 3.8 */ > int SwitchOnEnumNotBelow15 = TypeRelated + 890; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=360317 > /** >+ * Errors/warnings from annotation based null analysis >+ */ >+ /** @since 3.8 */ >+ int RequiredNonNullButProvidedNull = TypeRelated + 910; >+ /** @since 3.8 */ >+ int RequiredNonNullButProvidedPotentialNull = TypeRelated + 911; >+ /** @since 3.8 */ >+ int RequiredNonNullButProvidedUnknown = TypeRelated + 912; >+ /** @since 3.8 */ >+ int MissingNullAnnotationType = ImportRelated + 913; >+ /** @since 3.8 */ >+ int NullAnnotationNameMustBeQualified = Internal + 914; >+ /** @since 3.8 */ >+ int IllegalReturnNullityRedefinition = MethodRelated + 915; >+ /** @since 3.8 */ >+ int IllegalRedefinitionToNonNullParameter = MethodRelated + 916; >+ /** @since 3.8 */ >+ int IllegalDefinitionToNonNullParameter = MethodRelated + 917; >+ /** @since 3.8 */ >+ int ParameterLackingNonNullAnnotation = MethodRelated + 918; >+ /** @since 3.8 */ >+ int ParameterLackingNullableAnnotation = MethodRelated + 919; >+ /** @since 3.8 */ >+ int PotentialNullMessageSendReference = Internal + 920; >+ /** @since 3.8 */ >+ int RedundantNullCheckOnNonNullMessageSend = Internal + 921; >+ /** @since 3.8 */ >+ int CannotImplementIncompatibleNullness = Internal + 922; >+ /** @since 3.8 */ >+ int RedundantNullAnnotation = MethodRelated + 923; >+ /** @since 3.8 */ >+ int IllegalAnnotationForBaseType = TypeRelated + 924; >+ >+ /** > * External problems -- These are problems defined by other plugins > */ > >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java >index 44a69a0..cbe6196 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/Compiler.java >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - contribution for bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment >+ * Stephan Herrmann - contributions for >+ * bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler; > >@@ -695,6 +697,7 @@ > > // Switch the current policy and compilation result for this unit to the requested one. > for (int i = 0; i < maxUnits; i++) { >+ CompilationResult unitResult = null; > try { > if (this.options.verbose) { > this.out.println( >@@ -707,8 +710,7 @@ > } > // diet parsing for large collection of units > CompilationUnitDeclaration parsedUnit; >- CompilationResult unitResult = >- new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit); >+ unitResult = new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit); > long parseStart = System.currentTimeMillis(); > if (this.totalUnits < this.parseThreshold) { > parsedUnit = this.parser.parse(sourceUnits[i], unitResult); >@@ -727,6 +729,11 @@ > } > //} catch (AbortCompilationUnit e) { > // requestor.acceptResult(unitResult.tagAsAccepted()); >+ } catch (AbortCompilation a) { >+ // best effort to find a way for reporting this problem: >+ if (a.compilationResult == null) >+ a.compilationResult = unitResult; >+ throw a; > } finally { > sourceUnits[i] = null; // no longer hold onto the unit > } >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 892538a..4cedddc 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 >@@ -9,7 +9,9 @@ > * IBM Corporation - initial API and implementation > * Matt McCutchen - partial fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=122995 > * Karen Moore - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=207411 >- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read >+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for >+ * bug 185682 - Increment/decrement operators mark local variables as read >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -253,6 +255,11 @@ > > // this is only used for method invocation as the expression inside an expression statement > public static final int InsideExpressionStatement = Bit5; >+ >+ // for annotation reference, signal if annotation was created from a default: >+ public static final int IsSynthetic = ASTNode.Bit7; >+ // for name reference within a memberValuePair of an annotation: >+ public static final int IsMemberValueReference = ASTNode.Bit15; > > public ASTNode() { > >@@ -601,7 +608,7 @@ > local.tagBits |= (TagBits.AnnotationResolved | TagBits.DeprecatedAnnotationResolved); > if (length > 0) { > annotations = new AnnotationBinding[length]; >- local.setAnnotations(annotations); >+ local.setAnnotations(annotations, scope); > } > break; > default : >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 995b093..4526098 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 >@@ -7,11 +7,15 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >+import java.util.Arrays; >+ > import org.eclipse.jdt.core.compiler.*; > import org.eclipse.jdt.internal.compiler.*; >+import org.eclipse.jdt.internal.compiler.flow.FlowInfo; > import org.eclipse.jdt.internal.compiler.impl.*; > import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; > import org.eclipse.jdt.internal.compiler.codegen.*; >@@ -64,6 +68,61 @@ > throw new AbortType(this.compilationResult, problem); > default : > throw new AbortMethod(this.compilationResult, problem); >+ } >+ } >+ >+ /** >+ * Materialize a null annotation that has been added from the current default, >+ * in order to ensure that this annotation will be generated into the .class file, too. >+ */ >+ public void addNullnessAnnotation(ReferenceBinding annotationBinding) { >+ this.annotations = addAnnotation(this, this.annotations, annotationBinding); >+ } >+ >+ /** >+ * Materialize a null parameter annotation that has been added from the current default, >+ * in order to ensure that this annotation will be generated into the .class file, too. >+ */ >+ public void addParameterNonNullAnnotation(int i, ReferenceBinding annotationBinding) { >+ Argument argument = this.arguments[i]; >+ if (argument.type != null) // null happens for constructors of anonymous classes >+ argument.annotations = addAnnotation(argument.type, argument.annotations, annotationBinding); >+ } >+ >+ private Annotation[] addAnnotation(ASTNode location, Annotation[] oldAnnotations, ReferenceBinding annotationBinding) { >+ long pos = ((long)location.sourceStart<<32) + location.sourceEnd; >+ long[] poss = new long[annotationBinding.compoundName.length]; >+ Arrays.fill(poss, pos); >+ MarkerAnnotation annotation = new MarkerAnnotation(new QualifiedTypeReference(annotationBinding.compoundName, poss), location.sourceStart); >+ annotation.declarationSourceEnd = location.sourceEnd; >+ annotation.resolvedType = annotationBinding; >+ annotation.bits = IsSynthetic; >+ if (oldAnnotations == null) { >+ oldAnnotations = new Annotation[] {annotation}; >+ } else { >+ int len = oldAnnotations.length; >+ System.arraycopy(oldAnnotations, 0, oldAnnotations=new Annotation[len+1], 1, len); >+ oldAnnotations[0] = annotation; >+ } >+ return oldAnnotations; >+ } >+ >+ /** >+ * When a method is accessed via SourceTypeBinding.resolveTypesFor(MethodBinding) >+ * we create the argument binding and resolve annotations in order to compute null annotation tagbits. >+ */ >+ public void createArgumentBindings() { >+ if (this.arguments != null && this.binding != null) { >+ for (int i = 0, length = this.arguments.length; i < length; i++) { >+ Argument argument = this.arguments[i]; >+ argument.createBinding(this.scope, this.binding.parameters[i]); >+ // createBinding() has resolved annotations, now transfer nullness info from the argument to the method: >+ if ((argument.binding.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) != 0) { >+ if (this.binding.parameterNonNullness == null) >+ this.binding.parameterNonNullness = new Boolean[this.arguments.length]; >+ this.binding.parameterNonNullness[i] = Boolean.valueOf((argument.binding.tagBits & TagBits.AnnotationNonNull) != 0); >+ } >+ } > } > } > >@@ -138,6 +197,21 @@ > bindingIndex++; > } > } >+ } >+ } >+ } >+ } >+ >+ void analyseArgumentNullity(FlowInfo info) { >+ if (this.arguments != null && this.binding.parameterNonNullness != null) { >+ for (int i = 0, count = this.arguments.length; i < count; i++) { >+ // leverage null-info from parameter annotations: >+ Boolean nonNullNess = this.binding.parameterNonNullness[i]; >+ if (nonNullNess != null) { >+ if (nonNullNess.booleanValue()) >+ info.markAsDefinitelyNonNull(this.arguments[i].binding); >+ else >+ info.markPotentiallyNullBit(this.arguments[i].binding); > } > } > } >@@ -415,6 +489,7 @@ > bindThrownExceptions(); > resolveJavadoc(); > resolveAnnotations(this.scope, this.annotations, this.binding); >+ validateAnnotations(); > resolveStatements(); > // check @Deprecated annotation presence > if (this.binding != null >@@ -478,4 +553,17 @@ > public TypeParameter[] typeParameters() { > return null; > } >+ >+ void validateAnnotations() { >+ // null annotations on parameters? >+ if (this.binding != null && this.binding.parameterNonNullness != null) { >+ for (int i=0; i<this.binding.parameters.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); >+ } >+ } >+ } >+ } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java >index 6baa903..0175d6f 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java >@@ -11,6 +11,7 @@ > * bug 236385 - [compiler] Warn for potential programming problem if an object is created but not used > * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -53,6 +54,7 @@ > this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); > } > } >+ analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); > } > // record some dependency information for exception types > ReferenceBinding[] thrownExceptions; >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 d19c24c..bc76ad7 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 >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -173,6 +174,22 @@ > case TypeIds.T_JavaxAnnotationPreDestroy : > tagBits |= TagBits.AnnotationPreDestroy; > break; >+ case TypeIds.T_ConfiguredAnnotationNullable : >+ tagBits |= TagBits.AnnotationNullable; >+ break; >+ case TypeIds.T_ConfiguredAnnotationNonNull : >+ tagBits |= TagBits.AnnotationNonNull; >+ break; >+ case TypeIds.T_ConfiguredAnnotationNonNullByDefault : >+ if (valueAttribute != null >+ && valueAttribute.value instanceof FalseLiteral) >+ { >+ // parameter 'false' means: this annotation cancels any defaults >+ tagBits |= TagBits.AnnotationNullUnspecifiedByDefault; >+ break; >+ } >+ tagBits |= TagBits.AnnotationNonNullByDefault; >+ break; > } > return tagBits; > } >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 acad2e6..2a226ca 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 >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -30,7 +31,25 @@ > this.bits |= (IsLocalDeclarationReachable | IsArgument); > } > >+ public void 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); >+ } else if (!this.binding.type.isValidBinding()) { >+ AbstractMethodDeclaration methodDecl = scope.referenceMethod(); >+ if (methodDecl != null) { >+ MethodBinding methodBinding = methodDecl.binding; >+ if (methodBinding != null) { >+ methodBinding.tagBits |= TagBits.HasUnresolvedArguments; >+ } >+ } >+ } >+ resolveAnnotations(scope, this.annotations, this.binding); >+ this.binding.declaration = this; >+ } >+ > public void bind(MethodScope scope, TypeBinding typeBinding, boolean used) { >+ 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*/); >@@ -51,23 +70,9 @@ > } > scope.problemReporter().localVariableHiding(this, existingVariable, isSpecialArgument); > } >- } >- >- if (this.binding == null) { >- this.binding = new LocalVariableBinding(this, typeBinding, this.modifiers, true); >- } else if (!this.binding.type.isValidBinding()) { >- AbstractMethodDeclaration methodDecl = scope.referenceMethod(); >- if (methodDecl != null) { >- MethodBinding methodBinding = methodDecl.binding; >- if (methodBinding != null) { >- methodBinding.tagBits |= TagBits.HasUnresolvedArguments; >- } >- } >- } >+ } > scope.addLocalVariable(this.binding); >- resolveAnnotations(scope, this.annotations, this.binding); > //true stand for argument instead of just local >- this.binding.declaration = this; > this.binding.useFlag = used ? LocalVariableBinding.USED : LocalVariableBinding.UNUSED; > } > >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java >index 6a8fa14..540b958 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java >@@ -13,6 +13,7 @@ > * bug 292478 - Report potentially null across variable assignment > * bug 335093 - [compiler][null] minimal hook for future null annotation support > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -65,7 +66,7 @@ > FlowContext.CAN_ONLY_NULL | FlowContext.IN_ASSIGNMENT, flowInfo); > } > } >- nullStatus = checkAgainstNullAnnotation(currentScope, local, nullStatus); >+ nullStatus = checkAssignmentAgainstNullAnnotation(currentScope, flowContext, local, nullStatus, this.expression); > if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { > flowInfo.markNullStatus(local, nullStatus); > if (flowContext.initsOnFinally != null) >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 8de8919..52a4398 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 >@@ -10,6 +10,7 @@ > * Stephan Herrmann - Contributions for > * bug 343713 - [compiler] bogus line number in constructor of inner class in 1.5 compliance > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -113,9 +114,19 @@ > } > } > >- // tag parameters as being set > if (this.arguments != null) { > for (int i = 0, count = this.arguments.length; i < count; i++) { >+ if (this.binding.parameterNonNullness != null) { >+ // leverage null-info from parameter annotations: >+ Boolean nonNullNess = this.binding.parameterNonNullness[i]; >+ if (nonNullNess != null) { >+ if (nonNullNess.booleanValue()) >+ flowInfo.markAsDefinitelyNonNull(this.arguments[i].binding); >+ else >+ flowInfo.markPotentiallyNullBit(this.arguments[i].binding); >+ } >+ } >+ // tag parameters as being set > flowInfo.markAsDefinitelyAssigned(this.arguments[i].binding); > } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java >index a9ce3fc..851cd6e 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/EqualExpression.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2010 IBM Corporation and others. >+ * Copyright (c) 2000, 2011 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -23,6 +24,21 @@ > super(left,right,operator); > } > private void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, FlowInfo initsWhenTrue, FlowInfo initsWhenFalse) { >+ // check sends to null-annotated methods: >+ MessageSend leftMessage = (this.left instanceof MessageSend) ? (MessageSend) this.left : null; >+ if ( leftMessage != null >+ && leftMessage.nullStatus(flowInfo) == FlowInfo.NON_NULL >+ && this.right.nullStatus(flowInfo) == FlowInfo.NULL) >+ { >+ scope.problemReporter().messageSendRedundantCheckOnNonNull(leftMessage.binding, leftMessage); >+ } >+ MessageSend rightMessage = (this.right instanceof MessageSend) ? (MessageSend) this.right : null; >+ if ( rightMessage != null >+ && rightMessage.nullStatus(flowInfo) == FlowInfo.NON_NULL >+ && this.left.nullStatus(flowInfo) == FlowInfo.NULL) >+ { >+ scope.problemReporter().messageSendRedundantCheckOnNonNull(rightMessage.binding, rightMessage); >+ } > > LocalVariableBinding local = this.left.localVariableBinding(); > if (local != null && (local.type.tagBits & TagBits.IsBaseType) == 0) { >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java >index ba2219f..eecc1a0 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - Contribution for bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE >+ * Stephan Herrmann - Contributions for >+ * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -81,6 +83,7 @@ > this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); > } > } >+ analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); > } > > ReferenceBinding[] thrownExceptions; >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java >index 2d0066b..20d8913 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java >@@ -12,6 +12,7 @@ > * bug 292478 - Report potentially null across variable assignment > * bug 335093 - [compiler][null] minimal hook for future null annotation support > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -85,7 +86,7 @@ > this.bits &= ~FirstAssignmentToLocal; // int i = (i = 0); > } > flowInfo.markAsDefinitelyAssigned(this.binding); >- nullStatus = checkAgainstNullAnnotation(currentScope, this.binding, nullStatus); >+ nullStatus = checkAssignmentAgainstNullAnnotation(currentScope, flowContext, this.binding, nullStatus, this.initialization); > if ((this.binding.type.tagBits & TagBits.IsBaseType) == 0) { > flowInfo.markNullStatus(this.binding, nullStatus); > // no need to inform enclosing try block since its locals won't get >@@ -250,6 +251,7 @@ > } > // only resolve annotation at the end, for constant to be positioned before (96991) > resolveAnnotations(scope, this.annotations, this.binding); >+ scope.validateNullAnnotation(this.binding.tagBits, this.type, this.annotations); > } > > public void traverse(ASTVisitor visitor, BlockScope scope) { >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java >index a083742..0902e21 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2010 IBM Corporation and others. >+ * Copyright (c) 2000, 2011 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -41,6 +42,9 @@ > if (value instanceof ArrayInitializer) { > value.bits |= IsAnnotationDefaultValue; > } >+ if (value instanceof NameReference) { >+ value.bits |= IsMemberValueReference; >+ } > } > > /* (non-Javadoc) >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 795a345..4694a2e 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 >@@ -11,6 +11,7 @@ > * Stephan Herrmann - Contributions for > * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -99,6 +100,7 @@ > flowInfo = FakedTrackingVariable.markPassedToOutside(currentScope, this.arguments[i], flowInfo); > flowInfo = this.arguments[i].analyseCode(currentScope, flowContext, flowInfo).unconditionalInits(); > } >+ analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); > } > ReferenceBinding[] thrownExceptions; > if ((thrownExceptions = this.binding.thrownExceptions) != Binding.NO_EXCEPTIONS) { >@@ -114,6 +116,11 @@ > } > manageSyntheticAccessIfNecessary(currentScope, flowInfo); > return flowInfo; >+} >+public void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) { >+ super.checkNPE(scope, flowContext, flowInfo); >+ if (nullStatus(flowInfo) == FlowInfo.POTENTIALLY_NULL) >+ scope.problemReporter().messageSendPotentialNullReference(this.binding, this); > } > /** > * @see org.eclipse.jdt.internal.compiler.ast.Expression#computeConversion(org.eclipse.jdt.internal.compiler.lookup.Scope, org.eclipse.jdt.internal.compiler.lookup.TypeBinding, org.eclipse.jdt.internal.compiler.lookup.TypeBinding) >@@ -268,9 +275,16 @@ > } > } > public int nullStatus(FlowInfo flowInfo) { >+ 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; >+ if ((tagBits & TagBits.AnnotationNonNull) != 0) >+ return FlowInfo.NON_NULL; >+ if ((tagBits & TagBits.AnnotationNullable) != 0) >+ return FlowInfo.POTENTIALLY_NULL; >+ } > return FlowInfo.UNKNOWN; > } >- > /** > * @see org.eclipse.jdt.internal.compiler.ast.Expression#postConversionType(Scope) > */ >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 5a5c24c..74e9684 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 >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - Contribution for bug 349326 - [1.7] new warning for missing try-with-resources >+ * Stephan Herrmann - Contributions for >+ * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -79,9 +81,19 @@ > this.scope, > FlowInfo.DEAD_END); > >- // tag parameters as being set > if (this.arguments != null) { > for (int i = 0, count = this.arguments.length; i < count; i++) { >+ if (this.binding.parameterNonNullness != null) { >+ // leverage null-info from parameter annotations: >+ Boolean nonNullNess = this.binding.parameterNonNullness[i]; >+ if (nonNullNess != null) { >+ if (nonNullNess.booleanValue()) >+ flowInfo.markAsDefinitelyNonNull(this.arguments[i].binding); >+ else >+ flowInfo.markPotentiallyNullBit(this.arguments[i].binding); >+ } >+ } >+ // tag parameters as being set: > flowInfo.markAsDefinitelyAssigned(this.arguments[i].binding); > // if this method uses a type parameter declared by the declaring class, > // it can't be static. https://bugs.eclipse.org/bugs/show_bug.cgi?id=318682 >@@ -307,4 +319,11 @@ > public TypeParameter[] typeParameters() { > return this.typeParameters; > } >+ >+ void validateAnnotations() { >+ super.validateAnnotations(); >+ // null-annotations on the return type? >+ if (this.binding != null) >+ this.scope.validateNullAnnotation(this.binding.tagBits, this.returnType, this.annotations); >+ } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java >index 93bf5b5..3a2a0ed 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java >@@ -10,6 +10,7 @@ > * Stephan Herrmann - Contributions for > * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -86,6 +87,7 @@ > this.arguments[i].checkNPE(currentScope, flowContext, flowInfo); > } > } >+ analyseArguments(currentScope, flowContext, flowInfo, this.binding, this.arguments); > } > > // analyse the anonymous nested type >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >index 1b4a530..330cafd 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contribution for bug 185682 - Increment/decrement operators mark local variables as read >+ * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for >+ * bug 185682 - Increment/decrement operators mark local variables as read >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -963,7 +965,11 @@ > && methodScope.lastVisibleFieldID >= 0 > && fieldBinding.id >= methodScope.lastVisibleFieldID > && (!fieldBinding.isStatic() || methodScope.isStatic)) { >- scope.problemReporter().forwardReference(this, this.indexOfFirstFieldBinding-1, fieldBinding); >+ if ((this.bits & IsMemberValueReference) != 0 && fieldBinding.id == methodScope.lastVisibleFieldID) { >+ // false alarm, location is NOT a field initializer but the value in a memberValuePair >+ } else { >+ scope.problemReporter().forwardReference(this, this.indexOfFirstFieldBinding-1, fieldBinding); >+ } > } > if (isFieldUseDeprecated(fieldBinding, scope, this.indexOfFirstFieldBinding == this.tokens.length ? this.bits : 0)) { > scope.problemReporter().deprecatedField(fieldBinding, this); >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 520cf38..23db3e9 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 >@@ -11,6 +11,7 @@ > * bug 319201 - [null] no warning when unboxing SingleNameReference causes NPE > * bug 349326 - [1.7] new warning for missing try-with-resources > * bug 360328 - [compiler][null] detect null problems in nested code (local class inside a loop) >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -44,6 +45,8 @@ > if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0) { > this.expression.checkNPE(currentScope, flowContext, flowInfo); > } >+ if (flowInfo.reachMode() == FlowInfo.REACHABLE) >+ checkAgainstNullAnnotation(currentScope, this.expression.nullStatus(flowInfo)); > FakedTrackingVariable trackingVariable = FakedTrackingVariable.getCloseTrackingVariable(this.expression); > if (trackingVariable != null) { > if (methodScope != trackingVariable.methodScope) >@@ -119,6 +122,23 @@ > currentScope.checkUnclosedCloseables(flowInfo, this, currentScope); > return FlowInfo.DEAD_END; > } >+void checkAgainstNullAnnotation(BlockScope scope, int nullStatus) { >+ 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; >+ try { >+ methodBinding = scope.methodScope().referenceMethod().binding; >+ tagBits = methodBinding.tagBits; >+ } catch (NullPointerException npe) { >+ return; >+ } >+ if ((tagBits & TagBits.AnnotationNonNull) != 0) { >+ char[][] annotationName = scope.environment().getNonNullAnnotationName(); >+ scope.problemReporter().nullityMismatch(this.expression, methodBinding.returnType, nullStatus, annotationName); >+ } >+ } >+} > > /** > * Retrun statement code generation >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 c723aff..76029c5 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 >@@ -10,6 +10,7 @@ > * Stephan Herrmann - Contributions for > * bug 335093 - [compiler][null] minimal hook for future null annotation support > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.ast; > >@@ -57,8 +58,35 @@ > public static final int COMPLAINED_FAKE_REACHABLE = 1; > public static final int COMPLAINED_UNREACHABLE = 2; > >-/** Empty hook for checking null status against declaration using null annotations, once this will be supported. */ >-protected int checkAgainstNullAnnotation(BlockScope currentScope, LocalVariableBinding local, int nullStatus) { >+ >+/** Analysing arguments of MessageSend, ExplicitConstructorCall, AllocationExpression. */ >+protected void analyseArguments(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo, MethodBinding methodBinding, Expression[] arguments) >+{ >+ // compare actual null-status against parameter annotations of the called method: >+ if (arguments != null && methodBinding.parameterNonNullness != null) { >+ for (int i = 0; i < arguments.length; i++) { >+ if (methodBinding.parameterNonNullness[i] == Boolean.TRUE) { >+ TypeBinding expectedType = methodBinding.parameters[i]; >+ Expression argument = arguments[i]; >+ int nullStatus = argument.nullStatus(flowInfo); // 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, nullStatus, expectedType); >+ } >+ } >+ } >+} >+ >+/** Check null-ness of 'local' against a possible null annotation */ >+protected int checkAssignmentAgainstNullAnnotation(BlockScope currentScope, FlowContext flowContext, >+ LocalVariableBinding local, int nullStatus, Expression expression) >+{ >+ if ( local != null >+ && (local.tagBits & TagBits.AnnotationNonNull) != 0 >+ && nullStatus != FlowInfo.NON_NULL) >+ { >+ flowContext.recordNullityMismatch(currentScope, expression, nullStatus, local.type); >+ nullStatus=FlowInfo.NON_NULL; >+ } > return nullStatus; > } > >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >index 7f7638d..261c8df 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FinallyFlowContext.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.flow; > >@@ -17,6 +18,7 @@ > import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; > import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; > import org.eclipse.jdt.internal.compiler.lookup.Scope; >+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; > import org.eclipse.jdt.internal.compiler.lookup.TypeIds; > import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; > >@@ -83,8 +85,13 @@ > // check inconsistent null checks > if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // within an enclosing loop, be conservative > for (int i = 0; i < this.nullCount; i++) { >- this.parent.recordUsingNullReference(scope, this.nullLocals[i], >- this.nullReferences[i], this.nullCheckTypes[i], flowInfo); >+ if (this.nullCheckTypes[i] == ASSIGN_TO_NONNULL) >+ this.parent.recordNullityMismatch(scope, this.nullReferences[i], >+ flowInfo.nullStatus(this.nullLocals[i]), this.expectedTypes[i]); >+ else >+ this.parent.recordUsingNullReference(scope, this.nullLocals[i], >+ this.nullReferences[i], this.nullCheckTypes[i], flowInfo); >+ > } > } > else { // no enclosing loop, be as precise as possible right now >@@ -165,6 +172,13 @@ > } > if (flowInfo.isPotentiallyNull(local)) { > scope.problemReporter().localVariablePotentialNullReference(local, expression); >+ } >+ break; >+ case ASSIGN_TO_NONNULL: >+ int nullStatus = flowInfo.nullStatus(local); >+ if (nullStatus != FlowInfo.NON_NULL) { >+ char[][] annotationName = scope.environment().getNonNullAnnotationName(); >+ scope.problemReporter().nullityMismatch(expression, this.expectedTypes[i], nullStatus, annotationName); > } > break; > default: >@@ -442,4 +456,14 @@ > this.nullReferences[this.nullCount] = expression; > this.nullCheckTypes[this.nullCount++] = status; > } >+protected boolean internalRecordNullityMismatch(Expression expression, int nullStatus, TypeBinding expectedType, int checkType) { >+ // cf. decision structure inside FinallyFlowContext.recordUsingNullReference(..) >+ if (nullStatus == FlowInfo.UNKNOWN || >+ ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0 && nullStatus != FlowInfo.NULL)) { >+ recordExpectedType(expectedType, this.nullCount); >+ recordNullReference(expression.localVariableBinding(), expression, checkType); >+ return true; >+ } >+ return false; >+} > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java >index ccb5fb6..02756df 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - Contribution for bug 358827 - [1.7] exception analysis for t-w-r spoils null analysis >+ * Stephan Herrmann - Contributions for >+ * bug 358827 - [1.7] exception analysis for t-w-r spoils null analysis >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.flow; > >@@ -52,6 +54,10 @@ > // any null related operation happening within the try block > > public int tagBits; >+ >+ // array to store the expected type from the potential error location (for display in error messages): >+ public TypeBinding[] expectedTypes = null; >+ > public static final int DEFER_NULL_DIAGNOSTIC = 0x1; > public static final int PREEMPT_NULL_DIAGNOSTIC = 0x2; > /** >@@ -66,6 +72,8 @@ > public static final int CAN_ONLY_NON_NULL = 0x0002; > //check against non null, with definite values -- comparisons > public static final int MAY_NULL = 0x0003; >+//check binding a value to a @NonNull variable >+public final static int ASSIGN_TO_NONNULL = 0x0080; > // check against null, with potential values -- NPE guard > public static final int CHECK_MASK = 0x00FF; > public static final int IN_COMPARISON_NULL = 0x0100; >@@ -548,6 +556,21 @@ > // default implementation: do nothing > } > >+protected void recordExpectedType(TypeBinding expectedType, int nullCount) { >+ if (nullCount == 0) { >+ this.expectedTypes = new TypeBinding[5]; >+ } else if (this.expectedTypes == null) { >+ int size = 5; >+ while (size <= nullCount) size *= 2; >+ this.expectedTypes = new TypeBinding[size]; >+ } >+ else if (nullCount == this.expectedTypes.length) { >+ System.arraycopy(this.expectedTypes, 0, >+ this.expectedTypes = new TypeBinding[nullCount * 2], 0, nullCount); >+ } >+ this.expectedTypes[nullCount] = expectedType; >+} >+ > protected boolean recordFinalAssignment(VariableBinding variable, Reference finalReference) { > return true; // keep going > } >@@ -746,4 +769,31 @@ > buffer.append(individualToString()).append('\n'); > return buffer.toString(); > } >+ >+/** >+ * Record that a nullity mismatch was detected against an annotated type reference. >+ * @param currentScope scope for error reporting >+ * @param expression the expression violating the specification >+ * @param nullStatus the null status of expression at the current location >+ * @param expectedType the declared type of the spec'ed variable, for error reporting. >+ */ >+public void recordNullityMismatch(BlockScope currentScope, Expression expression, int nullStatus, TypeBinding expectedType) { >+ if (expression.localVariableBinding() != null) { // flowContext cannot yet handle non-localvar expressions (e.g., fields) >+ // find the inner-most flowContext that might need deferred handling: >+ FlowContext currentContext = this; >+ while (currentContext != null) { >+ // some flow contexts implement deferred checking, should we participate in that? >+ if (currentContext.internalRecordNullityMismatch(expression, nullStatus, expectedType, ASSIGN_TO_NONNULL)) >+ return; >+ currentContext = currentContext.parent; >+ } >+ } >+ // no reason to defer, so report now: >+ char[][] annotationName = currentScope.environment().getNonNullAnnotationName(); >+ currentScope.problemReporter().nullityMismatch(expression, expectedType, nullStatus, annotationName); >+} >+protected boolean internalRecordNullityMismatch(Expression expression, int nullStatus, TypeBinding expectedType, int checkType) { >+ // nop, to be overridden in subclasses >+ return false; // not recorded >+} > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >index 919bb4c..38295c8 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/LoopingFlowContext.java >@@ -7,11 +7,14 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - contribution for Bug 336428 - [compiler][null] bogus warning "redundant null check" in condition of do {} while() loop >+ * Stephan Herrmann - contributions for >+ * bug 336428 - [compiler][null] bogus warning "redundant null check" in condition of do {} while() loop >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.flow; > > import java.util.ArrayList; >+ > import org.eclipse.jdt.internal.compiler.ast.ASTNode; > import org.eclipse.jdt.internal.compiler.ast.Expression; > import org.eclipse.jdt.internal.compiler.ast.Reference; >@@ -21,6 +24,7 @@ > import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; > import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; > import org.eclipse.jdt.internal.compiler.lookup.Scope; >+import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; > import org.eclipse.jdt.internal.compiler.lookup.TypeIds; > import org.eclipse.jdt.internal.compiler.lookup.VariableBinding; > >@@ -246,6 +250,9 @@ > continue; > } > break; >+ case ASSIGN_TO_NONNULL: >+ this.parent.recordNullityMismatch(scope, expression, flowInfo.nullStatus(local), this.expectedTypes[i]); >+ break; > default: > // never happens > } >@@ -337,6 +344,13 @@ > this.nullReferences[i] = null; > scope.problemReporter().localVariablePotentialNullReference(local, expression); > continue; >+ } >+ break; >+ case ASSIGN_TO_NONNULL: >+ int nullStatus = flowInfo.nullStatus(local); >+ if (nullStatus != FlowInfo.NON_NULL) { >+ char[][] annotationName = scope.environment().getNonNullAnnotationName(); >+ scope.problemReporter().nullityMismatch(expression, this.expectedTypes[i], nullStatus, annotationName); > } > break; > default: >@@ -667,4 +681,10 @@ > public boolean hasEscapingExceptions() { > return this.escapingExceptionCatchSites != null; > } >+ >+ protected boolean internalRecordNullityMismatch(Expression expression, int nullStatus, TypeBinding expectedType, int checkType) { >+ recordExpectedType(expectedType, this.nullCount); >+ recordNullReference(expression.localVariableBinding(), expression, checkType); >+ return true; >+ } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java >index b269d89..b949de4 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java >@@ -12,6 +12,7 @@ > * bug 236385 - [compiler] Warn for potential programming problem if an object is created but not used > * bug 295551 - Add option to automatically promote all warnings to errors > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.impl; > >@@ -26,6 +27,7 @@ > import org.eclipse.jdt.internal.compiler.ast.ASTNode; > import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; > import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; >+import org.eclipse.jdt.internal.compiler.lookup.TagBits; > import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities; > import org.eclipse.jdt.internal.compiler.util.Util; > >@@ -142,6 +144,19 @@ > public static final String OPTION_ReportUnclosedCloseable = "org.eclipse.jdt.core.compiler.problem.unclosedCloseable"; //$NON-NLS-1$ > public static final String OPTION_ReportPotentiallyUnclosedCloseable = "org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable"; //$NON-NLS-1$ > public static final String OPTION_ReportExplicitlyClosedAutoCloseable = "org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable"; //$NON-NLS-1$ >+ public static final String OPTION_ReportNullSpecViolation = "org.eclipse.jdt.core.compiler.problem.nullSpecViolation"; //$NON-NLS-1$ >+ public static final String OPTION_ReportPotentialNullSpecViolation = "org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation"; //$NON-NLS-1$ >+ public static final String OPTION_ReportNullSpecInsufficientInfo = "org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo"; //$NON-NLS-1$ >+ public static final String OPTION_ReportRedundantNullAnnotation = "org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation"; //$NON-NLS-1$ >+ public static final String OPTION_AnnotationBasedNullAnalysis = "org.eclipse.jdt.core.compiler.annotation.nullanalysis"; //$NON-NLS-1$ >+ public static final String OPTION_NullableAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nullable"; //$NON-NLS-1$ >+ public static final String OPTION_NonNullAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nonnull"; //$NON-NLS-1$ >+ public static final String OPTION_NonNullByDefaultAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nonnullbydefault"; //$NON-NLS-1$ >+ // defaults for the above: >+ static final char[][] DEFAULT_NULLABLE_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.Nullable".toCharArray()); //$NON-NLS-1$ >+ static final char[][] DEFAULT_NONNULL_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNull".toCharArray()); //$NON-NLS-1$ >+ static final char[][] DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.NonNullByDefault".toCharArray()); //$NON-NLS-1$ >+ public static final String OPTION_NonNullIsDefault = "org.eclipse.jdt.core.compiler.annotation.nonnullisdefault"; //$NON-NLS-1$ > /** > * Possible values for configurable options > */ >@@ -245,10 +260,13 @@ > public static final int MethodCanBeStatic = IrritantSet.GROUP2 | ASTNode.Bit5; > public static final int MethodCanBePotentiallyStatic = IrritantSet.GROUP2 | ASTNode.Bit6; > public static final int RedundantSpecificationOfTypeArguments = IrritantSet.GROUP2 | ASTNode.Bit7; >- // bits 8-10 reserved for https://bugs.eclipse.org/bugs/show_bug.cgi?id=186342 >- public static final int UnclosedCloseable = IrritantSet.GROUP2 | ASTNode.Bit11; >- public static final int PotentiallyUnclosedCloseable = IrritantSet.GROUP2 | ASTNode.Bit12; >- public static final int ExplicitlyClosedAutoCloseable = IrritantSet.GROUP2 | ASTNode.Bit13; >+ public static final int UnclosedCloseable = IrritantSet.GROUP2 | ASTNode.Bit8; >+ public static final int PotentiallyUnclosedCloseable = IrritantSet.GROUP2 | ASTNode.Bit9; >+ public static final int ExplicitlyClosedAutoCloseable = IrritantSet.GROUP2 | ASTNode.Bit10; >+ public static final int NullSpecViolation = IrritantSet.GROUP2 | ASTNode.Bit11; >+ public static final int PotentialNullSpecViolation = IrritantSet.GROUP2 | ASTNode.Bit12; >+ public static final int NullSpecInsufficientInfo = IrritantSet.GROUP2 | ASTNode.Bit13; >+ public static final int RedundantNullAnnotation = IrritantSet.GROUP2 | ASTNode.Bit14; > > // Severity level for handlers > /** >@@ -370,6 +388,18 @@ > public boolean includeNullInfoFromAsserts; > /** Controls whether forced generic type problems get reported */ > public boolean reportUnavoidableGenericTypeProblems; >+ >+ // === Support for Null Annotations: === >+ /** Master switch for null analysis based on annotations: */ >+ public boolean isAnnotationBasedNullAnalysisEnabled; >+ /** Fully qualified name of annotation to use as marker for nullable types. */ >+ public char[][] nullableAnnotationName; >+ /** Fully qualified name of annotation to use as marker for nonnull types. */ >+ public char[][] nonNullAnnotationName; >+ /** Fully qualified name of annotation to use as marker for default nonnull. */ >+ public char[][] nonNullByDefaultAnnotationName; >+ /** TagBits-encoded default for non-annotated types. */ >+ public long defaultNonNullness; // 0 or TagBits#AnnotationNonNull > > // keep in sync with warningTokenToIrritant and warningTokenFromIrritant > public final static String[] warningTokens = { >@@ -567,6 +597,14 @@ > return OPTION_ReportPotentiallyUnclosedCloseable; > case ExplicitlyClosedAutoCloseable : > return OPTION_ReportExplicitlyClosedAutoCloseable; >+ case NullSpecViolation : >+ return OPTION_ReportNullSpecViolation; >+ case PotentialNullSpecViolation : >+ return OPTION_ReportPotentialNullSpecViolation; >+ case NullSpecInsufficientInfo : >+ return OPTION_ReportNullSpecInsufficientInfo; >+ case RedundantNullAnnotation : >+ return OPTION_ReportRedundantNullAnnotation; > } > return null; > } >@@ -733,6 +771,14 @@ > OPTION_ReportUnclosedCloseable, > OPTION_ReportPotentiallyUnclosedCloseable, > OPTION_ReportExplicitlyClosedAutoCloseable, >+ OPTION_AnnotationBasedNullAnalysis, >+ OPTION_NonNullAnnotationName, >+ OPTION_NullableAnnotationName, >+ OPTION_NonNullByDefaultAnnotationName, >+ OPTION_NonNullIsDefault, >+ OPTION_ReportNullSpecViolation, >+ OPTION_ReportPotentialNullSpecViolation, >+ OPTION_ReportRedundantNullAnnotation > }; > return result; > } >@@ -795,6 +841,10 @@ > case NullReference : > case PotentialNullReference : > case RedundantNullCheck : >+ case NullSpecViolation : >+ case PotentialNullSpecViolation : >+ case NullSpecInsufficientInfo : >+ case RedundantNullAnnotation : > return "null"; //$NON-NLS-1$ > case FallthroughCase : > return "fallthrough"; //$NON-NLS-1$ >@@ -1008,6 +1058,18 @@ > optionsMap.put(OPTION_ReportUnclosedCloseable, getSeverityString(UnclosedCloseable)); > optionsMap.put(OPTION_ReportPotentiallyUnclosedCloseable, getSeverityString(PotentiallyUnclosedCloseable)); > optionsMap.put(OPTION_ReportExplicitlyClosedAutoCloseable, getSeverityString(ExplicitlyClosedAutoCloseable)); >+ optionsMap.put(OPTION_AnnotationBasedNullAnalysis, this.isAnnotationBasedNullAnalysisEnabled ? ENABLED : DISABLED); >+ optionsMap.put(OPTION_ReportNullSpecViolation, getSeverityString(NullSpecViolation)); >+ optionsMap.put(OPTION_ReportPotentialNullSpecViolation, getSeverityString(PotentialNullSpecViolation)); >+ optionsMap.put(OPTION_ReportNullSpecInsufficientInfo, getSeverityString(NullSpecInsufficientInfo)); >+ optionsMap.put(OPTION_ReportRedundantNullAnnotation, getSeverityString(RedundantNullAnnotation)); >+ optionsMap.put(OPTION_NullableAnnotationName, String.valueOf(CharOperation.concatWith(this.nullableAnnotationName, '.'))); >+ optionsMap.put(OPTION_NonNullAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullAnnotationName, '.'))); >+ optionsMap.put(OPTION_NonNullByDefaultAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullByDefaultAnnotationName, '.'))); >+ if (this.defaultNonNullness == TagBits.AnnotationNonNull) >+ optionsMap.put(OPTION_NonNullIsDefault, CompilerOptions.ENABLED); >+ else >+ optionsMap.put(OPTION_NonNullIsDefault, CompilerOptions.DISABLED); > return optionsMap; > } > >@@ -1158,6 +1220,11 @@ > > // allow null info from asserts to be considered downstream by default > this.includeNullInfoFromAsserts = false; >+ >+ this.isAnnotationBasedNullAnalysisEnabled = false; >+ this.nullableAnnotationName = DEFAULT_NULLABLE_ANNOTATION_NAME; >+ this.nonNullAnnotationName = DEFAULT_NONNULL_ANNOTATION_NAME; >+ this.nonNullByDefaultAnnotationName = DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME; > } > > public void set(Map optionsMap) { >@@ -1441,6 +1508,30 @@ > if ((optionValue = optionsMap.get(OPTION_ReportUnclosedCloseable)) != null) updateSeverity(UnclosedCloseable, optionValue); > if ((optionValue = optionsMap.get(OPTION_ReportPotentiallyUnclosedCloseable)) != null) updateSeverity(PotentiallyUnclosedCloseable, optionValue); > if ((optionValue = optionsMap.get(OPTION_ReportExplicitlyClosedAutoCloseable)) != null) updateSeverity(ExplicitlyClosedAutoCloseable, optionValue); >+ if ((optionValue = optionsMap.get(OPTION_AnnotationBasedNullAnalysis)) != null) { >+ this.isAnnotationBasedNullAnalysisEnabled = ENABLED.equals(optionValue); >+ } >+ if (this.isAnnotationBasedNullAnalysisEnabled) { >+ if ((optionValue = optionsMap.get(OPTION_ReportNullSpecViolation)) != null) updateSeverity(NullSpecViolation, optionValue); >+ if ((optionValue = optionsMap.get(OPTION_ReportPotentialNullSpecViolation)) != null) updateSeverity(PotentialNullSpecViolation, optionValue); >+ if ((optionValue = optionsMap.get(OPTION_ReportNullSpecInsufficientInfo)) != null) updateSeverity(NullSpecInsufficientInfo, optionValue); >+ if ((optionValue = optionsMap.get(OPTION_ReportRedundantNullAnnotation)) != null) updateSeverity(RedundantNullAnnotation, optionValue); >+ if ((optionValue = optionsMap.get(OPTION_NullableAnnotationName)) != null) { >+ this.nullableAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray()); >+ } >+ if ((optionValue = optionsMap.get(OPTION_NonNullAnnotationName)) != null) { >+ this.nonNullAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray()); >+ } >+ if ((optionValue = optionsMap.get(OPTION_NonNullByDefaultAnnotationName)) != null) { >+ this.nonNullByDefaultAnnotationName = CharOperation.splitAndTrimOn('.', ((String)optionValue).toCharArray()); >+ } >+ if ((optionValue = optionsMap.get(OPTION_NonNullIsDefault)) != null) { >+ if (CompilerOptions.ENABLED.equals(optionValue)) >+ this.defaultNonNullness = TagBits.AnnotationNonNull; >+ else if (CompilerOptions.DISABLED.equals(optionValue)) >+ this.defaultNonNullness = 0; >+ } >+ } > > // Javadoc options > if ((optionValue = optionsMap.get(OPTION_DocCommentSupport)) != null) { >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java >index 38dd14f..e8ae215 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - Contribution for bug 349326 - [1.7] new warning for missing try-with-resources >+ * Stephan Herrmann - Contributions for >+ * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > > package org.eclipse.jdt.internal.compiler.impl; >@@ -102,8 +104,14 @@ > .set( > CompilerOptions.DeadCode > |CompilerOptions.Tasks >- |CompilerOptions.UnclosedCloseable); >- >+ |CompilerOptions.UnclosedCloseable >+ |CompilerOptions.NullSpecInsufficientInfo >+ |CompilerOptions.RedundantNullAnnotation); >+ // default errors IF AnnotationBasedNullAnalysis is enabled: >+ COMPILER_DEFAULT_ERRORS.set( >+ CompilerOptions.NullSpecViolation >+ |CompilerOptions.PotentialNullSpecViolation); >+ > ALL.setAll(); > HIDING > .set(CompilerOptions.FieldHiding) >@@ -111,7 +119,12 @@ > .set(CompilerOptions.TypeHiding); > NULL > .set(CompilerOptions.PotentialNullReference) >- .set(CompilerOptions.RedundantNullCheck); >+ .set(CompilerOptions.RedundantNullCheck) >+ .set(CompilerOptions.NullSpecViolation) >+ .set(CompilerOptions.PotentialNullSpecViolation) >+ .set(CompilerOptions.NullSpecInsufficientInfo) >+ .set(CompilerOptions.RedundantNullAnnotation); >+ > RESTRICTION.set(CompilerOptions.DiscouragedReference); > STATIC_ACCESS.set(CompilerOptions.NonStaticAccessToStatic); > UNUSED >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 b8581e3..a5452e2 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 >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - Contribution for bug 349326 - [1.7] new warning for missing try-with-resources >+ * Stephan Herrmann - Contributions for >+ * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -16,6 +18,7 @@ > import org.eclipse.jdt.core.compiler.CharOperation; > import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; > import org.eclipse.jdt.internal.compiler.env.*; >+import org.eclipse.jdt.internal.compiler.impl.BooleanConstant; > import org.eclipse.jdt.internal.compiler.impl.Constant; > import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; > import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; >@@ -383,6 +386,8 @@ > } > if (this.environment.globalOptions.storeAnnotations) > setAnnotations(createAnnotations(binaryType.getAnnotations(), this.environment, missingTypeNames)); >+ >+ scanTypeForNullAnnotation(binaryType); > } finally { > // protect against incorrect use of the needFieldsAndMethods flag, see 48459 > if (this.fields == null) >@@ -589,6 +594,9 @@ > // fixup the declaring element of the type variable > for (int i = 0, length = typeVars.length; i < length; i++) > typeVars[i].declaringElement = result; >+ >+ scanMethodForNullAnnotation(method, result); >+ > return result; > } > >@@ -1142,6 +1150,96 @@ > } > return this.storedAnnotations; > } >+void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding methodBinding) { >+ char[][] nullableAnnotationName = this.environment.getNullableAnnotationName(); >+ char[][] nonNullAnnotationName = this.environment.getNonNullAnnotationName(); >+ if (nullableAnnotationName == null || nonNullAnnotationName == null) >+ return; // not configured to use null annotations >+ >+ // return: >+ IBinaryAnnotation[] annotations = method.getAnnotations(); >+ if (annotations != null) { >+ for (int i = 0; i < annotations.length; i++) { >+ char[] annotationTypeName = annotations[i].getTypeName(); >+ if (annotationTypeName[0] != 'L') >+ continue; >+ char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' >+ if (CharOperation.equals(typeName, nonNullAnnotationName)) { >+ methodBinding.tagBits |= TagBits.AnnotationNonNull; >+ break; >+ } >+ if (CharOperation.equals(typeName, nullableAnnotationName)) { >+ methodBinding.tagBits |= TagBits.AnnotationNullable; >+ break; >+ } >+ } >+ } >+ >+ // parameters: >+ TypeBinding[] parameters = methodBinding.parameters; >+ for (int j = 0; j < parameters.length; j++) { >+ IBinaryAnnotation[] paramAnnotations = method.getParameterAnnotations(j); >+ if (paramAnnotations != null) { >+ for (int i = 0; i < paramAnnotations.length; i++) { >+ char[] annotationTypeName = paramAnnotations[i].getTypeName(); >+ if (annotationTypeName[0] != 'L') >+ continue; >+ char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' >+ if (CharOperation.equals(typeName, nonNullAnnotationName)) { >+ if (methodBinding.parameterNonNullness == null) >+ methodBinding.parameterNonNullness = new Boolean[parameters.length]; >+ methodBinding.parameterNonNullness[j] = Boolean.TRUE; >+ break; >+ } else if (CharOperation.equals(typeName, nullableAnnotationName)) { >+ if (methodBinding.parameterNonNullness == null) >+ methodBinding.parameterNonNullness = new Boolean[parameters.length]; >+ methodBinding.parameterNonNullness[j] = Boolean.FALSE; >+ break; >+ } >+ } >+ } >+ } >+} >+void scanTypeForNullAnnotation(IBinaryType binaryType) { >+ char[][] nonNullByDefaultAnnotationName = this.environment.getNonNullByDefaultAnnotationName(); >+ if (nonNullByDefaultAnnotationName == null) >+ return; // not configured to use null annotations >+ >+ IBinaryAnnotation[] annotations = binaryType.getAnnotations(); >+ if (annotations != null) { >+ long annotationBit = 0L; >+ TypeBinding defaultNullness = null; >+ for (int i = 0; i < annotations.length; i++) { >+ char[] annotationTypeName = annotations[i].getTypeName(); >+ if (annotationTypeName[0] != 'L') >+ continue; >+ char[][] typeName = CharOperation.splitOn('/', annotationTypeName, 1, annotationTypeName.length-1); // cut of leading 'L' and trailing ';' >+ if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) { >+ IBinaryElementValuePair[] elementValuePairs = annotations[i].getElementValuePairs(); >+ if (elementValuePairs != null && elementValuePairs.length == 1) { >+ Object value = elementValuePairs[0].getValue(); >+ if (value instanceof BooleanConstant >+ && !((BooleanConstant)value).booleanValue()) >+ { >+ // parameter is 'false': this means we cancel defaults from outer scopes: >+ annotationBit = TagBits.AnnotationNullUnspecifiedByDefault; >+ defaultNullness = ReferenceBinding.NULL_UNSPECIFIED; >+ break; >+ } >+ } >+ annotationBit = TagBits.AnnotationNonNullByDefault; >+ defaultNullness = this.environment.getNullAnnotationBinding(TagBits.AnnotationNonNull, false/*resolve*/); >+ break; >+ } >+ } >+ if (annotationBit != 0L) { >+ this.tagBits |= annotationBit; >+ if (CharOperation.equals(this.sourceName(), TypeConstants.PACKAGE_INFO_NAME)) >+ this.getPackage().nullnessDefaultAnnotation = defaultNullness; >+ } >+ } >+} >+ > /* Answer the receiver's superclass... null if the receiver is Object or an interface. > * > * NOTE: superclass of a binary type is resolved when needed >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java >index b163168..fe16519 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LocalVariableBinding.java >@@ -10,6 +10,7 @@ > * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for > * bug 185682 - Increment/decrement operators mark local variables as read > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -158,7 +159,7 @@ > annotations = new AnnotationBinding[length]; > for (int i = 0; i < length; i++) > annotations[i] = new AnnotationBinding(annotationNodes[i]); >- setAnnotations(annotations); >+ setAnnotations(annotations, this.declaringScope); > } > } > } >@@ -212,10 +213,12 @@ > this.initializationCount++; > } > >- public void setAnnotations(AnnotationBinding[] annotations) { >- if (this.declaringScope == null) return; >- >- SourceTypeBinding sourceType = this.declaringScope.enclosingSourceType(); >+ public void setAnnotations(AnnotationBinding[] annotations, Scope scope) { >+ // note: we don's use this.declaringScope because we might be called before Scope.addLocalVariable(this) >+ // which is where this.declaringScope is set. >+ if (scope == null) >+ return; >+ SourceTypeBinding sourceType = scope.enclosingSourceType(); > if (sourceType != null) > sourceType.storeAnnotations(this, annotations); > } >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 2e7fe97..710011a 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 >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - contribution for bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment >+ * Stephan Herrmann - contributions for >+ * bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -76,6 +78,9 @@ > public boolean isProcessingAnnotations = false; > public boolean mayTolerateMissingType = false; > >+ private char[][][] nullAnnotationPackageNames = null; // package parts of globalOptions.{nullableAnnotationName,nonNullAnnotationName,nonNullByDefaultAnnotationName} >+ private char[][] nullAnnotationSimpleNames = null; // type parts of globalOptions.{nullableAnnotationName,nonNullAnnotationName,nonNullByDefaultAnnotationName} >+ > final static int BUILD_FIELDS_AND_METHODS = 4; > final static int BUILD_TYPE_HIERARCHY = 1; > final static int CHECK_AND_SET_IMPORTS = 2; >@@ -83,6 +88,7 @@ > > static final ProblemPackageBinding TheNotFoundPackage = new ProblemPackageBinding(CharOperation.NO_CHAR, NotFound); > static final ProblemReferenceBinding TheNotFoundType = new ProblemReferenceBinding(CharOperation.NO_CHAR_CHAR, null, NotFound); >+ > > public LookupEnvironment(ITypeRequestor typeRequestor, CompilerOptions globalOptions, ProblemReporter problemReporter, INameEnvironment nameEnvironment) { > this.typeRequestor = typeRequestor; >@@ -1073,6 +1079,104 @@ > return packageBinding.getType0(compoundName[compoundName.length - 1]); > } > >+public char[][] getNullableAnnotationName() { >+ return this.globalOptions.nullableAnnotationName; >+} >+ >+public char[][] getNonNullAnnotationName() { >+ return this.globalOptions.nonNullAnnotationName; >+} >+ >+public char[][] getNonNullByDefaultAnnotationName() { >+ return this.globalOptions.nonNullByDefaultAnnotationName; >+} >+ >+/** >+ * Answer the type binding representing the null-annotation identified by the given tag bits. >+ * @param annotationTagBit tag bits potentially denoting a null-annotation >+ * @param resolve should the resulting type binding be resolved? >+ * @return the corresponding annotation type binding >+ * or <code>null</code> if no annotation bits are contained in the given tag bits. >+ */ >+public TypeBinding getNullAnnotationBinding(long annotationTagBit, boolean resolve) { >+ char[][] name = null; >+ if (annotationTagBit == TagBits.AnnotationNonNull) >+ name = getNonNullAnnotationName(); >+ else if (annotationTagBit == TagBits.AnnotationNullable) >+ name = getNullableAnnotationName(); >+ else >+ return null; >+ if (resolve) >+ return getType(name); >+ else >+ return getTypeFromCompoundName(name, false, false); >+} >+ >+/** >+ * Inspect the given tag bits and answer a corresponding null annotation type binding >+ * @param defaultTagBit tag bits representing the default applicable at the current code location >+ * @param resolve should the resulting type binding be resolved? >+ * @return the corresponding concrete annotation type binding (<code>@NonNull</code> or <code>@Nullable</code>) >+ * or <code>null</code> if no bits of a default-annotation are contained in the given tag bits. >+ */ >+public TypeBinding getNullAnnotationBindingFromDefault(long defaultTagBit, boolean resolve) { >+ if ((defaultTagBit & TagBits.AnnotationNullUnspecifiedByDefault) != 0) >+ return ReferenceBinding.NULL_UNSPECIFIED; >+ if ((defaultTagBit & TagBits.AnnotationNonNullByDefault) != 0) >+ return getNullAnnotationBinding(TagBits.AnnotationNonNull, resolve); >+ return null; >+} >+ >+/** answer the simple names of annotation types nullable/nonnull/nonnullbydefault if their package is packageName. */ >+char[][] getNullAnnotationNames(char[][] packageName) { >+ if (this.nullAnnotationPackageNames == null) { >+ // fetch and split names from globalOptions to local fields nullAnnotationPackageNames & nullAnnotationsSimpleNames: >+ this.nullAnnotationPackageNames = new char[3][][]; >+ this.nullAnnotationSimpleNames = new char[3][]; >+ char[][][] compoundNames = new char[][][] {getNullableAnnotationName(), getNonNullAnnotationName(), getNonNullByDefaultAnnotationName() }; >+ for (int i=0; i<3; i++) { >+ int len = compoundNames[i].length; >+ if (len < 2) { >+ this.problemReporter.nullAnnotationNameMustBeQualified(compoundNames[i], this.unitBeingCompleted); >+ // the above error may or may not throw AbortCompilation (is problemReporter.referenceContext set?) >+ continue; >+ } >+ this.nullAnnotationPackageNames[i] = CharOperation.subarray(compoundNames[i], 0, len-1); >+ this.nullAnnotationSimpleNames[i] = compoundNames[i][len-1]; >+ } >+ } >+ // search in local storage: >+ boolean found = false; >+ char[][] simpleNames = new char[3][]; >+ for (int i=0; i<3; i++) { >+ if (CharOperation.equals(packageName, this.nullAnnotationPackageNames[i])) { >+ simpleNames[i] = this.nullAnnotationSimpleNames[i]; >+ found = true; >+ } >+ } >+ if (found) >+ return simpleNames; >+ else >+ return null; >+} >+ >+TypeBinding getNullAnnotationResolved(TypeBinding nullAnnotation, Scope scope) { >+ // avoid unspecific error "The type in.valid cannot be resolved. It is indirectly referenced from required .class files" >+ boolean tolerateMissing = this.mayTolerateMissingType; >+ this.mayTolerateMissingType = true; >+ try { >+ nullAnnotation = BinaryTypeBinding.resolveType(nullAnnotation, this, false); >+ } finally { >+ this.mayTolerateMissingType = tolerateMissing; >+ } >+ if (nullAnnotation instanceof MissingTypeBinding) { >+ // convert error into a specific one: >+ scope.problemReporter().missingNullAnnotationType(((MissingTypeBinding)nullAnnotation).compoundName); >+ return null; >+ } >+ return nullAnnotation; >+} >+ > /* Answer the top level package named name if it exists in the cache. > * Answer theNotFoundPackage if it could not be resolved the first time > * it was looked up, otherwise answer null. >@@ -1454,6 +1558,7 @@ > this.unitBeingCompleted = null; // in case AbortException occurred > > this.classFilePool.reset(); >+ > // name environment has a longer life cycle, and must be reset in > // the code which created it. > } >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 33c56ef..c1fcc99 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 >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -33,6 +34,9 @@ > public TypeVariableBinding[] typeVariables = Binding.NO_TYPE_VARIABLES; > char[] signature; > public long tagBits; >+ >+ /** Store nullness information from annotation (incl. applicable default). */ >+ public Boolean[] parameterNonNullness; // TRUE means @NonNull declared, FALSE means @Nullable declared, null means nothing declared > > protected MethodBinding() { > // for creating problem or synthetic method >@@ -439,6 +443,41 @@ > return this.selector; > } > >+/** >+ * After method verifier has finished, fill in missing nullness values from the applicable default. >+ * @param annotationBinding the null annotation specified to be the default at the current code location. >+ */ >+protected void fillInDefaultNonNullness(TypeBinding annotationBinding) { >+ if (this.parameterNonNullness == null) >+ this.parameterNonNullness = new Boolean[this.parameters.length]; >+ AbstractMethodDeclaration sourceMethod = sourceMethod(); >+ for (int i = 0; i < this.parameterNonNullness.length; i++) { >+ if (this.parameters[i].isBaseType()) >+ continue; >+ boolean added = false; >+ if (this.parameterNonNullness[i] == null) { >+ added = true; >+ this.parameterNonNullness[i] = Boolean.TRUE; >+ if (sourceMethod != null) >+ sourceMethod.addParameterNonNullAnnotation(i, (ReferenceBinding)annotationBinding); >+ } else if (this.parameterNonNullness[i].booleanValue()) { >+ sourceMethod.scope.problemReporter().nullAnnotationIsRedundant(sourceMethod, i); >+ } >+ if (added) >+ this.tagBits |= TagBits.HasParameterAnnotations; >+ } >+ if ( this.returnType != null >+ && !this.returnType.isBaseType() >+ && (this.tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable)) == 0) >+ { >+ this.tagBits |= TagBits.AnnotationNonNull; >+ if (sourceMethod != null) >+ sourceMethod.addNullnessAnnotation((ReferenceBinding)annotationBinding); >+ } else if ((this.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/MethodVerifier15.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java >index 3b0cae1..dbe17b9 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodVerifier15.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -165,6 +166,8 @@ > || this.type.superclass.erasure().findSuperTypeOriginatingFrom(originalInherited.declaringClass) == null) > this.type.addSyntheticBridgeMethod(originalInherited, concreteMethod.original()); > } >+ if (!concreteMethod.isStatic() && !abstractMethod.isStatic()) >+ checkNullSpecInheritance(concreteMethod, abstractMethod); > } > } > void checkForBridgeMethod(MethodBinding currentMethod, MethodBinding inheritedMethod, MethodBinding[] allInheritedMethods) { >@@ -359,6 +362,100 @@ > } > > return false; >+} >+void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length, MethodBinding[] allInheritedMethods) >+{ >+ super.checkAgainstInheritedMethods(currentMethod, methods, length, allInheritedMethods); >+ for (int i = length; --i >= 0;) >+ if (!currentMethod.isStatic() && !methods[i].isStatic()) >+ checkNullSpecInheritance(currentMethod, methods[i]); >+} >+ >+void checkNullSpecInheritance(MethodBinding currentMethod, MethodBinding inheritedMethod) { >+ long inheritedBits = inheritedMethod.tagBits; >+ long currentBits = currentMethod.tagBits; >+ AbstractMethodDeclaration srcMethod = null; >+ if (this.type.equals(currentMethod.declaringClass)) // is currentMethod from the current type? >+ srcMethod = currentMethod.sourceMethod(); >+ >+ // return type: >+ if ((inheritedBits & TagBits.AnnotationNonNull) != 0) { >+ long currentNullBits = currentBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable); >+ if (currentNullBits != TagBits.AnnotationNonNull) { >+ if (srcMethod != null) { >+ this.type.scope.problemReporter().illegalReturnRedefinition(srcMethod, inheritedMethod, >+ this.environment.getNonNullAnnotationName()); >+ } else { >+ this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod); >+ return; >+ } >+ } >+ } >+ >+ // parameters: >+ Argument[] currentArguments = srcMethod == null ? null : srcMethod.arguments; >+ if (inheritedMethod.parameterNonNullness != null) { >+ // inherited method has null-annotations, check compatibility: >+ >+ for (int i = 0; i < inheritedMethod.parameterNonNullness.length; i++) { >+ Argument currentArgument = currentArguments == null ? null : currentArguments[i]; >+ >+ Boolean inheritedNonNullNess = inheritedMethod.parameterNonNullness[i]; >+ Boolean currentNonNullNess = (currentMethod.parameterNonNullness == null) >+ ? null : currentMethod.parameterNonNullness[i]; >+ if (inheritedNonNullNess != null) { // super has a null annotation >+ if (currentNonNullNess == null) { // current parameter lacks null annotation >+ boolean needNonNull = false; >+ char[][] annotationName; >+ if (inheritedNonNullNess == Boolean.TRUE) { >+ needNonNull = true; >+ annotationName = this.environment.getNonNullAnnotationName(); >+ } else { >+ annotationName = this.environment.getNullableAnnotationName(); >+ } >+ if (currentArgument != null) { >+ this.type.scope.problemReporter().parameterLackingNonNullAnnotation( >+ currentArgument, >+ inheritedMethod.declaringClass, >+ needNonNull, >+ annotationName); >+ continue; >+ } else { >+ this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod); >+ break; >+ } >+ } >+ } >+ if (inheritedNonNullNess != Boolean.TRUE) { // super parameter is not restricted to @NonNull >+ if (currentNonNullNess == Boolean.TRUE) { // current parameter is restricted to @NonNull >+ if (currentArgument != null) >+ this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter( >+ currentArgument, >+ inheritedMethod.declaringClass, >+ inheritedNonNullNess == null >+ ? null >+ : this.environment.getNullableAnnotationName()); >+ else >+ this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod); >+ } >+ } >+ } >+ } else if (currentMethod.parameterNonNullness != null) { >+ // super method has no annotations but current has >+ for (int i = 0; i < currentMethod.parameterNonNullness.length; i++) { >+ if (currentMethod.parameterNonNullness[i] == Boolean.TRUE) { // tightening from unconstrained to @NonNull >+ if (currentArguments != null) { >+ this.type.scope.problemReporter().illegalRedefinitionToNonNullParameter( >+ currentArguments[i], >+ inheritedMethod.declaringClass, >+ null); >+ } else { >+ this.type.scope.problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod); >+ break; >+ } >+ } >+ } >+ } > } > > void reportRawReferences() { >@@ -935,7 +1032,7 @@ > boolean isUnsafeReturnTypeOverride(MethodBinding currentMethod, MethodBinding inheritedMethod) { > // called when currentMethod's return type is NOT compatible with inheritedMethod's return type > >- // JLS 3 �8.4.5: more are accepted, with an unchecked conversion >+ // JLS 3 �8.4.5: more are accepted, with an unchecked conversion > if (currentMethod.returnType == inheritedMethod.returnType.erasure()) { > TypeBinding[] currentParams = currentMethod.parameters; > TypeBinding[] inheritedParams = inheritedMethod.parameters; >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java >index 0e59232..bc6862b 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2009 IBM Corporation and others. >+ * Copyright (c) 2000, 2011 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -22,6 +23,14 @@ > public LookupEnvironment environment; > HashtableOfType knownTypes; > HashtableOfPackage knownPackages; >+ >+ // if this package contains configured null-annotations, store their simple names here: >+ protected char[] nullableName = null; >+ protected char[] nonNullName = null; >+ protected char[] nonNullByDefaultName = null; >+ // annotation type binding representing the default that has been defined for this package (using @NonNullByDefault) >+ protected TypeBinding nullnessDefaultAnnotation; >+ > protected PackageBinding() { > // for creating problem package > } >@@ -36,6 +45,8 @@ > this.environment = environment; > this.knownTypes = null; // initialized if used... class counts can be very large 300-600 > this.knownPackages = new HashtableOfPackage(3); // sub-package counts are typically 0-3 >+ if (compoundName != CharOperation.NO_CHAR_CHAR && environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) >+ initNullAnnotationPackage(); > } > > public PackageBinding(LookupEnvironment environment) { >@@ -58,6 +69,8 @@ > if (this.knownTypes == null) > this.knownTypes = new HashtableOfType(25); > this.knownTypes.put(element.compoundName[element.compoundName.length - 1], element); >+ if (this.nullableName != null || this.nonNullName != null || this.nonNullByDefaultName != null) >+ setupNullAnnotationType(element); > } > > void clearMissingTagBit() { >@@ -204,6 +217,19 @@ > > return null; > } >+ >+private void initNullAnnotationPackage() { >+ char[][] simpleAnnotNames = this.environment.getNullAnnotationNames(this.compoundName); >+ if (simpleAnnotNames == null) >+ return; >+ if (simpleAnnotNames[0] != null) >+ this.nullableName = simpleAnnotNames[0]; >+ if (simpleAnnotNames[1] != null) >+ this.nonNullName = simpleAnnotNames[1]; >+ if (simpleAnnotNames[2] != null) >+ this.nonNullByDefaultName = simpleAnnotNames[2]; >+} >+ > public final boolean isViewedAsDeprecated() { > if ((this.tagBits & TagBits.DeprecatedAnnotationResolved) == 0) { > this.tagBits |= TagBits.DeprecatedAnnotationResolved; >@@ -230,6 +256,26 @@ > return ProblemReasons.NoError; > } > >+void setupNullAnnotationType(ReferenceBinding type) { >+ int id = 0; >+ if (CharOperation.equals(this.nullableName, type.sourceName)) >+ id = TypeIds.T_ConfiguredAnnotationNullable; >+ else if (CharOperation.equals(this.nonNullName, type.sourceName)) >+ id = TypeIds.T_ConfiguredAnnotationNonNull; >+ else if (CharOperation.equals(this.nonNullByDefaultName, type.sourceName)) >+ id = TypeIds.T_ConfiguredAnnotationNonNullByDefault; >+ else >+ return; >+ >+ type.id = id; // ensure annotations of this type are detected as standard annotations. >+} >+ >+public TypeBinding getNullnessDefaultAnnotation(Scope scope) { >+ if (this.nullnessDefaultAnnotation instanceof UnresolvedReferenceBinding) >+ return this.nullnessDefaultAnnotation = this.environment.getNullAnnotationResolved(this.nullnessDefaultAnnotation, scope); >+ return this.nullnessDefaultAnnotation; >+} >+ > public char[] readableName() /*java.lang*/ { > return CharOperation.concatWith(this.compoundName, '.'); > } >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 1769362..7ad4461 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 >@@ -7,7 +7,9 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >- * Stephan Herrmann - Contribution for bug 349326 - [1.7] new warning for missing try-with-resources >+ * Stephan Herrmann - Contributions for >+ * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -48,6 +50,14 @@ > public boolean hasTypeBit(int bit) { return false; } > }; > >+ /** >+ * This faked annotation type binding marks types with unspecified nullness. >+ * For use in {@link PackageBinding#nullnessDefaultAnnotation} and {@link SourceTypeBinding#nullnessDefaultAnnotation} >+ */ >+ final static ReferenceBinding NULL_UNSPECIFIED = new ReferenceBinding() { /* faked type binding */ >+ public boolean hasTypeBit(int bit) { return false; } >+ }; >+ > private static final Comparator FIELD_COMPARATOR = new Comparator() { > public int compare(Object o1, Object o2) { > char[] n1 = ((FieldBinding) o1).name; >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 >index f29b6c3..7d2fdea 100644 >--- 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 >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -4289,4 +4290,18 @@ > } > return visibleIndex == 1 ? visible[0] : mostSpecificMethodBinding(visible, visibleIndex, argumentTypes, allocationSite, allocationType); > } >+ >+ public void validateNullAnnotation(long tagBits, TypeReference typeRef, Annotation[] annotations) { >+ long nullAnnotationTagBit = tagBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable); >+ if (nullAnnotationTagBit != 0) { >+ TypeBinding type = typeRef.resolvedType; >+ if (type != null && type.isBaseType()) { >+ char[][] annotationName = (nullAnnotationTagBit == TagBits.AnnotationNonNull) >+ ? environment().getNonNullAnnotationName() >+ : environment().getNullableAnnotationName(); >+ problemReporter().illegalAnnotationForBaseType(typeRef, annotations, >+ annotationName[annotationName.length-1], nullAnnotationTagBit); >+ } >+ } >+ } > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java >index d1f9cf4..07ef1da 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java >@@ -10,6 +10,7 @@ > * Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for > * bug 328281 - visibility leaks not detected when analyzing unused field in private class > * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -54,6 +55,9 @@ > char[] genericReferenceTypeSignature; > > private SimpleLookupTable storedAnnotations = null; // keys are this ReferenceBinding & its fields and methods, value is an AnnotationHolder >+ >+ private TypeBinding nullnessDefaultAnnotation; >+ private int nullnessDefaultInitialized = 0; // 0: nothing; 1: type; 2: package > > public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) { > this.compoundName = compoundName; >@@ -781,6 +785,7 @@ > } > if ((this.tagBits & TagBits.AnnotationDeprecated) != 0) > this.modifiers |= ClassFileConstants.AccDeprecated; >+ evaluateNullAnnotations(this.tagBits); > } > return this.tagBits; > } >@@ -1088,6 +1093,8 @@ > this.modifiers |= ClassFileConstants.AccDeprecated; > } > } >+ if (CharOperation.equals(this.sourceName, TypeConstants.PACKAGE_INFO_NAME)) >+ getAnnotationTagBits(); // initialize > } > > // ensure the receiver knows its hierarchy & fields/methods so static imports can be resolved correctly >@@ -1590,12 +1597,102 @@ > typeParameters[i].binding = null; > return null; > } >+ createArgumentBindings(method); > if (foundReturnTypeProblem) > return method; // but its still unresolved with a null return type & is still connected to its method declaration > > method.modifiers &= ~ExtraCompilerModifiers.AccUnresolved; > return method; > } >+private void createArgumentBindings(MethodBinding method) { >+ // ensure nullness defaults are initialized at all enclosing levels: >+ switch (this.nullnessDefaultInitialized) { >+ case 0: >+ getAnnotationTagBits(); // initialize >+ //$FALL-THROUGH$ >+ case 1: >+ getPackage().isViewedAsDeprecated(); // initialize annotations >+ this.nullnessDefaultInitialized = 2; >+ } >+ AbstractMethodDeclaration methodDecl = method.sourceMethod(); >+ if (methodDecl != null) { >+ if (method.parameters != Binding.NO_PARAMETERS) >+ methodDecl.createArgumentBindings(); >+ TypeBinding annotationBinding = findDefaultNullness(method, methodDecl.scope.environment()); >+ if (annotationBinding != null && annotationBinding.id == TypeIds.T_ConfiguredAnnotationNonNull) >+ method.fillInDefaultNonNullness(annotationBinding); >+ } >+} >+private void evaluateNullAnnotations(long annotationTagBits) { >+ if (this.nullnessDefaultInitialized > 0) >+ return; >+ this.nullnessDefaultInitialized = 1; >+ // transfer nullness info from tagBits to this.nullnessDefaultAnnotation >+ TypeBinding defaultAnnotation = getPackage().environment >+ .getNullAnnotationBindingFromDefault(annotationTagBits, false/*resolve*/); >+ if (defaultAnnotation != null) { >+ if (CharOperation.equals(this.sourceName, TypeConstants.PACKAGE_INFO_NAME)) { >+ getPackage().nullnessDefaultAnnotation = defaultAnnotation; >+ } else { >+ this.nullnessDefaultAnnotation = defaultAnnotation; >+ } >+ } >+} >+private TypeBinding getNullnessDefaultAnnotation() { >+ if (this.nullnessDefaultAnnotation instanceof UnresolvedReferenceBinding) >+ this.nullnessDefaultAnnotation = this.scope.environment().getNullAnnotationResolved(this.nullnessDefaultAnnotation, this.scope); >+ return this.nullnessDefaultAnnotation; >+} >+/** >+ * Answer the nullness default applicable at the given method binding. >+ * Possible values:<ul> >+ * <li>the type binding for @NonNulByDefault</li> >+ * <li>the synthetic type {@link ReferenceBinding#NULL_UNSPECIFIED} if a default from outer scope has been canceled</li> >+ * <li>null if no default has been defined</li> >+ * </ul> >+ */ >+private TypeBinding findDefaultNullness(MethodBinding methodBinding, LookupEnvironment environment) { >+ // find the applicable default inside->out: >+ >+ // method >+ TypeBinding annotationBinding = environment.getNullAnnotationBindingFromDefault(methodBinding.tagBits, true/*resolve*/); >+ if (annotationBinding != null) >+ return annotationBinding; >+ >+ // type >+ ReferenceBinding type = methodBinding.declaringClass; >+ ReferenceBinding currentType = type; >+ while (currentType instanceof SourceTypeBinding) { >+ annotationBinding = ((SourceTypeBinding) currentType).getNullnessDefaultAnnotation(); >+ if (annotationBinding != null) >+ return annotationBinding; >+ currentType = currentType.enclosingType(); >+ } >+ >+ // package >+ annotationBinding = type.getPackage().getNullnessDefaultAnnotation(this.scope); >+ if (annotationBinding != null) >+ return annotationBinding; >+ >+ // global >+ long defaultNullness = environment.globalOptions.defaultNonNullness; >+ if (defaultNullness != 0) { >+ // we have a default, so we need an annotation type to record this during compile and in the byte code >+ annotationBinding = environment.getNullAnnotationBinding(defaultNullness, true/*resolve*/); >+ if (annotationBinding != null) >+ return annotationBinding; >+ >+ // on this branch default was not defined using an annotation, thus annotation type can still be missing >+ if (defaultNullness == TagBits.AnnotationNonNull) >+ this.scope.problemReporter().missingNullAnnotationType(environment.getNonNullAnnotationName()); >+ else >+ this.scope.problemReporter().abortDueToInternalError("Illegal default nullness value: "+defaultNullness); //$NON-NLS-1$ >+ // reset default to avoid duplicate errors: >+ environment.globalOptions.defaultNonNullness = 0; >+ } >+ return null; >+} >+ > public AnnotationHolder retrieveAnnotationHolder(Binding binding, boolean forceInitialization) { > if (forceInitialization) > binding.getAnnotationTagBits(); // ensure annotations are up to date >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java >index 55d5b88..9ba6da0 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TagBits.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -132,6 +133,14 @@ > long AnnotationPreDestroy = ASTNode.Bit54L; > /** @since 3.8 */ > long AnnotationPostConstruct = ASTNode.Bit55L; >+ /** @since 3.8 null annotation for MethodBinding or LocalVariableBinding (argument): */ >+ long AnnotationNullable = ASTNode.Bit56L; >+ /** @since 3.8 null annotation for MethodBinding or LocalVariableBinding (argument): */ >+ long AnnotationNonNull = ASTNode.Bit57L; >+ /** @since 3.8 null-default annotation for PackageBinding or TypeBinding or MethodBinding: */ >+ long AnnotationNonNullByDefault = ASTNode.Bit58L; >+ /** @since 3.8 canceling null-default annotation for PackageBinding or TypeBinding or MethodBinding: */ >+ long AnnotationNullUnspecifiedByDefault = ASTNode.Bit59L; > > long AllStandardAnnotationsMask = > AnnotationTargetMASK >@@ -144,10 +153,14 @@ > | AnnotationSafeVarargs > | AnnotationPolymorphicSignature > | AnnotationPostConstruct >- | AnnotationPreDestroy; >+ | AnnotationPreDestroy >+ | AnnotationNullable >+ | AnnotationNonNull >+ | AnnotationNonNullByDefault >+ | AnnotationNullUnspecifiedByDefault; > >- long DefaultValueResolved = ASTNode.Bit56L; >+ long DefaultValueResolved = ASTNode.Bit60L; > > // set when type contains non-private constructor(s) >- long HasNonPrivateConstructor = ASTNode.Bit57L; >+ long HasNonPrivateConstructor = ASTNode.Bit61L; > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java >index 115bf83..7fff434 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java >@@ -10,6 +10,7 @@ > * Stephan Herrmann - Contributions for > * bug 349326 - [1.7] new warning for missing try-with-resources > * bug 359362 - FUP of bug 349326: Resource leak on non-Closeable resource >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.lookup; > >@@ -104,6 +105,11 @@ > final int T_JavaxAnnotationPostConstruct = 63; > > final int T_JavaxAnnotationPreDestroy = 64; >+ >+ // new in 3.8 for null annotations: >+ final int T_ConfiguredAnnotationNullable = 65; >+ final int T_ConfiguredAnnotationNonNull = 66; >+ final int T_ConfiguredAnnotationNonNullByDefault = 67; > > final int NoId = Integer.MAX_VALUE; > >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java >index 26d3599..ca6b9fa 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/Scanner.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.parser; > >@@ -546,11 +547,11 @@ > > if (this.lineEnds == null || this.linePtr == -1) > return -1; >- if (lineNumber > this.lineEnds.length+1) >+ if (lineNumber > this.linePtr + 2) > return -1; > if (lineNumber <= 0) > return -1; >- if (lineNumber == this.lineEnds.length + 1) >+ if (lineNumber == this.linePtr + 2) > return this.eofPosition; > return this.lineEnds[lineNumber-1]; // next line start one character behind the lineEnd of the previous line > } >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java >index 18688fa..7650069 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java >@@ -9,9 +9,10 @@ > * IBM Corporation - initial API and implementation > * Benjamin Muskalla - Contribution for bug 239066 > * Stephan Herrmann - Contributions for >- * bug 236385 - >- * bug 338303 - Warning about Redundant assignment conflicts with definite assignment >- * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 236385 - >+ * bug 338303 - Warning about Redundant assignment conflicts with definite assignment >+ * bug 349326 - [1.7] new warning for missing try-with-resources >+ * bug 186342 - [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.compiler.problem; > >@@ -84,6 +85,7 @@ > import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; > import org.eclipse.jdt.internal.compiler.env.AccessRestriction; > import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; >+import org.eclipse.jdt.internal.compiler.flow.FlowInfo; > import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; > import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; > import org.eclipse.jdt.internal.compiler.lookup.ArrayBinding; >@@ -287,6 +289,7 @@ > return CompilerOptions.NullReference; > > case IProblem.PotentialNullLocalVariableReference: >+ case IProblem.PotentialNullMessageSendReference: > return CompilerOptions.PotentialNullReference; > > case IProblem.RedundantLocalVariableNullAssignment: >@@ -295,7 +298,24 @@ > case IProblem.NonNullLocalVariableComparisonYieldsFalse: > case IProblem.NullLocalVariableComparisonYieldsFalse: > case IProblem.NullLocalVariableInstanceofYieldsFalse: >+ case IProblem.RedundantNullCheckOnNonNullMessageSend: > return CompilerOptions.RedundantNullCheck; >+ >+ case IProblem.RequiredNonNullButProvidedNull: >+ case IProblem.IllegalReturnNullityRedefinition: >+ case IProblem.IllegalRedefinitionToNonNullParameter: >+ case IProblem.IllegalDefinitionToNonNullParameter: >+ case IProblem.ParameterLackingNonNullAnnotation: >+ case IProblem.ParameterLackingNullableAnnotation: >+ case IProblem.CannotImplementIncompatibleNullness: >+ return CompilerOptions.NullSpecViolation; >+ >+ case IProblem.RequiredNonNullButProvidedPotentialNull: >+ return CompilerOptions.PotentialNullSpecViolation; >+ case IProblem.RequiredNonNullButProvidedUnknown: >+ return CompilerOptions.NullSpecInsufficientInfo; >+ case IProblem.RedundantNullAnnotation: >+ return CompilerOptions.RedundantNullAnnotation; > > case IProblem.BoxingConversion : > case IProblem.UnboxingConversion : >@@ -542,6 +562,13 @@ > case CompilerOptions.DiscouragedReference : > return CategorizedProblem.CAT_RESTRICTION; > >+ case CompilerOptions.NullSpecViolation : >+ case CompilerOptions.PotentialNullSpecViolation : >+ case CompilerOptions.NullSpecInsufficientInfo : >+ return CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM; >+ case CompilerOptions.RedundantNullAnnotation : >+ return CategorizedProblem.CAT_UNNECESSARY_CODE; >+ > default: > break categorizeOnIrritant; > } >@@ -550,6 +577,8 @@ > switch (problemID) { > case IProblem.IsClassPathCorrect : > case IProblem.CorruptedSignature : >+ case IProblem.MissingNullAnnotationType : >+ case IProblem.NullAnnotationNameMustBeQualified : > return CategorizedProblem.CAT_BUILDPATH; > > default : >@@ -8022,4 +8051,189 @@ > trackVar.sourceStart, > trackVar.sourceEnd); > } >-} >\ No newline at end of file >+ >+public void nullityMismatch(Expression expression, TypeBinding requiredType, int nullStatus, char[][] annotationName) { >+ int problemId = IProblem.RequiredNonNullButProvidedUnknown; >+ if ((nullStatus & FlowInfo.NULL) != 0) >+ problemId = IProblem.RequiredNonNullButProvidedNull; >+ if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0) >+ problemId = IProblem.RequiredNonNullButProvidedPotentialNull; >+ String[] arguments = new String[] { >+ String.valueOf(CharOperation.concatWith(annotationName, '.')), >+ String.valueOf(requiredType.readableName()) >+ }; >+ String[] argumentsShort = new String[] { >+ String.valueOf(annotationName[annotationName.length-1]), >+ String.valueOf(requiredType.shortReadableName()) >+ }; >+ this.handle( >+ problemId, >+ arguments, >+ argumentsShort, >+ expression.sourceStart, >+ expression.sourceEnd); >+} >+public void illegalRedefinitionToNonNullParameter(Argument argument, ReferenceBinding declaringClass, char[][] inheritedAnnotationName) { >+ int sourceStart = argument.type.sourceStart; >+ if (argument.annotations != null) { >+ for (int i=0; i<argument.annotations.length; i++) { >+ Annotation annotation = argument.annotations[i]; >+ if ( annotation.resolvedType.id == TypeIds.T_ConfiguredAnnotationNullable >+ || annotation.resolvedType.id == TypeIds.T_ConfiguredAnnotationNonNull) >+ { >+ sourceStart = annotation.sourceStart; >+ break; >+ } >+ } >+ } >+ if (inheritedAnnotationName == null) { >+ this.handle( >+ IProblem.IllegalDefinitionToNonNullParameter, >+ new String[] { new String(argument.name), new String(declaringClass.readableName()) }, >+ new String[] { new String(argument.name), new String(declaringClass.shortReadableName()) }, >+ sourceStart, >+ argument.type.sourceEnd); >+ >+ } else { >+ this.handle( >+ IProblem.IllegalRedefinitionToNonNullParameter, >+ new String[] { new String(argument.name), new String(declaringClass.readableName()), CharOperation.toString(inheritedAnnotationName)}, >+ new String[] { new String(argument.name), new String(declaringClass.shortReadableName()), new String(inheritedAnnotationName[inheritedAnnotationName.length-1])}, >+ sourceStart, >+ argument.type.sourceEnd); >+ } >+} >+public void parameterLackingNonNullAnnotation(Argument argument, ReferenceBinding declaringClass, boolean needNonNull, char[][] inheritedAnnotationName) { >+ this.handle( >+ needNonNull ? IProblem.ParameterLackingNonNullAnnotation : IProblem.ParameterLackingNullableAnnotation, >+ new String[] { new String(argument.name), new String(declaringClass.readableName()), CharOperation.toString(inheritedAnnotationName)}, >+ new String[] { new String(argument.name), new String(declaringClass.shortReadableName()), new String(inheritedAnnotationName[inheritedAnnotationName.length-1])}, >+ argument.type.sourceStart, >+ argument.type.sourceEnd); >+} >+public void illegalReturnRedefinition(AbstractMethodDeclaration abstractMethodDecl, >+ MethodBinding inheritedMethod, char[][] nonNullAnnotationName) >+{ >+ MethodDeclaration methodDecl = (MethodDeclaration) abstractMethodDecl; >+ StringBuffer methodSignature = new StringBuffer(); >+ methodSignature >+ .append(inheritedMethod.declaringClass.readableName()) >+ .append('.') >+ .append(inheritedMethod.readableName()); >+ >+ StringBuffer shortSignature = new StringBuffer(); >+ shortSignature >+ .append(inheritedMethod.declaringClass.shortReadableName()) >+ .append('.') >+ .append(inheritedMethod.shortReadableName()); >+ int sourceStart = methodDecl.returnType.sourceStart; >+ Annotation[] annotations = methodDecl.annotations; >+ if (annotations != null) { >+ for (int i=0; i<annotations.length; i++) { >+ if (annotations[i].resolvedType.id == TypeIds.T_ConfiguredAnnotationNullable) { >+ sourceStart = annotations[i].sourceStart; >+ break; >+ } >+ } >+ } >+ this.handle( >+ IProblem.IllegalReturnNullityRedefinition, >+ new String[] { methodSignature.toString(), CharOperation.toString(nonNullAnnotationName)}, >+ new String[] { shortSignature.toString(), new String(nonNullAnnotationName[nonNullAnnotationName.length-1])}, >+ sourceStart, >+ methodDecl.returnType.sourceEnd); >+} >+public void messageSendPotentialNullReference(MethodBinding method, ASTNode location) { >+ String[] arguments = new String[] {new String(method.readableName())}; >+ this.handle( >+ IProblem.PotentialNullMessageSendReference, >+ arguments, >+ arguments, >+ location.sourceStart, >+ location.sourceEnd); >+} >+public void messageSendRedundantCheckOnNonNull(MethodBinding method, ASTNode location) { >+ String[] arguments = new String[] {new String(method.readableName()) }; >+ this.handle( >+ IProblem.RedundantNullCheckOnNonNullMessageSend, >+ arguments, >+ arguments, >+ location.sourceStart, >+ location.sourceEnd); >+} >+ >+public void missingNullAnnotationType(char[][] nullAnnotationName) { >+ String[] args = { new String(CharOperation.concatWith(nullAnnotationName, '.')) }; >+ this.handle(IProblem.MissingNullAnnotationType, args, args, 0, 0); >+} >+ >+public void cannotImplementIncompatibleNullness(MethodBinding currentMethod, MethodBinding inheritedMethod) { >+ int sourceStart = 0, sourceEnd = 0; >+ if (this.referenceContext instanceof TypeDeclaration) { >+ sourceStart = ((TypeDeclaration) this.referenceContext).sourceStart; >+ sourceEnd = ((TypeDeclaration) this.referenceContext).sourceEnd; >+ } >+ String[] problemArguments = { >+ new String(currentMethod.readableName()), >+ new String(currentMethod.declaringClass.readableName()), >+ new String(inheritedMethod.declaringClass.readableName()) >+ }; >+ String[] messageArguments = { >+ new String(currentMethod.shortReadableName()), >+ new String(currentMethod.declaringClass.shortReadableName()), >+ new String(inheritedMethod.declaringClass.shortReadableName()) >+ }; >+ this.handle( >+ IProblem.CannotImplementIncompatibleNullness, >+ problemArguments, >+ messageArguments, >+ sourceStart, >+ sourceEnd); >+} >+ >+public void nullAnnotationNameMustBeQualified(char[][] typeName, CompilationUnitDeclaration unitBeingCompleted) { >+ this.referenceContext = unitBeingCompleted; >+ String[] name = {new String(typeName[0])}; >+ this.handle(IProblem.NullAnnotationNameMustBeQualified, name, name, 0, 0); >+} >+ >+public void nullAnnotationIsRedundant(AbstractMethodDeclaration sourceMethod, int i) { >+ int sourceStart, sourceEnd; >+ if (i == -1) { >+ MethodDeclaration methodDecl = (MethodDeclaration) sourceMethod; >+ sourceStart = findAnnotationSourceStart(methodDecl.annotations, methodDecl.returnType.sourceStart, TypeIds.T_ConfiguredAnnotationNonNull); >+ sourceEnd = methodDecl.returnType.sourceEnd; >+ } else { >+ Argument arg = sourceMethod.arguments[i]; >+ sourceStart = arg.declarationSourceStart; >+ sourceEnd = arg.sourceEnd; >+ } >+ this.handle(IProblem.RedundantNullAnnotation, ProblemHandler.NoArgument, ProblemHandler.NoArgument, sourceStart, sourceEnd); >+} >+ >+public void illegalAnnotationForBaseType(TypeReference type, Annotation[] annotations, char[] annotationName, long nullAnnotationTagBit) >+{ >+ int typeId = (nullAnnotationTagBit == TagBits.AnnotationNullable) >+ ? TypeIds.T_ConfiguredAnnotationNullable : TypeIds.T_ConfiguredAnnotationNonNull; >+ String[] args = new String[] { new String(annotationName), new String(type.getLastToken()) }; >+ this.handle(IProblem.IllegalAnnotationForBaseType, >+ args, >+ args, >+ findAnnotationSourceStart(annotations, type.sourceStart, typeId), >+ type.sourceEnd); >+} >+ >+private int findAnnotationSourceStart(Annotation[] annotations, int startFallback, int typeId) { >+ int sourceStart = startFallback; >+ if (annotations != null) { >+ // should have a @NonNull/@Nullable annotation, search for it: >+ for (int j=0; j<annotations.length; j++) { >+ if (annotations[j].resolvedType.id == typeId) { >+ sourceStart = annotations[j].sourceStart; >+ break; >+ } >+ } >+ } >+ return sourceStart; >+} >+} >diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties >index 1fde136..b636a4b 100644 >--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties >+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties >@@ -11,6 +11,7 @@ > # Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for > # bug 185682 - Increment/decrement operators mark local variables as read > # bug 349326 - [1.7] new warning for missing try-with-resources >+# bug 186342 - [compiler][null] Using annotations for null checking > ############################################################################### > 0 = {0} > 1 = super cannot be used in java.lang.Object >@@ -654,6 +655,23 @@ > 889 = Resource ''{0}'' should be managed by try-with-resource > 890 = Cannot switch on an enum value for source level below 1.5. Only convertible int values are permitted > >+### NULL ANNOTATIONS >+910 = Type mismatch: required ''@{0} {1}'' but the provided value is null >+911 = Type mismatch: required ''@{0} {1}'' but the provided value can be null >+912 = Potential type mismatch: required ''@{0} {1}'' but nullness of the provided value is unknown >+913 = Buildpath problem: the type {0}, which is configured as a null annotation type, cannot be resolved >+914 = Cannot use the unqualified name ''{0}'' as an annotation name for null specification >+915 = The return type is incompatible with the @{1} return from {0} >+916 = Illegal redefinition of parameter {0}, inherited method from {1} declares this parameter as @{2} >+917 = Illegal redefinition of parameter {0}, inherited method from {1} does not constrain this parameter >+918 = Missing non-null annotation: inherited method from {1} declares this parameter as @{2} >+919 = Missing nullable annotation: inherited method from {1} declares this parameter as @{2} >+920 = Potential null pointer access: The method {0} may return null >+921 = Redundant null check: The method {0} cannot return null >+922 = The method {0} from {1} cannot implement the corresponding method from {2} due to incompatible nullness constraints >+923 = The nullness annotation is redundant with a default that applies to this location >+924 = The nullness annotation @{0} is not applicable for the primitive type {1} >+ > ### ELABORATIONS > ## Access restrictions > 78592 = The type {1} is not accessible due to restriction on classpath entry {0} >diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java >index 0fdbd0a..00b8222 100644 >--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java >+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 363858 - [dom] early throwing of AbortCompilation causes NPE in CompilationUnitResolver > *******************************************************************************/ > package org.eclipse.jdt.core.dom; > >@@ -99,6 +100,7 @@ > DefaultBindingResolver.BindingTables bindingTables; > > boolean hasCompilationAborted; >+ CategorizedProblem abortProblem; > > private IProgressMonitor monitor; > >@@ -364,6 +366,7 @@ > removeUnresolvedBindings(unit); > } > this.hasCompilationAborted = true; >+ this.abortProblem = abortException.problem; > } > > public static void parse(ICompilationUnit[] compilationUnits, ASTRequestor astRequestor, int apiLevel, Map options, int flags, IProgressMonitor monitor) { >@@ -689,11 +692,16 @@ > // the bindings could not be resolved due to missing types in name environment > // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=86541 > CompilationUnitDeclaration unitDeclaration = parse(sourceUnit, nodeSearcher, options, flags); >- final int problemCount = unit.compilationResult.problemCount; >- if (problemCount != 0) { >- unitDeclaration.compilationResult.problems = new CategorizedProblem[problemCount]; >- System.arraycopy(unit.compilationResult.problems, 0, unitDeclaration.compilationResult.problems, 0, problemCount); >- unitDeclaration.compilationResult.problemCount = problemCount; >+ if (unit != null) { >+ final int problemCount = unit.compilationResult.problemCount; >+ if (problemCount != 0) { >+ unitDeclaration.compilationResult.problems = new CategorizedProblem[problemCount]; >+ System.arraycopy(unit.compilationResult.problems, 0, unitDeclaration.compilationResult.problems, 0, problemCount); >+ unitDeclaration.compilationResult.problemCount = problemCount; >+ } >+ } else if (resolver.abortProblem != null) { >+ unitDeclaration.compilationResult.problemCount = 1; >+ unitDeclaration.compilationResult.problems = new CategorizedProblem[] { resolver.abortProblem }; > } > return unitDeclaration; > } >diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java >index 750cc95..3fbce93 100644 >--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java >+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java >@@ -87,6 +87,12 @@ > * COMPILER_PB_UNCLOSED_CLOSEABLE, > * COMPILER_PB_POTENTIALLY_UNCLOSED_CLOSEABLE > * COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE >+ * COMPILER_ANNOTATION_NULL_ANALYSIS >+ * COMPILER_NULLABLE_ANNOTATION_NAME >+ * COMPILER_NONNULL_ANNOTATION_NAME >+ * COMPILER_PB_NULL_SPECIFICATION_VIOLATION >+ * COMPILER_PB_POTENTIAL_NULL_SPECIFICATION_VIOLATION >+ * COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO > *******************************************************************************/ > > package org.eclipse.jdt.core; >@@ -1407,6 +1413,213 @@ > */ > public static final String COMPILER_PB_EXPLICITLY_CLOSED_AUTOCLOSEABLE = PLUGIN_ID + ".compiler.problem.explicitlyClosedAutoCloseable"; //$NON-NLS-1$ > /** >+ * Compiler option ID: Annotation-based Null Analysis. >+ * <p>This option controls whether the compiler will use null annotations for >+ * improved analysis of (potential) null references.</p> >+ * <p>If enabled the compiler will interpret the annotation types defined using >+ * {@link #COMPILER_NONNULL_ANNOTATION_NAME} and {@link #COMPILER_NULLABLE_ANNOTATION_NAME} >+ * as specifying whether or not a given type includes the value <code>null</code>.</p> >+ * <p>The effect of these analyses is further controled by the options >+ * {@link #COMPILER_PB_NULL_SPECIFICATION_VIOLATION}, >+ * {@link #COMPILER_PB_POTENTIAL_NULL_SPECIFICATION_VIOLATION} and >+ * {@link #COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO}. >+ * </p> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nullanalysis"</code></dd> >+ * <dt>Possible values:</dt><dd><code>{ "disabled", "enabled" }</dd> >+ * <dt>Default:</dt><dd><code>"disabled"</code></dd> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_ANNOTATION_NULL_ANALYSIS = PLUGIN_ID + ".compiler.annotation.nullanalysis"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Name of Annotation Type for Nullable Types. >+ * <p>This option defines a fully qualified Java type name that the compiler may use >+ * to perform special null analysis.</p> >+ * <p>If the annotation specified by this option is applied to a type in a method >+ * signature or variable declaration this will be interpreted as a specification >+ * that <code>null</code> is a legal value in that position. Currently supported >+ * positions are: method parameters, method return type and local variables.</p> >+ * <p>If a value whose type >+ * is annotated with this annotation is dereferenced without checking for null >+ * the compiler will trigger a diagnostic as further controlled by >+ * {@link #COMPILER_PB_POTENTIAL_NULL_REFERENCE}.</p> >+ * <p>The compiler may furthermore check adherence to the null specification as >+ * further controlled by {@link #COMPILER_PB_NULL_SPECIFICATION_VIOLATION}, >+ * {@link #COMPILER_PB_POTENTIAL_NULL_SPECIFICATION_VIOLATION} and >+ * {@link #COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO}. >+ * </p> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nullable"</code></dd> >+ * <dt>Possible values:</dt><dd>any legal, fully qualified Java type name, must resolve to an annotation type.</dd> >+ * <dt>Default:</dt><dd><code>"org.eclipse.jdt.annotation.Nullable"</code></dd> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_NULLABLE_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nullable"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Name of Annotation Type for Non-Null Types. >+ * <p>This option defines a fully qualified Java type name that the compiler may use >+ * to perform special null analysis.</p> >+ * <p>If the annotation specified by this option is applied to a type in a method >+ * signature or variable declaration this will be interpreted as a specification >+ * that <code>null</code> is <b>not</b> a legal value in that position. Currently >+ * supported positions are: method parameters, method return type and local variables.</p> >+ * <p>For values declared with this annotation the compiler will never trigger a null >+ * reference diagnostic (as controlled by {@link #COMPILER_PB_POTENTIAL_NULL_REFERENCE} >+ * and {@link #COMPILER_PB_NULL_REFERENCE}), because the assumption is made that null >+ * will never occur at runtime in these positions.</p> >+ * <p>The compiler may furthermore check adherence to the null specification as further >+ * controlled by {@link #COMPILER_PB_NULL_SPECIFICATION_VIOLATION}, >+ * {@link #COMPILER_PB_POTENTIAL_NULL_SPECIFICATION_VIOLATION} and >+ * {@link #COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO}. >+ * </p> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nonnull"</code></dd> >+ * <dt>Possible values:</dt><dd>any legal, fully qualified Java type name, must resolve to an annotation type.</dd> >+ * <dt>Default:</dt><dd><code>"org.eclipse.jdt.annotation.NonNull"</code></dd> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_NONNULL_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nonnull"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Name of Annotation Type to specify a nullness default for unannotated types. >+ * <p>This option defines a fully qualified Java type name that the compiler may use >+ * to perform special null analysis.</p> >+ * <p>If the annotation is applied without an argument all unannotated types in method signatures >+ * within the annotated element will be treated as if they were specified with the non-null annotation >+ * (see {@link #COMPILER_NONNULL_ANNOTATION_NAME}).</p> >+ * <p>If the annotation is applied without the constant <code>false</code> as its argument >+ * all corresponding defaults at outer scopes will be canceled for the annotated element. >+ * This includes defaults specified using this annotation type or a default defined using >+ * the compiler option {@link #COMPILER_NONNULL_IS_DEFAULT}.</p> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault"</code></dd> >+ * <dt>Possible values:</dt><dd>any legal, fully qualified Java type name, must resolve to an annotation type. >+ * That annotation type should have exactly one boolean parameter.</dd> >+ * <dt>Default:</dt><dd><code>"org.eclipse.jdt.annotation.NonNullByDefault"</code></dd> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nonnullbydefault"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Globally specify non-null as the assumed default for unannotated types. >+ * <p>When enabled this option globally achieves the same effect >+ * as specifying {@link #COMPILER_NONNULL_ANNOTATION_NAME} does for individual elements.</p> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nonnullisdefault"</code></dd> >+ * <dt>Possible values:</dt><dd><code>{ "disabled", "enabled" }</code>.</dd> >+ * <dt>Default:</dt><dd><code>"disabled"</code></dd> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_NONNULL_IS_DEFAULT = PLUGIN_ID + ".compiler.annotation.nonnullisdefault"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Reporting Violations of Null Specifications. >+ * <p>When enabled, the compiler will issue an error or a warning whenever one of the >+ * following situations is detected: >+ * <ol> >+ * <li>A method declared with a nonnull annotation returns an expression that is >+ * statically known to evaluate to a null value.</li> >+ * <li>An expression that is statically known to evaluate to a null value is passed >+ * as an argument in a method call where the corresponding parameter of the called >+ * method is declared with a nonnull annotation.</li> >+ * <li>An expression that is statically known to evaluate to a null value is assigned >+ * to a local variable that is declared with a nonnull annotation.</li> >+ * <li>A method that overrides an inherited method declared with a nonnull annotation >+ * tries to relax that contract by specifying a nullable annotation >+ * (prohibition of contravariant return).</li> >+ * <li>A method that overrides an inherited method which has a nullable declaration >+ * for at least one of its parameters, tries to tighten that null contract by >+ * specifying a nonnull annotation for its corresponding parameter >+ * (prohibition of covariant parameters).</li> >+ * </ol> >+ * </p> >+ * <p>The compiler options {@link #COMPILER_NONNULL_ANNOTATION_NAME} and >+ * {@link #COMPILER_NULLABLE_ANNOTATION_NAME} control which annotations the compiler >+ * shall interpret as nonnull or nullable annotations, respectively. >+ * </p> >+ * <dl> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.nullContractViolation"</code></dd> >+ * <dt>Possible values:</dt><dd><code>{ "error", "warning", "ignore" }</code></dd> >+ * <dt>Default:</dt><dd><code>"error"</code></dd> >+ * </dl> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_PB_NULL_SPECIFICATION_VIOLATION = PLUGIN_ID + ".compiler.problem.nullSpecViolation"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Reporting Violations of Null Specifications with Potential Null Value. >+ * <p>When enabled, the compiler will issue an error or a warning whenever one of the >+ * following situations is detected: >+ * <ol> >+ * <li>A method declared with a nonnull annotation returns an expression that is >+ * statically known to evaluate to a null value on some flow.</li> >+ * <li>An expression that is statically known to evaluate to a null value on some flow >+ * is passed as an argument in a method call where the corresponding parameter of >+ * the called method is declared with a nonnull annotation.</li> >+ * <li>An expression that is statically known to evaluate to a null value on some flow >+ * is assigned to a local variable that is declared with a nonnull annotation.</li> >+ * </ol> >+ * </p> >+ * <p>The compiler options {@link #COMPILER_NONNULL_ANNOTATION_NAME} and >+ * {@link #COMPILER_NULLABLE_ANNOTATION_NAME} control which annotations the compiler >+ * shall interpret as nonnull or nullable annotations, respectively. >+ * </p> >+ * <dl> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.potentialNullSpecViolation"</code></dd> >+ * <dt>Possible values:</dt><dd><code>{ "error", "warning", "ignore" }</code></dd> >+ * <dt>Default:</dt><dd><code>"error"</code></dd> >+ * </dl> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_PB_POTENTIAL_NULL_SPECIFICATION_VIOLATION = PLUGIN_ID + ".compiler.problem.potentialNullSpecViolation"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Reporting Insufficient Information for Analysing Adherence to Null Specifications. >+ * <p>When enabled, the compiler will issue an error or a warning whenever one of the >+ * following situations is detected: >+ * <ol> >+ * <li>A method declared with a nonnull annotation returns an expression for which >+ * insufficient nullness information is available for statically proving that no >+ * flow will pass a null value at runtime.</li> >+ * <li>An expression for which insufficient nullness information is available for >+ * statically proving that it will never evaluate to a null value at runtime >+ * is passed as an argument in a method call where the corresponding parameter of >+ * the called method is declared with a nonnull annotation.</li> >+ * <li>An expression for which insufficient nullness information is available for >+ * statically proving that it will never evaluate to a null value at runtime >+ * is assigned to a local variable that is declared with a nonnull annotation.</li> >+ * </ol> >+ * Insufficient nullness information is usually a consequence of using other unannotated >+ * variables or methods. >+ * </p> >+ * <p>The compiler options {@link #COMPILER_NONNULL_ANNOTATION_NAME} and >+ * {@link #COMPILER_NULLABLE_ANNOTATION_NAME} control which annotations the compiler >+ * shall interpret as nonnull or nullable annotations, respectively. >+ * </p> >+ * <dl> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.nullSpecInsufficientInfo"</code></dd> >+ * <dt>Possible values:</dt><dd><code>{ "error", "warning", "ignore" }</code></dd> >+ * <dt>Default:</dt><dd><code>"warning"</code></dd> >+ * </dl> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO = PLUGIN_ID + ".compiler.problem.nullSpecInsufficientInfo"; //$NON-NLS-1$ >+ /** >+ * Compiler option ID: Reporting Redundant Null Annotations. >+ * <p>When enabled, the compiler will issue an error or a warning when a non-null annotation >+ * (see {@link #COMPILER_NONNULL_ANNOTATION_NAME}) >+ * is applied although the same effect is already achieved by a default applicable at the >+ * current location. Such default may be effective by enabling the option >+ * {@link #COMPILER_NONNULL_IS_DEFAULT} or by using the annotation specified by the option >+ * {@link #COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME}. >+ * </p> >+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation"</code></dd> >+ * <dt>Possible values:</dt><dd><code>{ "error", "warning", "ignore" }</code></dd> >+ * <dt>Default:</dt><dd><code>"warning"</code></dd> >+ * </dl> >+ * @since 3.8 >+ * @category CompilerOptionID >+ */ >+ public static final String COMPILER_PB_REDUNDANT_NULL_ANNOTATION = PLUGIN_ID + ".compiler.problem.redundantNullAnnotation"; //$NON-NLS-1$ >+ /** > * Compiler option ID: Setting Source Compatibility Mode. > * <p>Specify whether which source level compatibility is used. From 1.4 on, <code>'assert'</code> is a keyword > * reserved for assertion support. Also note, than when toggling to 1.4 mode, the target VM >diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ReconcileWorkingCopyOperation.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ReconcileWorkingCopyOperation.java >index 8095a3d..382ec02 100644 >--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ReconcileWorkingCopyOperation.java >+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ReconcileWorkingCopyOperation.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2008 IBM Corporation and others. >+ * Copyright (c) 2000, 2011 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.core; > >@@ -132,6 +133,8 @@ > if (categorizedProblems == null) continue; > for (int i = 0, length = categorizedProblems.length; i < length; i++) { > CategorizedProblem problem = categorizedProblems[i]; >+ if (problem.getCategoryID() == CategorizedProblem.CAT_BUILDPATH) >+ continue; // don't report these problems against any CU, are already reported against the project > if (JavaModelManager.VERBOSE){ > System.out.println("PROBLEM FOUND while reconciling : " + problem.getMessage());//$NON-NLS-1$ > } >diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java >index 531b2b5..45d24ca 100644 >--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java >+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2009 IBM Corporation and others. >+ * Copyright (c) 2000, 2011 IBM Corporation and others. > * All rights reserved. This program and the accompanying materials > * are made available under the terms of the Eclipse Public License v1.0 > * which accompanies this distribution, and is available at >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Stephan Herrmann - Contribution for bug 186342 [compiler][null] Using annotations for null checking > *******************************************************************************/ > package org.eclipse.jdt.internal.core.builder; > >@@ -700,6 +701,9 @@ > ); > // even if we're not keeping more markers, still fall through rest of the problem reporting, so that offending > // IsClassPathCorrect problem gets recorded since it may help locate the offending reference >+ } else if (problem.getCategoryID() == CategorizedProblem.CAT_BUILDPATH) { >+ // also report other build-path problems against the project, but using a normal problem marker >+ resource = this.javaBuilder.currentProject; > } > > String markerType = problem.getMarkerType();
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 186342
:
74851
|
86246
|
86247
|
89354
|
184335
|
185507
|
186796
|
186798
|
186872
|
186876
|
186889
|
186890
|
206525
|
206557
|
207270
|
207308
|
207478
|
207565
|
207567
|
207573
|
207592
|
207604
|
207635
|
207637
|
207639
|
207657
|
207659
|
207672
|
207677
|
207685