Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Herrmann2015-11-17 13:26:53 +0000
committerStephan Herrmann2015-12-03 14:50:32 +0000
commit6dbe2813dd7277bd2252f3441ba09237b6b43790 (patch)
tree76c61d1c66ac33d85a9b274b7fcfd65613c4af35
parentc6083a2d48fa11ffa1e9554a2f00be6af36feafe (diff)
downloadeclipse.jdt.core-6dbe2813dd7277bd2252f3441ba09237b6b43790.tar.gz
eclipse.jdt.core-6dbe2813dd7277bd2252f3441ba09237b6b43790.tar.xz
eclipse.jdt.core-6dbe2813dd7277bd2252f3441ba09237b6b43790.zip
Bug 369079: [null] Allow multiple null annotations I20151203-1230
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java3
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java149
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java60
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java59
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java9
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java43
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java15
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java49
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java37
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java12
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java9
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java12
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java35
-rw-r--r--org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java66
19 files changed, 466 insertions, 133 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
index f3cdee896c..45d9147eef 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
@@ -1959,8 +1959,11 @@ public void test012b(){
" <option key=\"org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation\" value=\"ignore\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnull\" value=\"org.eclipse.jdt.annotation.NonNull\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnull.secondary\" value=\"\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault\" value=\"org.eclipse.jdt.annotation.NonNullByDefault\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary\" value=\"\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.nullable\" value=\"org.eclipse.jdt.annotation.Nullable\"/>\n" +
+ " <option key=\"org.eclipse.jdt.core.compiler.annotation.nullable.secondary\" value=\"\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.annotation.nullanalysis\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode\" value=\"disabled\"/>\n" +
" <option key=\"org.eclipse.jdt.core.compiler.codegen.lambda.genericSignature\" value=\"do not generate\"/>\n" +
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
index 66c9b0ad53..42f263bc91 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationTest.java
@@ -16,6 +16,7 @@ package org.eclipse.jdt.core.tests.compiler.regression;
import java.io.File;
+import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
@@ -8660,4 +8661,152 @@ public void testBug482075() {
""
);
}
+public void testMultipleAnnotations() {
+ Map options1 = new HashMap<>(getCompilerOptions());
+ options1.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo1.NonNull");
+ options1.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo1.Nullable");
+ runConformTest(
+ new String[] {
+ "org/foo1/Nullable.java",
+ "package org.foo1;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\n" +
+ "public @interface Nullable {}\n",
+ "org/foo1/NonNull.java",
+ "package org.foo1;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\n" +
+ "public @interface NonNull {}\n",
+ "p1/TestNulls.java",
+ "package p1;\n" +
+ "import org.foo1.*;\n" +
+ "\n" +
+ "public class TestNulls {\n" +
+ " public @Nullable String weaken(@NonNull String theValue) {\n" +
+ " return theValue;\n" +
+ " }\n" +
+ "\n" +
+ "}"
+ },
+ options1);
+ Map options2 = new HashMap<>(getCompilerOptions());
+ options2.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo2.NonNull2");
+ options2.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo2.Nullable2");
+ options2.put(JavaCore.COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME, "org.foo2.NoNulls2");
+ runConformTest(
+ false, // flush
+ new String[] {
+ "org/foo2/Nullable2.java",
+ "package org.foo2;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\n" +
+ "public @interface Nullable2 {}\n",
+ "org/foo2/NonNull2.java",
+ "package org.foo2;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\n" +
+ "public @interface NonNull2 {}\n",
+ "org/foo2/NoNulls2.java",
+ "package org.foo2;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.FIELD, ElementType.METHOD, ElementType.TYPE})\n" +
+ "public @interface NoNulls2 {}\n",
+ "p2/TestNulls2.java",
+ "package p2;\n" +
+ "import org.foo2.*;\n" +
+ "\n" +
+ "public class TestNulls2 {\n" +
+ " public @Nullable2 String weaken(@NonNull2 String theValue) {\n" +
+ " return theValue;\n" +
+ " }\n" +
+ " @NoNulls2\n" +
+ " public String strong(String theValue) {\n" +
+ " return theValue;\n" +
+ " }\n" +
+ "\n" +
+ "}",
+ "p2/TestNulls2a.java",
+ "package p2;\n" +
+ "import org.foo2.*;\n" +
+ "\n" +
+ "@NoNulls2\n" +
+ "public class TestNulls2a {\n" +
+ " public String strong(String theValue) {\n" +
+ " return theValue;\n" +
+ " }\n" +
+ "\n" +
+ "}"
+ },
+ null, //libs
+ options1,
+ "",
+ "",
+ "",
+ JavacTestOptions.DEFAULT);
+ Map options3 = getCompilerOptions();
+ options3.put(JavaCore.COMPILER_NONNULL_ANNOTATION_SECONDARY_NAMES, "org.foo1.NonNull,org.foo2.NonNull2");
+ options3.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_SECONDARY_NAMES, " org.foo1.Nullable , org.foo2.Nullable2 "); // some spaces to test trimming
+ options3.put(JavaCore.COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_SECONDARY_NAMES, "org.foo2.NoNulls2");
+ String specifiedOrInferred = (this.complianceLevel < ClassFileConstants.JDK1_8 ? "specified" : "inferred");
+ runNegativeTestWithLibs(
+ new String[] {
+ "p3/Test.java",
+ "package p3;\n" +
+ "import p1.TestNulls;\n" +
+ "import p2.TestNulls2;\n" +
+ "import p2.TestNulls2a;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class Test {\n" +
+ " @NonNull String test1(TestNulls test, @Nullable String input) {\n" +
+ " return test.weaken(input);\n" +
+ " }\n" +
+ " @NonNull String test2(TestNulls2 test, @Nullable String input) {\n" +
+ " return test.weaken(input);\n" +
+ " }\n" +
+ " @NonNull String test3(TestNulls2 test, @Nullable String input) {\n" +
+ " return test.strong(input); // requires nonnull due to method-level default\n" +
+ " }\n" +
+ " @NonNull String test4(TestNulls2a test, @Nullable String input) {\n" +
+ " return test.strong(input); // requires nonnull due to type-level default\n" +
+ " }\n" +
+ "}\n"
+ },
+ options3,
+ "----------\n" +
+ "1. ERROR in p3\\Test.java (at line 8)\n" +
+ " return test.weaken(input);\n" +
+ " ^^^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch: required \'@NonNull String\' but the provided value is "+specifiedOrInferred+" as @Nullable\n" +
+ "----------\n" +
+ "2. ERROR in p3\\Test.java (at line 8)\n" +
+ " return test.weaken(input);\n" +
+ " ^^^^^\n" +
+ mismatch_NonNull_Nullable("String") +
+ "----------\n" +
+ "3. ERROR in p3\\Test.java (at line 11)\n" +
+ " return test.weaken(input);\n" +
+ " ^^^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch: required \'@NonNull String\' but the provided value is "+specifiedOrInferred+" as @Nullable\n" +
+ "----------\n" +
+ "4. ERROR in p3\\Test.java (at line 11)\n" +
+ " return test.weaken(input);\n" +
+ " ^^^^^\n" +
+ mismatch_NonNull_Nullable("String") +
+ "----------\n" +
+ "5. ERROR in p3\\Test.java (at line 14)\n" +
+ " return test.strong(input); // requires nonnull due to method-level default\n" +
+ " ^^^^^\n" +
+ mismatch_NonNull_Nullable("String") +
+ "----------\n" +
+ "6. ERROR in p3\\Test.java (at line 17)\n" +
+ " return test.strong(input); // requires nonnull due to type-level default\n" +
+ " ^^^^^\n" +
+ mismatch_NonNull_Nullable("String") +
+ "----------\n");
+}
}
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
index 690aeb4f24..53ba667238 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullTypeAnnotationTest.java
@@ -15,12 +15,14 @@
package org.eclipse.jdt.core.tests.compiler.regression;
import java.io.File;
+import java.util.HashMap;
import java.util.Map;
import junit.framework.Test;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.tests.util.Util;
+import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
@SuppressWarnings({ "unchecked", "rawtypes" })
public class NullTypeAnnotationTest extends AbstractNullAnnotationTest {
@@ -8853,5 +8855,63 @@ public void testBug483527() {
},
compilerOptions,
"");
+}
+public void testMultipleAnnotations1() {
+ Map options1 = new HashMap<>(getCompilerOptions());
+ options1.put(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "org.foo.NonNull");
+ options1.put(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "org.foo.Nullable");
+ runConformTest(
+ new String[] {
+ "org/foo/Nullable.java",
+ "package org.foo;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.TYPE_USE})\n" +
+ "public @interface Nullable {}\n",
+ "org/foo/NonNull.java",
+ "package org.foo;\n" +
+ "import java.lang.annotation.*;\n" +
+ "@Retention(RetentionPolicy.CLASS)\n" +
+ "@Target({ElementType.TYPE_USE})\n" +
+ "public @interface NonNull {}\n",
+ "p1/TestNulls.java",
+ "package p1;\n" +
+ "import org.foo.*;\n" +
+ "\n" +
+ "public class TestNulls {\n" +
+ " public @Nullable String weaken(@NonNull String theValue) {\n" +
+ " return theValue;\n" +
+ " }\n" +
+ "\n" +
+ "}"
+ },
+ options1);
+ Map options2 = getCompilerOptions();
+ options2.put(CompilerOptions.OPTION_NonNullAnnotationSecondaryNames, "org.foo.NonNull");
+ options2.put(CompilerOptions.OPTION_NullableAnnotationSecondaryNames, "org.foo.Nullable");
+ runNegativeTestWithLibs(
+ new String[] {
+ "p2/Test.java",
+ "package p2;\n" +
+ "import p1.TestNulls;\n" +
+ "import org.eclipse.jdt.annotation.*;\n" +
+ "public class Test {\n" +
+ " @NonNull String test(TestNulls test, @Nullable String input) {\n" +
+ " return test.weaken(input);\n" +
+ " }\n" +
+ "}\n"
+ },
+ options2,
+ "----------\n" +
+ "1. ERROR in p2\\Test.java (at line 6)\n" +
+ " return test.weaken(input);\n" +
+ " ^^^^^^^^^^^^^^^^^^\n" +
+ "Null type mismatch (type annotations): required \'@NonNull String\' but this expression has type \'@Nullable String\'\n" +
+ "----------\n" +
+ "2. ERROR in p2\\Test.java (at line 6)\n" +
+ " return test.weaken(input);\n" +
+ " ^^^^^\n" +
+ "Null type mismatch (type annotations): required \'@NonNull String\' but this expression has type \'@Nullable String\'\n" +
+ "----------\n");
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
index 4651364ad6..5d489477c7 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java
@@ -972,10 +972,10 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
System.arraycopy(se8Annotations, 0, se8Annotations = new AnnotationBinding[se8count + 1], 0, se8count);
se8Annotations[se8count++] = annotation;
}
- if (annotationType.id == TypeIds.T_ConfiguredAnnotationNonNull) {
+ if (annotationType.hasNullBit(TypeIds.BitNonNullAnnotation)) {
se8nullBits |= TagBits.AnnotationNonNull;
se8NullAnnotation = annotations[i];
- } else if (annotationType.id == TypeIds.T_ConfiguredAnnotationNullable) {
+ } else if (annotationType.hasNullBit(TypeIds.BitNullableAnnotation)) {
se8nullBits |= TagBits.AnnotationNullable;
se8NullAnnotation = annotations[i];
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
index 94b8b08caa..0222db4be2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
@@ -388,34 +388,32 @@ public abstract class Annotation extends Expression {
case TypeIds.T_JavaLangInvokeMethodHandlePolymorphicSignature :
tagBits |= TagBits.AnnotationPolymorphicSignature;
break;
- case TypeIds.T_ConfiguredAnnotationNullable :
- tagBits |= TagBits.AnnotationNullable;
- break;
- case TypeIds.T_ConfiguredAnnotationNonNull :
- tagBits |= TagBits.AnnotationNonNull;
- break;
- case TypeIds.T_ConfiguredAnnotationNonNullByDefault :
- // seeing this id implies that null annotation analysis is enabled
- Object value = null;
- if (valueAttribute != null) {
- if (valueAttribute.compilerElementPair != null)
- value = valueAttribute.compilerElementPair.value;
- } else { // fetch default value - TODO: cache it?
- MethodBinding[] methods = annotationType.methods();
- if (methods != null && methods.length == 1)
- value = methods[0].getDefaultValue();
- else
- tagBits |= TagBits.AnnotationNonNullByDefault; // custom unconfigurable NNBD
- }
- if (value instanceof BooleanConstant) {
- // boolean value is used for declaration annotations, signal using the annotation tag bit:
- tagBits |= ((BooleanConstant)value).booleanValue() ? TagBits.AnnotationNonNullByDefault : TagBits.AnnotationNullUnspecifiedByDefault;
- } else if (value != null) {
- // non-boolean value signals type annotations, evaluate from DefaultLocation[] to bitvector a la Binding#NullnessDefaultMASK:
- tagBits |= nullLocationBitsFromAnnotationValue(value);
- }
- break;
}
+ if (annotationType.hasNullBit(TypeIds.BitNullableAnnotation)) {
+ tagBits |= TagBits.AnnotationNullable;
+ } else if (annotationType.hasNullBit(TypeIds.BitNonNullAnnotation)) {
+ tagBits |= TagBits.AnnotationNonNull;
+ } else if (annotationType.hasNullBit(TypeIds.BitNonNullByDefaultAnnotation)) {
+ Object value = null;
+ if (valueAttribute != null) {
+ if (valueAttribute.compilerElementPair != null)
+ value = valueAttribute.compilerElementPair.value;
+ } else { // fetch default value - TODO: cache it?
+ MethodBinding[] methods = annotationType.methods();
+ if (methods != null && methods.length == 1)
+ value = methods[0].getDefaultValue();
+ else
+ tagBits |= TagBits.AnnotationNonNullByDefault; // custom unconfigurable NNBD
+ }
+ if (value instanceof BooleanConstant) {
+ // boolean value is used for declaration annotations, signal using the annotation tag bit:
+ tagBits |= ((BooleanConstant)value).booleanValue() ? TagBits.AnnotationNonNullByDefault : TagBits.AnnotationNullUnspecifiedByDefault;
+ } else if (value != null) {
+ // non-boolean value signals type annotations, evaluate from DefaultLocation[] to bitvector a la Binding#NullnessDefaultMASK:
+ tagBits |= nullLocationBitsFromAnnotationValue(value);
+ }
+ }
+
return tagBits;
}
@@ -1160,8 +1158,7 @@ public abstract class Annotation extends Expression {
QualifiedTypeReference.rejectAnnotationsOnStaticMemberQualififer(scope, currentType, new Annotation [] { annotation });
continue nextAnnotation;
} else {
- int id = annotation.resolvedType.id;
- if (id == TypeIds.T_ConfiguredAnnotationNonNull || id == TypeIds.T_ConfiguredAnnotationNullable) {
+ if (annotation.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) {
scope.problemReporter().nullAnnotationUnsupportedLocation(annotation);
continue nextAnnotation;
}
@@ -1172,6 +1169,10 @@ public abstract class Annotation extends Expression {
}
}
+ public boolean hasNullBit(int bit) {
+ return this.resolvedType instanceof ReferenceBinding && ((ReferenceBinding) this.resolvedType).hasNullBit(bit);
+ }
+
public abstract void traverse(ASTVisitor visitor, BlockScope scope);
public abstract void traverse(ASTVisitor visitor, ClassScope scope);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
index 08515e553c..0a89f0384c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java
@@ -581,13 +581,8 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
AnnotationBinding [] annotations = descParameters[i].getTypeAnnotations();
for (int j = 0, length = annotations.length; j < length; j++) {
AnnotationBinding annotation = annotations[j];
- if (annotation != null) {
- switch (annotation.getAnnotationType().id) {
- case TypeIds.T_ConfiguredAnnotationNullable :
- case TypeIds.T_ConfiguredAnnotationNonNull :
- ourParameters[i] = env.createAnnotatedType(ourParameters[i], new AnnotationBinding [] { annotation });
- break;
- }
+ if (annotation != null && annotation.getAnnotationType().hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) {
+ ourParameters[i] = env.createAnnotatedType(ourParameters[i], new AnnotationBinding [] { annotation });
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
index d9e0cec29e..f1c4feef85 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Statement.java
@@ -192,6 +192,11 @@ void internalAnalyseOneArgument18(BlockScope currentScope, FlowContext flowConte
// immediate reporting:
currentScope.problemReporter().nullityMismatchingTypeAnnotation(argument, argument.resolvedType, expectedType, annotationStatus);
} else if (annotationStatus.isAnyMismatch() || (statusFromAnnotatedNull & FlowInfo.POTENTIALLY_NULL) != 0) {
+ if (!expectedType.hasNullTypeAnnotations() && expectedNonNullness == Boolean.TRUE) {
+ // improve problem rendering when using a declaration annotation in a 1.8 setting
+ LookupEnvironment env = currentScope.environment();
+ expectedType = env.createAnnotatedType(expectedType, new AnnotationBinding[] {env.getNonNullAnnotation()});
+ }
flowContext.recordNullityMismatch(currentScope, argument, argument.resolvedType, expectedType, flowInfo, nullStatus, annotationStatus);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
index ce7b8fa081..fad1c0a224 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java
@@ -699,11 +699,9 @@ public Annotation findAnnotation(long nullTagBits) {
if (this.annotations != null) {
Annotation[] innerAnnotations = this.annotations[this.annotations.length-1];
if (innerAnnotations != null) {
- int annId = nullTagBits == TagBits.AnnotationNonNull ? TypeIds.T_ConfiguredAnnotationNonNull : TypeIds.T_ConfiguredAnnotationNullable;
+ int annBit = nullTagBits == TagBits.AnnotationNonNull ? TypeIds.BitNonNullAnnotation : TypeIds.BitNullableAnnotation;
for (int i = 0; i < innerAnnotations.length; i++) {
- if (innerAnnotations[i] != null
- && innerAnnotations[i].resolvedType != null
- && innerAnnotations[i].resolvedType.id == annId)
+ if (innerAnnotations[i] != null && innerAnnotations[i].hasNullBit(annBit))
return innerAnnotations[i];
}
}
@@ -727,10 +725,7 @@ public boolean hasNullTypeAnnotation(AnnotationPosition position) {
public static boolean containsNullAnnotation(Annotation[] annotations) {
if (annotations != null) {
for (int i = 0; i < annotations.length; i++) {
- if (annotations[i] != null
- && annotations[i].resolvedType != null
- && (annotations[i].resolvedType.id == TypeIds.T_ConfiguredAnnotationNonNull
- || annotations[i].resolvedType.id == TypeIds.T_ConfiguredAnnotationNullable))
+ if (annotations[i] != null && (annotations[i].hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)))
return true;
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
index 1b006ee54a..bae98724c5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
@@ -172,6 +172,9 @@ public class CompilerOptions {
public static final String OPTION_NullableAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nullable"; //$NON-NLS-1$
public static final String OPTION_NonNullAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nonnull"; //$NON-NLS-1$
public static final String OPTION_NonNullByDefaultAnnotationName = "org.eclipse.jdt.core.compiler.annotation.nonnullbydefault"; //$NON-NLS-1$
+ public static final String OPTION_NullableAnnotationSecondaryNames = "org.eclipse.jdt.core.compiler.annotation.nullable.secondary"; //$NON-NLS-1$
+ public static final String OPTION_NonNullAnnotationSecondaryNames = "org.eclipse.jdt.core.compiler.annotation.nonnull.secondary"; //$NON-NLS-1$
+ public static final String OPTION_NonNullByDefaultAnnotationSecondaryNames = "org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary"; //$NON-NLS-1$
public static final String OPTION_ReportUninternedIdentityComparison = "org.eclipse.jdt.core.compiler.problem.uninternedIdentityComparison"; //$NON-NLS-1$
// defaults for the above:
static final char[][] DEFAULT_NULLABLE_ANNOTATION_NAME = CharOperation.splitOn('.', "org.eclipse.jdt.annotation.Nullable".toCharArray()); //$NON-NLS-1$
@@ -213,6 +216,8 @@ public class CompilerOptions {
public static final String NO_TAG = "no_tag"; //$NON-NLS-1$
public static final String ALL_STANDARD_TAGS = "all_standard_tags"; //$NON-NLS-1$
+ private static final String[] NO_STRINGS = new String[0];
+
/**
* Bit mask for configurable problems (error/warning threshold)
* Note: bitmask assumes 3 highest bits to denote irritant group (to allow storing 8 groups of 29 bits each
@@ -445,6 +450,12 @@ public class CompilerOptions {
public char[][] nonNullAnnotationName;
/** Fully qualified name of annotation to use as marker for default nonnull. */
public char[][] nonNullByDefaultAnnotationName;
+ /** Fully qualified names of secondary annotations to use as marker for nullable types. */
+ public String[] nullableAnnotationSecondaryNames = NO_STRINGS;
+ /** Fully qualified names of secondary annotations to use as marker for nonnull types. */
+ public String[] nonNullAnnotationSecondaryNames = NO_STRINGS;
+ /** Fully qualified names of secondary annotations to use as marker for default nonnull. */
+ public String[] nonNullByDefaultAnnotationSecondaryNames = NO_STRINGS;
/** TagBits-encoded default for non-annotated types. */
public long intendedDefaultNonNullness; // 0 or TagBits#AnnotationNonNull
/** Should resources (objects of type Closeable) be analysed for matching calls to close()? */
@@ -1182,6 +1193,9 @@ public class CompilerOptions {
optionsMap.put(OPTION_NullableAnnotationName, String.valueOf(CharOperation.concatWith(this.nullableAnnotationName, '.')));
optionsMap.put(OPTION_NonNullAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullAnnotationName, '.')));
optionsMap.put(OPTION_NonNullByDefaultAnnotationName, String.valueOf(CharOperation.concatWith(this.nonNullByDefaultAnnotationName, '.')));
+ optionsMap.put(OPTION_NullableAnnotationSecondaryNames, nameListToString(this.nullableAnnotationSecondaryNames));
+ optionsMap.put(OPTION_NonNullAnnotationSecondaryNames, nameListToString(this.nonNullAnnotationSecondaryNames));
+ optionsMap.put(OPTION_NonNullByDefaultAnnotationSecondaryNames, nameListToString(this.nonNullByDefaultAnnotationSecondaryNames));
optionsMap.put(OPTION_ReportMissingNonNullByDefaultAnnotation, getSeverityString(MissingNonNullByDefaultAnnotation));
optionsMap.put(OPTION_ReportUnusedTypeParameter, getSeverityString(UnusedTypeParameter));
optionsMap.put(OPTION_SyntacticNullAnalysisForFields, this.enableSyntacticNullAnalysisForFields ? ENABLED : DISABLED);
@@ -1705,6 +1719,15 @@ public class CompilerOptions {
if ((optionValue = optionsMap.get(OPTION_NonNullByDefaultAnnotationName)) != null) {
this.nonNullByDefaultAnnotationName = CharOperation.splitAndTrimOn('.', optionValue.toCharArray());
}
+ if ((optionValue = optionsMap.get(OPTION_NullableAnnotationSecondaryNames)) != null) {
+ this.nullableAnnotationSecondaryNames = stringToNameList(optionValue);
+ }
+ if ((optionValue = optionsMap.get(OPTION_NonNullAnnotationSecondaryNames)) != null) {
+ this.nonNullAnnotationSecondaryNames = stringToNameList(optionValue);
+ }
+ if ((optionValue = optionsMap.get(OPTION_NonNullByDefaultAnnotationSecondaryNames)) != null) {
+ this.nonNullByDefaultAnnotationSecondaryNames = stringToNameList(optionValue);
+ }
if ((optionValue = optionsMap.get(OPTION_ReportMissingNonNullByDefaultAnnotation)) != null) updateSeverity(MissingNonNullByDefaultAnnotation, optionValue);
if ((optionValue = optionsMap.get(OPTION_SyntacticNullAnalysisForFields)) != null) {
this.enableSyntacticNullAnalysisForFields = ENABLED.equals(optionValue);
@@ -1850,6 +1873,26 @@ public class CompilerOptions {
}
}
}
+
+ private String[] stringToNameList(String optionValue) {
+ String[] result = optionValue.split(","); //$NON-NLS-1$
+ if (result == null)
+ return NO_STRINGS;
+ for (int i = 0; i < result.length; i++)
+ result[i] = result[i].trim();
+ return result;
+ }
+
+ String nameListToString(String[] names) {
+ if (names == null) return ""; //$NON-NLS-1$
+ StringBuilder buf = new StringBuilder();
+ for (int i = 0; i < names.length; i++) {
+ if (i > 0) buf.append(',');
+ buf.append(names[i]);
+ }
+ return buf.toString();
+ }
+
public String toString() {
StringBuffer buf = new StringBuffer("CompilerOptions:"); //$NON-NLS-1$
buf.append("\n\t- local variables debug attributes: ").append((this.produceDebugAttributes & ClassFileConstants.ATTR_VARS) != 0 ? "ON" : " OFF"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
index 7a08e9c14f..e36c9bc874 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ArrayBinding.java
@@ -400,15 +400,12 @@ public void setTypeAnnotations(AnnotationBinding[] annotations, boolean evalNull
for (int i = 0, length = annotations.length; i < length; i++) {
AnnotationBinding annotation = annotations[i];
if (annotation != null) {
- switch (annotation.type.id) {
- case TypeIds.T_ConfiguredAnnotationNullable :
- nullTagBits |= TagBits.AnnotationNullable;
- this.tagBits |= TagBits.HasNullTypeAnnotation;
- break;
- case TypeIds.T_ConfiguredAnnotationNonNull :
- nullTagBits |= TagBits.AnnotationNonNull;
- this.tagBits |= TagBits.HasNullTypeAnnotation;
- break;
+ if (annotation.type.hasNullBit(TypeIds.BitNullableAnnotation)) {
+ nullTagBits |= TagBits.AnnotationNullable;
+ this.tagBits |= TagBits.HasNullTypeAnnotation;
+ } else if (annotation.type.hasNullBit(TypeIds.BitNonNullAnnotation)) {
+ nullTagBits |= TagBits.AnnotationNonNull;
+ this.tagBits |= TagBits.HasNullTypeAnnotation;
}
} else {
// null signals end of annotations for the current dimension in the serialized form.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
index 5330998195..a68bba5194 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryTypeBinding.java
@@ -554,10 +554,9 @@ private ITypeAnnotationWalker getTypeAnnotationWalker(IBinaryTypeAnnotation[] an
private int getNullDefaultFrom(IBinaryAnnotation[] declAnnotations) {
if (declAnnotations != null) {
- char[][] nonNullByDefaultAnnotationName = this.environment.getNonNullByDefaultAnnotationName();
for (IBinaryAnnotation annotation : declAnnotations) {
char[][] typeName = signature2qualifiedTypeName(annotation.getTypeName());
- if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName))
+ if (this.environment.getNullAnnotationBit(typeName) == TypeIds.BitNonNullByDefaultAnnotation)
return getNonNullByDefaultValue(annotation);
}
}
@@ -1531,10 +1530,6 @@ private void scanFieldForNullAnnotation(IBinaryField field, FieldBinding fieldBi
}
// global option is checked by caller
- char[][] nullableAnnotationName = this.environment.getNullableAnnotationName();
- char[][] nonNullAnnotationName = this.environment.getNonNullAnnotationName();
- if (nullableAnnotationName == null || nonNullAnnotationName == null)
- return; // not well-configured to use null annotations
if (fieldBinding.type == null || fieldBinding.type.isBaseType())
return; // null annotations are only applied to reference types
@@ -1548,13 +1543,13 @@ private void scanFieldForNullAnnotation(IBinaryField field, FieldBinding fieldBi
char[] annotationTypeName = annotations[i].getTypeName();
if (annotationTypeName[0] != Util.C_RESOLVED)
continue;
- char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
- if (CharOperation.equals(typeName, nonNullAnnotationName)) {
+ int typeBit = this.environment.getNullAnnotationBit(signature2qualifiedTypeName(annotationTypeName));
+ if (typeBit == TypeIds.BitNonNullAnnotation) {
fieldBinding.tagBits |= TagBits.AnnotationNonNull;
explicitNullness = true;
break;
}
- if (CharOperation.equals(typeName, nullableAnnotationName)) {
+ if (typeBit == TypeIds.BitNullableAnnotation) {
fieldBinding.tagBits |= TagBits.AnnotationNullable;
explicitNullness = true;
break;
@@ -1588,11 +1583,6 @@ private void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding met
return;
}
}
- char[][] nullableAnnotationName = this.environment.getNullableAnnotationName();
- char[][] nonNullAnnotationName = this.environment.getNonNullAnnotationName();
- char[][] nonNullByDefaultAnnotationName = this.environment.getNonNullByDefaultAnnotationName();
- if (nullableAnnotationName == null || nonNullAnnotationName == null || nonNullByDefaultAnnotationName == null)
- return; // not well-configured to use null annotations
// return:
ITypeAnnotationWalker returnWalker = externalAnnotationWalker.toMethodReturn();
@@ -1604,16 +1594,21 @@ private void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding met
char[] annotationTypeName = annotations[i].getTypeName();
if (annotationTypeName[0] != Util.C_RESOLVED)
continue;
- char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
- if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) {
+ int typeBit = this.environment.getNullAnnotationBit(signature2qualifiedTypeName(annotationTypeName));
+ if (typeBit == TypeIds.BitNonNullByDefaultAnnotation) {
methodBinding.defaultNullness = getNonNullByDefaultValue(annotations[i]);
- if (methodBinding.defaultNullness == Binding.NULL_UNSPECIFIED_BY_DEFAULT)
+ if (methodBinding.defaultNullness == Binding.NULL_UNSPECIFIED_BY_DEFAULT) {
methodBinding.tagBits |= TagBits.AnnotationNullUnspecifiedByDefault;
- else if (methodBinding.defaultNullness != 0)
+ } else if (methodBinding.defaultNullness != 0) {
methodBinding.tagBits |= TagBits.AnnotationNonNullByDefault;
- } else if (CharOperation.equals(typeName, nonNullAnnotationName)) {
+ if (methodBinding.defaultNullness == Binding.NONNULL_BY_DEFAULT && this.environment.usesNullTypeAnnotations()) {
+ // reading a decl-nnbd in a project using type annotations, mimic corresponding semantics by enumerating:
+ methodBinding.defaultNullness |= Binding.DefaultLocationParameter | Binding.DefaultLocationReturnType;
+ }
+ }
+ } else if (typeBit == TypeIds.BitNonNullAnnotation) {
methodBinding.tagBits |= TagBits.AnnotationNonNull;
- } else if (CharOperation.equals(typeName, nullableAnnotationName)) {
+ } else if (typeBit == TypeIds.BitNullableAnnotation) {
methodBinding.tagBits |= TagBits.AnnotationNullable;
}
}
@@ -1638,13 +1633,13 @@ private void scanMethodForNullAnnotation(IBinaryMethod method, MethodBinding met
char[] annotationTypeName = paramAnnotations[i].getTypeName();
if (annotationTypeName[0] != Util.C_RESOLVED)
continue;
- char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
- if (CharOperation.equals(typeName, nonNullAnnotationName)) {
+ int typeBit = this.environment.getNullAnnotationBit(signature2qualifiedTypeName(annotationTypeName));
+ if (typeBit == TypeIds.BitNonNullAnnotation) {
if (methodBinding.parameterNonNullness == null)
methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
methodBinding.parameterNonNullness[j] = Boolean.TRUE;
break;
- } else if (CharOperation.equals(typeName, nullableAnnotationName)) {
+ } else if (typeBit == TypeIds.BitNullableAnnotation) {
if (methodBinding.parameterNonNullness == null)
methodBinding.parameterNonNullness = new Boolean[numVisibleParams];
methodBinding.parameterNonNullness[j] = Boolean.FALSE;
@@ -1676,14 +1671,18 @@ private void scanTypeForNullDefaultAnnotation(IBinaryType binaryType, PackageBin
char[] annotationTypeName = annotations[i].getTypeName();
if (annotationTypeName[0] != Util.C_RESOLVED)
continue;
- char[][] typeName = signature2qualifiedTypeName(annotationTypeName);
- if (CharOperation.equals(typeName, nonNullByDefaultAnnotationName)) {
+ int typeBit = this.environment.getNullAnnotationBit(signature2qualifiedTypeName(annotationTypeName));
+ if (typeBit == TypeIds.BitNonNullByDefaultAnnotation) {
// using NonNullByDefault we need to inspect the details of the value() attribute:
nullness = getNonNullByDefaultValue(annotations[i]);
if (nullness == NULL_UNSPECIFIED_BY_DEFAULT) {
annotationBit = TagBits.AnnotationNullUnspecifiedByDefault;
} else if (nullness != 0) {
annotationBit = TagBits.AnnotationNonNullByDefault;
+ if (nullness == Binding.NONNULL_BY_DEFAULT && this.environment.usesNullTypeAnnotations()) {
+ // reading a decl-nnbd in a project using type annotations, mimic corresponding semantics by enumerating:
+ nullness |= Binding.DefaultLocationParameter | Binding.DefaultLocationReturnType | Binding.DefaultLocationField;
+ }
}
this.defaultNullness = nullness;
break;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
index 8882848fd2..1d5950dea8 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java
@@ -109,6 +109,8 @@ public class LookupEnvironment implements ProblemReasons, TypeConstants {
AnnotationBinding nonNullAnnotation;
AnnotationBinding nullableAnnotation;
+ Map<String,Integer> allNullAnnotations = null;
+
final List<MethodBinding> deferredEnumMethods = new ArrayList<>(); // during early initialization we cannot mark Enum-methods as nonnull.
/** Global access to the outermost active inference context as the universe for inference variable interning. */
@@ -1003,9 +1005,10 @@ public TypeBinding createAnnotatedType(TypeBinding type, AnnotationBinding[] new
continue;
}
long tagBits = 0;
- switch (newbies[i].type.id) {
- case TypeIds.T_ConfiguredAnnotationNonNull : tagBits = TagBits.AnnotationNonNull; break;
- case TypeIds.T_ConfiguredAnnotationNullable : tagBits = TagBits.AnnotationNullable; break;
+ if (newbies[i].type.hasNullBit(TypeIds.BitNonNullAnnotation)) {
+ tagBits = TagBits.AnnotationNonNull;
+ } else if (newbies[i].type.hasNullBit(TypeIds.BitNullableAnnotation)) {
+ tagBits = TagBits.AnnotationNullable;
}
if ((tagBitsSeen & tagBits) == 0) {
tagBitsSeen |= tagBits;
@@ -1109,6 +1112,27 @@ public char[][] getNonNullByDefaultAnnotationName() {
return this.globalOptions.nonNullByDefaultAnnotationName;
}
+int getNullAnnotationBit(char[][] qualifiedTypeName) {
+ if (this.allNullAnnotations == null) {
+ this.allNullAnnotations = new HashMap<>();
+ this.allNullAnnotations.put(CharOperation.toString(this.globalOptions.nonNullAnnotationName), TypeIds.BitNonNullAnnotation);
+ this.allNullAnnotations.put(CharOperation.toString(this.globalOptions.nullableAnnotationName), TypeIds.BitNullableAnnotation);
+ this.allNullAnnotations.put(CharOperation.toString(this.globalOptions.nonNullByDefaultAnnotationName), TypeIds.BitNonNullByDefaultAnnotation);
+ for (String name : this.globalOptions.nullableAnnotationSecondaryNames)
+ this.allNullAnnotations.put(name, TypeIds.BitNullableAnnotation);
+ for (String name : this.globalOptions.nonNullAnnotationSecondaryNames)
+ this.allNullAnnotations.put(name, TypeIds.BitNonNullAnnotation);
+ for (String name : this.globalOptions.nonNullByDefaultAnnotationSecondaryNames)
+ this.allNullAnnotations.put(name, TypeIds.BitNonNullByDefaultAnnotation);
+ }
+ String qualifiedTypeString = CharOperation.toString(qualifiedTypeName);
+ Integer typeBit = this.allNullAnnotations.get(qualifiedTypeString);
+ return typeBit == null ? 0 : typeBit;
+}
+public boolean isNullnessAnnotationPackage(PackageBinding pkg) {
+ return this.nonnullAnnotationPackage == pkg || this.nullableAnnotationPackage == pkg || this.nonnullByDefaultAnnotationPackage == pkg;
+}
+
public boolean usesNullTypeAnnotations() {
if (this.globalOptions.useNullTypeAnnotations != null)
return this.globalOptions.useNullTypeAnnotations;
@@ -1695,8 +1719,7 @@ public AnnotationBinding[] filterNullTypeAnnotations(AnnotationBinding[] typeAnn
if (typeAnnotation == null) {
count++; // sentinel in annotation sequence for array dimensions
} else {
- int id = typeAnnotation.type.id;
- if (id != TypeIds.T_ConfiguredAnnotationNonNull && id != TypeIds.T_ConfiguredAnnotationNullable)
+ if (!typeAnnotation.type.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation))
filtered[count++] = typeAnnotation;
}
}
@@ -1711,15 +1734,13 @@ public AnnotationBinding[] filterNullTypeAnnotations(AnnotationBinding[] typeAnn
public boolean containsNullTypeAnnotation(IBinaryAnnotation[] typeAnnotations) {
if (typeAnnotations.length == 0)
return false;
- char[][] nonNullAnnotationName = this.getNonNullAnnotationName();
- char[][] nullableAnnotationName = this.getNullableAnnotationName();
for (int i = 0; i < typeAnnotations.length; i++) {
IBinaryAnnotation typeAnnotation = typeAnnotations[i];
char[] typeName = typeAnnotation.getTypeName();
// typeName must be "Lfoo/X;"
if (typeName == null || typeName.length < 3 || typeName[0] != 'L') continue;
char[][] name = CharOperation.splitOn('/', typeName, 1, typeName.length-1);
- if (CharOperation.equals(name, nonNullAnnotationName) || CharOperation.equals(name, nullableAnnotationName))
+ if (getNullAnnotationBit(name) != 0)
return true;
}
return false;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java
index a182e1fa29..78325ab451 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/PackageBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -273,22 +273,24 @@ private boolean isPackageOfQualifiedTypeName(char[][] packageName, char[][] type
void checkIfNullAnnotationType(ReferenceBinding type) {
// check if type is one of the configured null annotation types
- // if so mark as a well known type using the corresponding typeID:
+ // if so mark as a well known type using the corresponding typeBit:
if (this.environment.nullableAnnotationPackage == this
&& CharOperation.equals(type.compoundName, this.environment.getNullableAnnotationName())) {
- type.id = TypeIds.T_ConfiguredAnnotationNullable;
+ type.typeBits |= TypeIds.BitNullableAnnotation;
if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type
this.environment.nullableAnnotationPackage = null; // don't check again
} else if (this.environment.nonnullAnnotationPackage == this
&& CharOperation.equals(type.compoundName, this.environment.getNonNullAnnotationName())) {
- type.id = TypeIds.T_ConfiguredAnnotationNonNull;
+ type.typeBits |= TypeIds.BitNonNullAnnotation;
if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type
this.environment.nonnullAnnotationPackage = null; // don't check again
} else if (this.environment.nonnullByDefaultAnnotationPackage == this
&& CharOperation.equals(type.compoundName, this.environment.getNonNullByDefaultAnnotationName())) {
- type.id = TypeIds.T_ConfiguredAnnotationNonNullByDefault;
+ type.typeBits |= TypeIds.BitNonNullByDefaultAnnotation;
if (!(type instanceof UnresolvedReferenceBinding)) // unresolved will need to check back for the resolved type
this.environment.nonnullByDefaultAnnotationPackage = null; // don't check again
+ } else {
+ type.typeBits |= this.environment.getNullAnnotationBit(type.compoundName);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
index 6684dba973..569bc996e2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ReferenceBinding.java
@@ -1154,6 +1154,11 @@ public final boolean hasRestrictedAccess() {
return (this.modifiers & ExtraCompilerModifiers.AccRestrictedAccess) != 0;
}
+/** Query typeBits without triggering supertype lookup. */
+public boolean hasNullBit(int mask) {
+ return (this.typeBits & mask) != 0;
+}
+
/** Answer true if the receiver implements anInterface or is identical to anInterface.
* If searchHierarchy is true, then also search the receiver's superclasses.
*
@@ -1649,8 +1654,8 @@ protected void appendNullAnnotation(StringBuffer nameBuffer, CompilerOptions opt
if (options.isAnnotationBasedNullAnalysisEnabled) {
if (options.usesNullTypeAnnotations()) {
for (AnnotationBinding annotation : this.typeAnnotations) {
- TypeBinding annotationType = annotation.getAnnotationType();
- if (annotationType.id == TypeIds.T_ConfiguredAnnotationNonNull || annotation.type.id == TypeIds.T_ConfiguredAnnotationNullable) {
+ ReferenceBinding annotationType = annotation.getAnnotationType();
+ if (annotationType.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) {
nameBuffer.append('@').append(annotationType.shortReadableName()).append(' ');
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
index 480cdd6780..75535967a1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java
@@ -2033,8 +2033,7 @@ public void evaluateNullAnnotations() {
for (int i = 0; i < annotations.length; i++) {
ReferenceBinding annotationType = annotations[i].getCompilerAnnotation().getAnnotationType();
if (annotationType != null) {
- if (annotationType.id == TypeIds.T_ConfiguredAnnotationNonNull
- || annotationType.id == TypeIds.T_ConfiguredAnnotationNullable) {
+ if (annotationType.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) {
this.scope.problemReporter().nullAnnotationUnsupportedLocation(annotations[i]);
this.tagBits &= ~TagBits.AnnotationNullMASK;
}
@@ -2046,10 +2045,7 @@ public void evaluateNullAnnotations() {
PackageBinding pkg = getPackage();
boolean isInDefaultPkg = (pkg.compoundName == CharOperation.NO_CHAR_CHAR);
if (!isPackageInfo) {
- boolean isInNullnessAnnotationPackage =
- pkg == this.scope.environment().nonnullAnnotationPackage
- || pkg == this.scope.environment().nullableAnnotationPackage
- || pkg == this.scope.environment().nonnullByDefaultAnnotationPackage;
+ boolean isInNullnessAnnotationPackage = this.scope.environment().isNullnessAnnotationPackage(pkg);
if (pkg.defaultNullness == NO_NULL_DEFAULT && !isInDefaultPkg && !isInNullnessAnnotationPackage && !(this instanceof NestedTypeBinding)) {
ReferenceBinding packageInfo = pkg.getType(TypeConstants.PACKAGE_INFO_NAME);
if (packageInfo == null) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
index 52c8148b61..29fe3dfa57 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBinding.java
@@ -1526,14 +1526,10 @@ public void setTypeAnnotations(AnnotationBinding[] annotations, boolean evalNull
for (int i = 0, length = annotations.length; i < length; i++) {
AnnotationBinding annotation = annotations[i];
if (annotation != null) {
- switch (annotation.type.id) {
- case TypeIds.T_ConfiguredAnnotationNullable :
- this.tagBits |= TagBits.AnnotationNullable | TagBits.HasNullTypeAnnotation;
- break;
- case TypeIds.T_ConfiguredAnnotationNonNull :
- this.tagBits |= TagBits.AnnotationNonNull | TagBits.HasNullTypeAnnotation;
- break;
- }
+ if (annotation.type.hasNullBit(TypeIds.BitNullableAnnotation))
+ this.tagBits |= TagBits.AnnotationNullable | TagBits.HasNullTypeAnnotation;
+ else if (annotation.type.hasNullBit(TypeIds.BitNonNullAnnotation))
+ this.tagBits |= TagBits.AnnotationNonNull | TagBits.HasNullTypeAnnotation;
}
}
// we do accept contradictory tagBits here, to support detecting contradictions caused by type substitution
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java
index 1693a65875..af32c78013 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeIds.java
@@ -108,10 +108,7 @@ public interface TypeIds {
// java 7 java.lang.AutoCloseable
final int T_JavaLangAutoCloseable = 62;
- // new in 3.8 for null annotations:
- final int T_ConfiguredAnnotationNullable = 65;
- final int T_ConfiguredAnnotationNonNull = 66;
- final int T_ConfiguredAnnotationNonNullByDefault = 67;
+ // new in 3.8 for null annotations, removed in 4.6 (ids 65-67)
// new in 3.8 to identify org.eclipse.core.runtime.Assert
final int T_OrgEclipseCoreRuntimeAssert = 68;
@@ -247,6 +244,14 @@ public interface TypeIds {
final int BitResourceFreeCloseable = 8;
final int BitUninternedType = 16;
+
+ /** Bit for a type configured as a @NonNull annotation. */
+ final int BitNonNullAnnotation = 32;
+ /** Bit for a type configured as a @Nullable annotation. */
+ final int BitNullableAnnotation = 64;
+ /** Bit for a type configured as a @NonNullByDefault annotation. */
+ final int BitNonNullByDefaultAnnotation = 128;
+
/**
* Set of type bits that should be inherited by any sub types.
*/
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index 7094b485f4..f3f50e2757 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -8776,9 +8776,6 @@ private boolean excludeDueToAnnotation(Annotation[] annotations, int problemId)
case TypeIds.T_JavaLangSuppressWarnings:
case TypeIds.T_JavaLangDeprecated:
case TypeIds.T_JavaLangSafeVarargs:
- case TypeIds.T_ConfiguredAnnotationNonNull:
- case TypeIds.T_ConfiguredAnnotationNullable:
- case TypeIds.T_ConfiguredAnnotationNonNullByDefault:
break;
case TypeIds.T_JavaxInjectInject:
case TypeIds.T_ComGoogleInjectInject:
@@ -8787,8 +8784,10 @@ private boolean excludeDueToAnnotation(Annotation[] annotations, int problemId)
return true; // @Inject on method/ctor does constitute a relevant use, just on fields it doesn't
break;
default:
- // non-standard annotation found, don't warn
- return true;
+ if (resolvedType instanceof ReferenceBinding)
+ if (((ReferenceBinding) resolvedType).hasNullBit(TypeIds.BitNullableAnnotation|TypeIds.BitNonNullAnnotation|TypeIds.BitNonNullByDefaultAnnotation))
+ break;
+ return true; // non-standard annotation found, don't warn
}
}
}
@@ -9289,9 +9288,7 @@ public void illegalRedefinitionToNonNullParameter(Argument argument, ReferenceBi
if (argument.annotations != null) {
for (int i=0; i<argument.annotations.length; i++) {
Annotation annotation = argument.annotations[i];
- if ( annotation.resolvedType.id == TypeIds.T_ConfiguredAnnotationNullable
- || annotation.resolvedType.id == TypeIds.T_ConfiguredAnnotationNonNull)
- {
+ if (annotation.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) {
sourceStart = annotation.sourceStart;
break;
}
@@ -9342,9 +9339,7 @@ public void illegalParameterRedefinition(Argument argument, ReferenceBinding dec
if (argument.annotations != null) {
for (int i=0; i<argument.annotations.length; i++) {
Annotation annotation = argument.annotations[i];
- if ( annotation.resolvedType.id == TypeIds.T_ConfiguredAnnotationNullable
- || annotation.resolvedType.id == TypeIds.T_ConfiguredAnnotationNonNull)
- {
+ if (annotation.hasNullBit(TypeIds.BitNonNullAnnotation|TypeIds.BitNullableAnnotation)) {
sourceStart = annotation.sourceStart;
break;
}
@@ -9373,7 +9368,7 @@ public void illegalReturnRedefinition(AbstractMethodDeclaration abstractMethodDe
.append(inheritedMethod.shortReadableName());
int sourceStart = methodDecl.returnType.sourceStart;
Annotation[] annotations = methodDecl.annotations;
- Annotation annotation = findAnnotation(annotations, TypeIds.T_ConfiguredAnnotationNullable);
+ Annotation annotation = findAnnotation(annotations, TypeIds.BitNullableAnnotation);
if (annotation != null) {
sourceStart = annotation.sourceStart;
}
@@ -9528,7 +9523,7 @@ public void nullAnnotationIsRedundant(AbstractMethodDeclaration sourceMethod, in
int sourceStart, sourceEnd;
if (i == -1) {
MethodDeclaration methodDecl = (MethodDeclaration) sourceMethod;
- Annotation annotation = findAnnotation(methodDecl.annotations, TypeIds.T_ConfiguredAnnotationNonNull);
+ Annotation annotation = findAnnotation(methodDecl.annotations, TypeIds.BitNonNullAnnotation);
sourceStart = annotation != null ? annotation.sourceStart : methodDecl.returnType.sourceStart;
sourceEnd = methodDecl.returnType.sourceEnd;
} else {
@@ -9540,14 +9535,14 @@ public void nullAnnotationIsRedundant(AbstractMethodDeclaration sourceMethod, in
}
public void nullAnnotationIsRedundant(FieldDeclaration sourceField) {
- Annotation annotation = findAnnotation(sourceField.annotations, TypeIds.T_ConfiguredAnnotationNonNull);
+ Annotation annotation = findAnnotation(sourceField.annotations, TypeIds.BitNonNullAnnotation);
int sourceStart = annotation != null ? annotation.sourceStart : sourceField.type.sourceStart;
int sourceEnd = sourceField.type.sourceEnd;
this.handle(IProblem.RedundantNullAnnotation, ProblemHandler.NoArgument, ProblemHandler.NoArgument, sourceStart, sourceEnd);
}
public void nullDefaultAnnotationIsRedundant(ASTNode location, Annotation[] annotations, Binding outer) {
- Annotation annotation = findAnnotation(annotations, TypeIds.T_ConfiguredAnnotationNonNullByDefault);
+ Annotation annotation = findAnnotation(annotations, TypeIds.BitNonNullByDefaultAnnotation);
int start = annotation != null ? annotation.sourceStart : location.sourceStart;
int end = annotation != null ? annotation.sourceEnd : location.sourceStart;
String[] args = NoArgument;
@@ -9667,13 +9662,13 @@ public void conflictingInheritedNullAnnotations(ASTNode location, boolean previo
public void illegalAnnotationForBaseType(TypeReference type, Annotation[] annotations, long nullAnnotationTagBit)
{
- int typeId = (nullAnnotationTagBit == TagBits.AnnotationNullable)
- ? TypeIds.T_ConfiguredAnnotationNullable : TypeIds.T_ConfiguredAnnotationNonNull;
+ int typeBit = (nullAnnotationTagBit == TagBits.AnnotationNullable)
+ ? TypeIds.BitNullableAnnotation : TypeIds.BitNonNullAnnotation;
char[][] annotationNames = (nullAnnotationTagBit == TagBits.AnnotationNonNull)
? this.options.nonNullAnnotationName
: this.options.nullableAnnotationName;
String[] args = new String[] { new String(annotationNames[annotationNames.length-1]), new String(type.getLastToken()) };
- Annotation annotation = findAnnotation(annotations, typeId);
+ Annotation annotation = findAnnotation(annotations, typeBit);
int start = annotation != null ? annotation.sourceStart : type.sourceStart;
int end = annotation != null ? annotation.sourceEnd : type.sourceEnd;
this.handle(IProblem.IllegalAnnotationForBaseType,
@@ -9735,12 +9730,12 @@ String internalAnnotatedTypeName(char[] annotationName, char[] typeName, int dim
}
return String.valueOf(fullName);
}
-private Annotation findAnnotation(Annotation[] annotations, int typeId) {
+private Annotation findAnnotation(Annotation[] annotations, int typeBit) {
if (annotations != null) {
// should have a @NonNull/@Nullable annotation, search for it:
int length = annotations.length;
for (int j=0; j<length; j++) {
- if (annotations[j].resolvedType != null && annotations[j].resolvedType.id == typeId) {
+ if (annotations[j].hasNullBit(typeBit)) {
return annotations[j];
}
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
index 2d390392fd..59ed127ffc 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
@@ -1545,6 +1545,29 @@ public final class JavaCore extends Plugin {
*/
public static final String COMPILER_NULLABLE_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nullable"; //$NON-NLS-1$
/**
+ * Compiler option ID: Names of Secondary Annotation Types for Nullable Types.
+ * <p>This option defines a comma-separated list of fully qualified Java type names
+ * that the compiler may use to perform special null analysis.</p>
+ * <p>The annotation types identified by the names in this list are interpreted in the same way
+ * as the annotation identified by {@link #COMPILER_NULLABLE_ANNOTATION_NAME}.
+ * The intention is to support libraries using different sets of null annotations,
+ * in addition to those used by the current project. Secondary null annotations should not be
+ * used in the project's own source code.</p>
+ * <p>JDT will never actively use any secondary annotation names from this list,
+ * i.e., inferred null annotations and content assist proposals mentioning null annotations
+ * are always rendered using the primary name from {@link #COMPILER_NULLABLE_ANNOTATION_NAME}.</p>
+ * <p>This option only has an effect if the option {@link #COMPILER_ANNOTATION_NULL_ANALYSIS} is enabled.</p>
+ * <dl>
+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nullable.secondary"</code></dd>
+ * <dt>Possible values:</dt><dd>a comma-separated list of legal, fully qualified Java type names;
+ * each name in the list must resolve to an annotation type.</dd>
+ * <dt>Default:</dt><dd><code>""</code></dd>
+ * </dl>
+ * @since 3.12
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_NULLABLE_ANNOTATION_SECONDARY_NAMES = PLUGIN_ID + ".compiler.annotation.nullable.secondary"; //$NON-NLS-1$
+ /**
* Compiler option ID: Name of Annotation Type for Non-Null Types.
* <p>This option defines a fully qualified Java type name that the compiler may use
* to perform special null analysis.</p>
@@ -1571,6 +1594,29 @@ public final class JavaCore extends Plugin {
*/
public static final String COMPILER_NONNULL_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nonnull"; //$NON-NLS-1$
/**
+ * Compiler option ID: Names of Secondary Annotation Types for Non-Null Types.
+ * <p>This option defines a comma-separated list of fully qualified Java type names
+ * that the compiler may use to perform special null analysis.</p>
+ * <p>The annotation types identified by the names in this list are interpreted in the same way
+ * as the annotation identified by {@link #COMPILER_NONNULL_ANNOTATION_NAME}.
+ * The intention is to support libraries using different sets of null annotations,
+ * in addition to those used by the current project. Secondary null annotations should not be
+ * used in the project's own source code.</p>
+ * <p>JDT will never actively use any secondary annotation names from this list,
+ * i.e., inferred null annotations and content assist proposals mentioning null annotations
+ * are always rendered using the primary name from {@link #COMPILER_NONNULL_ANNOTATION_NAME}.</p>
+ * <p>This option only has an effect if the option {@link #COMPILER_ANNOTATION_NULL_ANALYSIS} is enabled.</p>
+ * <dl>
+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nonnull.secondary"</code></dd>
+ * <dt>Possible values:</dt><dd>a comma-separated list of legal, fully qualified Java type names;
+ * each name in the list must resolve to an annotation type.</dd>
+ * <dt>Default:</dt><dd><code>""</code></dd>
+ * </dl>
+ * @since 3.12
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_NONNULL_ANNOTATION_SECONDARY_NAMES = PLUGIN_ID + ".compiler.annotation.nonnull.secondary"; //$NON-NLS-1$
+ /**
* Compiler option ID: Name of Annotation Type to specify a nullness default for unannotated types.
* <p>This option defines a fully qualified Java type name that the compiler may use
* to perform special null analysis.</p>
@@ -1591,6 +1637,26 @@ public final class JavaCore extends Plugin {
*/
public static final String COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME = PLUGIN_ID + ".compiler.annotation.nonnullbydefault"; //$NON-NLS-1$
/**
+ * Compiler option ID: Names of Secondary Annotation Types to specify a nullness default for unannotated types.
+ * <p>This option defines a comma-separated list of fully qualified Java type names
+ * that the compiler may use to perform special null analysis.</p>
+ * <p>The annotation types identified by the names in this list are interpreted in the same way
+ * as the annotation identified by {@link #COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_NAME}.
+ * The intention is to support libraries using different sets of null annotations,
+ * in addition to those used by the current project. Secondary null annotations should not be
+ * used in the project's own source code.</p>
+ * <p>This option only has an effect if the option {@link #COMPILER_ANNOTATION_NULL_ANALYSIS} is enabled.</p>
+ * <dl>
+ * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary"</code></dd>
+ * <dt>Possible values:</dt><dd>a comma-separated list of legal, fully qualified Java type names;
+ * each name in the list must resolve to an annotation type.</dd>
+ * <dt>Default:</dt><dd><code>""</code></dd>
+ * </dl>
+ * @since 3.12
+ * @category CompilerOptionID
+ */
+ public static final String COMPILER_NONNULL_BY_DEFAULT_ANNOTATION_SECONDARY_NAMES = PLUGIN_ID + ".compiler.annotation.nonnullbydefault.secondary"; //$NON-NLS-1$
+ /**
* Compiler option ID: Reporting missing default nullness annotation.
* <p>When enabled, the compiler will issue an error or a warning in the following cases:</p>
* <ul>

Back to the top