blob: 0ad4ccfaadbfb61025bde46bfde32e38b40e25fe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 GK Software AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
import java.io.File;
import java.util.Map;
import junit.framework.Test;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
// see bug 186342 - [compiler][null] Using annotations for null checking
public class NullAnnotationTest extends AbstractComparableTest {
// class libraries including our default null annotation types:
String[] LIBS;
// names and content of custom annotations used in a few tests:
private static final String CUSTOM_NONNULL_NAME = "org/foo/NonNull.java";
private static final String CUSTOM_NONNULL_CONTENT =
"package org.foo;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({METHOD,PARAMETER,LOCAL_VARIABLE})\n" +
"public @interface NonNull {\n" +
"}\n";
private static final String CUSTOM_NULLABLE_NAME = "org/foo/Nullable.java";
private static final String CUSTOM_NULLABLE_CONTENT = "package org.foo;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({METHOD,PARAMETER,LOCAL_VARIABLE})\n" +
"public @interface Nullable {\n" +
"}\n";
public NullAnnotationTest(String name) {
super(name);
}
// Static initializer to specify tests subset using TESTS_* static variables
// All specified tests which do not belong to the class are skipped...
static {
// TESTS_NAMES = new String[] { "test_missing_default_annotation_03" };
// 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"));
if (bundleFile.isDirectory())
this.LIBS[len] = bundleFile.getPath()+"/bin";
else
this.LIBS[len] = bundleFile.getPath();
}
}
// Conditionally augment problem detection settings
static boolean setNullRelatedOptions = true;
protected Map getCompilerOptions() {
Map defaultOptions = super.getCompilerOptions();
if (setNullRelatedOptions) {
defaultOptions.put(JavaCore.COMPILER_PB_NULL_REFERENCE, JavaCore.ERROR);
defaultOptions.put(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE, JavaCore.ERROR);
defaultOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
defaultOptions.put(JavaCore.COMPILER_PB_INCLUDE_ASSERTS_IN_NULL_ANALYSIS, JavaCore.ENABLED);
defaultOptions.put(JavaCore.COMPILER_PB_MISSING_OVERRIDE_ANNOTATION_FOR_INTERFACE_METHOD_IMPLEMENTATION, JavaCore.DISABLED);
// enable null annotations:
defaultOptions.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED);
// leave other new options at these defaults:
// defaultOptions.put(CompilerOptions.OPTION_ReportNullContractViolation, JavaCore.ERROR);
// defaultOptions.put(CompilerOptions.OPTION_ReportPotentialNullContractViolation, JavaCore.ERROR);
// defaultOptions.put(CompilerOptions.OPTION_ReportNullContractInsufficientInfo, CompilerOptions.WARNING);
// defaultOptions.put(CompilerOptions.OPTION_NullableAnnotationName, "org.eclipse.jdt.annotation.Nullable");
// defaultOptions.put(CompilerOptions.OPTION_NonNullAnnotationName, "org.eclipse.jdt.annotation.NonNull");
}
return defaultOptions;
}
void runNegativeTestWithLibs(String[] testFiles, String expectedErrorLog) {
runNegativeTest(
testFiles,
expectedErrorLog,
this.LIBS,
false /*shouldFlush*/);
}
void runNegativeTestWithLibs(boolean shouldFlushOutputDirectory, String[] testFiles, Map customOptions, String expectedErrorLog) {
runNegativeTest(
shouldFlushOutputDirectory,
testFiles,
this.LIBS,
customOptions,
expectedErrorLog,
// runtime options
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
void runNegativeTestWithLibs(String[] testFiles, Map customOptions, String expectedErrorLog) {
runNegativeTestWithLibs(false /* flush output directory */, testFiles, customOptions, expectedErrorLog);
}
void runConformTestWithLibs(String[] testFiles, Map customOptions, String expectedCompilerLog) {
runConformTestWithLibs(false /* flush output directory */, testFiles, customOptions, expectedCompilerLog);
}
void runConformTestWithLibs(boolean shouldFlushOutputDirectory, String[] testFiles, Map customOptions, String expectedCompilerLog) {
runConformTest(
shouldFlushOutputDirectory,
testFiles,
this.LIBS,
customOptions,
expectedCompilerLog,
"",/* expected output */
"",/* expected error */
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
void runConformTest(String[] testFiles, Map customOptions, String expectedOutputString) {
runConformTest(
testFiles,
expectedOutputString,
null /*classLibraries*/,
true /*shouldFlushOutputDirectory*/,
null /*vmArguments*/,
customOptions,
null /*customRequestor*/);
}
// a nullable argument is dereferenced without a check
public void test_nullable_paramter_001() {
runNegativeTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" void foo(@Nullable Object o) {\n" +
" System.out.print(o.toString());\n" +
" }\n" +
"}\n"},
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" System.out.print(o.toString());\n" +
" ^\n" +
"Potential null pointer access: The variable o may be null at this location\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
}
// a null value is passed to a nullable argument
public void test_nullable_paramter_002() {
runConformTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" void foo(@Nullable Object o) {\n" +
" // nop\n" +
" }\n" +
" void bar() {\n" +
" foo(null);\n" +
" }\n" +
"}\n"},
"",
this.LIBS,
false/*shouldFlush*/,
null/*vmArgs*/);
}
// a non-null argument is checked for null
public void test_nonnull_parameter_001() {
runNegativeTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" void foo(@NonNull Object o) {\n" +
" if (o != null)\n" +
" System.out.print(o.toString());\n" +
" }\n" +
"}\n"},
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
"Redundant null check: The variable o is specified as @NonNull\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" +
"Null 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" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\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" +
"Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n");
}
// a ternary non-null expression is passed to a nonnull parameter
public void test_nonnull_parameter_006() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" void m1(@NonNull String a) {}\n" +
" void m2(@Nullable String b) {\n" +
" m1(b == null ? \"\" : b);\n" +
" }\n" +
"}\n"},
customOptions,
"" /* compiler output */);
}
// nullable value passed to a non-null parameter in a super-call
public void test_nonnull_parameter_007() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"XSub.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class XSub extends XSuper {\n" +
" XSub(@Nullable String b) {\n" +
" super(b);\n" +
" }\n" +
"}\n",
"XSuper.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class XSuper {\n" +
" XSuper(@NonNull String b) {\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in XSub.java (at line 4)\n" +
" super(b);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n");
}
// a nullable value is passed to a non-null parameter in an allocation expression
public void test_nonnull_parameter_008() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" X(@NonNull String a) {}\n" +
" static X create(@Nullable String b) {\n" +
" return new X(b);\n" +
" }\n" +
"}\n"},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" return new X(b);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n" /* compiler output */);
}
// a nullable value is passed to a non-null parameter in a qualified allocation expression
public void test_nonnull_parameter_009() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" class Local {\n" +
" Local(@NonNull String a) {}\n" +
" }\n" +
" Local create(@Nullable String b) {\n" +
" return this.new Local(b);\n" +
" }\n" +
"}\n"},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" return this.new Local(b);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n" /* compiler output */);
}
// null is passed to a non-null parameter in a qualified allocation expression, across CUs
public void test_nonnull_parameter_010() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"ContainingInner2.java",
"public class ContainingInner2 {\n" +
" public ContainingInner2 (@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" }\n" +
" public class Inner {\n" +
" public Inner (@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" }\n" +
" }\n" +
"}\n",
"X.java",
"public class X {\n" +
" void create() {\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" }\n" +
"}\n"},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in X.java (at line 4)\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" /* compiler output */);
}
// null is passed to a non-null parameter in a qualified allocation expression, target class read from .class
public void test_nonnull_parameter_011() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"ContainingInner2.java",
"public class ContainingInner2 {\n" +
" public ContainingInner2 (@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" }\n" +
" public class Inner {\n" +
" public Inner (@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" }\n" +
" }\n" +
"}\n",
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // flush directory
new String[] {
"X.java",
"public class X {\n" +
" void create() {\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" }\n" +
"}\n"},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in X.java (at line 4)\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" /* compiler output */);
}
// null is passed to a non-null parameter in a qualified allocation expression, generic constructor, target class read from .class
public void test_nonnull_parameter_012() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"ContainingInner2.java",
"public class ContainingInner2 {\n" +
" public ContainingInner2 (@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" }\n" +
" public class Inner {\n" +
" public <T> Inner (@org.eclipse.jdt.annotation.NonNull T o) {\n" +
" }\n" +
" }\n" +
"}\n",
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // flush directory
new String[] {
"X.java",
"public class X {\n" +
" void create() {\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" }\n" +
"}\n"},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in X.java (at line 4)\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" /* compiler output */);
}
// a method of a local class has a non-null parameter, client passes null
public void test_nonnull_parameter_013() {
runNegativeTestWithLibs(
new String[] {
"B.java",
"class B {\n" +
" void bar () {\n" +
" class Local {\n" +
" void callMe(@org.eclipse.jdt.annotation.NonNull Object o){\n" +
" }\n" +
" }\n" +
" Local l = new Local();\n" +
" l.callMe(null);\n" +
" } \n" +
"}\n"
},
"----------\n" +
"1. ERROR in B.java (at line 8)\n" +
" l.callMe(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// non-null varargs (message send)
public void test_nonnull_parameter_015() {
if (this.complianceLevel > ClassFileConstants.JDK1_7) {
fail("Reminder: should check if JSR 308 mandates a change in handling vararg elements (see bug 365983).");
return;
}
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" +
" void foo2(int i, @NonNull Object ... o) {\n" +
" if (o.length > 0 && o[0] != null)\n" +
" System.out.print(o[0].toString());\n" +
" }\n" +
" void bar() {\n" +
" foo((Object)null);\n" + // unchecked: single plain argument
" Object[] objs = null;\n" +
" foo(objs);\n" + // error
" foo(this, null);\n" + // unchecked: multiple plain arguments
" foo2(2, (Object)null);\n" + // unchecked: single plain argument
" foo2(2, null, this);\n" + // unchecked: multiple plain arguments
" foo2(2, null);\n" + // error
" }\n" +
"}\n"},
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
"Redundant null check: The variable o is specified as @NonNull\n" +
"----------\n" +
"2. ERROR in X.java (at line 14)\n" +
" foo(objs);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
"----------\n" +
"3. WARNING in X.java (at line 18)\n" +
" foo2(2, null);\n" +
" ^^^^^^^^^^^^^\n" +
"The argument of type null should explicitly be cast to Object[] for the invocation of the varargs method foo2(int, Object...) from type X. It could alternatively be cast to Object for a varargs invocation\n" +
"----------\n" +
"4. ERROR in X.java (at line 18)\n" +
" foo2(2, null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
}
// non-null varargs (allocation and explicit constructor calls)
public void test_nonnull_parameter_016() {
if (this.complianceLevel > ClassFileConstants.JDK1_7) {
fail("Reminder: should check if JSR 308 mandates a change in handling vararg elements (see bug 365983).");
return;
}
runNegativeTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" X(@NonNull Object ... o) {\n" +
" if (o != null)\n" +
" System.out.print(o.toString());\n" +
" }\n" +
" class Y extends X {\n" +
" Y(int i, @NonNull Object ... o) {\n" +
" super(i, (Object)null);\n" +
" }\n" +
" Y(char c, @NonNull Object ... o) {\n" +
" this(1, new Object(), null);\n" +
" }\n" +
" }\n" +
" void bar() {\n" +
" new X((Object[])null);\n" +
" new X(this, null);\n" +
" X x = new X(null, this);\n" +
" x.new Y(2, (Object)null);\n" +
" this.new Y(2, null, this);\n" +
" this.new Y(2, (Object[])null);\n" +
" }\n" +
"}\n"},
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
"Redundant null check: The variable o is specified as @NonNull\n" +
"----------\n" +
"2. ERROR in X.java (at line 16)\n" +
" new X((Object[])null);\n" +
" ^^^^^^^^^^^^^^\n" +
"Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
"----------\n" +
"3. ERROR in X.java (at line 21)\n" +
" this.new Y(2, (Object[])null);\n" +
" ^^^^^^^^^^^^^^\n" +
"Null type mismatch: required \'@NonNull Object[]\' but the provided value is null\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
}
// Bug 367203 - [compiler][null] detect assigning null to nonnull argument
public void test_nonnull_argument_001() {
runNegativeTestWithLibs(
new String[] {
"ShowNPE2.java",
"import org.eclipse.jdt.annotation.NonNullByDefault;\n" +
"@NonNullByDefault\n" +
"public class ShowNPE2 {\n" +
" public Object foo(Object o1, final boolean b) {\n" +
" o1 = null; // expect NPE error\n" +
" System.out.println(o1.toString()); \n" +
" return null; // expect NPE error\n" +
" }\n" +
"}"
},
"----------\n" +
"1. ERROR in ShowNPE2.java (at line 5)\n" +
" o1 = null; // expect NPE error\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in ShowNPE2.java (at line 7)\n" +
" return null; // expect NPE error\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// Bug 367203 - [compiler][null] detect assigning null to nonnull argument
public void test_nonnull_argument_002() {
runNegativeTestWithLibs(
new String[] {
"ShowNPE2.java",
"import org.eclipse.jdt.annotation.NonNullByDefault;\n" +
"@NonNullByDefault\n" +
"public class ShowNPE2 {\n" +
" public Object foo(Object o1, final boolean b) {\n" +
" bar(o1); // expecting no problem\n" +
" return null; // expect NPE error\n" +
" }\n" +
" void bar(Object o2) {}\n" +
"}"
},
"----------\n" +
"1. ERROR in ShowNPE2.java (at line 6)\n" +
" return null; // expect NPE error\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// a method of a local class has a non-null parameter, client passes potential null (msg send)
public void test_nonnull_parameter_014() {
runNegativeTestWithLibs(
new String[] {
"B.java",
"class B {\n" +
" void bar () {\n" +
" class Local {\n" +
" void callMe(@org.eclipse.jdt.annotation.NonNull Object o){\n" +
" }\n" +
" }\n" +
" Local l = new Local();\n" +
" l.callMe(getNull());\n" +
" }\n" +
" @org.eclipse.jdt.annotation.Nullable Object getNull() { return null; }" +
"}\n"
},
"----------\n" +
"1. ERROR in B.java (at line 8)\n" +
" l.callMe(getNull());\n" +
" ^^^^^^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
"----------\n");
}
// 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" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 6)\n" +
" o2 = null;\n" +
" ^^^^\n" +
"Null 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" +
"Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
}
// assigning potential null to a nonnull local variable - separate decl and assignment
public void test_nonnull_local_002() {
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;\n" +
" o1 = b ? null : new Object();\n" +
" @NonNull String o2;\n" +
" o2 = \"\";\n" +
" o2 = null;\n" +
" @NonNull Object o3;\n" +
" o3 = p;\n" +
" }\n" +
"}\n"},
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" o1 = b ? null : new Object();\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" o2 = null;\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is null\n" +
"----------\n" +
"3. WARNING in X.java (at line 10)\n" +
" o3 = p;\n" +
" ^\n" +
"Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n",
this.LIBS,
true /* shouldFlush*/);
}
// a method tries to tighten the type specification, super declares parameter o as @Nullable
// other parameters: s is redefined from not constrained to @Nullable which is OK
// third is redefined from not constrained to @NonNull which is bad, too
public void test_parameter_specification_inheritance_001() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" void foo(String s, @Nullable Object o, Object third) { }\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X extends Lib {\n" +
" @Override\n" +
" void foo(@Nullable String s, @NonNull Object o, @NonNull Object third) { System.out.print(o.toString()); }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" void foo(@Nullable String s, @NonNull Object o, @NonNull Object third) { System.out.print(o.toString()); }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter o, inherited method from Lib declares this parameter as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 4)\n" +
" void foo(@Nullable String s, @NonNull Object o, @NonNull Object third) { System.out.print(o.toString()); }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter third, inherited method from Lib does not constrain this parameter\n" +
"----------\n");
}
// a method body fails to redeclare the inherited null annotation, super declares parameter as @Nullable
public void test_parameter_specification_inheritance_002() {
runConformTest(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" void foo(@Nullable Object o) { }\n" +
"}\n"
},
"",
this.LIBS,
false/*shouldFlush*/,
null/*vmArgs*/);
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"public class X extends Lib {\n" +
" @Override\n" +
" void foo(Object o) {\n" +
" System.out.print(o.toString());\n" +
" }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" void foo(Object o) {\n" +
" ^^^^^^\n" +
"Missing nullable annotation: inherited method from Lib declares this parameter as @Nullable\n" +
"----------\n");
}
// a method relaxes the parameter null specification, super interface declares parameter o as @NonNull
// other (first) parameter just repeats the inherited @NonNull
public void test_parameter_specification_inheritance_003() {
runConformTest(
new String[] {
"IX.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public interface IX {\n" +
" void foo(@NonNull String s, @NonNull Object o);\n" +
"}\n",
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X implements IX {\n" +
" public void foo(@NonNull String s, @Nullable Object o) { ; }\n" +
" void bar() { foo(\"OK\", null); }\n" +
"}\n"
},
"",
this.LIBS,
false/*shouldFlush*/,
null/*vmArgs*/);
}
// a method adds a @NonNull annotation, super interface has no null annotation
// changing other from unconstrained to @Nullable is OK
public void test_parameter_specification_inheritance_004() {
runConformTest(
new String[] {
"IX.java",
"public interface IX {\n" +
" void foo(Object o, Object other);\n" +
"}\n"
});
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X implements IX {\n" +
" public void foo(@NonNull Object o, @Nullable Object other) { System.out.print(o.toString()); }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" public void foo(@NonNull Object o, @Nullable Object other) { System.out.print(o.toString()); }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter o, inherited method from IX does not constrain this parameter\n" +
"----------\n");
}
// a method tries to relax the null contract, super declares @NonNull return
public void test_parameter_specification_inheritance_005() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" @NonNull Object getObject() { return new Object(); }\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, //dont' flush
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X extends Lib {\n" +
" @Override\n" +
" @Nullable Object getObject() { return null; }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" @Nullable Object getObject() { return null; }\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with the @NonNull return from Lib.getObject()\n" +
"----------\n");
}
// super has no constraint for return, sub method confirms the null contract as @Nullable
public void test_parameter_specification_inheritance_006() {
runConformTest(
new String[] {
"Lib.java",
"public class Lib {\n" +
" Object getObject() { return null; }\n" +
"}\n"
});
runConformTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X extends Lib {\n" +
" @Override\n" +
" @Nullable Object getObject() { return null; }\n" +
"}\n"
},
null /*customOptions*/,
"");
}
// a method body violates the inherited null specification, super declares @NonNull return, missing redeclaration
public void test_parameter_specification_inheritance_007() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" @NonNull Object getObject() { return new Object(); }\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"public class X extends Lib {\n" +
" @Override\n" +
" Object getObject() { return null; }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" Object getObject() { return null; }\n" +
" ^^^^^^\n" +
"The return type is incompatible with the @NonNull return from Lib.getObject()\n" +
"----------\n");
}
//a method body violates the @NonNull return specification (repeated from super)
public void test_parameter_specification_inheritance_007a() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" @NonNull Object getObject() { return new Object(); }\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X extends Lib {\n" +
" @Override\n" +
" @NonNull Object getObject() { return null; }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" @NonNull Object getObject() { return null; }\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// a client potentially violates the inherited null specification, super interface declares @NonNull parameter
public void test_parameter_specification_inheritance_008() {
Map options = getCompilerOptions();
options.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"IX.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public interface IX {\n" +
" void printObject(@NonNull Object o);\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"public class X implements IX {\n" +
" public void printObject(Object o) { System.out.print(o.toString()); }\n" +
"}\n",
"M.java",
"public class M{\n" +
" void foo(IX x, Object o) {\n" +
" x.printObject(o);\n" +
" }\n" +
"}\n"
},
options,
"----------\n" +
// additional error:
"1. ERROR in X.java (at line 2)\n" +
" public void printObject(Object o) { System.out.print(o.toString()); }\n" +
" ^^^^^^\n" +
"Missing non-null annotation: inherited method from IX declares this parameter as @NonNull\n" +
"----------\n" +
// main error:
"----------\n" +
"1. ERROR in M.java (at line 3)\n" +
" x.printObject(o);\n" +
" ^\n" +
"Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n");
}
// a static method has a more relaxed null contract than a like method in the super class, but no overriding.
public void test_parameter_specification_inheritance_009() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" @NonNull static Object getObject() { return new Object(); }\n" +
"}\n",
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X extends Lib {\n" +
" @Nullable static Object getObject() { return null; }\n" +
"}\n"
},
null /*customOptions*/,
"");
}
// class default is nonnull, method and its super both use the default
public void test_parameter_specification_inheritance_010() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" protected String getString(String s) {\n" +
" if (Character.isLowerCase(s.charAt(0)))\n" +
" return getString(s);\n" +
" return s;\n" +
" }\n" +
"}\n",
"p1/Y.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y extends X {\n" +
" @Override\n" +
" protected String getString(String s) {\n" +
" return super.getString(s);\n" +
" }\n" +
"}\n",
},
customOptions,
"");
}
// class default is nonnull, method and its super both use the default, super-call passes null
public void test_parameter_specification_inheritance_011() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" protected String getString(String s) {\n" +
" if (Character.isLowerCase(s.charAt(0)))\n" +
" return getString(s);\n" +
" return s;\n" +
" }\n" +
"}\n",
"p1/Y.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y extends X {\n" +
" @Override\n" +
" protected String getString(String s) {\n" +
" return super.getString(null);\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in p1\\Y.java (at line 7)\n" +
" return super.getString(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is null\n" +
"----------\n");
}
// methods from two super types have different null contracts.
// sub-class merges both using the weakest common contract
public void test_parameter_specification_inheritance_012() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" public @Nullable String getString(String s1, @Nullable String s2, @NonNull String s3) {\n" +
" return s1;\n" +
" }\n" +
"}\n",
"p1/IY.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public interface IY {\n" +
" @NonNull String getString(@NonNull String s1, @NonNull String s2, @Nullable String s3);\n" +
"}\n",
"p1/Y.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Y extends X implements IY {\n" +
" @Override\n" +
" public @NonNull String getString(@Nullable String s1, @Nullable String s2, @Nullable String s3) {\n" +
" return \"\";\n" +
" }\n" +
"}\n",
},
customOptions,
"");
}
// methods from two super types have different null contracts.
// sub-class overrides this method in non-conforming ways
public void test_parameter_specification_inheritance_013() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" public @Nullable String getString(String s1, @Nullable String s2, @NonNull String s3) {\n" +
" return s1;\n" +
" }\n" +
"}\n",
"p1/IY.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public interface IY {\n" +
" @NonNull String getString(@NonNull String s1, @NonNull String s2, @Nullable String s3);\n" +
"}\n",
"p1/Y.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Y extends X implements IY {\n" +
" @Override\n" +
" public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" +
" return \"\";\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in p1\\Y.java (at line 5)\n" +
" public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with the @NonNull return from IY.getString(String, String, String)\n" +
"----------\n" +
"2. ERROR in p1\\Y.java (at line 5)\n" +
" public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" +
" ^^^^^^\n" +
"Missing non-null annotation: inherited method from IY declares this parameter as @NonNull\n" +
"----------\n" +
"3. ERROR in p1\\Y.java (at line 5)\n" +
" public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter s2, inherited method from X declares this parameter as @Nullable\n" +
"----------\n" +
"4. ERROR in p1\\Y.java (at line 5)\n" +
" public @Nullable String getString(String s1, @NonNull String s2, @NonNull String s3) {\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter s3, inherited method from IY declares this parameter as @Nullable\n" +
"----------\n");
}
// methods from two super types have different null contracts.
// sub-class does not override, but should to bridge the incompatibility
public void test_parameter_specification_inheritance_014() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"p1/IY.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public interface IY {\n" +
" public @NonNull String getString1(String s);\n" +
" public @NonNull String getString2(String s);\n" +
" public String getString3(@Nullable String s);\n" +
" public @NonNull String getString4(@Nullable String s);\n" +
" public @NonNull String getString5(@Nullable String s);\n" +
" public @Nullable String getString6(@NonNull String s);\n" +
"}\n",
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" public @Nullable String getString1(String s) {\n" + // incomp. return
" return s;\n" +
" }\n" +
" public String getString2(String s) {\n" + // incomp. return
" return s;\n" +
" }\n" +
" public String getString3(String s) {\n" + // incomp. arg
" return \"\";\n" +
" }\n" +
" public @NonNull String getString4(@Nullable String s) {\n" +
" return \"\";\n" +
" }\n" +
" public @NonNull String getString5(@NonNull String s) {\n" + // incomp. arg
" return s;\n" +
" }\n" +
" public @NonNull String getString6(@Nullable String s) {\n" +
" return \"\";\n" +
" }\n" +
"}\n",
"p1/Y.java",
"package p1;\n" +
"public class Y extends X implements IY {\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in p1\\Y.java (at line 2)\n" +
" public class Y extends X implements IY {\n" +
" ^\n" +
"The method getString1(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" +
"----------\n" +
"2. ERROR in p1\\Y.java (at line 2)\n" +
" public class Y extends X implements IY {\n" +
" ^\n" +
"The method getString2(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" +
"----------\n" +
"3. ERROR in p1\\Y.java (at line 2)\n" +
" public class Y extends X implements IY {\n" +
" ^\n" +
"The method getString5(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" +
"----------\n" +
"4. ERROR in p1\\Y.java (at line 2)\n" +
" public class Y extends X implements IY {\n" +
" ^\n" +
"The method getString3(String) from X cannot implement the corresponding method from IY due to incompatible nullness constraints\n" +
"----------\n");
}
// a nullable return value is dereferenced without a check
public void test_nullable_return_001() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @Nullable Object getObject() { return null; }\n" +
" void foo() {\n" +
" Object o = getObject();\n" +
" System.out.print(o.toString());\n" +
" }\n" +
"}\n"
},
"----------\n" +
"1. ERROR in X.java (at line 6)\n" +
" System.out.print(o.toString());\n" +
" ^\n" +
"Potential null pointer access: The variable o may be null at this location\n" +
"----------\n");
}
// a nullable return value is dereferenced without a check, method is read from .class file
public void test_nullable_return_002() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" @Nullable Object getObject() { return null; }\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"public class X {\n" +
" void foo(Lib l) {\n" +
" Object o = l.getObject();\n" +
" System.out.print(o.toString());\n" +
" }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" System.out.print(o.toString());\n" +
" ^\n" +
"Potential null pointer access: The variable o may be null at this location\n" +
"----------\n");
}
// a non-null return value is checked for null, method is read from .class file
public void test_nonnull_return_001() {
runConformTestWithLibs(
new String[] {
"Lib.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Lib {\n" +
" @NonNull Object getObject() { return new Object(); }\n" +
"}\n"
},
null /*customOptions*/,
"");
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"X.java",
"public class X {\n" +
" void foo(Lib l) {\n" +
" Object o = l.getObject();\n" +
" if (o != null)\n" +
" System.out.print(o.toString());\n" +
" }\n" +
"}\n"
},
null /*customOptions*/,
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" if (o != null)\n" +
" ^\n" +
"Redundant null check: The variable o cannot be null at this location\n" +
"----------\n");
}
// a non-null method returns null
public void test_nonnull_return_003() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object getObject(boolean b) {\n" +
" if (b)\n" +
" return null;\n" + // definite specification violation despite enclosing "if"
" return new Object();\n" +
" }\n" +
"}\n"
},
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" return null;\n" +
" ^^^^\n" +
"Null 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" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
"----------\n");
}
// a non-null method returns its non-null argument
public void test_nonnull_return_005() {
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object getObject(@NonNull Object o) {\n" +
" return o;\n" +
" }\n" +
"}\n"
},
null, // options
"");
}
//a non-null method has insufficient nullness info for its return value
public void test_nonnull_return_006() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object getObject(Object o) {\n" +
" return o;\n" +
" }\n" +
"}\n"
},
"----------\n" +
"1. WARNING in X.java (at line 4)\n" +
" return o;\n" +
" ^\n" +
"Null type safety: The expression of type Object needs unchecked conversion to conform to \'@NonNull Object\'\n" +
"----------\n");
}
// a result from a nullable method is directly dereferenced
public void test_nonnull_return_007() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @Nullable Object getObject() {\n" +
" return null;\n" +
" }\n" +
" void test() {\n" +
" getObject().toString();\n" +
" }\n" +
"}\n"
},
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" getObject().toString();\n" +
" ^^^^^^^^^^^\n" +
"Potential null pointer access: The method getObject() may return null\n" +
"----------\n");
}
// a result from a nonnull method is directly checked for null: redundant
public void test_nonnull_return_008() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object getObject() {\n" +
" return new Object();\n" +
" }\n" +
" void test() {\n" +
" if (getObject() == null)\n" +
" throw new RuntimeException();\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" if (getObject() == null)\n" +
" ^^^^^^^^^^^\n" +
"Redundant null check: The method getObject() cannot return null\n" +
"----------\n");
}
// a result from a nonnull method is directly checked for null (from local): redundant
public void test_nonnull_return_009() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object getObject() {\n" +
" return new Object();\n" +
" }\n" +
" void test() {\n" +
" Object left = null;\n" +
" if (left != getObject())\n" +
" throw new RuntimeException();\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 8)\n" +
" if (left != getObject())\n" +
" ^^^^\n" +
"Redundant null check: The variable left can only be null at this location\n" +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" if (left != getObject())\n" +
" ^^^^^^^^^^^\n" +
"Redundant null check: The method getObject() cannot return null\n" +
"----------\n");
}
// a result from a nonnull method is directly checked for null (from local): not redundant due to loop
public void test_nonnull_return_009a() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
runConformTestWithLibs(
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" +
" for (int i=0; i<3; i++) {\n" +
" if (left != getObject())\n" +
" throw new RuntimeException();\n" +
" left = new Object();\n" +
" }\n" +
" }\n" +
"}\n"
},
customOptions,
"");
}
// a result from a nonnull method is directly checked for null (from local): redundant despite loop
// disabled because only one of two desirable errors is raised
// need to integrate @NonNull expressions (MessageSend and more) into deferred analysis by FlowContext
public void _test_nonnull_return_009b() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
runConformTestWithLibs(
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" +
" for (int i=0; i<3; i++) {\n" +
" if (left != getObject())\n" +
" throw new RuntimeException();\n" +
" // left remains null\n" +
" }\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 9)\n" +
" if (left != getObject())\n" +
" ^^^^\n" +
"Redundant null check: The variable left can only be null at this location\n" +
"----------\n" +
"2. ERROR in X.java (at line 9)\n" +
" if (left != getObject())\n" +
" ^^^^^^^^^^^\n" +
"Redundant null check: The method getObject() cannot return null\n" +
"----------\n");
}
// a result from a nullable method is assigned and checked for null (from local): not redundant
// see also Bug 336428 - [compiler][null] bogus warning "redundant null check" in condition of do {} while() loop
public void test_nonnull_return_010() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @Nullable X getX() {\n" +
" return new X();\n" +
" }\n" +
" void test() {\n" +
" X left = this;\n" +
" do {\n" +
" if (left == null) \n" +
" throw new RuntimeException();\n" +
" } while ((left = left.getX()) != null);\n" + // no warning/error here!
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 9)\n" +
" if (left == null) \n" +
" ^^^^\n" +
"Null comparison always yields false: The variable left cannot be null at this location\n" +
"----------\n");
}
// a non-null method returns a checked-for null value, but that branch is dead code
public void test_nonnull_return_011() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" Object getObject(Object dubious) {\n" +
" if (dubious == null)\n" + // redundant
" return dubious;\n" + // definitely null, but not reported inside dead code
" return new Object();\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" if (dubious == null)\n" +
" ^^^^^^^\n" +
"Null comparison always yields false: The variable dubious is specified as @NonNull\n" +
"----------\n" +
"2. WARNING in X.java (at line 6)\n" +
" return dubious;\n" +
" ^^^^^^^^^^^^^^^\n" +
"Dead code\n" +
"----------\n");
}
// a non-null method returns a definite null from a conditional expression
// requires the fix for Bug 354554 - [null] conditional with redundant condition yields weak error message
// TODO(SH): ENABLE!
public void _test_nonnull_return_012() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" Object getObject(Object dubious) {\n" +
" return dubious == null ? dubious : null;\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 5)\n" +
" return dubious == null ? dubious : null;\n" +
" ^^^^^^^\n" +
"Null comparison always yields false: The variable dubious cannot be null at this location\n" +
"----------\n" +
"2. ERROR in X.java (at line 5)\n" +
" return dubious == null ? dubious : null;\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// don't apply any default annotations to return void
public void test_nonnull_return_013() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" void getObject() {}\n" +
"}\n",
"Y.java",
"public class Y extends X {\n" +
" @Override\n" +
" void getObject() {}\n" + // don't complain, void takes no (default) annotation
"}\n"
},
customOptions,
"");
}
// bug 365835: [compiler][null] inconsistent error reporting.
public void test_nonnull_return_014() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.NonNull;\n" +
"\n" +
"public class X {\n" +
" @NonNull\n" +
" public Object foo(Object x, int y) {\n" +
" @NonNull Object local;\n" +
" while (true) {\n" +
" if (y == 4) {\n" +
" local = x; // error\n" +
" return x; // only a warning.\n" +
" }\n" +
" x = null;\n" +
" }\n" +
" }\n" +
"}"
},
"----------\n" +
"1. ERROR in X.java (at line 9)\n" +
" local = x; // error\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 10)\n" +
" return x; // only a warning.\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
"----------\n");
}
// suppress an error regarding null-spec violation
public void test_suppress_001() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS, JavaCore.ENABLED);
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @SuppressWarnings(\"null\")\n" +
" @NonNull Object getObject(@Nullable Object o) {\n" +
" return o;\n" + // 'o' is only potentially null
" }\n" +
"}\n"
},
customOptions,
"");
}
// mixed use of fully qualified name / explicit import
public void test_annotation_import_001() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.Nullable");
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull");
runConformTestWithLibs(
new String[] {
CUSTOM_NULLABLE_NAME,
CUSTOM_NULLABLE_CONTENT,
CUSTOM_NONNULL_NAME,
CUSTOM_NONNULL_CONTENT,
"Lib.java",
"public class Lib {\n" +
" @org.foo.NonNull Object getObject() { return new Object(); }\n" + // FQN
"}\n",
"X.java",
"import org.foo.NonNull;\n" + // explicit import
"public class X {\n" +
" @NonNull Object getObject(@NonNull Lib l) {\n" +
" return l.getObject();\n" +
" }\n" +
"}\n"
},
customOptions,
"");
}
// use of explicit imports throughout
public void test_annotation_import_002() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.Nullable");
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull");
runConformTest(
new String[] {
CUSTOM_NULLABLE_NAME,
CUSTOM_NULLABLE_CONTENT,
CUSTOM_NONNULL_NAME,
CUSTOM_NONNULL_CONTENT,
"Lib.java",
"import org.foo.NonNull;\n" +
"public class Lib {\n" +
" @NonNull Object getObject() { return new Object(); }\n" +
"}\n",
"X.java",
"import org.foo.NonNull;\n" +
"public class X {\n" +
" @NonNull Object getObject(@org.foo.Nullable String dummy, @NonNull Lib l) {\n" +
" Object o = l.getObject();" +
" return o;\n" +
" }\n" +
"}\n"
},
customOptions,
"");
}
// explicit import of existing annotation types
// using a Lib without null specifications
public void test_annotation_import_005() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.MayBeNull");
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.MustNotBeNull");
runNegativeTest(
true/*shouldFlushOutputDirectory*/,
new String[] {
"org/foo/MayBeNull.java",
"package org.foo;\n" +
"import java.lang.annotation.*;\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"public @interface MayBeNull {}\n",
"org/foo/MustNotBeNull.java",
"package org.foo;\n" +
"import java.lang.annotation.*;\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"public @interface MustNotBeNull {}\n",
"Lib.java",
"public class Lib {\n" +
" Object getObject() { return new Object(); }\n" +
"}\n",
"X.java",
"import org.foo.*;\n" +
"public class X {\n" +
" @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" +
" return l.getObject();\n" +
" }\n" +
"}\n",
},
null /*no libs*/,
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" return l.getObject();\n" +
" ^^^^^^^^^^^^^\n" +
"Null type safety: The expression of type Object needs unchecked conversion to conform to \'@MustNotBeNull Object\'\n" +
"----------\n",
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
// a non-null method returns a value obtained from an unannotated method, missing annotation types
public void test_annotation_import_006() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.MayBeNull");
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.MustNotBeNull");
runNegativeTest(
true/*shouldFlushOutputDirectory*/,
new String[] {
"Lib.java",
"public class Lib {\n" +
" Object getObject() { return new Object(); }\n" +
"}\n",
"X.java",
"public class X {\n" +
" @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" +
" return l.getObject();\n" +
" }\n" +
"}\n"
},
null /* no libs */,
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 2)\n" +
" @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" +
" ^^^^^^^^^^^^^\n" +
"MustNotBeNull cannot be resolved to a type\n" +
"----------\n" +
"2. ERROR in X.java (at line 2)\n" +
" @MustNotBeNull Object getObject(@MustNotBeNull Lib l) {\n" +
" ^^^^^^^^^^^^^\n" +
"MustNotBeNull cannot be resolved to a type\n" +
"----------\n",
JavacTestOptions.Excuse.EclipseWarningConfiguredAsError);
}
// 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
// disabled because specific error is not currently raised
public void _test_illegal_annotation_002() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "NichtNull");
runNegativeTestWithLibs(
new String[] {
"X.java",
"public class X {\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 1)\n" +
" public class X {\n" +
" ^\n" +
"Cannot use the unqualified name \'NichtNull\' as an annotation name for null specification\n" +
"----------\n");
}
// a null annotation is illegally used on a void method:
public void test_illegal_annotation_003() {
runNegativeTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull void foo() {}\n" +
"}\n"
},
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" @NonNull void foo() {}\n" +
" ^^^^^^^^^^^^^\n" +
"The nullness annotation @NonNull is not applicable for the primitive type void\n" +
"----------\n",
this.LIBS,
false/*shouldFlush*/);
}
// a null annotation is illegally used on a primitive type parameter
public void test_illegal_annotation_004() {
runNegativeTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" void foo(@Nullable int i) {}\n" +
"}\n"
},
"----------\n" +
"1. ERROR in X.java (at line 3)\n" +
" void foo(@Nullable int i) {}\n" +
" ^^^^^^^^^^^^^\n" +
"The nullness annotation @Nullable is not applicable for the primitive type int\n" +
"----------\n",
this.LIBS,
false/*shouldFlush*/);
}
// a null annotation is illegally used on a primitive type local var
public void test_illegal_annotation_005() {
runNegativeTest(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" int foo() {\n" +
" @Nullable int i = 3;\n" +
" return i;\n" +
" }\n" +
"}\n"
},
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" @Nullable int i = 3;\n" +
" ^^^^^^^^^^^^^\n" +
"The nullness annotation @Nullable is not applicable for the primitive type int\n" +
"----------\n",
this.LIBS,
false/*shouldFlush*/);
}
// a configured annotation type does not exist
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=186342#c133
public void test_illegal_annotation_006() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "nullAnn.Nullable");
runNegativeTestWithLibs(
new String[] {
"p/Test.java",
"package p;\n" +
"import nullAnn.*; // 1 \n" +
"\n" +
"public class Test { \n" +
"\n" +
" void foo(@nullAnn.Nullable Object o) { // 2\n" +
" o.toString(); \n" +
" }\n" +
"}"
},
customOptions,
"----------\n" +
"1. ERROR in p\\Test.java (at line 2)\n" +
" import nullAnn.*; // 1 \n" +
" ^^^^^^^\n" +
"The import nullAnn cannot be resolved\n" +
"----------\n" +
"2. ERROR in p\\Test.java (at line 6)\n" +
" void foo(@nullAnn.Nullable Object o) { // 2\n" +
" ^^^^^^^\n" +
"nullAnn cannot be resolved to a type\n" +
"----------\n");
}
// a configured annotation type does not exist
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=186342#c186
public void test_illegal_annotation_007() {
Map customOptions = getCompilerOptions();
runNegativeTestWithLibs(
new String[] {
"p/Test.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"interface TestInt{\n" +
" @NonNull Object foo();\n" +
"}\n" +
"\n" +
"public class Test { \n" +
" void bar() {" +
" new TestInt() {\n" +
" @org public Object foo() {\n" +
" }\n" +
" };\n" +
" }\n" +
"}"
},
customOptions,
"----------\n" +
"1. ERROR in p\\Test.java (at line 9)\n" +
" @org public Object foo() {\n" +
" ^^^\n" +
"org cannot be resolved to a type\n" +
"----------\n" +
"2. ERROR in p\\Test.java (at line 9)\n" +
" @org public Object foo() {\n" +
" ^^^^^^\n" +
"The return type is incompatible with the @NonNull return from TestInt.foo()\n" +
"----------\n");
}
public void test_default_nullness_002() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
// This option currently does nothing:
// customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" Object getObject(@Nullable Object o) {\n" +
" return new Object();\n" +
" }\n" +
"}\n",
"Y.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\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 5)\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 5)\n" +
" @Nullable Object getObject(Object o) {\n" +
" ^^^^^^\n" +
"Illegal redefinition of parameter o, inherited method from X declares this parameter as @Nullable\n" +
"----------\n");
}
// package default is non-null
public void test_default_nullness_003() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" protected Object getObject(@Nullable Object o) {\n" +
" return new Object();\n" +
" }\n" +
"}\n",
"p2/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"package p2;\n",
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Y extends p1.X {\n" +
" @Override\n" +
" protected @Nullable Object getObject(@Nullable Object o) {\n" +
" bar(o);\n" +
" return o;\n" +
" }\n" +
" void bar(Object o2) { }\n" + // parameter is nonnull per package default
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in p2\\Y.java (at line 5)\n" +
" protected @Nullable Object getObject(@Nullable Object o) {\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with the @NonNull return from X.getObject(Object)\n" +
"----------\n" +
"2. ERROR in p2\\Y.java (at line 6)\n" +
" bar(o);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
"----------\n");
}
// package level default is consumed from package-info.class, similarly for type level default
public void test_default_nullness_003a() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" protected Object getObject(@Nullable Object o) {\n" +
" return new Object();\n" +
" }\n" +
" protected void bar(Object o2) { }\n" + // parameter is nonnull per type default
"}\n",
"p2/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"package p2;\n",
},
customOptions,
"");
// check if default is visible from package-info.class.
runNegativeTestWithLibs(
false, // don't flush
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Y extends p1.X {\n" +
" @Override\n" +
" protected @Nullable Object getObject(@Nullable Object o) {\n" + // can't override inherited default nonnull
" bar(o);\n" + // parameter is nonnull in super class's .class file
" accept(o);\n" +
" return o;\n" +
" }\n" +
" void accept(Object a) {}\n" + // governed by package level default
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in p2\\Y.java (at line 5)\n" +
" protected @Nullable Object getObject(@Nullable Object o) {\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with the @NonNull return from X.getObject(Object)\n" +
"----------\n" +
"2. ERROR in p2\\Y.java (at line 6)\n" +
" bar(o);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
"----------\n" +
"3. ERROR in p2\\Y.java (at line 7)\n" +
" accept(o);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
"----------\n");
}
// same as test_default_nullness_003a, but default-induced annotations are combined with explicit ones (not null related)
public void test_default_nullness_003b() {
Map customOptions = getCompilerOptions();
runConformTestWithLibs(
new String[] {
"p1/Annot.java",
"package p1;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({METHOD,PARAMETER})\n" +
"public @interface Annot {}\n",
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" protected @Annot Object getObject(@Annot @Nullable Object o) {\n" +
" return new Object();\n" +
" }\n" +
" protected @Annot void bar(@Annot 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(@p1.Annot 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" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
"----------\n" +
"3. ERROR in p2\\Y.java (at line 7)\n" +
" accept(o);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is specified as @Nullable\n" +
"----------\n");
}
// don't apply type-level default to non-reference type
public void test_default_nullness_004() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" protected Object getObject(boolean o) {\n" +
" return new Object();\n" +
" }\n" +
"}\n",
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Y extends p1.X {\n" +
" @Override\n" +
" protected @NonNull Object getObject(boolean o) {\n" +
" return o ? this : new Object();\n" +
" }\n" +
"}\n"
},
customOptions,
"");
}
// package default is non-null
// see also Bug 354536 - compiling package-info.java still depends on the order of compilation units
public void test_default_nullness_005() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull");
runNegativeTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"public class X {\n" +
" class Inner {" +
" protected Object getObject(String s) {\n" +
" return null;\n" +
" }\n" +
" }\n" +
"}\n",
"p1/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"package p1;\n",
CUSTOM_NONNULL_NAME,
CUSTOM_NONNULL_CONTENT
},
customOptions,
"----------\n" +
"1. ERROR in p1\\X.java (at line 4)\n" +
" return null;\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// package default is non-null, package-info.java read before the annotation type
// compile order: beginToCompile(X.Inner) triggers reading of package-info.java before the annotation type was read
public void test_default_nullness_006() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
customOptions.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull");
runNegativeTestWithLibs(
new String[] {
"p1/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"package p1;\n",
"p1/X.java",
"package p1;\n" +
"public class X {\n" +
" class Inner {" +
" protected Object getObject(String s) {\n" +
" return null;\n" +
" }\n" +
" }\n" +
"}\n",
CUSTOM_NONNULL_NAME,
CUSTOM_NONNULL_CONTENT
},
customOptions,
"----------\n" +
"1. ERROR in p1\\X.java (at line 4)\n" +
" return null;\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// global default nonnull, but return may be null
// DISABLED due to dysfunctional global default after Bug 366063 - Compiler should not add synthetic @NonNull annotations
public void _test_default_nullness_007() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
// customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @Nullable Object dangerous() {\n" +
" return null;\n" +
" }\n" +
" Object broken() {\n" +
" return dangerous();\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" return dangerous();\n" +
" ^^^^^^^^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is inferred as @Nullable\n" +
"----------\n");
}
// cancel type level default to comply with super specification
public void test_default_nullness_008() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"public class X {\n" +
" protected Object getObject(Object o) {\n" +
" return new Object();\n" +
" }\n" +
"}\n",
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y extends p1.X {\n" +
" @Override\n" +
" @NonNullByDefault(false)\n" +
" protected Object getObject(Object o) {\n" +
" if (o.toString().length() == 0)\n" + // dereference without a warning
" return null;\n" + // return null without a warning
" return o.toString();\n" +
" }\n" +
"}\n"
},
customOptions,
"");
}
// cancel outer type level default to comply with super specification
public void test_default_nullness_009() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"p1/X.java",
"package p1;\n" +
"public class X {\n" +
" protected Object getObject(Object o) {\n" +
" return new Object();\n" +
" }\n" +
"}\n",
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y { \n" +
" @NonNullByDefault(false)\n" +
" static class Z extends p1.X {\n" +
" @Override\n" +
" protected Object getObject(Object o) {\n" +
" if (o.toString().length() == 0) {\n" +
" o = null;\n" + // assign null without a warning
" bar(o); // error: arg is declared @NonNull\n" +
" return null;\n" +
" }\n" +
" return o.toString();\n" +
" }\n" +
" String bar(@NonNull Object o) {\n" +
" return getObject(o).toString();" +
" }\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in p2\\Y.java (at line 11)\n" +
" bar(o); // error: arg is declared @NonNull\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// non-null declarations are redundant within a default scope.
public void test_default_nullness_010() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y {\n" +
" protected @NonNull Object getObject(@NonNull Object o) {\n" +
" return o;\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. WARNING in p2\\Y.java (at line 5)\n" +
" protected @NonNull Object getObject(@NonNull Object o) {\n" +
" ^^^^^^^^^^^^^^^\n" +
"The nullness annotation is redundant with a default that applies to this location\n" +
"----------\n" +
"2. WARNING in p2\\Y.java (at line 5)\n" +
" protected @NonNull Object getObject(@NonNull Object o) {\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"The nullness annotation is redundant with a default that applies to this location\n" +
"----------\n");
}
// package-info declares nonnull-by-default
// special compile order due to import of type from that package
// cf. https://bugs.eclipse.org/bugs/show_bug.cgi?id=186342#add_comment
public void test_default_nullness_011() {
runNegativeTestWithLibs(
new String[] {
"Main.java",
"import p1.C;\n" +
"public class Main {\n" +
" void test(@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" o = null;\n" +
" new C(null);\n" +
" }\n" +
"}\n",
"p1/C.java",
"package p1;\n" +
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"public class C {\n" +
" public C (Object o) {}\n" +
"}\n",
"p1/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"package p1;\n"
},
"----------\n" +
"1. ERROR in Main.java (at line 4)\n" +
" o = null;\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in Main.java (at line 5)\n" +
" new C(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"----------\n" +
"1. WARNING in p1\\C.java (at line 2)\n" +
" @org.eclipse.jdt.annotation.NonNullByDefault\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing package p1\n" +
"----------\n");
}
// Bug 365836 - [compiler][null] Incomplete propagation of null defaults.
public void test_default_nullness_012() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.NonNullByDefault;\n" +
"import org.eclipse.jdt.annotation.Nullable;\n" +
"\n" +
"public class X {\n" +
" @NonNullByDefault \n" +
" public void foo(@Nullable String [] args) {\n" +
" class local {\n" +
" void zoo(Object o) {\n" +
" }\n" +
" };\n" +
" new local().zoo(null); // defaults applying from foo\n" +
" }\n" +
"}"
},
"----------\n" +
"1. ERROR in X.java (at line 11)\n" +
" new local().zoo(null); // defaults applying from foo\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// Bug 365836 - [compiler][null] Incomplete propagation of null defaults.
public void test_default_nullness_013() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.NonNullByDefault;\n" +
"import org.eclipse.jdt.annotation.Nullable;\n" +
"\n" +
"@SuppressWarnings(\"unused\")\n" +
"public class X {\n" +
" @NonNullByDefault \n" +
" public void foo(@Nullable String [] args) {\n" +
" class local {\n" +
" class Deeply {\n" +
" Object zoo() {\n" +
" return null; // defaults applying from foo\n" +
" }\n" +
" }\n" +
" };\n" +
" }\n" +
"}"
},
"----------\n" +
"1. ERROR in X.java (at line 11)\n" +
" return null; // defaults applying from foo\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// bug 367154 - [compiler][null] Problem in propagating null defaults.
public void test_default_nullness_014() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.NonNullByDefault;\n" +
"import org.eclipse.jdt.annotation.Nullable;\n" +
"\n" +
"@SuppressWarnings(\"unused\")\n" +
"public class X {\n" +
"\n" +
" public void foo(@Nullable String [] args) {\n" +
" @NonNullByDefault\n" +
" class local {\n" +
" class Deeply {\n" +
" Object zoo() {\n" +
" return null; // expect error here\n" +
" }\n" +
" }\n" +
" };\n" +
" }\n" +
"}"
},
"----------\n" +
"1. ERROR in X.java (at line 12)\n" +
" return null; // expect error here\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// bug 367154 - [compiler][null] Problem in propagating null defaults.
// initializer involved
public void test_default_nullness_015() {
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.NonNullByDefault;\n" +
"import org.eclipse.jdt.annotation.Nullable;\n" +
"\n" +
"@SuppressWarnings(\"unused\")\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" {\n" +
" class local {\n" +
" class Deeply {\n" +
" Object zoo() {\n" +
" return null; // expect error here\n" +
" }\n" +
" }\n" +
" };\n" +
" }\n" +
"}"
},
"----------\n" +
"1. ERROR in X.java (at line 11)\n" +
" return null; // expect error here\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n");
}
// redundant default annotations - class vs. inner class
public void test_redundant_annotation_01() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y {\n" +
" @NonNullByDefault class Inner {\n" +
" @NonNullByDefault class DeepInner {}\n" +
" }\n" +
" class Inner2 {\n" +
" @NonNullByDefault class DeepInner2 {\n" +
" }\n" +
" void foo() {\n" +
" @SuppressWarnings(\"unused\") @NonNullByDefault class Local {}\n" +
" }\n" +
" }\n" +
"}\n" +
"@NonNullByDefault class V {}\n",
"p3/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault package p3;\n",
"p3/Z.java",
"package p3;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Z {\n" +
"}\n" +
"class X {\n" +
" @NonNullByDefault class Inner {}\n" +
" class Inner2 {\n" +
" @NonNullByDefault class DeepInner {}\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. WARNING in p2\\Y.java (at line 5)\n" +
" @NonNullByDefault class Inner {\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Y\n" +
"----------\n" +
"2. WARNING in p2\\Y.java (at line 6)\n" +
" @NonNullByDefault class DeepInner {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Y.Inner\n" +
"----------\n" +
"3. WARNING in p2\\Y.java (at line 9)\n" +
" @NonNullByDefault class DeepInner2 {\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Y\n" +
"----------\n" +
"4. WARNING in p2\\Y.java (at line 12)\n" +
" @SuppressWarnings(\"unused\") @NonNullByDefault class Local {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Y\n" +
"----------\n" +
"----------\n" +
"1. WARNING in p3\\Z.java (at line 3)\n" +
" @NonNullByDefault\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing package p3\n" +
"----------\n" +
"2. WARNING in p3\\Z.java (at line 7)\n" +
" @NonNullByDefault class Inner {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing package p3\n" +
"----------\n" +
"3. WARNING in p3\\Z.java (at line 9)\n" +
" @NonNullByDefault class DeepInner {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing package p3\n" +
"----------\n");
}
// redundant default annotations - class vs. method
public void test_redundant_annotation_02() {
Map customOptions = getCompilerOptions();
runConformTestWithLibs(
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y {\n" +
" @NonNullByDefault void foo() {}\n" +
"}\n" +
"class Z {\n" +
" @NonNullByDefault void bar() {\n" +
" @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" +
" @NonNullByDefault void fubar() {}\n" +
" }\n" +
" }\n" +
" @NonNullByDefault void zink() {\n" +
" @SuppressWarnings(\"unused\") class Bork {\n" +
" @NonNullByDefault void jubar() {}\n" +
" }\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. WARNING in p2\\Y.java (at line 5)\n" +
" @NonNullByDefault void foo() {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Y\n" +
"----------\n" +
"2. WARNING in p2\\Y.java (at line 9)\n" +
" @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing method bar()\n" +
"----------\n" +
"3. WARNING in p2\\Y.java (at line 10)\n" +
" @NonNullByDefault void fubar() {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Zork\n" +
"----------\n" +
"4. WARNING in p2\\Y.java (at line 15)\n" +
" @NonNullByDefault void jubar() {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing method zink()\n" +
"----------\n");
}
//redundant default annotations - class vs. method - generics
public void test_redundant_annotation_02g() {
Map customOptions = getCompilerOptions();
runConformTestWithLibs(
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y<TY> {\n" +
" @NonNullByDefault <TF> void foo(TF arg) {}\n" +
"}\n" +
"class Z {\n" +
" @NonNullByDefault <TB> void bar() {\n" +
" @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" +
" @NonNullByDefault void fubar(TB arg) {}\n" +
" }\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. WARNING in p2\\Y.java (at line 5)\n" +
" @NonNullByDefault <TF> void foo(TF arg) {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Y<TY>\n" +
"----------\n" +
"2. WARNING in p2\\Y.java (at line 9)\n" +
" @NonNullByDefault @SuppressWarnings(\"unused\") class Zork {\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing method bar()\n" +
"----------\n" +
"3. WARNING in p2\\Y.java (at line 10)\n" +
" @NonNullByDefault void fubar(TB arg) {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing type Zork\n" +
"----------\n");
}
// test missing default nullness annotation for types in default package
public void test_missing_default_annotation_01() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_MISSING_NONNULL_BY_DEFAULT_ANNOTATION, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"Lib.java",
"public class Lib {\n" +
" Object getObject() { return new Object(); }\n" +
"}\n",
"X.java",
"public class X {\n" +
" class XInner{}\n" + // don't warn for inner types
" Object getObject(Lib l) {\n" +
" return l.getObject();\n" +
" }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in Lib.java (at line 1)\n" +
" public class Lib {\n" +
" ^^^\n" +
"A default nullness annotation has not been specified for the type Lib\n" +
"----------\n" +
"----------\n" +
"1. ERROR in X.java (at line 1)\n" +
" public class X {\n" +
" ^\n" +
"A default nullness annotation has not been specified for the type X\n" +
"----------\n");
}
// test missing default nullness annotation for a package with package-info
public void test_missing_default_annotation_02() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_MISSING_NONNULL_BY_DEFAULT_ANNOTATION, JavaCore.ERROR);
runNegativeTestWithLibs(
new String[] {
"p2/package-info.java",
"package p2;\n",
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y {\n" +
" void foo() {}\n" +
"}\n",
"p3/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault package p3;\n",
"p3/Z.java",
"package p3;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Z {\n" +
" @NonNullByDefault void bar() {}\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in p2\\package-info.java (at line 1)\n" +
" package p2;\n" +
" ^^\n" +
"A default nullness annotation has not been specified for the package p2\n" +
"----------\n" +
"----------\n" +
"1. WARNING in p3\\Z.java (at line 4)\n" +
" @NonNullByDefault void bar() {}\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"Nullness default is redundant with a default specified for the enclosing package p3\n" +
"----------\n");
}
// redundant default annotations - class vs. inner class
// ensure that disabling null annotations also disables this diagnostic
public void test_redundant_annotation_04() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.DISABLED);
runConformTestWithLibs(
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Y {\n" +
" @NonNullByDefault class Inner {\n" +
" @NonNullByDefault class DeepInner {}\n" +
" }\n" +
" class Inner2 {\n" +
" @NonNullByDefault class DeepInner2 {\n" +
" }\n" +
" @NonNullByDefault void foo(@Nullable @NonNull Object arg) {\n" +
" @NonNullByDefault class Local {}\n" +
" }\n" +
" }\n" +
"}\n" +
"@NonNullByDefault class V {}\n",
"p3/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault package p3;\n",
"p3/Z.java",
"package p3;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class Z {\n" +
"}\n" +
"class X {\n" +
" @NonNullByDefault class Inner {}\n" +
" class Inner2 {\n" +
" @NonNullByDefault class DeepInner {}\n" +
" }\n" +
"}\n"
},
customOptions,
"");
}
// contradictory null annotations
public void test_contradictory_annotations_01() {
Map customOptions = getCompilerOptions();
runNegativeTestWithLibs(
new String[] {
"p2/Y.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Y {\n" +
" void foo(@NonNull @Nullable Object o) {}\n" +
" @Nullable @NonNull Object bar() {\n" +
" @NonNull @Nullable Object o = null;\n" +
" return o;\n" +
" }\n" +
"}\n" +
"class Z {\n" +
" @NonNullByDefault void bar() {}\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in p2\\Y.java (at line 4)\n" +
" void foo(@NonNull @Nullable Object o) {}\n" +
" ^^^^^^^^^\n" +
"Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location\n" +
"----------\n" +
"2. ERROR in p2\\Y.java (at line 5)\n" +
" @Nullable @NonNull Object bar() {\n" +
" ^^^^^^^^\n" +
"Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location\n" +
"----------\n" +
"3. ERROR in p2\\Y.java (at line 6)\n" +
" @NonNull @Nullable Object o = null;\n" +
" ^^^^^^^^^\n" +
"Contradictory null specification; only one of @NonNull and @Nullable can be specified at any location\n" +
"----------\n");
}
// a nonnull variable is dereferenced in a loop
public void test_nonnull_var_in_constrol_structure_1() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
// This option currently does nothing:
// customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" void print4(@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 4)\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 10)\n" +
" print(s);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n" +
"3. ERROR in X.java (at line 15)\n" +
" print(s);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is inferred as @Nullable\n" +
"----------\n" +
"4. WARNING in X.java (at line 17)\n" +
" void print(@NonNull String s) {\n" +
" ^^^^^^^^^^^^^^^^^\n" +
"The nullness annotation is redundant with a default that applies to this location\n" +
"----------\n");
}
// a nonnull variable is dereferenced in a finally block
public void test_nonnull_var_in_constrol_structure_2() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
// This option currently does nothing:
// customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;" +
"@NonNullByDefault\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" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 16)\n" +
" print(s);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is inferred as @Nullable\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(JavaCore.COMPILER_PB_REDUNDANT_NULL_ANNOTATION, JavaCore.IGNORE);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" void print4(@NonNull String s) {\n" +
" for (int i=0; i<4; i++)\n" +
" try { /*empty*/ } finally {\n" +
" print(s);\n" +
" }\n" +
" }\n" +
" void print5(@Nullable String s) {\n" +
" for (int i=0; i<5; i++)\n" +
" try { /*empty*/ } finally {\n" +
" print(s);\n" +
" }\n" +
" }\n" +
" void print6(boolean b) {\n" +
" String s = b ? null : \"\";\n" +
" for (int i=0; i<4; i++)\n" +
" try { /*empty*/ } finally {\n" +
" print(s);\n" +
" }\n" +
" }\n" +
" void print(@NonNull String s) {\n" +
" System.out.print(s);\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 12)\n" +
" print(s);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 19)\n" +
" print(s);\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is inferred as @Nullable\n" +
"----------\n");
}
// witness for an AIOOBE in FlowContext.recordExpectedType()
public void test_message_send_in_control_structure_01() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.IGNORE);
customOptions.put(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE, JavaCore.WARNING);
runNegativeTestWithLibs(
new String[] {
"p/Scope.java",
"package p;\n" +
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"public abstract class Scope {\n" +
" public ReferenceBinding findMemberType(char[] typeName, ReferenceBinding enclosingType) {\n" +
" ReferenceBinding enclosingSourceType = enclosingSourceType();\n" +
" PackageBinding currentPackage = getCurrentPackage();\n" +
" CompilationUnitScope unitScope = compilationUnitScope();\n" +
" ReferenceBinding memberType = enclosingType.getMemberType(typeName);\n" +
" ReferenceBinding currentType = enclosingType;\n" +
" ReferenceBinding[] interfacesToVisit = null;\n" +
" while (true) {\n" +
" ReferenceBinding[] itsInterfaces = currentType.superInterfaces();\n" +
" if (itsInterfaces != null) {\n" +
" if (interfacesToVisit == null) {\n" +
" interfacesToVisit = itsInterfaces;\n" +
" }\n" +
" }\n" +
" unitScope.recordReference(currentType, typeName);\n" +
" \n" +
" if ((memberType = currentType.getMemberType(typeName)) != null) {\n" +
" if (enclosingSourceType == null\n" +
" ? memberType.canBeSeenBy(currentPackage)\n" +
" : memberType.canBeSeenBy(enclosingType, enclosingSourceType)) {\n" +
" return memberType;\n" +
" }\n" +
" }\n" +
" }\n" +
" }\n" +
" private CompilationUnitScope compilationUnitScope() {\n" +
" return compilationUnitScope();\n" +
" }\n" +
" private PackageBinding getCurrentPackage() {\n" +
" return getCurrentPackage();\n" +
" }\n" +
" private ReferenceBinding enclosingSourceType() {\n" +
" return enclosingSourceType();\n" +
" }\n" +
"}\n",
"p/CompilationUnitScope.java",
"package p;\n" +
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"public class CompilationUnitScope {\n" +
" void recordReference(ReferenceBinding rb, char[] name) {}\n" +
"}\n",
"p/PackageBinding.java",
"package p;\n" +
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"public class PackageBinding {\n" +
"}\n",
"p/ReferenceBinding.java",
"package p;\n" +
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"public class ReferenceBinding {\n" +
" ReferenceBinding getMemberType(char[] name) { return this; }\n" +
" ReferenceBinding[] superInterfaces() { return new ReferenceBinding[0]; }\n" +
" boolean canBeSeenBy(PackageBinding ob) { return true; }\n" +
" boolean canBeSeenBy(ReferenceBinding rb, ReferenceBinding rb2) { return true; }\n" +
"}\n"
},
customOptions,
"----------\n" +
"1. ERROR in p\\Scope.java (at line 13)\n" +
" if (itsInterfaces != null) {\n" +
" ^^^^^^^^^^^^^\n" +
"Redundant null check: The variable itsInterfaces cannot be null at this location\n" +
"----------\n" +
"2. ERROR in p\\Scope.java (at line 20)\n" +
" if ((memberType = currentType.getMemberType(typeName)) != null) {\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Redundant null check: The variable memberType cannot be null at this location\n" +
"----------\n" +
"3. ERROR in p\\Scope.java (at line 21)\n" +
" if (enclosingSourceType == null\n" +
" ^^^^^^^^^^^^^^^^^^^\n" +
"Null comparison always yields false: The variable enclosingSourceType cannot be null at this location\n" +
"----------\n");
}
// Bug 370930 - NonNull annotation not considered for enhanced for loops
public void test_message_send_in_control_structure_02() {
runNegativeTestWithLibs(
new String[] {
"Bug370930.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import java.util.*;\n" +
"public class Bug370930 {\n" +
" void loop(Collection<String> list) {\n" +
" for(@NonNull String s: list) { // warning here: insufficient info on elements\n" +
" expectNonNull(s); // no warning here\n" +
" }\n" +
" }\n" +
" \n" +
" void expectNonNull(@NonNull String s) {}\n" +
"}\n"
},
"----------\n" +
"1. WARNING in Bug370930.java (at line 5)\n" +
" for(@NonNull String s: list) { // warning here: insufficient info on elements\n" +
" ^^^^\n" +
"Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n");
}
//Bug 370930 - NonNull annotation not considered for enhanced for loops over array
public void test_message_send_in_control_structure_02a() {
runNegativeTestWithLibs(
new String[] {
"Bug370930.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class Bug370930 {\n" +
" void loop(String[] array) {\n" +
" for(@NonNull String s: array) { // warning here: insufficient info on elements\n" +
" expectNonNull(s); // no warning here\n" +
" }\n" +
" }\n" +
" \n" +
" void expectNonNull(@NonNull String s) {}\n" +
"}\n"
},
"----------\n" +
"1. WARNING in Bug370930.java (at line 4)\n" +
" for(@NonNull String s: array) { // warning here: insufficient info on elements\n" +
" ^^^^^\n" +
"Null type safety: The expression of type String needs unchecked conversion to conform to \'@NonNull String\'\n" +
"----------\n");
}
//Bug 370930 - NonNull annotation not considered for enhanced for loops
public void test_message_send_in_control_structure_03() {
runNegativeTestWithLibs(
new String[] {
"Bug370930.java",
"import org.eclipse.jdt.annotation.*;\n" +
"import java.util.*;\n" +
"public class Bug370930 {\n" +
" void loop(Collection<String> list) {\n" +
" for(@Nullable String s: list) {\n" +
" expectNonNull(s); // warning here\n" +
" }\n" +
" }\n" +
" \n" +
" void expectNonNull(@NonNull String s) {}\n" +
"}\n"
},
"----------\n" +
"1. ERROR in Bug370930.java (at line 6)\n" +
" expectNonNull(s); // warning here\n" +
" ^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n");
}
public void test_assignment_expression_1() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @Nullable Object foo() {\n" +
" Object o = null;\n" +
" boolean keepLooking = true;\n" +
" while(keepLooking) {\n" +
" if ((o=getO()) != null) {\n" +
" return o;\n" +
" }\n" +
" }\n" +
" return null;\n" +
" }\n" +
"\n" +
" private @Nullable Object getO() {\n" +
" return new Object();\n" +
" }\n" +
"}\n",
},
customOptions,
"");
}
// a nonnull variable is dereferenced in a method of a nested type
public void test_nesting_1() {
Map customOptions = getCompilerOptions();
// customOptions.put(CompilerOptions.OPTION_ReportPotentialNullSpecViolation, JavaCore.ERROR);
// This option currently does nothing:
// customOptions.put(JavaCore.COMPILER_NONNULL_IS_DEFAULT, JavaCore.ENABLED);
runNegativeTestWithLibs(
new String[] {
"X.java",
"import org.eclipse.jdt.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" void print4(final String s1) {\n" +
" for (int i=0; i<3; i++)\n" +
" new Runnable() {\n" +
" public void run() {\n" +
" print(s1);\n" +
" }\n" +
" }.run();\n" +
" }\n" +
" void print8(final @Nullable String s2) {\n" +
" for (int i=0; i<3; i++)\n" +
" new Runnable() {\n" +
" public void run() {\n" +
" print(s2);\n" +
" }\n" +
" }.run();\n" +
" }\n" +
" void print16(boolean b) {\n" +
" final String s3 = b ? null : \"\";\n" +
" for (int i=0; i<3; i++)\n" +
" new Runnable() {\n" +
" public void run() {\n" +
" @NonNull String s3R = s3;\n" +
" }\n" +
" }.run();\n" +
" }\n" +
" void print(String s) {\n" +
" System.out.print(s);\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in X.java (at line 16)\n" +
" print(s2);\n" +
" ^^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is specified as @Nullable\n" +
"----------\n" +
"2. ERROR in X.java (at line 25)\n" +
" @NonNull String s3R = s3;\n" +
" ^^\n" +
"Null type mismatch: required \'@NonNull String\' but the provided value is inferred as @Nullable\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"
},
"");
}
// test analysis disablement, binary type contains annotation
public void test_options_01() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_INSUFFICIENT_INFO, JavaCore.ERROR);
runConformTestWithLibs(
new String[] {
"ContainingInner2.java",
"public class ContainingInner2 {\n" +
" public ContainingInner2 (@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" }\n" +
" public class Inner {\n" +
" public <T> Inner (@org.eclipse.jdt.annotation.NonNull T o) {\n" +
" }\n" +
" }\n" +
"}\n",
},
null /*customOptions*/,
"");
customOptions.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.DISABLED);
runConformTestWithLibs(
false, // flush directory
new String[] {
"X.java",
"public class X {\n" +
" void create() {\n" +
" ContainingInner2 container = new ContainingInner2(null);\n" +
" ContainingInner2.Inner inner = container.new Inner(null);\n" +
" }\n" +
"}\n"},
customOptions,
"" /* compiler output */);
}
// test illegally trying to ignore null spec violations
public void test_options_02() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.IGNORE); // has no effect
runNegativeTestWithLibs(
new String[] {
"Test.java",
"public class Test {\n" +
" public void foo(@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" o = null;\n" +
" Object p = o;\n" +
" if (p == null)\n" +
" p.toString();\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. ERROR in Test.java (at line 3)\n" +
" o = null;\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in Test.java (at line 5)\n" +
" if (p == null)\n" +
" ^\n" +
"Null comparison always yields false: The variable p cannot be null at this location\n" +
"----------\n" +
"3. WARNING in Test.java (at line 6)\n" +
" p.toString();\n" +
" ^^^^^^^^^^^^\n" +
"Dead code\n" +
"----------\n");
}
// test setting null spec violations to "warning"
public void test_options_03() {
Map customOptions = getCompilerOptions();
customOptions.put(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.WARNING); // OK
runNegativeTestWithLibs(
new String[] {
"Test.java",
"public class Test {\n" +
" public void foo(@org.eclipse.jdt.annotation.NonNull Object o) {\n" +
" o = null;\n" +
" Object p = o;\n" +
" if (p == null)\n" +
" p.toString();\n" +
" }\n" +
"}\n",
},
customOptions,
"----------\n" +
"1. WARNING in Test.java (at line 3)\n" +
" o = null;\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in Test.java (at line 5)\n" +
" if (p == null)\n" +
" ^\n" +
"Null comparison always yields false: The variable p cannot be null at this location\n" +
"----------\n" +
"3. WARNING in Test.java (at line 6)\n" +
" p.toString();\n" +
" ^^^^^^^^^^^^\n" +
"Dead code\n" +
"----------\n");
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=372011
// Test whether @NonNullByDefault on a binary package or an enclosing type is respected from enclosed elements.
public void testBug372011() {
String path = this.getCompilerTestsPluginDirectoryPath() + File.separator + "workspace" + File.separator + "Test372011.jar";
String[] libs = new String[this.LIBS.length + 1];
System.arraycopy(this.LIBS, 0, libs, 0, this.LIBS.length);
libs[this.LIBS.length] = path;
runNegativeTest(
new String[] {
"X.java",
"import p11.T11;\n" +
"import p12.T12;\n" +
"import p12.T12a;\n" +
"import p12.Public;\n" +
"public class X {\n" +
" void foo() {\n" +
" new T11().t11foo(null);\n" +
" new T12().new T122().foo122(null);\n" +
" }\n" +
" void trigger1 (Public o){\n" +
" o.bar(null);\n" +
" }\n" +
" @org.eclipse.jdt.annotation.NonNull Object foo2() {\n" +
" new T12a().foo12a(new Object());\n" + // don't complain
" new T12().new T122().new T1222().foo1222(null);\n" +
" return new T11().retSomething();\n" + // don't complain
" }\n" +
"}\n"},
"----------\n" +
"1. ERROR in X.java (at line 7)\n" +
" new T11().t11foo(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"2. ERROR in X.java (at line 8)\n" +
" new T12().new T122().foo122(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"3. ERROR in X.java (at line 11)\n" +
" o.bar(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n" +
"4. ERROR in X.java (at line 15)\n" +
" new T12().new T122().new T1222().foo1222(null);\n" +
" ^^^^\n" +
"Null type mismatch: required \'@NonNull Object\' but the provided value is null\n" +
"----------\n",
libs,
true /* shouldFlush*/);
}
}