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 206525 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]
tests & implementation v8
Bug_186342_v8.patch (text/plain), 175.69 KB, created by
Stephan Herrmann
on 2011-11-07 09:57:11 EST
(
hide
)
Description:
tests & implementation v8
Filename:
MIME Type:
Creator:
Stephan Herrmann
Created:
2011-11-07 09:57:11 EST
Size:
175.69 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..d676963 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,7 @@ > " <argument value=\"---OUTPUT_DIR_PLACEHOLDER---\"/>\n" + > " </command_line>\n" + > " <options>\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" + >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/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..b748675 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; > } >@@ -795,6 +832,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 +1049,29 @@ > 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); >+ if (this.isAnnotationBasedNullAnalysisEnabled) { >+ optionsMap.put(OPTION_ReportNullSpecViolation, getSeverityString(NullSpecViolation)); >+ optionsMap.put(OPTION_ReportPotentialNullSpecViolation, getSeverityString(PotentialNullSpecViolation)); >+ optionsMap.put(OPTION_ReportNullSpecInsufficientInfo, getSeverityString(NullSpecInsufficientInfo)); >+ optionsMap.put(OPTION_ReportRedundantNullAnnotation, getSeverityString(RedundantNullAnnotation)); >+ if (this.nullableAnnotationName != null) { >+ char[] compoundName = CharOperation.concatWith(this.nullableAnnotationName, '.'); >+ optionsMap.put(OPTION_NullableAnnotationName, String.valueOf(compoundName)); >+ } >+ if (this.nonNullAnnotationName != null) { >+ char[] compoundName = CharOperation.concatWith(this.nonNullAnnotationName, '.'); >+ optionsMap.put(OPTION_NonNullAnnotationName, String.valueOf(compoundName)); >+ } >+ if (this.nonNullByDefaultAnnotationName != null) { >+ char[] compoundName = CharOperation.concatWith(this.nonNullByDefaultAnnotationName, '.'); >+ optionsMap.put(OPTION_NonNullByDefaultAnnotationName, String.valueOf(compoundName)); >+ } >+ if (this.defaultNonNullness == TagBits.AnnotationNonNull) >+ optionsMap.put(OPTION_NonNullIsDefault, CompilerOptions.ENABLED); >+ else >+ optionsMap.put(OPTION_NonNullIsDefault, CompilerOptions.DISABLED); >+ } > return optionsMap; > } > >@@ -1441,6 +1505,38 @@ > 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) { >+ if (ENABLED.equals(optionValue)) { >+ this.isAnnotationBasedNullAnalysisEnabled = true; >+ // ensure that we actually have annotation names to use: >+ ensureNullAnnotationNames(); >+ } else if (DISABLED.equals(optionValue)) { >+ this.isAnnotationBasedNullAnalysisEnabled = false; >+ } >+ } >+ 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; >+ ensureNullAnnotationNames(); >+ } else { >+ this.defaultNonNullness = 0; >+ } >+ } >+ } > > // Javadoc options > if ((optionValue = optionsMap.get(OPTION_DocCommentSupport)) != null) { >@@ -1554,6 +1650,14 @@ > } > } > } >+ private void ensureNullAnnotationNames() { >+ if (this.nullableAnnotationName == null) >+ this.nullableAnnotationName = CompilerOptions.DEFAULT_NULLABLE_ANNOTATION_NAME; >+ if (this.nonNullAnnotationName == null) >+ this.nonNullAnnotationName = CompilerOptions.DEFAULT_NONNULL_ANNOTATION_NAME; >+ if (this.nonNullByDefaultAnnotationName == null) >+ this.nonNullByDefaultAnnotationName = CompilerOptions.DEFAULT_NONNULLBYDEFAULT_ANNOTATION_NAME; >+ } > public String toString() { > StringBuffer buf = new StringBuffer("CompilerOptions:"); //$NON-NLS-1$ > buf.append("\n\t- local variables debug attributes: ").append((this.produceDebugAttributes & ClassFileConstants.ATTR_VARS) != 0 ? "ON" : " OFF"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >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..4e84d7b 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,39 @@ > } > } > >+private void initNullAnnotationPackages() { >+ this.nullAnnotationsInitialized = true; >+ 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 +1542,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/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}
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