blob: f40a8aa446dedab837fd47be69c517e977df71fe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 GK Software AG and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Stephan Herrmann - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.core.tests.compiler.regression;
import java.io.File;
import java.io.IOException;
import org.eclipse.jdt.core.tests.util.Util;
import junit.framework.Test;
public class NullAnnotationBatchCompilerTest extends AbstractBatchCompilerTest {
protected static final String NONNULL_BY_DEFAULT_ANNOTATION_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ PACKAGE, TYPE, METHOD, CONSTRUCTOR })\n" +
"public @interface NonNullByDefault{\n" +
"}";
protected static final String NULLABLE_ANNOTATION_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ METHOD, PARAMETER, FIELD })\n" +
"public @interface Nullable{\n" +
"}\n";
protected static final String NONNULL_ANNOTATION_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ METHOD, PARAMETER, FIELD })\n" +
"public @interface NonNull{\n" +
"}\n";
protected static final String ELEMENT_TYPE_18_CONTENT = "package java.lang.annotation;\n" +
"public enum ElementType {\n" +
" TYPE,\n" +
" FIELD,\n" +
" METHOD,\n" +
" PARAMETER,\n" +
" CONSTRUCTOR,\n" +
" LOCAL_VARIABLE,\n" +
" ANNOTATION_TYPE,\n" +
" PACKAGE,\n" +
" TYPE_PARAMETER,\n" +
" TYPE_USE\n" +
"}\n" +
"";
protected static final String NONNULL_ANNOTATION_18_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"import java.lang.annotation.ElementType;\n" +
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ ElementType.TYPE_USE })\n" +
"public @interface NonNull{\n" +
"}\n";
protected static final String NULLABLE_ANNOTATION_18_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"import java.lang.annotation.ElementType;\n" +
"import java.lang.annotation.*;\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ ElementType.TYPE_USE })\n" +
"public @interface Nullable{\n" +
"}\n";
protected static final String NONNULL_BY_DEFAULT_ANNOTATION_18_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"import java.lang.annotation.ElementType;\n" +
"import static org.eclipse.jdt.annotation.DefaultLocation.*;\n" +
"\n" +
"import java.lang.annotation.Retention;\n" +
"import java.lang.annotation.RetentionPolicy;\n" +
"import java.lang.annotation.Target;\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ ElementType.PACKAGE, ElementType.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.LOCAL_VARIABLE })\n" +
"public @interface NonNullByDefault {\n" +
" DefaultLocation[] value() default { PARAMETER, RETURN_TYPE, FIELD, TYPE_BOUND, TYPE_ARGUMENT };\n" +
"}\n";
protected static final String DEFAULT_LOCATION_CONTENT = "package org.eclipse.jdt.annotation;\n" +
"public enum DefaultLocation {\n" +
" PARAMETER,\n" +
" RETURN_TYPE,\n" +
" FIELD,\n" +
" TYPE_PARAMETER,\n" +
" TYPE_BOUND,\n" +
" TYPE_ARGUMENT,\n" +
" ARRAY_CONTENTS\n" +
"}\n";
static {
// TESTS_NAMES = new String[] { "test490010NoEeaFile" };
// TESTS_NUMBERS = new int[] { 306 };
// TESTS_RANGE = new int[] { 298, -1 };
}
/**
* This test suite only needs to be run on one compliance.
* As it includes some specific 1.5 tests, it must be used with a least a 1.5 VM
* and not be duplicated in general test suite.
* @see TestAll
*/
public static Test suite() {
return buildMinimalComplianceTestSuite(testClass(), F_1_5);
}
public static Class<?> testClass() {
return NullAnnotationBatchCompilerTest.class;
}
public NullAnnotationBatchCompilerTest(String name) {
super(name);
}
@Override
protected void tearDown() throws Exception {
super.tearDown();
Util.delete(OUTPUT_DIR);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=325342
// -err option - regression tests to check option nullAnnot
// Null warnings because of annotations, null spec violations plus one specific problem configured as errors
public void test314_warn_options() {
this.runNegativeTest(
new String[] {
"p/X.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" +
" return this;\n" +
" }\n" +
"}\n" +
"class Y extends X {\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -err:+nullAnnot -warn:-null -err:+nonnullNotRepeated -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with \'@NonNull Object\' returned from X.foo(Object, Object) (mismatching null constraints)\n" +
"----------\n" +
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"3. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"3 problems (3 errors)\n",
true);
}
// -warn option - regression tests to check option nullAnnot and missingNullDefault
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=372012
public void test315_warn_options() {
this.runConformTest(
new String[] {
"p/package-info.java",
"@org.eclipse.jdt.annotation.NonNullByDefault\n" +
"package p;\n",
"p/X.java",
"package p;\n" +
"public class X {\n" +
"}\n",
"p1/X1.java",
"package p1;\n" +
"public class X1 {\n" +
"}\n",
"p1/X1a.java",
"package p1;\n" +
"public class X1a {\n" +
"}\n",
"Default1.java",
"public class Default1 {\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot -warn:+null -missingNullDefault -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"",
true);
}
// -warn option - regression tests to check option nullAnnot and missingNullDefault
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=372012
public void test315_warn_options_a() {
this.runConformTest(
new String[] {
"p1/X1.java",
"package p1;\n" +
"public class X1 {\n" +
" class Inner{};\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p1" + File.separator + "X1.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot -warn:+null -missingNullDefault -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p1/X1.java (at line 1)\n" +
" package p1;\n" +
" ^^\n" +
"A default nullness annotation has not been specified for the package p1\n" +
"----------\n" +
"1 problem (1 warning)\n",
true);
}
// -warn option - regression tests to check option nullAnnot and missingNullDefault
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=372012
public void test315_warn_options_b() {
this.runNegativeTest(
new String[] {
"X1.java",
"public class X1 {\n" +
" Zork z;\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "X1.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot -warn:+null -missingNullDefault -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/X1.java (at line 1)\n" +
" public class X1 {\n" +
" ^^\n" +
"A default nullness annotation has not been specified for the type X1\n" +
"----------\n" +
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/X1.java (at line 2)\n" +
" Zork z;\n" +
" ^^^^\n" +
"Zork cannot be resolved to a type\n" +
"----------\n" +
"2 problems (1 error, 1 warning)\n",
true);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=325342
// -warn option - regression tests to check option nullAnnot
// option syntax error
public void test316_warn_options() {
this.runNegativeTest(
new String[] {
"p/X.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"@SuppressWarnings(\"unused\")\n" +
"public class X {}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot(foo|bar) -warn:+null -nonNullByDefault -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"Token nullAnnot(foo|bar) is not in the expected format \"nullAnnot(<nullable annotation name> | <non null annotation name> | <non-null by default annotation name>)\"\n",
true);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=408815
// -warn option - regression tests to check option syntacticAnalysis
// Null warnings because of annotations, null spec violations, suppressed by null-check
public void test316b_warn_options() {
this.runConformTest(
new String[] {
"p/X.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @Nullable Object f;\n" +
" @NonNull Object foo() {\n" +
" if (this.f != null)\n" +
" return this.f;\n" +
" return this;\n" +
" }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot -warn:+null,syntacticAnalysis -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"",
true);
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=325342
// -warn option - regression tests to check option nullAnnot (no args)
// Null warnings because of annotations, null spec violations
public void test313_warn_options() {
this.runConformTest(
new String[] {
"p/X.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" +
" return this;\n" +
" }\n" +
"}\n" +
"class Y extends X {\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot -warn:-null -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with \'@NonNull Object\' returned from X.foo(Object, Object) (mismatching null constraints)\n" +
"----------\n" +
"2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"3 problems (3 warnings)\n",
true);
}
// Bug 388281 - [compiler][null] inheritance of null annotations as an option
// -warn option - regression tests to check option inheritNullAnnot
public void test320_warn_options() {
this.runNegativeTest(
new String[] {
"p/Super.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class Super {\n" +
" void foo(@NonNull String s) {}\n" +
"}\n",
"p/Sub.java",
"package p;\n" +
"public class Sub extends Super {\n" +
" void foo(String s) {\n" +
" s= null;\n" + // illegal since s inherits @NonNull
" super.foo(s);\n" + // legal
" }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "Sub.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -err:+nullAnnot,+null,+inheritNullAnnot -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/Sub.java (at line 4)\n" +
" s= null;\n" +
" ^^^^\n" +
"Null type mismatch: required '@NonNull String' but the provided value is null\n" +
"----------\n" +
"1 problem (1 error)\n",
true);
}
// -warn option - test multiple sets of null annotations
public void testBug466291() {
this.runConformTest(
new String[] {
"p/X.java",
"package p;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" public Object foo(@Nullable Object o, Object o2) {\n" +
" return new Object();\n" +
" }\n" +
" public Object bar() {\n" +
" return this;\n" +
" }\n" +
"}\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ METHOD, PARAMETER })\n" +
"@interface NonNull{\n" +
"}\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ METHOD, PARAMETER })\n" +
"@interface Nullable{\n" +
"}\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ PACKAGE, TYPE, METHOD, CONSTRUCTOR })\n" +
"@interface NonNullByDefault{\n" +
"}"
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -1.5"
+ " -warn:+nullAnnot(p.Nullable|p.NonNull|p.NonNullByDefault) -warn:+null -warn:-nullUncheckedConversion "
+ "-proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"",
true);
// test twice: 1. against SourceTypeBinding(p.X), 2. against BinaryTypeBinding(p.X):
for (int i=0; i<2; i++) {
this.runNegativeTest(
new String[] {
"p2/X2.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X2 {\n" +
" @NonNull Object test(@NonNull p.X nonnullX, @Nullable p.X nullableX) {\n" +
" nonnullX.foo(nullableX, nullableX);\n" +
" return nonnullX.bar();\n" +
" }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p2" + File.separator + "X2.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -classpath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot(org.eclipse.jdt.annotation.Nullable|org.eclipse.jdt.annotation.NonNull|org.eclipse.jdt.annotation.NonNullByDefault)"
+ " -warn:+nullAnnot(p.Nullable||p.NonNullByDefault) -warn+null -proc:none -d \"" + OUTPUT_DIR + "\"", // nonnull remains unset for secondaries
"",
"----------\n" +
"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p2/X2.java (at line 5)\n" +
" nonnullX.foo(nullableX, nullableX);\n" +
" ^^^^^^^^^\n" +
"Null type mismatch: required '@NonNull Object' but the provided value is specified as @Nullable\n" +
"----------\n" +
"1 problem (1 error)\n",
false);
// force reading of BinaryTypeBinding(p.X):
String xPath = OUTPUT_DIR + File.separator + "p" + File.separator + "X.java";
new File(xPath).delete();
}
}
//-warn option - test multiple sets of null annotations, three (partial) sets of secondary annotations
public void testBug466291b() {
this.runConformTest(
new String[] {
"p/X.java",
"package p;\n" +
"import static java.lang.annotation.ElementType.*;\n" +
"import java.lang.annotation.*;\n" +
"@NonNullByDefault\n" +
"public class X {\n" +
" public Object foo(@Nullable Object o, Object o2) {\n" +
" return new Object();\n" +
" }\n" +
" public Object bar() {\n" +
" return this;\n" +
" }\n" +
"}\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ METHOD, PARAMETER })\n" +
"@interface NonNull{\n" +
"}\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ METHOD, PARAMETER })\n" +
"@interface Nullable{\n" +
"}\n" +
"@Documented\n" +
"@Retention(RetentionPolicy.CLASS)\n" +
"@Target({ PACKAGE, TYPE, METHOD, CONSTRUCTOR })\n" +
"@interface NonNullByDefault{\n" +
"}"
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -1.5"
+ " -warn:+nullAnnot(p.Nullable|p.NonNull|p.NonNullByDefault) -warn:+null -warn:-nullUncheckedConversion "
+ "-proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"",
true);
// force reading of BinaryTypeBinding(p.X):
String xPath = OUTPUT_DIR + File.separator + "p" + File.separator + "X.java";
new File(xPath).delete();
this.runNegativeTest(
new String[] {
"p2/X2.java",
"package p2;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X2 {\n" +
" @NonNull Object test(@NonNull p.X nonnullX, @Nullable p.X nullableX) {\n" +
" nonnullX.foo(nullableX, nullableX);\n" +
" return nonnullX.bar();\n" +
" }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p2" + File.separator + "X2.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -classpath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -warn:+nullAnnot(org.eclipse.jdt.annotation.Nullable|org.eclipse.jdt.annotation.NonNull|org.eclipse.jdt.annotation.NonNullByDefault)"
+ " -warn:+nullAnnot(|x.AbsentNonNull|) "
+ " -warn:+nullAnnot(p.Nullable||p.NonNullByDefault) "
+ " -warn:+nullAnnot(yet.AnotherNullable|yet.AnotherNonnull|yet.anotherNNBD) "
+ " -warn+null -proc:none -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p2/X2.java (at line 5)\n" +
" nonnullX.foo(nullableX, nullableX);\n" +
" ^^^^^^^^^\n" +
"Null type mismatch: required '@NonNull Object' but the provided value is specified as @Nullable\n" +
"----------\n" +
"1 problem (1 error)\n",
false);
}
// == Variants of org.eclipse.jdt.core.tests.compiler.regression.BatchCompilerTest.testBug375366a(): ==
// Bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
// property file enables null annotation support
public void testBug375366c() throws IOException {
createOutputTestDirectory("regression/.settings");
Util.createFile(OUTPUT_DIR+"/.settings/org.eclipse.jdt.core.prefs",
"eclipse.preferences.version=1\n" +
"org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled\n");
this.runNegativeTest(
new String[] {
"p/X.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" +
" return this;\n" +
" }\n" +
"}\n" +
"class Y extends X {\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -properties " + OUTPUT_DIR + File.separator +".settings" + File.separator + "org.eclipse.jdt.core.prefs "
+ " -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with \'@NonNull Object\' returned from X.foo(Object, Object) (mismatching null constraints)\n" +
"----------\n" +
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing non-null annotation: inherited method from X specifies this parameter as @NonNull\n" +
"----------\n" +
"3 problems (2 errors, 1 warning)\n",
false/*don't flush*/);
}
// Bug 375366 - ECJ ignores unusedParameterIncludeDocCommentReference unless enableJavadoc option is set
// property file enables null annotation support, one optional warning disabled
public void testBug375366d() throws IOException {
createOutputTestDirectory("regression/.settings");
Util.createFile(OUTPUT_DIR+"/.settings/org.eclipse.jdt.core.prefs",
"eclipse.preferences.version=1\n" +
"org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled\n" +
"org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=ignore\n");
this.runNegativeTest(
new String[] {
"p/X.java",
"package p;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"public class X {\n" +
" @NonNull Object foo(@Nullable Object o, @NonNull Object o2) {\n" +
" return this;\n" +
" }\n" +
"}\n" +
"class Y extends X {\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
"}\n",
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_CONTENT
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -sourcepath \"" + OUTPUT_DIR + "\""
+ " -1.5"
+ " -properties " + OUTPUT_DIR + File.separator +".settings" + File.separator + "org.eclipse.jdt.core.prefs "
+ " -d \"" + OUTPUT_DIR + "\"",
"",
"----------\n" +
"1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^^^^^^^^^^^\n" +
"The return type is incompatible with \'@NonNull Object\' returned from X.foo(Object, Object) (mismatching null constraints)\n" +
"----------\n" +
"2. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 9)\n" +
" @Nullable Object foo(Object o, Object o2) { return null; }\n" +
" ^^^^^^\n" +
"Missing nullable annotation: inherited method from X specifies this parameter as @Nullable\n" +
"----------\n" +
"2 problems (2 errors)\n",
false/*don't flush*/);
}
// Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
// - single external annotation directory
public void test440477() throws IOException {
String annots_dir = Util.getOutputDirectory() + File.separator + "annots";
String annots_java_util = annots_dir + File.separator + "java/util";
new File(annots_java_util).mkdirs();
Util.createFile(
annots_java_util + File.separator + "Map.eea",
TEST_440687_MAP_EEA_CONTENT);
String o_e_j_annotation_dir = OUTPUT_DIR + File.separator +
"org" + File.separator + "eclipse" + File.separator + "jdt" + File.separator + "annotation";
String j_l_annotation_dir = OUTPUT_DIR + File.separator +
"java" + File.separator + "lang" + File.separator + "annotation";
this.runConformTest(
new String[] {
"java/lang/annotation/ElementType.java",
ELEMENT_TYPE_18_CONTENT,
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_18_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_18_CONTENT,
"org/eclipse/jdt/annotation/DefaultLocation.java",
DEFAULT_LOCATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_18_CONTENT,
"test1/Test1.java",
"package test1;\n" +
"\n" +
"import java.util.Map;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"\n" +
"@NonNullByDefault\n" +
"public class Test1 {\n" +
" void test(Map<String,Test1> map, String key) {\n" +
" Test1 v = map.get(key);\n" +
" if (v == null)\n" +
" throw new RuntimeException(); // should not be reported as dead code, although V is a '@NonNull Test1'\n" +
" }\n" +
"}\n"
},
" -1.8 -proc:none -d none -warn:+nullAnnot -annotationpath " + annots_dir +
" -sourcepath \"" + OUTPUT_DIR + "\" " +
// explicitly mention all files to ensure a good order, cannot pull in source of NNBD on demand
"\"" + j_l_annotation_dir + File.separator + "ElementType.java\" " +
"\"" + o_e_j_annotation_dir + File.separator + "NonNull.java\" " +
"\"" + o_e_j_annotation_dir + File.separator + "DefaultLocation.java\" " +
"\"" + o_e_j_annotation_dir + File.separator + "NonNullByDefault.java\" " +
"\"" + OUTPUT_DIR + File.separator + "test1" + File.separator + "Test1.java\"",
"",
"",
true);
}
// file content for tests below:
private static final String TEST_440687_MAP_EEA_CONTENT =
"class java/util/Map\n" +
" <K:V:>\n" +
"\n" +
"get\n" +
" (Ljava/lang/Object;)TV;\n" +
" (Ljava/lang/Object;)T0V;\n" +
"put\n" +
" (TK;TV;)TV;\n" +
" (TK;TV;)T0V;\n" +
"remove\n" +
" (Ljava/lang/Object;)TV;\n" +
" (Ljava/lang/Object;)T0V;\n";
private static final String TEST_440687_OBJECT_EEA_CONTENT =
"class java/lang/Object\n" +
"\n" +
"equals\n" +
" (Ljava/lang/Object;)Z\n" +
" (L0java/lang/Object;)Z\n";
// Bug 440687 - [compiler][batch][null] improve command line option for external annotations
// work horse for tests below
void runTest440687(String compilerPathArgs, String extraSourcePaths, String expectedCompilerMessage, boolean isSuccess) {
String[] testFiles = new String[] {
"java/lang/annotation/ElementType.java",
ELEMENT_TYPE_18_CONTENT,
"org/eclipse/jdt/annotation/NonNull.java",
NONNULL_ANNOTATION_18_CONTENT,
"org/eclipse/jdt/annotation/Nullable.java",
NULLABLE_ANNOTATION_18_CONTENT,
"org/eclipse/jdt/annotation/DefaultLocation.java",
DEFAULT_LOCATION_CONTENT,
"org/eclipse/jdt/annotation/NonNullByDefault.java",
NONNULL_BY_DEFAULT_ANNOTATION_18_CONTENT,
"test1/Test1.java",
"package test1;\n" +
"\n" +
"import java.util.Map;\n" +
"import org.eclipse.jdt.annotation.*;\n" +
"\n" +
"@NonNullByDefault\n" +
"public class Test1 {\n" +
" void test(Map<String,Test1> map, String key) {\n" +
" Test1 v = map.get(key);\n" +
" if (v == null)\n" +
" throw new RuntimeException(); // should not be reported as dead code, although V is a '@NonNull Test1'\n" +
" }\n" +
" public boolean equals(@NonNull Object other) { return false; }\n" +
"}\n"
};
String o_e_j_annotation_dir = OUTPUT_DIR + File.separator +
"org" + File.separator + "eclipse" + File.separator + "jdt" + File.separator + "annotation";
String j_l_annotation_dir = OUTPUT_DIR + File.separator +
"java" + File.separator + "lang" + File.separator + "annotation";
String commandLine = " -1.8 -proc:none -d none -warn:+nullAnnot " + compilerPathArgs +
" -sourcepath \"" + OUTPUT_DIR + extraSourcePaths + "\" " +
// explicitly mention all files to ensure a good order, cannot pull in source of NNBD on demand
"\"" + j_l_annotation_dir + File.separator + "ElementType.java\" " +
"\"" + o_e_j_annotation_dir + File.separator + "NonNull.java\" " +
"\"" + o_e_j_annotation_dir + File.separator + "DefaultLocation.java\" " +
"\"" + o_e_j_annotation_dir + File.separator + "NonNullByDefault.java\" " +
"\"" + OUTPUT_DIR + File.separator + "test1" + File.separator + "Test1.java\"";
if (expectedCompilerMessage == null)
expectedCompilerMessage =
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 13)\n" +
" public boolean equals(@NonNull Object other) { return false; }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter other, inherited method from Object declares this parameter as @Nullable\n" +
"----------\n" +
"1 problem (1 warning)\n";
try {
if (isSuccess)
this.runConformTest(testFiles, commandLine, "", expectedCompilerMessage, true);
else
this.runNegativeTest(testFiles, commandLine, "", expectedCompilerMessage, true);
} finally {
Util.delete(Util.getOutputDirectory());
}
}
// Bug 440687 - [compiler][batch][null] improve command line option for external annotations
// - two external annotation directories as part of the sourcepath/classpath
public void test440687a() throws IOException {
String annots_dir1 = Util.getOutputDirectory() + File.separator + "annots1";
String annots_java_util = annots_dir1 + File.separator + "java/util";
new File(annots_java_util).mkdirs();
Util.createFile(annots_java_util + File.separator + "Map.eea",
TEST_440687_MAP_EEA_CONTENT);
String annots_dir2 = Util.getOutputDirectory() + File.separator + "annots2";
String annots_java_lang = annots_dir2 + File.separator + "java/lang";
new File(annots_java_lang).mkdirs();
Util.createFile(annots_java_lang + File.separator + "Object.eea",
TEST_440687_OBJECT_EEA_CONTENT);
runTest440687("-annotationpath CLASSPATH -classpath \"" + annots_dir2 + "\"",
File.pathSeparator + annots_dir1, // extra source path
null, // expect normal error
true);
}
// Bug 440687 - [compiler][batch][null] improve command line option for external annotations
// - two external annotation directories specifically configured.
public void test440687b() throws IOException {
String annots_dir = Util.getOutputDirectory() + File.separator + "annots1";
String annots_java_util = annots_dir + File.separator + "java/util";
new File(annots_java_util).mkdirs();
Util.createFile(
annots_java_util + File.separator + "Map.eea",
TEST_440687_MAP_EEA_CONTENT);
String annots_dir2 = Util.getOutputDirectory() + File.separator + "annots2";
String annots_java_lang = annots_dir2 + File.separator + "java/lang";
new File(annots_java_lang).mkdirs();
Util.createFile(
annots_java_lang + File.separator + "Object.eea",
TEST_440687_OBJECT_EEA_CONTENT);
runTest440687("-annotationpath \"" + annots_dir + File.pathSeparator + annots_dir2 + "\" ",
"", // no extra source path
null, // expect normal error
true);
}
// Bug 440687 - [compiler][batch][null] improve command line option for external annotations
// - single external annotation zip with 2 entries
public void test440687c() throws IOException {
String annots_dir = Util.getOutputDirectory() + File.separator + "annots";
new File(annots_dir).mkdirs();
String annotsZipFile = annots_dir+ File.separator + "jre-annots.zip";
Util.createSourceZip(
new String[] {
"java/util/Map.eea",
TEST_440687_MAP_EEA_CONTENT,
"java/lang/Object.eea",
TEST_440687_OBJECT_EEA_CONTENT
},
annotsZipFile);
runTest440687("-annotationpath CLASSPATH -classpath \"" + annotsZipFile + "\"",
"", // no extra source path
null, // expect normal error
true);
}
// Bug 440687 - [compiler][batch][null] improve command line option for external annotations
// - missing argument after -annotationpath
public void test440687d() throws IOException {
runTest440687("-annotationpath", // missing argument
"",
"Missing argument to -annotationpath at \'-sourcepath\'\n",
false);
}
// project is configured for eea (directory on classpath), but no specific file for Map found
public void test490010NoEeaFile1() throws IOException {
String annots_dir1 = Util.getOutputDirectory() + File.separator + "annots1";
new File(annots_dir1).mkdirs();
String annots_dir2 = Util.getOutputDirectory() + File.separator + "annots2";
String annots_java_lang = annots_dir2 + File.separator + "java/lang";
new File(annots_java_lang).mkdirs();
Util.createFile(annots_java_lang + File.separator + "Object.eea",
TEST_440687_OBJECT_EEA_CONTENT);
runTest440687("-annotationpath CLASSPATH -classpath \"" + annots_dir2 + "\"",
File.pathSeparator + annots_dir1, // extra source path
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 9)\n" +
" Test1 v = map.get(key);\n" +
" ^^^^^^^^^^^^\n" +
"Unsafe interpretation of method return type as \'@NonNull\' based on the receiver type \'@NonNull Map<@NonNull String,@NonNull Test1>\'. Type \'Map<K,V>\' doesn\'t seem to be designed with null type annotations in mind\n" +
"----------\n" +
"2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 11)\n" +
" throw new RuntimeException(); // should not be reported as dead code, although V is a \'@NonNull Test1\'\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Dead code\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 13)\n" +
" public boolean equals(@NonNull Object other) { return false; }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter other, inherited method from Object declares this parameter as @Nullable\n" +
"----------\n" +
"3 problems (3 warnings)\n",
true);
}
// project is configured for eea (jar on classpath), but no specific file for Map found
public void test490010NoEeaFile2() throws IOException {
String annots_dir1 = Util.getOutputDirectory() + File.separator + "annots1";
new File(annots_dir1).mkdirs();
String annots_dir2 = Util.getOutputDirectory() + File.separator + "annots2";
String annots_java_lang = annots_dir2 + File.separator + "java/lang";
new File(annots_java_lang).mkdirs();
Util.createFile(annots_java_lang + File.separator + "Object.eea",
TEST_440687_OBJECT_EEA_CONTENT);
String zipName = Util.getOutputDirectory() + File.separator + "annots2.zip";
Util.zip(new File(annots_dir2), zipName);
Util.delete(annots_dir2);
runTest440687("-annotationpath CLASSPATH -classpath \"" + zipName + "\"",
File.pathSeparator + annots_dir1, // extra source path
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 9)\n" +
" Test1 v = map.get(key);\n" +
" ^^^^^^^^^^^^\n" +
"Unsafe interpretation of method return type as \'@NonNull\' based on the receiver type \'@NonNull Map<@NonNull String,@NonNull Test1>\'. Type \'Map<K,V>\' doesn\'t seem to be designed with null type annotations in mind\n" +
"----------\n" +
"2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 11)\n" +
" throw new RuntimeException(); // should not be reported as dead code, although V is a \'@NonNull Test1\'\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Dead code\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 13)\n" +
" public boolean equals(@NonNull Object other) { return false; }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter other, inherited method from Object declares this parameter as @Nullable\n" +
"----------\n" +
"3 problems (3 warnings)\n",
true);
}
// project is configured for eea (dedicated annotation zip), but no specific file for Map found
public void test490010NoEeaFile3() throws IOException {
String annots_dir1 = Util.getOutputDirectory() + File.separator + "annots1";
new File(annots_dir1).mkdirs();
String annots_dir2 = Util.getOutputDirectory() + File.separator + "annots2";
String annots_java_lang = annots_dir2 + File.separator + "java/lang";
new File(annots_java_lang).mkdirs();
Util.createFile(annots_java_lang + File.separator + "Object.eea",
TEST_440687_OBJECT_EEA_CONTENT);
String zipName = Util.getOutputDirectory() + File.separator + "annots2.zip";
Util.zip(new File(annots_dir2), zipName);
Util.delete(annots_dir2);
runTest440687("-annotationpath \"" + zipName + "\"",
File.pathSeparator + annots_dir1, // extra source path
"----------\n" +
"1. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 9)\n" +
" Test1 v = map.get(key);\n" +
" ^^^^^^^^^^^^\n" +
"Unsafe interpretation of method return type as \'@NonNull\' based on the receiver type \'@NonNull Map<@NonNull String,@NonNull Test1>\'. Type \'Map<K,V>\' doesn\'t seem to be designed with null type annotations in mind\n" +
"----------\n" +
"2. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 11)\n" +
" throw new RuntimeException(); // should not be reported as dead code, although V is a \'@NonNull Test1\'\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Dead code\n" +
"----------\n" +
"3. WARNING in ---OUTPUT_DIR_PLACEHOLDER---/test1/Test1.java (at line 13)\n" +
" public boolean equals(@NonNull Object other) { return false; }\n" +
" ^^^^^^^^^^^^^^^\n" +
"Illegal redefinition of parameter other, inherited method from Object declares this parameter as @Nullable\n" +
"----------\n" +
"3 problems (3 warnings)\n",
true);
}
}