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 206557 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 v9
Bug_186342_v9.patch (text/plain), 191.20 KB, created by
Stephan Herrmann
on 2011-11-07 18:01:00 EST
(
hide
)
Description:
test & implementation v9
Filename:
MIME Type:
Creator:
Stephan Herrmann
Created:
2011-11-07 18:01:00 EST
Size:
191.20 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..bb67d89 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 >@@ -1799,6 +1799,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 +1862,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 9f0995d..9e23bb3 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 >@@ -380,6 +380,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)); >@@ -472,6 +473,7 @@ > expectedProblemAttributes.put("IllegalAccessFromTypeVariable", 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 +508,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 +694,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 +734,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 +751,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 +779,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 +788,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); >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..7d8ef09 >--- /dev/null >+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java >@@ -0,0 +1,2143 @@ >+/******************************************************************************* >+ * 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.internal.compiler.impl.CompilerOptions; >+ >+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_nonnull_var_in_constrol_structure_3" }; >+// 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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ defaultOptions.put(CompilerOptions.OPTION_ReportPotentialNullReference, CompilerOptions.ERROR); >+ defaultOptions.put(CompilerOptions.OPTION_ReportRedundantNullCheck, CompilerOptions.ERROR); >+ defaultOptions.put(CompilerOptions.OPTION_ReportRawTypeReference, CompilerOptions.IGNORE); >+ defaultOptions.put(CompilerOptions.OPTION_IncludeNullInfoFromAsserts, CompilerOptions.ENABLED); >+ >+ defaultOptions.put(CompilerOptions.OPTION_ReportMissingOverrideAnnotationForInterfaceMethodImplementation, CompilerOptions.DISABLED); >+ >+ // enable null annotations: >+ defaultOptions.put(CompilerOptions.OPTION_AnnotationBasedNullAnalysis, CompilerOptions.ENABLED); >+ // leave other new options at these defaults: >+// defaultOptions.put(CompilerOptions.OPTION_ReportNullContractViolation, CompilerOptions.ERROR); >+// defaultOptions.put(CompilerOptions.OPTION_ReportPotentialNullContractViolation, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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 null 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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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 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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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 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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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 class X cannot implement the corresponding method from type 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 class X cannot implement the corresponding method from type 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 class X cannot implement the corresponding method from type 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 class X cannot implement the corresponding method from type 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() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ 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" >+ }, >+ customOptions, >+ ""); >+} >+//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(CompilerOptions.OPTION_ReportRedundantNullCheck, CompilerOptions.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(CompilerOptions.OPTION_ReportRedundantNullCheck, CompilerOptions.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(CompilerOptions.OPTION_ReportRedundantNullCheck, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.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(CompilerOptions.OPTION_SuppressOptionalErrors, CompilerOptions.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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.foo.Nullable"); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.foo.Nullable"); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.foo.MayBeNull"); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.foo.MayBeNull"); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "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(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportNullSpecInsufficientInfo, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.foo.MayBeNull"); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "org.foo.MustNotBeNull"); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.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(CompilerOptions.OPTION_NonNullAnnotationName, "NichtNull"); >+ runNegativeTestWithLibs( >+ new String[] { >+ "X.java", >+ "public class X {\n" + >+ "}\n" >+ }, >+ customOptions, >+ "----------\n" + >+ "1. ERROR in X.java (at line 0)\n" + >+ " public class X {\n" + >+ " ^\n" + >+ "Cannot use the unqualified name \'NichtNull\' as an annotation name for null specification\n" + >+ "----------\n"); >+} >+public void test_default_nullness_002() { >+ Map customOptions = getCompilerOptions(); >+ customOptions.put(CompilerOptions.OPTION_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.ENABLED); >+ customOptions.put(CompilerOptions.OPTION_ReportRedundantNullAnnotation, CompilerOptions.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_ReportNullReference, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, CompilerOptions.ERROR); >+ customOptions.put(CompilerOptions.OPTION_NonNullIsDefault, CompilerOptions.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..1202528 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 >@@ -96,6 +96,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..060109d 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 >@@ -7494,7 +7494,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..f3af24f 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 >@@ -7497,7 +7497,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/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java >index a89866f..9ff2c32 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 >@@ -1412,6 +1412,38 @@ > /** @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; >+ >+ /** > * External problems -- These are problems defined by other plugins > */ > >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..8e2ff52 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 >@@ -254,6 +254,14 @@ > // 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; >+ >+ // for method declaration to avoid duplicate invocation of bindArguments() >+ public static final int HasBoundArguments = ASTNode.Bit10; >+ > public ASTNode() { > > super(); >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..28b526d 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 >@@ -10,8 +10,11 @@ > *******************************************************************************/ > 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.*; >@@ -66,13 +69,50 @@ > 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; >+ } > > /** > * Bind and add argument's binding into the scope of the method > */ > public void bindArguments() { > >- if (this.arguments != null) { >+ if (this.arguments != null && (this.bits & HasBoundArguments) == 0) { >+ this.bits |= HasBoundArguments; > // by default arguments in abstract/native methods are considered to be used (no complaint is expected) > if (this.binding == null) { > for (int i = 0, length = this.arguments.length; i < length; i++) { >@@ -85,6 +125,12 @@ > for (int i = 0, length = this.arguments.length; i < length; i++) { > Argument argument = this.arguments[i]; > argument.bind(this.scope, this.binding.parameters[i], used); >+ // argument.bind() 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); >+ } > if (argument.annotations != null) { > if (paramAnnotations == null) { > paramAnnotations = new AnnotationBinding[length][]; >@@ -143,6 +189,21 @@ > } > } > >+ 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); >+ } >+ } >+ } >+ } >+ > public CompilationResult compilationResult() { > > return this.compilationResult; >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..e03c085 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 >@@ -53,6 +53,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..f282456 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 >@@ -173,6 +173,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/Assignment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Assignment.java >index 6a8fa14..16e7fe3 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 >@@ -65,7 +65,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..4a202cc 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 >@@ -113,9 +113,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..296364d 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 >@@ -23,6 +23,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..a04838a 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 >@@ -81,6 +81,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 b11e476..7a33ee6 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 >@@ -85,7 +85,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 >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..40878dc 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 >@@ -41,6 +41,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..456370a 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 >@@ -99,6 +99,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 +115,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 +274,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..af17100 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 >@@ -79,9 +79,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 >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..0befde9 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 >@@ -86,6 +86,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..613528a 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 >@@ -963,7 +963,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..0966573 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 >@@ -44,6 +44,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 +121,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 99d0fcb..975ca3b 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 >@@ -57,8 +57,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..fbad42e 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 >@@ -17,6 +17,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 +84,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 +171,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 +455,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..2e7737b 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 >@@ -52,6 +52,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 +70,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 +554,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 +767,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..3e9d4cd 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 >@@ -12,6 +12,7 @@ > 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 +22,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 +248,9 @@ > continue; > } > break; >+ case ASSIGN_TO_NONNULL: >+ this.parent.recordNullityMismatch(scope, expression, flowInfo.nullStatus(local), this.expectedTypes[i]); >+ break; > default: > // never happens > } >@@ -337,6 +342,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 +679,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..c8c50df 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 >@@ -26,6 +26,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 +143,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 +259,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 +387,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 +596,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 +770,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 +840,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 +1057,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 +1219,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 +1507,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..3b3aca8 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 >@@ -102,8 +102,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 +117,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..e9abe25 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 >@@ -16,6 +16,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 +384,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 +592,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 +1148,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/LookupEnvironment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java >index 2e7fe97..0c80de1 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 >@@ -76,6 +76,8 @@ > public boolean isProcessingAnnotations = false; > public boolean mayTolerateMissingType = false; > >+ boolean nullAnnotationsInitialized = false; >+ > 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 +85,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; >@@ -723,6 +726,8 @@ > * 3. Create the method bindings > */ > public PackageBinding createPackage(char[][] compoundName) { >+ if (!this.nullAnnotationsInitialized) >+ initNullAnnotationPackages(); > PackageBinding packageBinding = getPackage0(compoundName[0]); > if (packageBinding == null || packageBinding == TheNotFoundPackage) { > packageBinding = new PackageBinding(compoundName[0], this); >@@ -1073,6 +1078,54 @@ > 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 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. >@@ -1106,6 +1159,8 @@ > * Answer null if the package cannot be found. > */ > PackageBinding getTopLevelPackage(char[] name) { >+ if (!this.nullAnnotationsInitialized) >+ initNullAnnotationPackages(); > PackageBinding packageBinding = getPackage0(name); > if (packageBinding != null) { > if (packageBinding == TheNotFoundPackage) >@@ -1387,6 +1442,41 @@ > } > } > >+private void initNullAnnotationPackages() { >+ this.nullAnnotationsInitialized = true; >+ if (!this.globalOptions.isAnnotationBasedNullAnalysisEnabled) >+ return; >+ char[][] compoundName = getNullableAnnotationName(); >+ if (compoundName != null) >+ initNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNullable); >+ compoundName = getNonNullAnnotationName(); >+ if (compoundName != null) >+ initNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNonNull); >+ compoundName = getNonNullByDefaultAnnotationName(); >+ if (compoundName != null) >+ initNullAnnotationPackage(compoundName, TypeIds.T_ConfiguredAnnotationNonNullByDefault); >+} >+ >+/** >+ * Create or retrieve the package holding the specified type and store the type name. >+ */ >+void initNullAnnotationPackage(char[][] typeName, int typeId) { >+ if (typeName.length < 2) { >+ this.problemReporter.nullAnnotationNameMustBeQualified(typeName); >+ return; >+ } >+ char[][] packageName = CharOperation.subarray(typeName, 0, typeName.length-1); >+ PackageBinding packageBinding = createPackage(packageName); >+ char[] simpleTypeName = typeName[typeName.length-1]; >+ if (typeId == TypeIds.T_ConfiguredAnnotationNullable) >+ packageBinding.nullableName = simpleTypeName; >+ else if (typeId == TypeIds.T_ConfiguredAnnotationNonNull) >+ packageBinding.nonNullName = simpleTypeName; >+ else if (typeId == TypeIds.T_ConfiguredAnnotationNonNullByDefault) >+ packageBinding.nonNullByDefaultName = simpleTypeName; >+} >+ >+ > boolean isMissingType(char[] typeName) { > for (int i = this.missingTypes == null ? 0 : this.missingTypes.size(); --i >= 0;) { > MissingTypeBinding missingType = (MissingTypeBinding) this.missingTypes.get(i); >@@ -1454,6 +1544,9 @@ > this.unitBeingCompleted = null; // in case AbortException occurred > > this.classFilePool.reset(); >+ >+ this.nullAnnotationsInitialized = false; >+ > // 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..0dce1a1 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 >@@ -34,6 +34,9 @@ > 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 +442,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 9522a2c..fb559f0 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 >@@ -164,6 +164,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) { >@@ -358,6 +360,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() { >@@ -932,7 +1028,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..af5664d 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 >@@ -22,6 +22,12 @@ > public LookupEnvironment environment; > HashtableOfType knownTypes; > HashtableOfPackage knownPackages; >+ >+ protected char[] nullableName = null; >+ protected char[] nonNullName = null; >+ protected char[] nonNullByDefaultName = null; >+ protected TypeBinding nullnessDefaultAnnotation; >+ > protected PackageBinding() { > // for creating problem package > } >@@ -58,6 +64,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() { >@@ -230,6 +238,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() { >+ if (this.nullnessDefaultAnnotation instanceof UnresolvedReferenceBinding) >+ return this.nullnessDefaultAnnotation = BinaryTypeBinding.resolveType(this.nullnessDefaultAnnotation, this.environment, false); >+ 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..e1995d1 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 >@@ -48,6 +48,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/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java >index d1f9cf4..565fdc4 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 >@@ -54,6 +54,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 +784,7 @@ > } > if ((this.tagBits & TagBits.AnnotationDeprecated) != 0) > this.modifiers |= ClassFileConstants.AccDeprecated; >+ evaluateNullAnnotations(this.tagBits); > } > return this.tagBits; > } >@@ -1088,6 +1092,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 +1596,103 @@ > typeParameters[i].binding = null; > return null; > } >+ callBindArguments(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 callBindArguments(MethodBinding method) { >+ 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.bindArguments(); >+ 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) >+ return this.nullnessDefaultAnnotation = >+ BinaryTypeBinding.resolveType(this.nullnessDefaultAnnotation, getPackage().environment, false); >+ 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(); >+ if (annotationBinding != null) >+ return annotationBinding; >+ >+ // global >+ long defaultNullness = environment.globalOptions.defaultNonNullness; >+ if (defaultNullness != 0) { >+ 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 if (defaultNullness == TagBits.AnnotationNullable) >+ this.scope.problemReporter().missingNullAnnotationType(environment.getNullableAnnotationName()); >+ 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..59a6a70 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 >@@ -132,6 +132,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 +152,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..18ab315 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 >@@ -104,6 +104,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..cbeeff7 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 >@@ -546,11 +546,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 0a3b191..efa3712 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 >@@ -84,6 +84,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 +288,7 @@ > return CompilerOptions.NullReference; > > case IProblem.PotentialNullLocalVariableReference: >+ case IProblem.PotentialNullMessageSendReference: > return CompilerOptions.PotentialNullReference; > > case IProblem.RedundantLocalVariableNullAssignment: >@@ -295,7 +297,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 +561,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 +576,8 @@ > switch (problemID) { > case IProblem.IsClassPathCorrect : > case IProblem.CorruptedSignature : >+ case IProblem.MissingNullAnnotationType : >+ case IProblem.NullAnnotationNameMustBeQualified : > return CategorizedProblem.CAT_BUILDPATH; > > default : >@@ -7999,4 +8027,172 @@ > trackVar.sourceStart, > trackVar.sourceEnd); > } >+ >+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) { >+ 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) { >+ org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration decl = sourceMethod; >+ org.eclipse.jdt.internal.compiler.ast.MethodDeclaration methodDecl = (org.eclipse.jdt.internal.compiler.ast.MethodDeclaration) decl; >+ sourceStart = methodDecl.returnType.sourceStart; >+ if (methodDecl.annotations != null) { >+ // should have a @NonNull annotation, search for it: >+ for (int j=0; j<methodDecl.annotations.length; j++) { >+ if (methodDecl.annotations[j].resolvedType.id == TypeIds.T_ConfiguredAnnotationNonNull) { >+ sourceStart = methodDecl.annotations[j].sourceStart; >+ break; >+ } >+ } >+ } >+ 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); >+} > } >\ No newline at end of file >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 ff1c56a..00e30c8 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 >@@ -653,6 +653,22 @@ > 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 null annotation: inherited method from {1} declares this parameter as @{2} >+919 = Missing null 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 class {1} cannot implement the corresponding method from type {2} due to incompatible nullness constraints >+923 = The nullness annotation is redundant with a default that applies to this location >+ > ### ELABORATIONS > ## Access restrictions > 78592 = The type {1} is not accessible due to restriction on classpath entry {0} >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..da9d4c7 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 >@@ -1407,6 +1407,166 @@ > */ > 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: 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: 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
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