Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephan Herrmann2015-12-08 18:38:48 +0000
committerStephan Herrmann2015-12-08 18:38:48 +0000
commitd1a89d726f70e4e87570ae93c703b6ccb95d1980 (patch)
tree84266cfcf9d82aba32000218bc51d927c3d2cb68 /org.eclipse.jdt.core/compiler/org/eclipse/jdt
parent78f7dfc554cc396c300f5e2309fef78cc569d57e (diff)
downloadorg.eclipse.objectteams-d1a89d726f70e4e87570ae93c703b6ccb95d1980.tar.gz
org.eclipse.objectteams-d1a89d726f70e4e87570ae93c703b6ccb95d1980.tar.xz
org.eclipse.objectteams-d1a89d726f70e4e87570ae93c703b6ccb95d1980.zip
Update jdt.core & tests to I20151208-0800 for 4.6 M4
Diffstat (limited to 'org.eclipse.jdt.core/compiler/org/eclipse/jdt')
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java378
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ASTNode.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java17
-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/ArrayAllocationExpression.java21
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java6
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/LambdaExpression.java77
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java17
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java164
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedQualifiedTypeReference.java5
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java18
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java3
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java10
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java26
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java10
-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/SwitchStatement.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java51
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/TypeReference.java34
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java116
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java11
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java34
-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.java23
-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/BoundSet.java13
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java2
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java15
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java16
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java7
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/IntersectionTypeBinding18.java8
-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/ParameterizedGenericMethodBinding.java40
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java8
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java23
-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/Scope.java10
-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.java21
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java23
-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/lookup/TypeSystem.java21
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java108
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java20
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java57
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties3
60 files changed, 1295 insertions, 436 deletions
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
index df0ad2fa8..e62e8e210 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/CharOperation.java
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Luiz-Otavio Zorzella <zorzella at gmail dot com> - Improve CamelCase algorithm
+ * Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
*******************************************************************************/
package org.eclipse.jdt.core.compiler;
@@ -694,6 +695,94 @@ public static final boolean camelCaseMatch(char[] pattern, int patternStart, int
}
/**
+ * Answers true if the characters of the pattern are contained in the
+ * name as a substring, in a case-insensitive way.
+ *
+ * @param pattern the given pattern
+ * @param name the given name
+ * @return true if the pattern matches the given name, false otherwise
+ * @since 3.12
+ */
+public static final boolean substringMatch(String pattern, String name) {
+ if (pattern == null || pattern.length() == 0) {
+ return true;
+ }
+ if (name == null) {
+ return false;
+ }
+ return checkSubstringMatch(pattern.toCharArray(), name.toCharArray());
+}
+
+/**
+ * Answers true if the characters of the pattern are contained in the
+ * name as a substring, in a case-insensitive way.
+ *
+ * @param pattern the given pattern
+ * @param name the given name
+ * @return true if the pattern matches the given name, false otherwise
+ * @since 3.12
+ */
+public static final boolean substringMatch(char[] pattern, char[] name) {
+ if (pattern == null || pattern.length == 0) {
+ return true;
+ }
+ if (name == null) {
+ return false;
+ }
+ return checkSubstringMatch(pattern, name);
+}
+
+/**
+ * Internal substring matching method; called after the null and length
+ * checks are performed.
+ *
+ * @param pattern the given pattern
+ * @param name the given name
+ * @return true if the pattern matches the given name, false otherwise
+ *
+ * @see CharOperation#substringMatch(char[], char[])
+ */
+private static final boolean checkSubstringMatch(char[] pattern, char[] name) {
+
+/* XXX: to be revised/enabled
+
+ // allow non-consecutive occurrence of pattern characters
+ if (pattern.length >= 3) {
+ int pidx = 0;
+
+ for (int nidx = 0; nidx < name.length; nidx++) {
+ if (Character.toLowerCase(name[nidx]) ==
+ Character.toLowerCase(pattern[pidx]))
+ pidx++;
+ if (pidx == pattern.length)
+ return true;
+ }
+
+ // for short patterns only allow consecutive occurrence
+ } else {
+*/
+ // outer loop iterates on the characters of the name; trying to
+ // match at any possible position
+ outer: for (int nidx = 0; nidx < name.length - pattern.length + 1; nidx++) {
+ // inner loop iterates on pattern characters
+ for (int pidx = 0; pidx < pattern.length; pidx++) {
+ if (Character.toLowerCase(name[nidx + pidx]) !=
+ Character.toLowerCase(pattern[pidx])) {
+ // no match until parameter list; do not match parameter list
+ if ((name[nidx + pidx] == '(') || (name[nidx + pidx] == ':'))
+ return false;
+ continue outer;
+ }
+ if (pidx == pattern.length - 1)
+ return true;
+ }
+ }
+ // XXX: }
+
+ return false;
+}
+
+/**
* Returns the char arrays as an array of Strings
*
* @param charArrays the char array to convert
@@ -1156,6 +1245,148 @@ public static final char[] concat(
System.arraycopy(third, 0, result, length1 + length2 + 2, length3);
return result;
}
+/**
+ * Answers the concatenation of the two arrays inserting the separator character between the two arrays.
+ * It answers null if the two arrays are null.
+ * If the first array is null or is empty, then the second array is returned.
+ * If the second array is null or is empty, then the first array is returned.
+ * <br>
+ * <br>
+ * For example:
+ * <ol>
+ * <li><pre>
+ * first = null
+ * second = { 'a' }
+ * separator = '/'
+ * => result = { ' a' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * first = { ' a' }
+ * second = null
+ * separator = '/'
+ * => result = { ' a' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * first = { ' a' }
+ * second = { ' b' }
+ * separator = '/'
+ * => result = { ' a' , '/', 'b' }
+ * </pre>
+ * </li>
+ * * <li><pre>
+ * first = { ' a' }
+ * second = { }
+ * separator = '/'
+ * => result = { ' a'}
+ * </pre>
+ * </li>
+
+ * </ol>
+ *
+ * @param first the first array to concatenate
+ * @param second the second array to concatenate
+ * @param separator the character to insert
+ * @return the concatenation of the two arrays inserting the separator character
+ * between the two arrays , or null if the two arrays are null.
+ * @since 3.12
+ */
+public static final char[] concatNonEmpty(
+ char[] first,
+ char[] second,
+ char separator) {
+ if (first == null || first.length == 0)
+ return second;
+ if (second == null || second.length == 0)
+ return first;
+ return concat(first, second, separator);
+}
+/**
+ * Answers the concatenation of the three arrays inserting the sep1 character between the
+ * first two arrays and sep2 between the last two.
+ * It answers null if the three arrays are null.
+ * If the first array is null or empty, then it answers the concatenation of second and third inserting
+ * the sep2 character between them.
+ * If the second array is null or empty, then it answers the concatenation of first and third inserting
+ * the sep1 character between them.
+ * If the third array is null or empty, then it answers the concatenation of first and second inserting
+ * the sep1 character between them.
+ * <br>
+ * <br>
+ * For example:
+ * <ol>
+ * <li><pre>
+ * first = null
+ * sep1 = '/'
+ * second = { 'a' }
+ * sep2 = ':'
+ * third = { 'b' }
+ * => result = { ' a' , ':', 'b' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * first = { 'a' }
+ * sep1 = '/'
+ * second = null
+ * sep2 = ':'
+ * third = { 'b' }
+ * => result = { ' a' , '/', 'b' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * first = { 'a' }
+ * sep1 = '/'
+ * second = { 'b' }
+ * sep2 = ':'
+ * third = null
+ * => result = { ' a' , '/', 'b' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * first = { 'a' }
+ * sep1 = '/'
+ * second = { 'b' }
+ * sep2 = ':'
+ * third = { 'c' }
+ * => result = { ' a' , '/', 'b' , ':', 'c' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * first = { 'a' }
+ * sep1 = '/'
+ * second = { }
+ * sep2 = ':'
+ * third = { 'c' }
+ * => result = { ' a', ':', 'c' }
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * @param first the first array to concatenate
+ * @param sep1 the character to insert
+ * @param second the second array to concatenate
+ * @param sep2 the character to insert
+ * @param third the second array to concatenate
+ * @return the concatenation of the three arrays inserting the sep1 character between the
+ * two arrays and sep2 between the last two.
+ * @since 3.12
+ */
+public static final char[] concatNonEmpty(
+ char[] first,
+ char sep1,
+ char[] second,
+ char sep2,
+ char[] third) {
+ if (first == null || first.length == 0)
+ return concatNonEmpty(second, third, sep2);
+ if (second == null || second.length == 0)
+ return concatNonEmpty(first, third, sep1);
+ if (third == null || third.length == 0)
+ return concatNonEmpty(first, second, sep1);
+
+ return concat(first, sep1, second, sep2, third);
+}
/**
* Answers a new array with prepending the prefix character and appending the suffix
@@ -1384,6 +1615,65 @@ public static final char[] concatWith(char[][] array, char separator) {
}
/**
+ * Answers the concatenation of the given array parts using the given separator between each part
+ * irrespective of whether an element is a zero length array or not.
+ * <br>
+ * <br>
+ * For example:<br>
+ * <ol>
+ * <li><pre>
+ * array = { { 'a' }, {}, { 'b' } }
+ * separator = ''
+ * => result = { 'a', '/', '/', 'b' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * array = { { 'a' }, { 'b' } }
+ * separator = '.'
+ * => result = { 'a', '.', 'b' }
+ * </pre>
+ * </li>
+ * <li><pre>
+ * array = null
+ * separator = '.'
+ * => result = { }
+ * </pre></li>
+ * </ol>
+ *
+ * @param array the given array
+ * @param separator the given separator
+ * @return the concatenation of the given array parts using the given separator between each part
+ * @since 3.12
+ */
+public static final char[] concatWithAll(char[][] array, char separator) {
+ int length = array == null ? 0 : array.length;
+ if (length == 0)
+ return CharOperation.NO_CHAR;
+
+ int size = length - 1;
+ int index = length;
+ while (--index >= 0) {
+ size += array[index].length;
+ }
+ char[] result = new char[size];
+ index = length;
+ while (--index >= 0) {
+ length = array[index].length;
+ if (length > 0) {
+ System.arraycopy(
+ array[index],
+ 0,
+ result,
+ (size -= length),
+ length);
+ }
+ if (--size >= 0)
+ result[size] = separator;
+ }
+ return result;
+}
+
+/**
* Answers true if the array contains an occurrence of character, false otherwise.
*
* <br>
@@ -3554,6 +3844,94 @@ public static final char[][] splitOn(
}
/**
+ * Return a new array which is the split of the given array using the given divider ignoring the
+ * text between (possibly nested) openEncl and closingEncl. If there are no openEncl in the code
+ * this is identical to {@link CharOperation#splitOn(char, char[], int, int)}. The given end
+ * is exclusive and the given start is inclusive.
+ * <br>
+ * <br>
+ * For example:
+ * <ol>
+ * <li><pre>
+ * divider = ','
+ * array = { 'A' , '<', 'B', ',', 'C', '>', ',', 'D' }
+ * start = 0
+ * end = 8
+ * result => { { 'A' , '<', 'B', ',', 'C', '>'}, { 'D' }}
+ * </pre>
+ * </li>
+ * </ol>
+ *
+ * @param divider the given divider
+ * @param openEncl the opening enclosure
+ * @param closeEncl the closing enclosure
+ * @param array the given array
+ * @param start the given starting index
+ * @param end the given ending index
+ * @return a new array which is the split of the given array using the given divider
+ * @throws ArrayIndexOutOfBoundsException if start is lower than 0 or end is greater than the array length
+ * @since 3.12
+ */
+public static final char[][] splitOnWithEnclosures(
+ char divider,
+ char openEncl,
+ char closeEncl,
+ char[] array,
+ int start,
+ int end) {
+ int length = array == null ? 0 : array.length;
+ if (length == 0 || start > end)
+ return NO_CHAR_CHAR;
+
+ int wordCount = 1;
+ int enclCount = 0;
+ for (int i = start; i < end; i++) {
+ if (array[i] == openEncl)
+ enclCount++;
+ else if (array[i] == divider)
+ wordCount++;
+ }
+ if (enclCount == 0)
+ return CharOperation.splitOn(divider, array, start, end);
+
+ int nesting = 0;
+ if (openEncl == divider || closeEncl == divider) // divider should be distinct
+ return CharOperation.NO_CHAR_CHAR;
+
+ int[][] splitOffsets = new int[wordCount][2]; //maximum
+ int last = start, currentWord = 0, prevOffset = start;
+ for (int i = start; i < end; i++) {
+ if (array[i] == openEncl) {
+ ++nesting;
+ continue;
+ }
+ if (array[i] == closeEncl) {
+ if (nesting > 0)
+ --nesting;
+ continue;
+ }
+ if (array[i] == divider && nesting == 0) {
+ splitOffsets[currentWord][0] = prevOffset;
+ last = splitOffsets[currentWord++][1] = i;
+ prevOffset = last + 1;
+ }
+ }
+ if (last < end - 1) {
+ splitOffsets[currentWord][0] = prevOffset;
+ splitOffsets[currentWord++][1] = end;
+ }
+ char[][] split = new char[currentWord][];
+ for (int i = 0; i < currentWord; ++i) {
+ int sStart = splitOffsets[i][0];
+ int sEnd = splitOffsets[i][1];
+ int size = sEnd - sStart;
+ split[i] = new char[size];
+ System.arraycopy(array, sStart, split[i], 0, size);
+ }
+ return split;
+ }
+
+/**
* Answers a new array which is a copy of the given array starting at the given start and
* ending at the given end. The given start is inclusive and the given end is exclusive.
* Answers null if start is greater than end, if start is lower than 0 or if end is greater
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
index f6a849161..89c602edd 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -1818,6 +1818,8 @@ void setSourceStart(int sourceStart);
int ContradictoryNullAnnotationsInferredFunctionType = MethodRelated + 973;
/** @since 3.11 */
int IllegalReturnNullityRedefinitionFreeTypeVariable = MethodRelated + 974;
+ /** @since 3.12 */
+ int IllegalRedefinitionOfTypeVariable = 975;
// Java 8 work
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 50a948d6a..aacac0a0f 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
@@ -735,12 +735,17 @@ public abstract class ASTNode implements TypeConstants, TypeIds {
continue; // not much we can do without a target type, assume it only happens after some resolve error
if (argumentTypes[i] != null && argumentTypes[i].isPolyType()) {
argument.setExpectedType(parameterType);
- TypeBinding updatedArgumentType = argument.resolveType(scope);
+ TypeBinding updatedArgumentType;
if (argument instanceof LambdaExpression) {
- // LE.resolveType may return a valid binding because resolve does not detect structural errors at this point.
LambdaExpression lambda = (LambdaExpression) argument;
+ // avoid complaining about non-kosher descriptor as secondary problem
+ boolean skipKosherCheck = method.problemId() == ProblemReasons.Ambiguous;
+ updatedArgumentType = lambda.resolveType(scope, skipKosherCheck);
+ // additional checks, because LE.resolveType may return a valid binding even in the presence of structural errors
if (!lambda.isCompatibleWith(parameterType, scope) || lambda.hasErrors())
continue;
+ } else {
+ updatedArgumentType = argument.resolveType(scope);
}
if (updatedArgumentType != null && updatedArgumentType.kind() != Binding.POLY_TYPE)
argumentTypes[i] = updatedArgumentType;
@@ -1040,10 +1045,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/AllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
index 1d5b5abfe..315d40b4e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/AllocationExpression.java
@@ -215,9 +215,10 @@ public Expression enclosingInstance() {
}
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
+ cleanUpInferenceContexts();
if (!valueRequired)
currentScope.problemReporter().unusedObjectAllocation(this);
-
+
//{ObjectTeams: redirect?
if (this.roleCreatorCall != null) {
this.roleCreatorCall.generateCode(currentScope, codeStream, valueRequired);
@@ -556,7 +557,6 @@ public TypeBinding resolveType(BlockScope scope) {
this.resolvedType = this.type.resolvedType = this.binding.declaringClass;
resolvePolyExpressionArguments(this, this.binding, this.argumentTypes, scope);
} else {
-
//{ObjectTeams: may need to instantiate parameters of constructor
AnchorMapping anchorMapping = AnchorMapping.setupNewMapping(null, this.arguments, scope);
try {
@@ -625,7 +625,7 @@ public TypeBinding resolveType(BlockScope scope) {
if (this.binding instanceof ParameterizedGenericMethodBinding && this.typeArguments != null) {
TypeVariableBinding[] typeVariables = this.binding.original().typeVariables();
for (int i = 0; i < this.typeArguments.length; i++)
- this.typeArguments[i].checkNullConstraints(scope, typeVariables, i);
+ this.typeArguments[i].checkNullConstraints(scope, (ParameterizedGenericMethodBinding) this.binding, typeVariables, i);
}
}
}
@@ -957,6 +957,17 @@ public InferenceContext18 getInferenceContext(ParameterizedMethodBinding method)
return null;
return (InferenceContext18) this.inferenceContexts.get(method);
}
+
+@Override
+public void cleanUpInferenceContexts() {
+ if (this.inferenceContexts == null)
+ return;
+ for (Object value : this.inferenceContexts.valueTable)
+ if (value != null)
+ ((InferenceContext18) value).cleanUp();
+ this.inferenceContexts = null;
+}
+
//-- interface InvocationSite: --
public ExpressionContext getExpressionContext() {
return this.expressionContext;
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 e37bca254..828fa15f1 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
@@ -400,34 +400,32 @@ public abstract class Annotation extends Expression {
tagBits |= TagBits.AnnotationInstantiation;
break;
// SH}
- 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;
}
@@ -1200,8 +1198,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;
}
@@ -1212,6 +1209,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/ArrayAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java
index 6aaaa5c25..a922effd6 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayAllocationExpression.java
@@ -48,6 +48,8 @@ public class ArrayAllocationExpression extends Expression {
public Annotation [][] annotationsOnDimensions; // jsr308 style annotations.
public ArrayInitializer initializer;
+ private TypeBinding expectedType;
+
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
for (int i = 0, max = this.dimensions.length; i < max; i++) {
Expression dim;
@@ -184,6 +186,21 @@ public class ArrayAllocationExpression extends Expression {
{
scope.problemReporter().contradictoryNullAnnotations(this.type.annotations[this.type.annotations.length-1]);
}
+ LookupEnvironment environment = scope.environment();
+ if (environment.usesNullTypeAnnotations()
+ && this.annotationsOnDimensions == null // don't annotate if explicit annotations are given on dimensions ...
+ && ((referenceType.tagBits & TagBits.AnnotationNullMASK) == 0) // ... or leaf type
+ && this.expectedType != null) // need this to determine our action
+ {
+ Expression lastDim = this.dimensions[this.dimensions.length-1];
+ if (lastDim instanceof IntLiteral && ((IntLiteral) lastDim).value == 0) {
+ long tagBit = this.expectedType.leafComponentType().tagBits & TagBits.AnnotationNullMASK;
+ // let new X[0] be seen as "@NonNull X[]", or "@Nullable X[]" just as expected
+ AnnotationBinding[] nullAnnotations = environment.nullAnnotationsFromTagBits(tagBit);
+ if (nullAnnotations != null)
+ referenceType = environment.createAnnotatedType(referenceType, nullAnnotations);
+ }
+ }
//{ObjectTeams:
referenceType = RoleTypeCreator.maybeWrapUnqualifiedRoleType(referenceType, scope, this);
// SH}
@@ -214,6 +231,10 @@ public class ArrayAllocationExpression extends Expression {
return this.resolvedType;
}
+ @Override
+ public void setExpectedType(TypeBinding expectedType) {
+ this.expectedType = expectedType;
+ }
public void traverse(ASTVisitor visitor, BlockScope scope) {
if (visitor.visit(this, scope)) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java
index 0f13ed995..b8163091e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ArrayReference.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
@@ -60,7 +60,7 @@ public FlowInfo analyseAssignment(BlockScope currentScope, FlowContext flowConte
}
public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo) {
- this.receiver.checkNPE(currentScope, flowContext, flowInfo);
+ this.receiver.checkNPE(currentScope, flowContext, flowInfo, 1);
flowInfo = this.receiver.analyseCode(currentScope, flowContext, flowInfo);
flowInfo = this.position.analyseCode(currentScope, flowContext, flowInfo);
this.position.checkNPEbyUnboxing(currentScope, flowContext, flowInfo);
@@ -69,12 +69,12 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return flowInfo;
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
if ((this.resolvedType.tagBits & TagBits.AnnotationNullable) != 0) {
scope.problemReporter().arrayReferencePotentialNullReference(this);
return true;
} else {
- return super.checkNPE(scope, flowContext, flowInfo);
+ return super.checkNPE(scope, flowContext, flowInfo, ttlForFieldCheck);
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
index 80f55b404..13f06d9e1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CastExpression.java
@@ -359,9 +359,9 @@ public static void checkNeedForArgumentCasts(BlockScope scope, int operator, int
}
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
checkNPEbyUnboxing(scope, flowContext, flowInfo);
- return this.expression.checkNPE(scope, flowContext, flowInfo);
+ return this.expression.checkNPE(scope, flowContext, flowInfo, ttlForFieldCheck);
}
private static void checkAlternateBinding(BlockScope scope, Expression receiver, TypeBinding receiverType, MethodBinding binding, Expression[] arguments, TypeBinding[] originalArgumentTypes, TypeBinding[] alternateArgumentTypes, final InvocationSite invocationSite) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java
index c12c1756e..c375e8aa2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ClassLiteralAccess.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2012 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
@@ -120,7 +120,8 @@ public class ClassLiteralAccess extends Expression {
Corollary wise, we should resolve the type of the class literal expression to be a raw type as
class literals exist only for the raw underlying type.
*/
- this.targetType = scope.environment().convertToRawType(this.targetType, true /* force conversion of enclosing types*/);
+ LookupEnvironment environment = scope.environment();
+ this.targetType = environment.convertToRawType(this.targetType, true /* force conversion of enclosing types*/);
if (this.targetType.isArrayType()) {
ArrayBinding arrayBinding = (ArrayBinding) this.targetType;
@@ -174,11 +175,13 @@ public class ClassLiteralAccess extends Expression {
// Integer.class --> Class<Integer>, perform boxing of base types (int.class --> Class<Integer>)
TypeBinding boxedType = null;
if (this.targetType.id == T_void) {
- boxedType = scope.environment().getResolvedType(JAVA_LANG_VOID, scope);
+ boxedType = environment.getResolvedType(JAVA_LANG_VOID, scope);
} else {
boxedType = scope.boxing(this.targetType);
}
- this.resolvedType = scope.environment().createParameterizedType(classType, new TypeBinding[]{ boxedType }, null/*not a member*/);
+ if (environment.usesNullTypeAnnotations())
+ boxedType = environment.createAnnotatedType(boxedType, new AnnotationBinding[] { environment.getNonNullAnnotation() });
+ this.resolvedType = environment.createParameterizedType(classType, new TypeBinding[]{ boxedType }, null/*not a member*/);
} else {
this.resolvedType = classType;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java
index 4eb6fb133..dee664d14 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/CompilationUnitDeclaration.java
@@ -196,6 +196,9 @@ public void cleanUp() {
}
this.suppressWarningAnnotations = null;
+
+ if (this.scope != null)
+ this.scope.cleanUpInferenceContexts();
}
private void cleanUp(TypeDeclaration type) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
index d285c6d72..57665e9e4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ConditionalExpression.java
@@ -187,7 +187,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext,
return mergedInfo;
}
- public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
if ((this.nullStatus & FlowInfo.NULL) != 0)
scope.problemReporter().expressionNullReference(this);
else if ((this.nullStatus & FlowInfo.POTENTIALLY_NULL) != 0)
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
index fab1d4d99..b640d8991 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ExplicitConstructorCall.java
@@ -887,6 +887,11 @@ public class ExplicitConstructorCall extends Statement implements Invocation {
return null;
}
+ @Override
+ public void cleanUpInferenceContexts() {
+ // Nothing to do.
+ }
+
public Expression[] arguments() {
return this.arguments;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
index f082e6835..e5b0f4dff 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Expression.java
@@ -765,9 +765,10 @@ boolean handledByGeneratedMethod(Scope scope, TypeBinding castType, TypeBinding
* @param scope the scope of the analysis
* @param flowContext the current flow context
* @param flowInfo the upstream flow info; caveat: may get modified
+ * @param ttlForFieldCheck if this is a reference to a field we will mark that field as nonnull for the specified timeToLive
* @return could this expression be checked by the current implementation?
*/
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
boolean isNullable = false;
if (this.resolvedType != null) {
// 1. priority: @NonNull
@@ -800,6 +801,9 @@ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flow
}
return false; // not checked
}
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ return checkNPE(scope, flowContext, flowInfo, 0); // default: don't mark field references as checked for null
+}
/** If this expression requires unboxing check if that operation can throw NPE. */
protected void checkNPEbyUnboxing(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
index 46ebd3afb..25b874298 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FieldReference.java
@@ -187,7 +187,7 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
boolean nonStatic = !this.binding.isStatic();
this.receiver.analyseCode(currentScope, flowContext, flowInfo, nonStatic);
if (nonStatic) {
- this.receiver.checkNPE(currentScope, flowContext, flowInfo);
+ this.receiver.checkNPE(currentScope, flowContext, flowInfo, 1);
}
if (valueRequired || currentScope.compilerOptions().complianceLevel >= ClassFileConstants.JDK1_4) {
@@ -196,11 +196,11 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return flowInfo;
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
if (flowContext.isNullcheckedFieldAccess(this)) {
return true; // enough seen
}
- return checkNullableFieldDereference(scope, this.binding, this.nameSourcePosition);
+ return checkNullableFieldDereference(scope, this.binding, this.nameSourcePosition, flowContext, ttlForFieldCheck);
}
/**
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
index 47db93b27..c92668eda 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ForeachStatement.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -106,7 +106,7 @@ public class ForeachStatement extends Statement {
int initialComplaintLevel = (flowInfo.reachMode() & FlowInfo.UNREACHABLE) != 0 ? Statement.COMPLAINED_FAKE_REACHABLE : Statement.NOT_COMPLAINED;
// process the element variable and collection
- this.collection.checkNPE(currentScope, flowContext, flowInfo);
+ this.collection.checkNPE(currentScope, flowContext, flowInfo, 1);
flowInfo = this.elementVariable.analyseCode(this.scope, flowContext, flowInfo);
FlowInfo condInfo = this.collection.analyseCode(this.scope, flowContext, flowInfo.copy());
LocalVariableBinding elementVarBinding = this.elementVariable.binding;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
index fa1201380..a765f78d1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/FunctionalExpression.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2013, 2014 IBM Corporation and others.
+ * Copyright (c) 2013, 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
@@ -167,6 +167,10 @@ public abstract class FunctionalExpression extends Expression {
}
public TypeBinding resolveType(BlockScope blockScope) {
+ return resolveType(blockScope, false);
+ }
+
+ public TypeBinding resolveType(BlockScope blockScope, boolean skipKosherCheck) {
this.constant = Constant.NotAConstant;
this.enclosingScope = blockScope;
MethodBinding sam = this.expectedType == null ? null : this.expectedType.getSingleAbstractMethod(blockScope, argumentsTypeElided());
@@ -179,7 +183,7 @@ public abstract class FunctionalExpression extends Expression {
}
this.descriptor = sam;
- if (kosherDescriptor(blockScope, sam, true)) {
+ if (skipKosherCheck || kosherDescriptor(blockScope, sam, true)) {
if (blockScope.environment().globalOptions.isAnnotationBasedNullAnalysisEnabled)
NullAnnotationMatching.checkForContradictions(sam, this, blockScope);
return this.resolvedType = this.expectedType;
@@ -268,7 +272,7 @@ public abstract class FunctionalExpression extends Expression {
status = false;
if (!inspector.visible(sam.thrownExceptions))
status = false;
- if (!inspector.visible(sam.declaringClass))
+ if (!inspector.visible(this.expectedType))
status = false;
return status;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java
index f77cd139a..2f30eea26 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Invocation.java
@@ -42,6 +42,8 @@ public interface Invocation extends InvocationSite {
*/
InferenceContext18 getInferenceContext(ParameterizedMethodBinding method);
+ void cleanUpInferenceContexts();
+
/** Record result against target type */
void registerResult(TypeBinding targetType, MethodBinding method);
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 12cf94c5f..b1242e0cd 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
@@ -233,7 +233,7 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
* @see org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding.resolveTypesFor(MethodBinding)
* @see org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolve(ClassScope)
*/
- public TypeBinding resolveType(BlockScope blockScope) {
+ public TypeBinding resolveType(BlockScope blockScope, boolean skipKosherCheck) {
boolean argumentsTypeElided = argumentsTypeElided();
int argumentsLength = this.arguments == null ? 0 : this.arguments.length;
@@ -257,11 +257,11 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
this.scope = new MethodScope(blockScope, this, methodScope.isStatic, methodScope.lastVisibleFieldID);
this.scope.isConstructorCall = methodScope.isConstructorCall;
- super.resolveType(blockScope); // compute & capture interface function descriptor.
+ super.resolveType(blockScope, skipKosherCheck); // compute & capture interface function descriptor.
final boolean haveDescriptor = this.descriptor != null;
- if (!haveDescriptor || this.descriptor.typeVariables != Binding.NO_TYPE_VARIABLES) // already complained in kosher*
+ if (!skipKosherCheck && (!haveDescriptor || this.descriptor.typeVariables != Binding.NO_TYPE_VARIABLES)) // already complained in kosher*
return this.resolvedType = null;
this.binding = new MethodBinding(ClassFileConstants.AccPrivate | ClassFileConstants.AccSynthetic | ExtraCompilerModifiers.AccUnresolved,
@@ -465,10 +465,14 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
if (targetType instanceof ReferenceBinding && targetType.isValidBinding()) {
ParameterizedTypeBinding withWildCards = InferenceContext18.parameterizedWithWildcard(targetType);
if (withWildCards != null) {
- if (!argumentTypesElided)
- return new InferenceContext18(blockScope).inferFunctionalInterfaceParameterization(this, blockScope, withWildCards);
- else
+ if (!argumentTypesElided) {
+ InferenceContext18 freshInferenceContext = new InferenceContext18(blockScope);
+ ReferenceBinding inferredType = freshInferenceContext.inferFunctionalInterfaceParameterization(this, blockScope, withWildCards);
+ freshInferenceContext.cleanUp();
+ return inferredType;
+ } else {
return findGroundTargetTypeForElidedLambda(blockScope, withWildCards);
+ }
}
return (ReferenceBinding) targetType;
}
@@ -577,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 });
}
}
}
@@ -609,7 +608,30 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
}
}
- public boolean isPertinentToApplicability(TypeBinding targetType, MethodBinding method) {
+ public boolean isPertinentToApplicability(final TypeBinding targetType, final MethodBinding method) {
+
+ class NotPertientToApplicability extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+ }
+ class ResultsAnalyser extends ASTVisitor {
+ public boolean visit(TypeDeclaration type, BlockScope skope) {
+ return false;
+ }
+ public boolean visit(TypeDeclaration type, ClassScope skope) {
+ return false;
+ }
+ public boolean visit(LambdaExpression type, BlockScope skope) {
+ return false;
+ }
+ public boolean visit(ReturnStatement returnStatement, BlockScope skope) {
+ if (returnStatement.expression != null) {
+ if (!returnStatement.expression.isPertinentToApplicability(targetType, method))
+ throw new NotPertientToApplicability();
+ }
+ return false;
+ }
+ }
+
if (targetType == null) // assumed to signal another primary error
return true;
@@ -624,9 +646,18 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
return false;
} else {
Expression [] returnExpressions = this.resultExpressions;
- for (int i = 0, length = returnExpressions.length; i < length; i++) {
- if (!returnExpressions[i].isPertinentToApplicability(targetType, method))
+ if (returnExpressions != NO_EXPRESSIONS) {
+ for (int i = 0, length = returnExpressions.length; i < length; i++) {
+ if (!returnExpressions[i].isPertinentToApplicability(targetType, method))
+ return false;
+ }
+ } else {
+ // return expressions not yet discovered by resolveType(), so traverse no looking just for one that's not pertinent
+ try {
+ this.body.traverse(new ResultsAnalyser(), this.scope);
+ } catch (NotPertientToApplicability npta) {
return false;
+ }
}
}
@@ -806,6 +837,10 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
if (!isPertinentToApplicability(targetType, null))
return true;
+ // catch up on one check deferred via skipKosherCheck=true (only if pertinent for applicability)
+ if (!kosherDescriptor(this.enclosingScope, sam, false))
+ return false;
+
Expression [] returnExpressions = copy.resultExpressions;
for (int i = 0, length = returnExpressions.length; i < length; i++) {
if (this.enclosingScope.parameterCompatibilityLevel(returnExpressions[i].resolvedType, sam.returnType) == Scope.NOT_COMPATIBLE) {
@@ -855,7 +890,7 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
copy.setExpressionContext(this.expressionContext);
copy.setExpectedType(targetType);
copy.inferenceContext = context;
- TypeBinding type = copy.resolveType(this.enclosingScope);
+ TypeBinding type = copy.resolveType(this.enclosingScope, true);
if (type == null || !type.isValidBinding())
return null;
@@ -1035,8 +1070,12 @@ public class LambdaExpression extends FunctionalExpression implements IPolyExpre
switch(parent.kind) {
case Scope.CLASS_SCOPE:
case Scope.METHOD_SCOPE:
- parent.referenceContext().tagAsHavingErrors();
- return;
+ ReferenceContext parentAST = parent.referenceContext();
+ if (parentAST != this) {
+ parentAST.tagAsHavingErrors();
+ return;
+ }
+ break;
default:
parent = parent.parent;
break;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
index 08bfabc69..ef581a557 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MessageSend.java
@@ -250,7 +250,8 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
}
if (nonStatic) {
- this.receiver.checkNPE(currentScope, flowContext, flowInfo);
+ int timeToLive = ((this.bits & ASTNode.InsideExpressionStatement) != 0) ? 3 : 2;
+ this.receiver.checkNPE(currentScope, flowContext, flowInfo, timeToLive);
}
if (this.arguments != null) {
@@ -497,7 +498,7 @@ private FlowInfo analyseNullAssertion(BlockScope currentScope, Expression argume
return flowInfo;
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
// message send as a receiver
if ((nullStatus(flowInfo, flowContext) & FlowInfo.POTENTIALLY_NULL) != 0) // note that flowInfo is not used inside nullStatus(..)
scope.problemReporter().messageSendPotentialNullReference(this.binding, this);
@@ -557,6 +558,7 @@ public void computeConversion(Scope scope, TypeBinding runtimeTimeType, TypeBind
* @param valueRequired boolean
*/
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
+ cleanUpInferenceContexts();
int pc = codeStream.position;
// generate receiver/enclosing instance access
MethodBinding codegenBinding = this.binding instanceof PolymorphicMethodBinding ? this.binding : this.binding.original();
@@ -1181,7 +1183,7 @@ public TypeBinding resolveType(BlockScope scope) {
if (this.binding instanceof ParameterizedGenericMethodBinding && this.typeArguments != null) {
TypeVariableBinding[] typeVariables = this.binding.original().typeVariables();
for (int i = 0; i < this.typeArguments.length; i++)
- this.typeArguments[i].checkNullConstraints(scope, typeVariables, i);
+ this.typeArguments[i].checkNullConstraints(scope, (ParameterizedGenericMethodBinding) this.binding, typeVariables, i);
}
}
}
@@ -1675,6 +1677,15 @@ public InferenceContext18 getInferenceContext(ParameterizedMethodBinding method)
return null;
return (InferenceContext18) this.inferenceContexts.get(method);
}
+@Override
+public void cleanUpInferenceContexts() {
+ if (this.inferenceContexts == null)
+ return;
+ for (Object value : this.inferenceContexts.valueTable)
+ if (value != null)
+ ((InferenceContext18) value).cleanUp();
+ this.inferenceContexts = null;
+}
public Expression[] arguments() {
return this.arguments;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
index 8f590f7a9..1d5bdd85c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/NullAnnotationMatching.java
@@ -27,6 +27,7 @@ import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
import org.eclipse.jdt.internal.compiler.lookup.RawTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
+import org.eclipse.jdt.internal.compiler.lookup.Substitution;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBindingVisitor;
@@ -50,10 +51,26 @@ public class NullAnnotationMatching {
public enum CheckMode {
/** in this mode we check normal assignment compatibility. */
COMPATIBLE,
- /** in this mode we do not tolerate incompatibly missing annotations on type parameters (for overriding analysis) */
- OVERRIDE,
+ /** in this mode we check similar to isTypeArgumentContained. */
+ EXACT,
/** in this mode we check compatibility of a type argument against the corresponding type parameter. */
- BOUND_CHECK
+ BOUND_CHECK,
+ /** allow covariant return types, but no other deviations. */
+ OVERRIDE_RETURN {
+ @Override CheckMode toDetail() {
+ return OVERRIDE;
+ }
+ },
+ /** in this mode we do not tolerate incompatibly missing annotations on type parameters (for overriding analysis) */
+ OVERRIDE {
+ @Override CheckMode toDetail() {
+ return OVERRIDE;
+ }
+ };
+
+ CheckMode toDetail() {
+ return CheckMode.EXACT;
+ }
}
/** 0 = OK, 1 = unchecked, 2 = definite mismatch */
@@ -126,7 +143,7 @@ public class NullAnnotationMatching {
* @return a status object representing the severity of mismatching plus optionally a supertype hint
*/
public static NullAnnotationMatching analyse(TypeBinding requiredType, TypeBinding providedType, int nullStatus) {
- return analyse(requiredType, providedType, null, nullStatus, CheckMode.COMPATIBLE);
+ return analyse(requiredType, providedType, null, null, nullStatus, CheckMode.COMPATIBLE);
}
/**
* Find any mismatches between the two given types, which are caused by null type annotations.
@@ -134,11 +151,12 @@ public class NullAnnotationMatching {
* @param providedType
* @param providedSubstitute in inheritance situations this maps the providedType into the realm of the subclass, needed for TVB identity checks.
* Pass null if not interested in these added checks.
+ * @param substitution TODO
* @param nullStatus we are only interested in NULL or NON_NULL, -1 indicates that we are in a recursion, where flow info is ignored
* @param mode controls the kind of check performed (see {@link CheckMode}).
* @return a status object representing the severity of mismatching plus optionally a supertype hint
*/
- public static NullAnnotationMatching analyse(TypeBinding requiredType, TypeBinding providedType, TypeBinding providedSubstitute, int nullStatus, CheckMode mode) {
+ public static NullAnnotationMatching analyse(TypeBinding requiredType, TypeBinding providedType, TypeBinding providedSubstitute, Substitution substitution, int nullStatus, CheckMode mode) {
if (!requiredType.enterRecursiveFunction())
return NullAnnotationMatching.NULL_ANNOTATIONS_OK;
try {
@@ -150,23 +168,32 @@ public class NullAnnotationMatching {
return NullAnnotationMatching.NULL_ANNOTATIONS_OK_NONNULL;
return okStatus;
}
+ if (requiredType instanceof TypeVariableBinding && substitution != null && (mode == CheckMode.EXACT || mode == CheckMode.COMPATIBLE)) {
+ requiredType.exitRecursiveFunction();
+ requiredType = Scope.substitute(substitution, requiredType);
+ if (!requiredType.enterRecursiveFunction())
+ return NullAnnotationMatching.NULL_ANNOTATIONS_OK;
+ }
if (mode == CheckMode.BOUND_CHECK && requiredType instanceof TypeVariableBinding) {
- // during bound check against a type variable check the provided type against all upper bounds:
- TypeBinding superClass = requiredType.superclass();
- if (superClass != null && superClass.hasNullTypeAnnotations()) {
- NullAnnotationMatching status = analyse(superClass, providedType, null, nullStatus, mode);
- severity = Math.max(severity, status.severity);
- if (severity == 2)
- return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
- }
- TypeBinding[] superInterfaces = requiredType.superInterfaces();
- if (superInterfaces != null) {
- for (int i = 0; i < superInterfaces.length; i++) {
- if (superInterfaces[i].hasNullTypeAnnotations()) {
- NullAnnotationMatching status = analyse(superInterfaces[i], providedType, null, nullStatus, mode);
- severity = Math.max(severity, status.severity);
- if (severity == 2)
- return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
+ boolean passedBoundCheck = (substitution instanceof ParameterizedTypeBinding) && (((ParameterizedTypeBinding) substitution).tagBits & TagBits.PassedBoundCheck) != 0;
+ if (!passedBoundCheck) {
+ // during bound check against a type variable check the provided type against all upper bounds:
+ TypeBinding superClass = requiredType.superclass();
+ if (superClass != null && (superClass.hasNullTypeAnnotations() || substitution != null)) { // annotations may enter when substituting a nested type variable
+ NullAnnotationMatching status = analyse(superClass, providedType, null, substitution, nullStatus, CheckMode.COMPATIBLE);
+ severity = Math.max(severity, status.severity);
+ if (severity == 2)
+ return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
+ }
+ TypeBinding[] superInterfaces = requiredType.superInterfaces();
+ if (superInterfaces != null) {
+ for (int i = 0; i < superInterfaces.length; i++) {
+ if (superInterfaces[i].hasNullTypeAnnotations() || substitution != null) { // annotations may enter when substituting a nested type variable
+ NullAnnotationMatching status = analyse(superInterfaces[i], providedType, null, substitution, nullStatus, CheckMode.COMPATIBLE);
+ severity = Math.max(severity, status.severity);
+ if (severity == 2)
+ return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
+ }
}
}
}
@@ -185,7 +212,7 @@ public class NullAnnotationMatching {
long providedBits = validNullTagBits(providedDimsTagBits[i]);
if (i > 0)
currentNullStatus = -1; // don't use beyond the outermost dimension
- severity = Math.max(severity, computeNullProblemSeverity(requiredBits, providedBits, currentNullStatus, mode == CheckMode.OVERRIDE && nullStatus == -1));
+ severity = Math.max(severity, computeNullProblemSeverity(requiredBits, providedBits, currentNullStatus, i == 0 ? mode : mode.toDetail(), false));
if (severity == 2)
return NullAnnotationMatching.NULL_ANNOTATIONS_MISMATCH;
if (severity == 0)
@@ -202,7 +229,7 @@ public class NullAnnotationMatching {
|| nullStatus == -1) // only at detail/recursion even nullable must be matched exactly
{
long providedBits = providedNullTagBits(providedType);
- int s = computeNullProblemSeverity(requiredBits, providedBits, nullStatus, mode == CheckMode.OVERRIDE && nullStatus == -1);
+ int s = computeNullProblemSeverity(requiredBits, providedBits, nullStatus, mode, requiredType.isTypeVariable());
severity = Math.max(severity, s);
if (severity == 0 && (providedBits & TagBits.AnnotationNonNull) != 0)
okStatus = NullAnnotationMatching.NULL_ANNOTATIONS_OK_NONNULL;
@@ -219,7 +246,7 @@ public class NullAnnotationMatching {
if (requiredArguments != null && providedArguments != null && requiredArguments.length == providedArguments.length) {
for (int i = 0; i < requiredArguments.length; i++) {
TypeBinding providedArgSubstitute = providedSubstitutes != null ? providedSubstitutes[i] : null;
- NullAnnotationMatching status = analyse(requiredArguments[i], providedArguments[i], providedArgSubstitute, -1, mode);
+ NullAnnotationMatching status = analyse(requiredArguments[i], providedArguments[i], providedArgSubstitute, substitution, -1, mode.toDetail());
severity = Math.max(severity, status.severity);
if (severity == 2)
return new NullAnnotationMatching(severity, nullStatus, superTypeHint);
@@ -230,7 +257,7 @@ public class NullAnnotationMatching {
TypeBinding providedEnclosing = providedType.enclosingType();
if (requiredEnclosing != null && providedEnclosing != null) {
TypeBinding providedEnclSubstitute = providedSubstitute != null ? providedSubstitute.enclosingType() : null;
- NullAnnotationMatching status = analyse(requiredEnclosing, providedEnclosing, providedEnclSubstitute, -1, mode);
+ NullAnnotationMatching status = analyse(requiredEnclosing, providedEnclosing, providedEnclSubstitute, substitution, -1, mode);
severity = Math.max(severity, status.severity);
}
}
@@ -307,8 +334,13 @@ public class NullAnnotationMatching {
return TagBits.AnnotationNullable; // type cannot require @NonNull
}
}
- if (mode != CheckMode.BOUND_CHECK) // no pessimistic checks during boundcheck (we *have* the instantiation)
- return TagBits.AnnotationNonNull; // instantiation could require @NonNull
+ switch (mode) {
+ case BOUND_CHECK: // no pessimistic checks during boundcheck (we *have* the instantiation)
+ case OVERRIDE_RETURN: // allow covariance
+ break;
+ default:
+ return TagBits.AnnotationNonNull; // instantiation could require @NonNull
+ }
}
return 0;
@@ -398,27 +430,60 @@ public class NullAnnotationMatching {
* @param requiredBits null tagBits of the required type
* @param providedBits null tagBits of the provided type
* @param nullStatus -1 means: don't use, other values see constants in FlowInfo
- * @param overrideDetailChecking true enables strictest mode during override analysis when checking type details (type argument, array content)
+ * @param mode check mode (see {@link CheckMode})
+ * @param requiredIsTypeVariable is the required type a type variable (possibly: "free type variable")?
* @return see {@link #severity} for interpretation of values
*/
- private static int computeNullProblemSeverity(long requiredBits, long providedBits, int nullStatus, boolean overrideDetailChecking) {
- // nullStatus:
- // overrideDetailChecking:
- if ((requiredBits != 0 || overrideDetailChecking) && requiredBits != providedBits) {
- if (requiredBits == TagBits.AnnotationNonNull && nullStatus == FlowInfo.NON_NULL) {
- return 0; // OK by flow analysis
+ private static int computeNullProblemSeverity(long requiredBits, long providedBits, int nullStatus, CheckMode mode, boolean requiredIsTypeVariable) {
+ if (requiredBits == providedBits)
+ return 0;
+ if (requiredBits == 0) {
+ switch (mode) {
+ case COMPATIBLE:
+ case BOUND_CHECK:
+ case EXACT:
+ return 0;
+ case OVERRIDE_RETURN:
+ if (providedBits == TagBits.AnnotationNonNull)
+ return 0; // covariant redefinition to nonnull is good
+ if (!requiredIsTypeVariable)
+ return 0; // refining an unconstrained non-TVB return to nullable is also legal
+ return 1;
+ case OVERRIDE:
+ return 1; // warn about dropped annotation
}
- if (requiredBits == TagBits.AnnotationNullMASK)
- return 0; // OK since LHS accepts either
- if (nullStatus != -1 && !overrideDetailChecking && requiredBits == TagBits.AnnotationNullable)
- return 0; // when using flow info, everything is compatible to nullable
- if (providedBits != 0) {
- return 2; // mismatching annotations
- } else {
- return 1; // need unchecked conversion regarding type detail
+ } else if (requiredBits == TagBits.AnnotationNullMASK) {
+ return 0; // OK since LHS accepts either
+ } else if (requiredBits == TagBits.AnnotationNonNull) {
+ switch (mode) {
+ case COMPATIBLE:
+ if (nullStatus == FlowInfo.NON_NULL)
+ return 0; // OK by flow analysis
+ //$FALL-THROUGH$
+ case BOUND_CHECK:
+ case EXACT:
+ case OVERRIDE_RETURN:
+ case OVERRIDE:
+ if (providedBits == 0)
+ return 1;
+ return 2;
+ }
+
+ } else if (requiredBits == TagBits.AnnotationNullable) {
+ switch (mode) {
+ case COMPATIBLE:
+ case OVERRIDE_RETURN:
+ return 0; // in these modes everything is compatible to nullable
+ case BOUND_CHECK:
+ case EXACT:
+ if (providedBits == 0)
+ return 1;
+ return 2;
+ case OVERRIDE:
+ return 2;
}
}
- return 0; // OK by tagBits
+ return 0; // shouldn't get here, requiredBits should be one of the listed cases
}
static class SearchContradictions extends TypeBindingVisitor {
@@ -565,4 +630,17 @@ public class NullAnnotationMatching {
}
return mainType;
}
+
+ @SuppressWarnings("nls")
+ @Override
+ public String toString() {
+ if (this == NULL_ANNOTATIONS_OK) return "OK";
+ if (this == NULL_ANNOTATIONS_MISMATCH) return "MISMATCH";
+ if (this == NULL_ANNOTATIONS_OK_NONNULL) return "OK NonNull";
+ if (this == NULL_ANNOTATIONS_UNCHECKED) return "UNCHECKED";
+ StringBuilder buf = new StringBuilder();
+ buf.append("Analysis result: severity="+this.severity);
+ buf.append(" nullStatus="+this.nullStatus);
+ return buf.toString();
+ }
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedQualifiedTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedQualifiedTypeReference.java
index 241dc3dd5..c641fcca2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedQualifiedTypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedQualifiedTypeReference.java
@@ -103,7 +103,6 @@ public class ParameterizedQualifiedTypeReference extends ArrayQualifiedTypeRefer
parameterizedType.boundCheck(scope, this.typeArguments[index]);
}
}
- checkNullConstraints(scope, this.typeArguments[index]);
}
public TypeReference augmentTypeWithAdditionalDimensions(int additionalDimensions, Annotation[][] additionalAnnotations, boolean isVarargs) {
int totalDimensions = this.dimensions() + additionalDimensions;
@@ -216,9 +215,9 @@ public class ParameterizedQualifiedTypeReference extends ArrayQualifiedTypeRefer
TypeBinding type = internalResolveLeafType(scope, checkBounds);
createArrayType(scope);
resolveAnnotations(scope, location);
- if (this.typeArguments != null && checkBounds)
+ if (this.typeArguments != null)
// relevant null annotations are on the inner most type:
- checkNullConstraints(scope, this.typeArguments[this.typeArguments.length-1]);
+ checkIllegalNullAnnotations(scope, this.typeArguments[this.typeArguments.length-1]);
return type == null ? type : this.resolvedType;
}
private TypeBinding internalResolveLeafType(Scope scope, boolean checkBounds) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java
index 4540e62f5..293f83191 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ParameterizedSingleTypeReference.java
@@ -92,14 +92,11 @@ public class ParameterizedSingleTypeReference extends ArrayTypeReference {
if (this.resolvedType.leafComponentType() instanceof ParameterizedTypeBinding) {
ParameterizedTypeBinding parameterizedType = (ParameterizedTypeBinding) this.resolvedType.leafComponentType();
- ReferenceBinding currentType = parameterizedType.genericType();
- TypeVariableBinding[] typeVariables = currentType.typeVariables();
TypeBinding[] argTypes = parameterizedType.arguments;
- if (argTypes != null && typeVariables != null) { // may be null in error cases
+ if (argTypes != null) { // may be null in error cases
parameterizedType.boundCheck(scope, this.typeArguments);
}
}
- checkNullConstraints(scope, this.typeArguments);
}
public TypeReference augmentTypeWithAdditionalDimensions(int additionalDimensions, Annotation [][] additionalAnnotations, boolean isVarargs) {
@@ -268,8 +265,6 @@ public class ParameterizedSingleTypeReference extends ArrayTypeReference {
// note: handling of arrays differs for role and regular types
if (len == 0) {
resolveAnnotations(scope, location);
- if (checkBounds)
- checkNullConstraints(scope, this.typeArguments);
return this.resolvedType; // we're done
}
@@ -302,21 +297,15 @@ public class ParameterizedSingleTypeReference extends ArrayTypeReference {
if (type == null) {
this.resolvedType = createArrayType(scope, this.resolvedType);
resolveAnnotations(scope, 0); // no defaultNullness for buggy type
- if (checkBounds)
- checkNullConstraints(scope, this.typeArguments);
return null; // (1) no useful type, but still captured dimensions into this.resolvedType
} else {
type = createArrayType(scope, type);
if (!this.resolvedType.isValidBinding() && this.resolvedType.dimensions() == type.dimensions()) {
resolveAnnotations(scope, 0); // no defaultNullness for buggy type
- if (checkBounds)
- checkNullConstraints(scope, this.typeArguments);
return type; // (2) found some error, but could recover useful type (like closestMatch)
} else {
this.resolvedType = type; // (3) no complaint, keep fully resolved type (incl. dimensions)
resolveAnnotations(scope, location);
- if (checkBounds)
- checkNullConstraints(scope, this.typeArguments);
return this.resolvedType; // pick up any annotated type.
}
}
@@ -458,6 +447,7 @@ public class ParameterizedSingleTypeReference extends ArrayTypeReference {
return null;
}
}
+
//{ObjectTeams: already done?
if (!isDiamond && argLength == 0)
return this.resolvedType;
@@ -472,7 +462,7 @@ public class ParameterizedSingleTypeReference extends ArrayTypeReference {
}
ParameterizedTypeBinding parameterizedType = scope.environment().createParameterizedType(currentOriginal, argTypes, anchor, valParPos, enclosingType, currentAnnotations);
/* orig:
- ParameterizedTypeBinding parameterizedType = scope.environment().createParameterizedType(currentOriginal, argTypes, enclosingType);
+ ParameterizedTypeBinding parameterizedType = scope.environment().createParameterizedType(currentOriginal, argTypes, enclosingType);
:giro */
// SH}
// check argument type compatibility for non <> cases - <> case needs no bounds check, we will scream foul if needed during inference.
@@ -487,6 +477,8 @@ public class ParameterizedSingleTypeReference extends ArrayTypeReference {
if (isTypeUseDeprecated(parameterizedType, scope))
reportDeprecatedType(parameterizedType, scope);
+ checkIllegalNullAnnotations(scope, this.typeArguments);
+
if (!this.resolvedType.isValidBinding()) {
return parameterizedType;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
index 7bd040497..47c44101b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedAllocationExpression.java
@@ -240,6 +240,7 @@ public static abstract class AbstractQualifiedAllocationExpression extends Alloc
}
public void generateCode(BlockScope currentScope, CodeStream codeStream, boolean valueRequired) {
+ cleanUpInferenceContexts();
if (!valueRequired)
currentScope.problemReporter().unusedObjectAllocation(this);
int pc = codeStream.position;
@@ -372,7 +373,7 @@ public static abstract class AbstractQualifiedAllocationExpression extends Alloc
if (this.binding instanceof ParameterizedGenericMethodBinding && this.typeArguments != null) {
TypeVariableBinding[] typeVariables = this.binding.original().typeVariables();
for (int i = 0; i < this.typeArguments.length; i++)
- this.typeArguments[i].checkNullConstraints(scope, typeVariables, i);
+ this.typeArguments[i].checkNullConstraints(scope, (ParameterizedGenericMethodBinding) this.binding, typeVariables, i);
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
index 78d7d9d30..d06692fff 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/QualifiedNameReference.java
@@ -292,18 +292,18 @@ private void checkInternalNPE(BlockScope scope, FlowContext flowContext, FlowInf
if (this.otherBindings != null) {
if ((this.bits & ASTNode.RestrictiveFlagMASK) == Binding.FIELD) {
// is the first field dereferenced annotated Nullable? If so, report immediately
- checkNullableFieldDereference(scope, (FieldBinding) this.binding, this.sourcePositions[this.indexOfFirstFieldBinding-1]);
+ checkNullableFieldDereference(scope, (FieldBinding) this.binding, this.sourcePositions[this.indexOfFirstFieldBinding-1], flowContext, 0);
}
// look for annotated fields, they do not depend on flow context -> check immediately:
int length = this.otherBindings.length - 1; // don't check the last binding
for (int i = 0; i < length; i++) {
- checkNullableFieldDereference(scope, this.otherBindings[i], this.sourcePositions[this.indexOfFirstFieldBinding+i]);
+ checkNullableFieldDereference(scope, this.otherBindings[i], this.sourcePositions[this.indexOfFirstFieldBinding+i], flowContext, 0);
}
}
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
- if (super.checkNPE(scope, flowContext, flowInfo)) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
+ if (super.checkNPE(scope, flowContext, flowInfo, ttlForFieldCheck)) {
return true;
}
FieldBinding fieldBinding = null;
@@ -318,7 +318,7 @@ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flow
position = this.sourcePositions[this.sourcePositions.length - 1];
}
if (fieldBinding != null) {
- return checkNullableFieldDereference(scope, fieldBinding, position);
+ return checkNullableFieldDereference(scope, fieldBinding, position, flowContext, ttlForFieldCheck);
}
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
index 281de66ff..a8cff6cdf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Reference.java
@@ -68,22 +68,26 @@ public FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, Fl
return flowInfo;
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
if (flowContext.isNullcheckedFieldAccess(this)) {
return true; // enough seen
}
- return super.checkNPE(scope, flowContext, flowInfo);
+ return super.checkNPE(scope, flowContext, flowInfo, ttlForFieldCheck);
}
-protected boolean checkNullableFieldDereference(Scope scope, FieldBinding field, long sourcePosition) {
- // preference to type annotations if we have any
- if ((field.type.tagBits & TagBits.AnnotationNullable) != 0) {
- scope.problemReporter().dereferencingNullableExpression(sourcePosition, scope.environment());
- return true;
- }
- if ((field.tagBits & TagBits.AnnotationNullable) != 0) {
- scope.problemReporter().nullableFieldDereference(field, sourcePosition);
- return true;
+protected boolean checkNullableFieldDereference(Scope scope, FieldBinding field, long sourcePosition, FlowContext flowContext, int ttlForFieldCheck) {
+ if (field != null) {
+ if (ttlForFieldCheck > 0 && scope.compilerOptions().enableSyntacticNullAnalysisForFields)
+ flowContext.recordNullCheckedFieldReference(this, ttlForFieldCheck);
+ // preference to type annotations if we have any
+ if ((field.type.tagBits & TagBits.AnnotationNullable) != 0) {
+ scope.problemReporter().dereferencingNullableExpression(sourcePosition, scope.environment());
+ return true;
+ }
+ if ((field.tagBits & TagBits.AnnotationNullable) != 0) {
+ scope.problemReporter().nullableFieldDereference(field, sourcePosition);
+ return true;
+ }
}
return false;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
index 1950dde5f..9f7216e75 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SingleNameReference.java
@@ -281,14 +281,12 @@ public TypeBinding checkFieldAccess(BlockScope scope) {
}
-public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
- if (!super.checkNPE(scope, flowContext, flowInfo)) {
+public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
+ if (!super.checkNPE(scope, flowContext, flowInfo, ttlForFieldCheck)) {
CompilerOptions compilerOptions = scope.compilerOptions();
if (compilerOptions.isAnnotationBasedNullAnalysisEnabled) {
- VariableBinding var = nullAnnotatedVariableBinding(compilerOptions.sourceLevel >= ClassFileConstants.JDK1_8);
- if (var instanceof FieldBinding) {
- checkNullableFieldDereference(scope, (FieldBinding) var, ((long)this.sourceStart<<32)+this.sourceEnd);
- return true;
+ if (this.binding instanceof FieldBinding) {
+ return checkNullableFieldDereference(scope, (FieldBinding) this.binding, ((long)this.sourceStart<<32)+this.sourceEnd, flowContext, ttlForFieldCheck);
}
}
}
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 9b7120fb6..12d85d563 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
@@ -200,6 +200,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/SwitchStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java
index 5f6e1110d..bd51350fd 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SwitchStatement.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -79,7 +79,7 @@ public class SwitchStatement extends Statement {
if ((this.expression.implicitConversion & TypeIds.UNBOXING) != 0
|| (this.expression.resolvedType != null
&& (this.expression.resolvedType.id == T_JavaLangString || this.expression.resolvedType.isEnum()))) {
- this.expression.checkNPE(currentScope, flowContext, flowInfo);
+ this.expression.checkNPE(currentScope, flowContext, flowInfo, 1);
}
SwitchFlowContext switchContext =
new SwitchFlowContext(flowContext, this, (this.breakLabel = new BranchLabel()), true);
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java
index cd2c2a408..61d86d63d 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/SynchronizedStatement.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2011 IBM Corporation and others.
+ * Copyright (c) 2000, 2011, 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
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Carmi Grushko - Bug 465048 - Binding is null for class literals in synchronized blocks
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;
@@ -170,31 +171,31 @@ public void resolve(BlockScope upperScope) {
// special scope for secret locals optimization.
this.scope = new BlockScope(upperScope);
TypeBinding type = this.expression.resolveType(this.scope);
- if (type == null)
- return;
- switch (type.id) {
- case T_boolean :
- case T_char :
- case T_float :
- case T_double :
- case T_byte :
- case T_short :
- case T_int :
- case T_long :
- this.scope.problemReporter().invalidTypeToSynchronize(this.expression, type);
- break;
- case T_void :
- this.scope.problemReporter().illegalVoidExpression(this.expression);
- break;
- case T_null :
- this.scope.problemReporter().invalidNullToSynchronize(this.expression);
- break;
+ if (type != null) {
+ switch (type.id) {
+ case T_boolean :
+ case T_char :
+ case T_float :
+ case T_double :
+ case T_byte :
+ case T_short :
+ case T_int :
+ case T_long :
+ this.scope.problemReporter().invalidTypeToSynchronize(this.expression, type);
+ break;
+ case T_void :
+ this.scope.problemReporter().illegalVoidExpression(this.expression);
+ break;
+ case T_null :
+ this.scope.problemReporter().invalidNullToSynchronize(this.expression);
+ break;
+ }
+ //continue even on errors in order to have the TC done into the statements
+ this.synchroVariable = new LocalVariableBinding(SecretLocalDeclarationName, type, ClassFileConstants.AccDefault, false);
+ this.scope.addLocalVariable(this.synchroVariable);
+ this.synchroVariable.setConstant(Constant.NotAConstant); // not inlinable
+ this.expression.computeConversion(this.scope, type, type);
}
- //continue even on errors in order to have the TC done into the statements
- this.synchroVariable = new LocalVariableBinding(SecretLocalDeclarationName, type, ClassFileConstants.AccDefault, false);
- this.scope.addLocalVariable(this.synchroVariable);
- this.synchroVariable.setConstant(Constant.NotAConstant); // not inlinable
- this.expression.computeConversion(this.scope, type, type);
this.block.resolveUsing(this.scope);
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java
index b9e751876..76c802117 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/ThisReference.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -78,7 +78,7 @@ public class ThisReference extends Reference {
return true;
}
- public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ public boolean checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo, int ttlForFieldCheck) {
return true; // never problematic
}
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 0822a281a..0101b0d5c 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
@@ -842,42 +842,39 @@ protected void resolveAnnotations(Scope scope, int location) {
public int getAnnotatableLevels() {
return 1;
}
-/** Check all typeArguments against null constraints on their corresponding type variables. */
-protected void checkNullConstraints(Scope scope, TypeReference[] typeArguments) {
- if (scope.environment().usesNullTypeAnnotations()
- && typeArguments != null)
- {
- TypeVariableBinding[] typeVariables = this.resolvedType.original().typeVariables();
+/** Check all typeArguments for illegal null annotations on base types. */
+protected void checkIllegalNullAnnotations(Scope scope, TypeReference[] typeArguments) {
+ if (scope.environment().usesNullTypeAnnotations() && typeArguments != null) {
for (int i = 0; i < typeArguments.length; i++) {
TypeReference arg = typeArguments[i];
if (arg.resolvedType != null)
- arg.checkNullConstraints(scope, typeVariables, i);
+ arg.checkIllegalNullAnnotation(scope);
}
}
}
/** Check whether this type reference conforms to the null constraints defined for the corresponding type variable. */
-protected void checkNullConstraints(Scope scope, TypeBinding[] variables, int rank) {
+protected void checkNullConstraints(Scope scope, Substitution substitution, TypeBinding[] variables, int rank) {
if (variables != null && variables.length > rank) {
TypeBinding variable = variables[rank];
if (variable.hasNullTypeAnnotations()) {
- if (NullAnnotationMatching.analyse(variable, this.resolvedType, null, -1, CheckMode.BOUND_CHECK).isAnyMismatch())
+ if (NullAnnotationMatching.analyse(variable, this.resolvedType, null, substitution, -1, CheckMode.BOUND_CHECK).isAnyMismatch())
scope.problemReporter().nullityMismatchTypeArgument(variable, this.resolvedType, this);
}
}
- if (this.resolvedType.leafComponentType().isBaseType() && hasNullTypeAnnotation(AnnotationPosition.LEAF_TYPE)) {
- scope.problemReporter().illegalAnnotationForBaseType(this, this.annotations[0], this.resolvedType.tagBits & TagBits.AnnotationNullMASK);
- }
+ checkIllegalNullAnnotation(scope);
+}
+protected void checkIllegalNullAnnotation(Scope scope) {
+ if (this.resolvedType.leafComponentType().isBaseType() && hasNullTypeAnnotation(AnnotationPosition.LEAF_TYPE))
+ scope.problemReporter().illegalAnnotationForBaseType(this, this.annotations[0], this.resolvedType.tagBits & TagBits.AnnotationNullMASK);
}
/** Retrieve the null annotation that has been translated to the given nullTagBits. */
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];
}
}
@@ -901,10 +898,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/classfmt/ClassFileReader.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java
index dc163a571..4828a697c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ClassFileReader.java
@@ -31,6 +31,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.eclipse.jdt.core.compiler.CharOperation;
+import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants;
import org.eclipse.jdt.internal.compiler.codegen.AttributeNamesConstants;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.impl.Constant;
@@ -1149,6 +1150,9 @@ public boolean hasStructuralChanges(byte[] newBytes, boolean orderRequired, bool
// annotations
if (hasStructuralAnnotationChanges(getAnnotations(), newClassFile.getAnnotations()))
return true;
+ if (this.version >= ClassFileConstants.JDK1_8
+ && hasStructuralTypeAnnotationChanges(getTypeAnnotations(), newClassFile.getTypeAnnotations()))
+ return true;
// generic signature
if (!CharOperation.equals(getGenericSignature(), newClassFile.getGenericSignature()))
@@ -1275,43 +1279,48 @@ private boolean hasStructuralAnnotationChanges(IBinaryAnnotation[] currentAnnota
if (currentAnnotationsLength != otherAnnotationsLength)
return true;
for (int i = 0; i < currentAnnotationsLength; i++) {
- if (!CharOperation.equals(currentAnnotations[i].getTypeName(), otherAnnotations[i].getTypeName()))
- return true;
- IBinaryElementValuePair[] currentPairs = currentAnnotations[i].getElementValuePairs();
- IBinaryElementValuePair[] otherPairs = otherAnnotations[i].getElementValuePairs();
- int currentPairsLength = currentPairs == null ? 0 : currentPairs.length;
- int otherPairsLength = otherPairs == null ? 0 : otherPairs.length;
- if (currentPairsLength != otherPairsLength)
- return true;
- for (int j = 0; j < currentPairsLength; j++) {
- if (!CharOperation.equals(currentPairs[j].getName(), otherPairs[j].getName()))
- return true;
- final Object value = currentPairs[j].getValue();
- final Object value2 = otherPairs[j].getValue();
- if (value instanceof Object[]) {
- Object[] currentValues = (Object[]) value;
- if (value2 instanceof Object[]) {
- Object[] currentValues2 = (Object[]) value2;
- final int length = currentValues.length;
- if (length != currentValues2.length) {
- return true;
- }
- for (int n = 0; n < length; n++) {
- if (!currentValues[n].equals(currentValues2[n])) {
- return true;
- }
+ Boolean match = matchAnnotations(currentAnnotations[i], otherAnnotations[i]);
+ if (match != null)
+ return match.booleanValue();
+ }
+ return false;
+}
+private Boolean matchAnnotations(IBinaryAnnotation currentAnnotation, IBinaryAnnotation otherAnnotation) {
+ if (!CharOperation.equals(currentAnnotation.getTypeName(), otherAnnotation.getTypeName()))
+ return true;
+ IBinaryElementValuePair[] currentPairs = currentAnnotation.getElementValuePairs();
+ IBinaryElementValuePair[] otherPairs = otherAnnotation.getElementValuePairs();
+ int currentPairsLength = currentPairs == null ? 0 : currentPairs.length;
+ int otherPairsLength = otherPairs == null ? 0 : otherPairs.length;
+ if (currentPairsLength != otherPairsLength)
+ return Boolean.TRUE;
+ for (int j = 0; j < currentPairsLength; j++) {
+ if (!CharOperation.equals(currentPairs[j].getName(), otherPairs[j].getName()))
+ return Boolean.TRUE;
+ final Object value = currentPairs[j].getValue();
+ final Object value2 = otherPairs[j].getValue();
+ if (value instanceof Object[]) {
+ Object[] currentValues = (Object[]) value;
+ if (value2 instanceof Object[]) {
+ Object[] currentValues2 = (Object[]) value2;
+ final int length = currentValues.length;
+ if (length != currentValues2.length) {
+ return Boolean.TRUE;
+ }
+ for (int n = 0; n < length; n++) {
+ if (!currentValues[n].equals(currentValues2[n])) {
+ return Boolean.TRUE;
}
- return false;
}
- return true;
- } else if (!value.equals(value2)) {
- return true;
+ return Boolean.FALSE;
}
+ return Boolean.TRUE;
+ } else if (!value.equals(value2)) {
+ return Boolean.TRUE;
}
}
- return false;
+ return null;
}
-
private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo otherFieldInfo) {
// generic signature
if (!CharOperation.equals(currentFieldInfo.getGenericSignature(), otherFieldInfo.getGenericSignature()))
@@ -1322,6 +1331,9 @@ private boolean hasStructuralFieldChanges(FieldInfo currentFieldInfo, FieldInfo
return true;
if (hasStructuralAnnotationChanges(currentFieldInfo.getAnnotations(), otherFieldInfo.getAnnotations()))
return true;
+ if (this.version >= ClassFileConstants.JDK1_8
+ && hasStructuralTypeAnnotationChanges(currentFieldInfo.getTypeAnnotations(), otherFieldInfo.getTypeAnnotations()))
+ return true;
if (!CharOperation.equals(currentFieldInfo.getName(), otherFieldInfo.getName()))
return true;
if (!CharOperation.equals(currentFieldInfo.getTypeName(), otherFieldInfo.getTypeName()))
@@ -1378,6 +1390,9 @@ private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodI
if (hasStructuralAnnotationChanges(currentMethodInfo.getParameterAnnotations(i, this.classFileName), otherMethodInfo.getParameterAnnotations(i, this.classFileName)))
return true;
}
+ if (this.version >= ClassFileConstants.JDK1_8
+ && hasStructuralTypeAnnotationChanges(currentMethodInfo.getTypeAnnotations(), otherMethodInfo.getTypeAnnotations()))
+ return true;
if (!CharOperation.equals(currentMethodInfo.getSelector(), otherMethodInfo.getSelector()))
return true;
@@ -1400,6 +1415,45 @@ private boolean hasStructuralMethodChanges(MethodInfo currentMethodInfo, MethodI
return false;
}
+private boolean hasStructuralTypeAnnotationChanges(IBinaryTypeAnnotation[] currentTypeAnnotations, IBinaryTypeAnnotation[] otherTypeAnnotations) {
+ if (otherTypeAnnotations != null) {
+ // copy so we can delete matched annotations:
+ int len = otherTypeAnnotations.length;
+ System.arraycopy(otherTypeAnnotations, 0, otherTypeAnnotations = new IBinaryTypeAnnotation[len], 0, len);
+ }
+ if (currentTypeAnnotations != null) {
+ loopCurrent:
+ for (IBinaryTypeAnnotation currentAnnotation : currentTypeAnnotations) {
+ if (!affectsSignature(currentAnnotation)) continue;
+ if (otherTypeAnnotations == null)
+ return true;
+ for (int i = 0; i < otherTypeAnnotations.length; i++) {
+ IBinaryTypeAnnotation otherAnnotation = otherTypeAnnotations[i];
+ if (otherAnnotation != null && matchAnnotations(currentAnnotation.getAnnotation(), otherAnnotation.getAnnotation()) == Boolean.TRUE) {
+ otherTypeAnnotations[i] = null; // matched
+ continue loopCurrent;
+ }
+ }
+ return true; // not matched
+ }
+ }
+ if (otherTypeAnnotations != null) {
+ for (IBinaryTypeAnnotation otherAnnotation : otherTypeAnnotations) {
+ if (affectsSignature(otherAnnotation))
+ return true;
+ }
+ }
+ return false;
+}
+
+private boolean affectsSignature(IBinaryTypeAnnotation typeAnnotation) {
+ if (typeAnnotation == null) return false;
+ int targetType = typeAnnotation.getTargetType();
+ if (targetType >= AnnotationTargetTypeConstants.LOCAL_VARIABLE && targetType <= AnnotationTargetTypeConstants.METHOD_REFERENCE_TYPE_ARGUMENT)
+ return false; // affects detail within a block
+ return true;
+}
+
/**
* This method is used to fully initialize the contents of the receiver. All methodinfos, fields infos
* will be therefore fully initialized and we can get rid of the bytes.
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
index bdce24ef5..cbb2bc1ca 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java
@@ -146,7 +146,7 @@ public class ExternalAnnotationProvider {
* Assert that the given line is a class header for 'typeName' (slash-separated qualified name).
*/
public static void assertClassHeader(String line, String typeName) throws IOException {
- if (line.startsWith(CLASS_PREFIX)) {
+ if (line != null && line.startsWith(CLASS_PREFIX)) {
line = line.substring(CLASS_PREFIX.length());
} else {
throw new IOException("missing class header in annotation file"); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
index 383f3d1be..eb58e53ca 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/codegen/CodeStream.java
@@ -3491,8 +3491,17 @@ public static TypeBinding getConstantPoolDeclaringClass(Scope currentScope, Meth
&& (options.complianceLevel >= ClassFileConstants.JDK1_4 || !(isImplicitThisReceiver && codegenBinding.isStatic()))
&& codegenBinding.declaringClass.id != TypeIds.T_JavaLangObject) // no change for Object methods
|| !codegenBinding.declaringClass.canBeSeenBy(currentScope)) {
- if (!actualReceiverType.isIntersectionType18()) // no constant pool representation. FIXME, visibility issue not handled.
+ if (actualReceiverType.isIntersectionType18()) {
+ TypeBinding[] intersectingTypes = ((IntersectionTypeBinding18)actualReceiverType).getIntersectingTypes();
+ for(int i = 0; i < intersectingTypes.length; i++) {
+ if (intersectingTypes[i].findSuperTypeOriginatingFrom(constantPoolDeclaringClass) != null) {
+ constantPoolDeclaringClass = intersectingTypes[i];
+ break;
+ }
+ }
+ } else {
constantPoolDeclaringClass = actualReceiverType.erasure();
+ }
}
}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
index c6dcc0aa3..6b1a868d9 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/flow/FlowContext.java
@@ -82,10 +82,10 @@ public class FlowContext implements TypeConstants {
public TypeBinding[][] providedExpectedTypes = null;
// record field references known to be non-null
- // this array will never shrink, only grow. reset happens by nulling the first cell
- // adding elements after reset ensures that the valid part of the array is always null-terminated
+ // this array will never shrink, only grow. reset happens by nulling expired entries
+ // this array grows in lock step with timesToLiveForNullCheckInfo, which controls expiration
private Reference[] nullCheckedFieldReferences = null;
- private int timeToLiveForNullCheckInfo = -1;
+ private int[] timesToLiveForNullCheckInfo = null;
public static final int DEFER_NULL_DIAGNOSTIC = 0x1;
public static final int PREEMPT_NULL_DIAGNOSTIC = 0x2;
@@ -131,6 +131,7 @@ public FlowContext(FlowContext parent, ASTNode associatedNode) {
this.initsOnFinally = parent.initsOnFinally;
this.conditionalLevel = parent.conditionalLevel;
this.nullCheckedFieldReferences = parent.nullCheckedFieldReferences; // re-use list if there is one
+ this.timesToLiveForNullCheckInfo = parent.timesToLiveForNullCheckInfo;
}
}
@@ -141,33 +142,35 @@ public FlowContext(FlowContext parent, ASTNode associatedNode) {
* @param timeToLive control how many expire events are needed to expire this information
*/
public void recordNullCheckedFieldReference(Reference reference, int timeToLive) {
- this.timeToLiveForNullCheckInfo = timeToLive;
if (this.nullCheckedFieldReferences == null) {
// first entry:
- this.nullCheckedFieldReferences = new Reference[2];
- this.nullCheckedFieldReferences[0] = reference;
+ this.nullCheckedFieldReferences = new Reference[] { reference, null };
+ this.timesToLiveForNullCheckInfo = new int[] { timeToLive, -1 };
} else {
int len = this.nullCheckedFieldReferences.length;
// insert into first empty slot:
for (int i=0; i<len; i++) {
if (this.nullCheckedFieldReferences[i] == null) {
this.nullCheckedFieldReferences[i] = reference;
- if (i+1 < len) {
- this.nullCheckedFieldReferences[i+1] = null; // lazily mark next as empty
- }
+ this.timesToLiveForNullCheckInfo[i] = timeToLive;
return;
}
}
- // grow array:
+ // grow arrays:
System.arraycopy(this.nullCheckedFieldReferences, 0, this.nullCheckedFieldReferences=new Reference[len+2], 0, len);
+ System.arraycopy(this.timesToLiveForNullCheckInfo, 0, this.timesToLiveForNullCheckInfo=new int[len+2], 0, len);
this.nullCheckedFieldReferences[len] = reference;
+ this.timesToLiveForNullCheckInfo[len] = timeToLive;
}
}
/** If a null checked field has been recorded recently, increase its time to live. */
public void extendTimeToLiveForNullCheckedField(int t) {
- if (this.timeToLiveForNullCheckInfo > 0)
- this.timeToLiveForNullCheckInfo += t;
+ if (this.timesToLiveForNullCheckInfo != null) {
+ for (int i = 0; i < this.timesToLiveForNullCheckInfo.length; i++)
+ if (this.timesToLiveForNullCheckInfo[i] > 0)
+ this.timesToLiveForNullCheckInfo[i] += t;
+ }
}
/**
@@ -178,8 +181,9 @@ public void extendTimeToLiveForNullCheckedField(int t) {
*/
public void expireNullCheckedFieldInfo() {
if (this.nullCheckedFieldReferences != null) {
- if (--this.timeToLiveForNullCheckInfo == 0) {
- this.nullCheckedFieldReferences[0] = null; // lazily wipe
+ for (int i = 0; i < this.nullCheckedFieldReferences.length; i++) {
+ if (--this.timesToLiveForNullCheckInfo[i] == 0)
+ this.nullCheckedFieldReferences[i] = null;
}
}
}
@@ -196,7 +200,7 @@ public boolean isNullcheckedFieldAccess(Reference reference) {
for (int i=0; i<len; i++) {
Reference checked = this.nullCheckedFieldReferences[i];
if (checked == null) {
- return false;
+ continue;
}
if (checked.isEquivalent(reference)) {
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 ff1a847fd..4ba481557 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
@@ -262,6 +262,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$
@@ -303,6 +306,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
@@ -572,6 +577,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()? */
@@ -1534,6 +1545,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);
@@ -2112,6 +2126,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);
@@ -2257,6 +2280,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 ac12a5e08..f4c1572cf 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -414,15 +414,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.
@@ -507,4 +504,10 @@ public TypeBinding uncapture(Scope scope) {
public boolean acceptsNonNullDefault() {
return true;
}
+@Override
+public long updateTagBits() {
+ if (this.leafComponentType != null)
+ this.tagBits |= this.leafComponentType.updateTagBits();
+ return super.updateTagBits();
+}
}
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 4b534395e..4ba51baa1 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
@@ -751,10 +751,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);
}
}
@@ -1958,10 +1957,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
@@ -1975,13 +1970,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;
@@ -2015,11 +2010,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();
@@ -2031,16 +2021,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;
}
}
@@ -2065,13 +2060,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;
@@ -2103,14 +2098,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/BoundSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
index 869e8b8e9..ee47b4da4 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BoundSet.java
@@ -590,8 +590,10 @@ class BoundSet {
// not per JLS: if the new constraint relates types where at least one has a null annotations,
// record all null tagBits as hints for the final inference solution.
long nullHints = (newConstraint.left.tagBits | newConstraint.right.tagBits) & TagBits.AnnotationNullMASK;
- boundI.nullHints |= nullHints;
- boundJ.nullHints |= nullHints;
+ if (nullHints != 0 && TypeBinding.equalsEquals(boundI.left, boundJ.left)) {
+ boundI.nullHints |= nullHints;
+ boundJ.nullHints |= nullHints;
+ }
}
}
ConstraintFormula[] typeArgumentConstraints = deriveTypeArgumentConstraints ? deriveTypeArgumentConstraints(boundI, boundJ) : null;
@@ -778,9 +780,12 @@ class BoundSet {
// α = U and S <: T imply ⟨S[α:=U] <: T[α:=U]⟩
TypeBinding u = boundS.right;
if (u.isProperType(true)) {
- TypeBinding left = (TypeBinding.equalsEquals(alpha, boundT.left)) ? u : boundT.left;
+ boolean substitute = TypeBinding.equalsEquals(alpha, boundT.left);
+ TypeBinding left = substitute ? u : boundT.left;
TypeBinding right = boundT.right.substituteInferenceVariable(alpha, u);
- return ConstraintTypeFormula.create(left, right, boundT.relation, boundT.isSoft||boundS.isSoft);
+ substitute |= TypeBinding.notEquals(right, boundT.right);
+ if (substitute) // avoid redundant constraint
+ return ConstraintTypeFormula.create(left, right, boundT.relation, boundT.isSoft||boundS.isSoft);
}
return null;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
index e430d9b1f..c6f68376e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
@@ -1682,7 +1682,7 @@ public class ClassScope extends Scope {
sourceType.tagBits |= (superType.tagBits & TagBits.HierarchyHasProblems); // propagate if missing supertpye
sourceType.setSuperClass(superType);
// bound check (in case of bogus definition of Enum type)
- if (refTypeVariables[0].boundCheck(superType, sourceType, this) != TypeConstants.OK) {
+ if (!refTypeVariables[0].boundCheck(superType, sourceType, this, null).isOKbyJLS()) {
problemReporter().typeMismatchError(rootEnumType, refTypeVariables[0], sourceType, null);
}
return !foundCycle;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
index 1f013a4cd..96ae35acd 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/CompilationUnitScope.java
@@ -17,6 +17,8 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
+import java.util.ArrayList;
+
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.internal.compiler.ast.*;
@@ -91,6 +93,7 @@ public class CompilationUnitScope extends Scope {
private boolean skipCachingImports;
boolean connectingHierarchy;
+ private ArrayList<Invocation> inferredInvocations;
//{ObjectTeams: when used as a baseimport scope, remember the original scope during this current lookup
public Scope originalScope;
// SH}
@@ -1296,4 +1299,16 @@ public boolean hasDefaultNullnessFor(int location) {
return (this.fPackage.defaultNullness & location) != 0;
return false;
}
+public void registerInferredInvocation(Invocation invocation) {
+ if (this.inferredInvocations == null)
+ this.inferredInvocations = new ArrayList<>();
+ this.inferredInvocations.add(invocation);
+}
+public void cleanUpInferenceContexts() {
+ if (this.inferredInvocations == null)
+ return;
+ for (Invocation invocation : this.inferredInvocations)
+ invocation.cleanUpInferenceContexts();
+ this.inferredInvocations = null;
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
index 10675ce15..7247fc3bb 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ImplicitNullAnnotationVerifier.java
@@ -306,7 +306,7 @@ public class ImplicitNullAnnotationVerifier {
ParameterizedGenericMethodBinding substitute = this.environment.createParameterizedGenericMethod(currentMethod, typeVariables);
substituteReturnType = substitute.returnType;
}
- if (NullAnnotationMatching.analyse(inheritedMethod.returnType, currentMethod.returnType, substituteReturnType, 0, CheckMode.OVERRIDE).isAnyMismatch()) {
+ if (NullAnnotationMatching.analyse(inheritedMethod.returnType, currentMethod.returnType, substituteReturnType, null, 0, CheckMode.OVERRIDE_RETURN).isAnyMismatch()) {
if (srcMethod != null)
scope.problemReporter().illegalReturnRedefinition(srcMethod, inheritedMethod,
this.environment.getNonNullAnnotationName());
@@ -437,7 +437,7 @@ public class ImplicitNullAnnotationVerifier {
if (useTypeAnnotations) {
TypeBinding inheritedParameter = inheritedMethod.parameters[i];
TypeBinding substituteParameter = substituteParameters != null ? substituteParameters[i] : null;
- if (NullAnnotationMatching.analyse(currentMethod.parameters[i], inheritedParameter, substituteParameter, 0, CheckMode.OVERRIDE).isAnyMismatch()) {
+ if (NullAnnotationMatching.analyse(currentMethod.parameters[i], inheritedParameter, substituteParameter, null, 0, CheckMode.OVERRIDE).isAnyMismatch()) {
if (currentArgument != null)
scope.problemReporter().illegalParameterRedefinition(currentArgument, inheritedMethod.declaringClass, inheritedParameter);
else
@@ -446,6 +446,18 @@ public class ImplicitNullAnnotationVerifier {
}
}
}
+
+ if (shouldComplain && useTypeAnnotations && srcMethod != null) {
+ TypeVariableBinding[] currentTypeVariables = currentMethod.typeVariables();
+ TypeVariableBinding[] inheritedTypeVariables = inheritedMethod.typeVariables();
+ if (currentTypeVariables != Binding.NO_TYPE_VARIABLES && currentTypeVariables.length == inheritedTypeVariables.length) {
+ for (int i = 0; i < currentTypeVariables.length; i++) {
+ TypeVariableBinding inheritedVariable = inheritedTypeVariables[i];
+ if (NullAnnotationMatching.analyse(inheritedVariable, currentTypeVariables[i], null, null, -1, CheckMode.BOUND_CHECK).isAnyMismatch())
+ scope.problemReporter().cannotRedefineTypeArgumentNullity(inheritedVariable, inheritedMethod, srcMethod.typeParameters()[i]);
+ }
+ }
+ }
}
void applyReturnNullBits(MethodBinding method, long nullnessBits) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
index 45b6c032a..5fecda127 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/InferenceContext18.java
@@ -212,6 +212,8 @@ public class InferenceContext18 {
this.invocationArguments = arguments;
this.currentInvocation = site;
this.outerContext = outerContext;
+ if (site instanceof Invocation)
+ scope.compilationUnitScope().registerInferredInvocation((Invocation) site);
}
public InferenceContext18(Scope scope) {
@@ -1656,4 +1658,9 @@ public class InferenceContext18 {
}
}
}
+
+ public void cleanUp() {
+ this.b2 = null;
+ this.currentBounds = null;
+ }
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/IntersectionTypeBinding18.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/IntersectionTypeBinding18.java
index fd43592f8..a93c1b59b 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/IntersectionTypeBinding18.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/IntersectionTypeBinding18.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -292,4 +292,10 @@ public class IntersectionTypeBinding18 extends ReferenceBinding { // abstraction
}
return false;
}
+ @Override
+ public long updateTagBits() {
+ for (TypeBinding intersectingType : this.intersectingTypes)
+ this.tagBits |= intersectingType.updateTagBits();
+ return super.updateTagBits();
+ }
}
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 3bb73da32..a4269c4c1 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
@@ -160,6 +160,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. */
@@ -1262,9 +1264,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;
@@ -1368,6 +1371,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;
@@ -2045,8 +2069,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;
}
}
@@ -2061,15 +2084,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 0d53059a3..41307d3d6 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
@@ -283,22 +283,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/ParameterizedGenericMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
index 983e84f5c..82e4b2d60 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedGenericMethodBinding.java
@@ -27,6 +27,7 @@
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
+import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.Invocation;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
@@ -193,12 +194,12 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
if (actualReceiverType instanceof ReferenceBinding)
actualReceiverRefType = (ReferenceBinding) actualReceiverType;
}
- switch (typeVariable.boundCheck(substitution, substituteForChecks, actualReceiverRefType, scope)) {
+ switch (typeVariable.boundCheck(substitution, substituteForChecks, actualReceiverRefType, scope, null)) {
/* orig:
- switch (typeVariable.boundCheck(substitution, substituteForChecks, scope)) {
+ switch (typeVariable.boundCheck(substitution, substituteForChecks, scope, null)) {
:giro */
// SH}
- case TypeConstants.MISMATCH :
+ case MISMATCH :
// incompatible due to bound check
int argLength = arguments.length;
TypeBinding[] augmentedArguments = new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
@@ -206,10 +207,12 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
augmentedArguments[argLength] = substitute;
augmentedArguments[argLength+1] = typeVariable;
return new ProblemMethodBinding(methodSubstitute, originalMethod.selector, augmentedArguments, ProblemReasons.ParameterBoundMismatch);
- case TypeConstants.UNCHECKED :
+ case UNCHECKED :
// tolerate unchecked bounds
methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
break;
+ default:
+ break;
}
}
// check presence of unchecked argument conversion a posteriori (15.12.2.6)
@@ -314,12 +317,7 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
if (invocationTypeInferred) {
if (compilerOptions.isAnnotationBasedNullAnalysisEnabled)
NullAnnotationMatching.checkForContradictions(methodSubstitute, invocationSite, scope);
-//{ObjectTeams: 2nd arg added:
-/* orig:
- MethodBinding problemMethod = methodSubstitute.boundCheck18(scope, arguments);
- :giro */
- MethodBinding problemMethod = methodSubstitute.boundCheck18(scope, invocationSite, arguments);
-// SH}
+ MethodBinding problemMethod = methodSubstitute.boundCheck18(scope, arguments, invocationSite);
if (problemMethod != null) {
return problemMethod;
}
@@ -343,12 +341,7 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
}
}
-//{ObjectTeams: second arg added:
-/*orig:
- MethodBinding boundCheck18(Scope scope, TypeBinding[] arguments) {
- :giro */
- MethodBinding boundCheck18(Scope scope, InvocationSite invocationSite, TypeBinding[] arguments) {
-// SH}
+ MethodBinding boundCheck18(Scope scope, TypeBinding[] arguments, InvocationSite site) {
Substitution substitution = this;
ParameterizedGenericMethodBinding methodSubstitute = this;
TypeVariableBinding[] originalTypeVariables = this.originalMethod.typeVariables;
@@ -369,17 +362,18 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
//{ObjectTeams: methods with generic declared lifting need to be checked in knowledge of the actual receiver type:
ReferenceBinding actualReceiverRefType = null;
- if (invocationSite instanceof MessageSend) {
- TypeBinding actualReceiverType = ((MessageSend)invocationSite).actualReceiverType;
+ if (site instanceof MessageSend) {
+ TypeBinding actualReceiverType = ((MessageSend)site).actualReceiverType;
if (actualReceiverType instanceof ReferenceBinding)
actualReceiverRefType = (ReferenceBinding) actualReceiverType;
}
- switch (typeVariable.boundCheck(substitution, substituteForChecks, actualReceiverRefType, scope)) {
+ ASTNode location = site instanceof ASTNode ? (ASTNode) site : null;
+ switch (typeVariable.boundCheck(substitution, substituteForChecks, actualReceiverRefType, scope, location)) {
/* orig:
- switch (typeVariable.boundCheck(substitution, substituteForChecks, scope)) {
+ switch (typeVariable.boundCheck(substitution, substituteForChecks, scope, location)) {
:giro */
// SH}
- case TypeConstants.MISMATCH :
+ case MISMATCH :
// incompatible due to bound check
int argLength = arguments.length;
TypeBinding[] augmentedArguments = new TypeBinding[argLength + 2]; // append offending substitute and typeVariable
@@ -387,10 +381,12 @@ public class ParameterizedGenericMethodBinding extends ParameterizedMethodBindin
augmentedArguments[argLength] = substitute;
augmentedArguments[argLength+1] = typeVariable;
return new ProblemMethodBinding(methodSubstitute, this.originalMethod.selector, augmentedArguments, ProblemReasons.ParameterBoundMismatch);
- case TypeConstants.UNCHECKED :
+ case UNCHECKED :
// tolerate unchecked bounds
methodSubstitute.tagBits |= TagBits.HasUncheckedTypeArgumentForBoundCheck;
break;
+ default:
+ break;
}
}
return null;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java
index 514c3e69e..40c4f6aba 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedMethodBinding.java
@@ -324,10 +324,18 @@ public class ParameterizedMethodBinding extends MethodBinding {
ReferenceBinding genericClassType = scope.getJavaLangClass();
LookupEnvironment environment = scope.environment();
TypeBinding rawType = environment.convertToRawType(receiverType.erasure(), false /*do not force conversion of enclosing types*/);
+ if (environment.usesNullTypeAnnotations())
+ rawType = environment.createAnnotatedType(rawType, new AnnotationBinding[] { environment.getNonNullAnnotation() });
method.returnType = environment.createParameterizedType(
genericClassType,
new TypeBinding[] { environment.createWildcard(genericClassType, 0, rawType, null /*no extra bound*/, Wildcard.EXTENDS) },
null);
+ if (environment.globalOptions.isAnnotationBasedNullAnalysisEnabled) {
+ if (environment.usesNullTypeAnnotations())
+ method.returnType = environment.createAnnotatedType(method.returnType, new AnnotationBinding[] { environment.getNonNullAnnotation() });
+ else
+ method.tagBits |= TagBits.AnnotationNonNull;
+ }
if ((method.returnType.tagBits & TagBits.HasMissingType) != 0) {
method.tagBits |= TagBits.HasMissingType;
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
index 1056a1c71..246f94c3c 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ParameterizedTypeBinding.java
@@ -56,6 +56,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants.BoundCheckStatus;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
import org.eclipse.objectteams.otdt.internal.core.compiler.util.RoleTypeCreator.TypeArgumentUpdater;
@@ -149,13 +150,12 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
TypeVariableBinding[] typeVariables = this.type.typeVariables();
if (this.arguments != null && typeVariables != null) { // arguments may be null in error cases
for (int i = 0, length = typeVariables.length; i < length; i++) {
- if (typeVariables[i].boundCheck(this, this.arguments[i], scope) != TypeConstants.OK) {
- hasErrors = true;
- if ((this.arguments[i].tagBits & TagBits.HasMissingType) == 0) {
- // do not report secondary error, if type reference already got complained against
- scope.problemReporter().typeMismatchError(this.arguments[i], typeVariables[i], this.type, argumentReferences[i]);
- }
- }
+ BoundCheckStatus checkStatus = typeVariables[i].boundCheck(this, this.arguments[i], scope, argumentReferences[i]);
+ hasErrors |= checkStatus != BoundCheckStatus.OK;
+ if (!checkStatus.isOKbyJLS() && (this.arguments[i].tagBits & TagBits.HasMissingType) == 0) {
+ // do not report secondary error, if type reference already got complained against
+ scope.problemReporter().typeMismatchError(this.arguments[i], typeVariables[i], this.type, argumentReferences[i]);
+ }
}
}
if (!hasErrors) this.tagBits |= TagBits.PassedBoundCheck; // no need to recheck it in the future
@@ -1610,7 +1610,7 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
declaringType = scope.environment().createParameterizedType(genericType, types, genericType.enclosingType());
TypeVariableBinding [] typeParameters = genericType.typeVariables();
for (int i = 0, length = typeParameters.length; i < length; i++) {
- if (typeParameters[i].boundCheck(declaringType, types[i], scope) != TypeConstants.OK)
+ if (!typeParameters[i].boundCheck(declaringType, types[i], scope, null).isOKbyJLS())
return this.singleAbstractMethod[index] = new ProblemMethodBinding(TypeConstants.ANONYMOUS_METHOD, null, ProblemReasons.NotAWellFormedParameterizedType);
}
ReferenceBinding substitutedDeclaringType = (ReferenceBinding) declaringType.findSuperTypeOriginatingFrom(theAbstractMethod.declaringClass);
@@ -1693,6 +1693,13 @@ public class ParameterizedTypeBinding extends ReferenceBinding implements Substi
}
return types;
}
+ @Override
+ public long updateTagBits() {
+ if (this.arguments != null)
+ for (TypeBinding argument : this.arguments)
+ this.tagBits |= argument.updateTagBits();
+ return super.updateTagBits();
+ }
//{ObjectTeams: recursive role wrapping:
@Override
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 a1d8b7c6a..0c878c44e 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
@@ -1442,6 +1442,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;
+}
+
//{ObjectTeams: support asymmetric comparison. // FIXME(SH): is this needed or is super-impl smart enough??
@Override
public boolean isProvablyDistinct(TypeBinding otherType) {
@@ -2066,8 +2071,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/Scope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
index 58bfb49f0..aaf60c319 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/Scope.java
@@ -1044,8 +1044,14 @@ public abstract class Scope {
}
// after bounds have been resolved we're ready for resolving the type parameter itself,
// which includes resolving/evaluating type annotations and checking for inconsistencies
- for (int i = 0; i < paramLength; i++)
+ boolean declaresNullTypeAnnotation = false;
+ for (int i = 0; i < paramLength; i++) {
resolveTypeParameter(typeParameters[i]);
+ declaresNullTypeAnnotation |= typeParameters[i].binding.hasNullTypeAnnotations();
+ }
+ if (declaresNullTypeAnnotation)
+ for (int i = 0; i < paramLength; i++)
+ typeParameters[i].binding.updateTagBits(); // <T extends List<U>, @NonNull U> --> tag T as having null type annotations
return noProblems;
}
@@ -3665,7 +3671,7 @@ public abstract class Scope {
unitScope.recordSimpleReference(name);
if ((mask & Binding.PACKAGE) != 0) {
PackageBinding packageBinding = unitScope.environment.getTopLevelPackage(name);
- if (packageBinding != null) {
+ if (packageBinding != null && (packageBinding.tagBits & TagBits.HasMissingType) == 0) {
if (typeOrPackageCache != null)
typeOrPackageCache.put(name, packageBinding);
return packageBinding;
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 069edf181..bb9e35f32 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
@@ -2789,8 +2789,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;
}
@@ -2802,10 +2801,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 f3ba9bc59..3a97bb9b1 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2014 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
@@ -1599,14 +1599,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
@@ -1778,4 +1774,11 @@ public void exitRecursiveFunction() {
public boolean isFunctionalType() {
return false;
}
+/**
+ * Refresh some tagBits from details into the main type.
+ * Currently handled: TagBits.HasNullTypeAnnotation
+ */
+public long updateTagBits() {
+ return this.tagBits & TagBits.HasNullTypeAnnotation; // subclasses to override
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
index 0284bd1c6..baa1c68f3 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeConstants.java
@@ -345,10 +345,25 @@ public interface TypeConstants {
int CONSTRAINT_EXTENDS = 1; // Actual << Formal
int CONSTRAINT_SUPER = 2; // Actual >> Formal
- // Constants used to perform bound checks
- int OK = 0;
- int UNCHECKED = 1;
- int MISMATCH = 2;
+ // status of bound checks
+ public static enum BoundCheckStatus {
+ OK, NULL_PROBLEM, UNCHECKED, MISMATCH;
+ /** true if no problem or only a null problem. */
+ boolean isOKbyJLS() {
+ switch (this) {
+ case OK:
+ case NULL_PROBLEM:
+ return true;
+ default:
+ return false;
+ }
+ }
+ public BoundCheckStatus betterOf(BoundCheckStatus other) {
+ if (this.ordinal() < other.ordinal())
+ return this;
+ return other;
+ }
+ }
// Synthetics
char[] INIT = "<init>".toCharArray(); //$NON-NLS-1$
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 1693a6587..af32c7801 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/lookup/TypeSystem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
index 74e292b43..cbf69ab1e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeSystem.java
@@ -11,6 +11,8 @@
* Bug 434602 - Possible error with inferred null annotations leading to contradictory null annotations
* Bug 456497 - [1.8][null] during inference nullness from target type is lost against weaker hint from applicability analysis
* Bug 456487 - [1.8][null] @Nullable type variant of @NonNull-constrained type parameter causes grief
+ * Till Brychcy - Contribution for
+ * Bug 473713 - [1.8][null] Type mismatch: cannot convert from @NonNull A1 to @NonNull A1
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.lookup;
@@ -217,14 +219,19 @@ public class TypeSystem {
// Given a type, answer its unannotated aka naked prototype. This is also a convenient way to "register" a type with TypeSystem and have it id stamped.
public final TypeBinding getUnannotatedType(TypeBinding type) {
UnresolvedReferenceBinding urb = null;
- if (type.isUnresolvedType() && CharOperation.indexOf('$', type.sourceName()) > 0) {
+ if (type.isUnresolvedType()) {
urb = (UnresolvedReferenceBinding) type;
- boolean mayTolerateMissingType = this.environment.mayTolerateMissingType;
- this.environment.mayTolerateMissingType = true;
- try {
- type = BinaryTypeBinding.resolveType(type, this.environment, true); // to ensure unique id assignment (when enclosing type is parameterized, inner type is also)
- } finally {
- this.environment.mayTolerateMissingType = mayTolerateMissingType;
+ ReferenceBinding resolvedType = urb.resolvedType;
+ if (resolvedType != null) {
+ type = resolvedType;
+ } else if (CharOperation.indexOf('$', type.sourceName()) > 0) {
+ boolean mayTolerateMissingType = this.environment.mayTolerateMissingType;
+ this.environment.mayTolerateMissingType = true;
+ try {
+ type = BinaryTypeBinding.resolveType(type, this.environment, true); // to ensure unique id assignment (when enclosing type is parameterized, inner type is also)
+ } finally {
+ this.environment.mayTolerateMissingType = mayTolerateMissingType;
+ }
}
}
if (type.id == TypeIds.NoId) {
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
index 633240832..fa31d41af 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeVariableBinding.java
@@ -47,8 +47,10 @@ import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.Wildcard;
+import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching.CheckMode;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
+import org.eclipse.jdt.internal.compiler.lookup.TypeConstants.BoundCheckStatus;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.CallinCalloutBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.DependentTypeBinding;
import org.eclipse.objectteams.otdt.internal.core.compiler.lookup.ITeamAnchor;
@@ -117,38 +119,39 @@ public class TypeVariableBinding extends ReferenceBinding {
/**
* Returns true if the argument type satisfies all bounds of the type parameter
+ * @param location if non-null this may be used for reporting errors relating to null type annotations (if enabled)
*/
+ public TypeConstants.BoundCheckStatus boundCheck(Substitution substitution, TypeBinding argumentType, Scope scope, ASTNode location) {
//{ObjectTeams: added optional argument actualReceiverType:
- public int boundCheck(Substitution substitution, TypeBinding argumentType, Scope scope) {
- return boundCheck(substitution, argumentType, null, scope);
+ return boundCheck(substitution, argumentType, null, scope, location);
}
- public int boundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType, Scope scope) {
- int code = internalBoundCheck(substitution, argumentType, actualReceiverType, scope);
+ public TypeConstants.BoundCheckStatus boundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType, Scope scope, ASTNode location) {
+ TypeConstants.BoundCheckStatus code = internalBoundCheck(substitution, argumentType, actualReceiverType, scope, location);
// SH}
- if (code == TypeConstants.MISMATCH) {
+ if (code == BoundCheckStatus.MISMATCH) {
if (argumentType instanceof TypeVariableBinding && scope != null) {
TypeBinding bound = ((TypeVariableBinding)argumentType).firstBound;
if (bound instanceof ParameterizedTypeBinding) {
- int code2 = boundCheck(substitution, bound.capture(scope, -1, -1), scope); // no position needed as this capture will never escape this context
- return Math.min(code, code2);
+ BoundCheckStatus code2 = boundCheck(substitution, bound.capture(scope, -1, -1), scope, location); // no capture position needed as this capture will never escape this context
+ return code.betterOf(code2);
}
}
}
return code;
}
//{ObjectTeams: added optional argument actualReceiverType:
- private int internalBoundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType, Scope scope) {
+ private TypeConstants.BoundCheckStatus internalBoundCheck(Substitution substitution, TypeBinding argumentType, ReferenceBinding actualReceiverType, Scope scope, ASTNode location) {
// SH}
if (argumentType == TypeBinding.NULL || TypeBinding.equalsEquals(argumentType, this)) {
- return TypeConstants.OK;
+ return BoundCheckStatus.OK;
}
boolean hasSubstitution = substitution != null;
if (!(argumentType instanceof ReferenceBinding || argumentType.isArrayType()))
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
// special case for re-entrant source types (selection, code assist, etc)...
// can request additional types during hierarchy walk that are found as source types that also 'need' to connect their hierarchy
if (this.superclass == null)
- return TypeConstants.OK;
+ return BoundCheckStatus.OK;
if (argumentType.kind() == Binding.WILDCARD_TYPE) {
WildcardBinding wildcard = (WildcardBinding) argumentType;
@@ -156,30 +159,30 @@ public class TypeVariableBinding extends ReferenceBinding {
case Wildcard.EXTENDS :
TypeBinding wildcardBound = wildcard.bound;
if (TypeBinding.equalsEquals(wildcardBound, this))
- return TypeConstants.OK;
+ return BoundCheckStatus.OK;
boolean isArrayBound = wildcardBound.isArrayType();
if (!wildcardBound.isInterface()) {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass;
if (substitutedSuperType.id != TypeIds.T_JavaLangObject) {
if (isArrayBound) {
if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope))
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
} else {
TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType);
if (match != null) {
if (substitutedSuperType.isProvablyDistinct(match)) {
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
} else {
match = substitutedSuperType.findSuperTypeOriginatingFrom(wildcardBound);
if (match != null) {
if (match.isProvablyDistinct(wildcardBound)) {
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
} else {
if (denotesRelevantSuperClass(wildcardBound) && denotesRelevantSuperClass(substitutedSuperType)) {
// non-object real superclass should have produced a valid 'match' above
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
}
}
@@ -191,15 +194,15 @@ public class TypeVariableBinding extends ReferenceBinding {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i];
if (isArrayBound) {
if (!wildcardBound.isCompatibleWith(substitutedSuperType, scope))
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
} else {
TypeBinding match = wildcardBound.findSuperTypeOriginatingFrom(substitutedSuperType);
if (match != null) {
if (substitutedSuperType.isProvablyDistinct(match)) {
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
} else if (mustImplement) {
- return TypeConstants.MISMATCH; // cannot be extended further to satisfy missing bounds
+ return BoundCheckStatus.MISMATCH; // cannot be extended further to satisfy missing bounds
}
}
@@ -210,19 +213,21 @@ public class TypeVariableBinding extends ReferenceBinding {
// if the wildcard is lower-bounded by a type variable that has no relevant upper bound there's nothing to check here (bug 282152):
if (wildcard.bound.isTypeVariable() && ((TypeVariableBinding)wildcard.bound).superclass.id == TypeIds.T_JavaLangObject)
break;
- return boundCheck(substitution, wildcard.bound, scope);
+ return boundCheck(substitution, wildcard.bound, scope, location);
case Wildcard.UNBOUND :
break;
}
- return TypeConstants.OK;
+ return BoundCheckStatus.OK;
}
boolean unchecked = false;
+ boolean checkNullAnnotations = scope.environment().usesNullTypeAnnotations();
+ boolean haveReportedNullProblem = false;
if (this.superclass.id != TypeIds.T_JavaLangObject) {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superclass) : this.superclass;
if (TypeBinding.notEquals(substitutedSuperType, argumentType)) {
if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) {
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType);
if (match != null){
@@ -231,6 +236,12 @@ public class TypeVariableBinding extends ReferenceBinding {
unchecked = true;
}
}
+ if (location != null && checkNullAnnotations) {
+ if (NullAnnotationMatching.analyse(this, argumentType, substitutedSuperType, substitution, -1, CheckMode.BOUND_CHECK).isAnyMismatch()) {
+ scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location);
+ haveReportedNullProblem = true;
+ }
+ }
}
//{ObjectTeams: type check <B base R>
if (this.roletype != null) {
@@ -239,14 +250,14 @@ public class TypeVariableBinding extends ReferenceBinding {
TypeVariableBinding argTypeVar = (TypeVariableBinding) argumentType;
if (argTypeVar.roletype != null) {
if (this.roletype.isCompatibleWith(argTypeVar.roletype))
- return TypeConstants.OK;
+ return BoundCheckStatus.OK;
else
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
}
TypeBinding substitutedRoleType = hasSubstitution ? Scope.substitute(substitution, this.roletype) : this.roletype;
if (!substitutedRoleType.isRole())
- return TypeConstants.MISMATCH; // actually roletype was already checked for role-ness.
+ return BoundCheckStatus.MISMATCH; // actually roletype was already checked for role-ness.
// FIXME(SH): handle arrays? enums? nested generics?
if (actualReceiverType != null)
substitutedRoleType = TeamModel.strengthenRoleType(actualReceiverType, substitutedRoleType);
@@ -254,21 +265,21 @@ public class TypeVariableBinding extends ReferenceBinding {
for (ReferenceBinding aRoletype : roletypes) {
ReferenceBinding basetype = aRoletype.baseclass(); // non-null by definition of getBoundDescendants()
if (TypeBinding.equalsEquals(basetype, argumentType) || argumentType.isCompatibleWith(basetype)) {
- return TypeConstants.OK;
+ return BoundCheckStatus.OK;
}
}
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
// check value-dependent type-variable:
if (this.anchors != null && substitution != null) {
ITeamAnchor anchor = substitution.substituteAnchor(this.anchors[0], 0); // TODO(SH): support multiple anchors
if (anchor != null) {
if (!(argumentType instanceof DependentTypeBinding)) {
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
} else {
ITeamAnchor argAnchor = ((DependentTypeBinding)argumentType).getAnchor();
if (!argAnchor.hasSameBestNameAs(anchor))
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
}
}
@@ -277,7 +288,7 @@ public class TypeVariableBinding extends ReferenceBinding {
TypeBinding substitutedSuperType = hasSubstitution ? Scope.substitute(substitution, this.superInterfaces[i]) : this.superInterfaces[i];
if (TypeBinding.notEquals(substitutedSuperType, argumentType)) {
if (!argumentType.isCompatibleWith(substitutedSuperType, scope)) {
- return TypeConstants.MISMATCH;
+ return BoundCheckStatus.MISMATCH;
}
TypeBinding match = argumentType.findSuperTypeOriginatingFrom(substitutedSuperType);
if (match != null){
@@ -286,15 +297,21 @@ public class TypeVariableBinding extends ReferenceBinding {
unchecked = true;
}
}
+ if (location != null && checkNullAnnotations) {
+ if (NullAnnotationMatching.analyse(this, argumentType, substitutedSuperType, substitution, -1, CheckMode.BOUND_CHECK).isAnyMismatch()) {
+ scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location);
+ haveReportedNullProblem = true;
+ }
+ }
}
- long nullTagBits = NullAnnotationMatching.validNullTagBits(this.tagBits);
- if (nullTagBits != 0) {
- long argBits = NullAnnotationMatching.validNullTagBits(argumentType.tagBits);
- if (argBits != nullTagBits) {
-// System.err.println("TODO(stephan): issue proper error: bound conflict at "+String.valueOf(this.declaringElement.readableName()));
- }
+ if (location != null && checkNullAnnotations && !haveReportedNullProblem) {
+ long nullBits = this.tagBits & TagBits.AnnotationNullMASK;
+ if (nullBits != 0 && nullBits != (argumentType.tagBits & TagBits.AnnotationNullMASK)) {
+ scope.problemReporter().nullityMismatchTypeArgument(this, argumentType, location);
+ haveReportedNullProblem = true;
+ }
}
- return unchecked ? TypeConstants.UNCHECKED : TypeConstants.OK;
+ return unchecked ? BoundCheckStatus.UNCHECKED : haveReportedNullProblem ? BoundCheckStatus.NULL_PROBLEM : BoundCheckStatus.OK;
}
boolean denotesRelevantSuperClass(TypeBinding type) {
@@ -1133,4 +1150,21 @@ public class TypeVariableBinding extends ReferenceBinding {
public boolean acceptsNonNullDefault() {
return false;
}
+
+ @Override
+ public long updateTagBits() {
+ if (!this.inRecursiveFunction) {
+ this.inRecursiveFunction = true;
+ try {
+ if (this.superclass != null)
+ this.tagBits |= this.superclass.updateTagBits();
+ if (this.superInterfaces != null)
+ for (TypeBinding superIfc : this.superInterfaces)
+ this.tagBits |= superIfc.updateTagBits();
+ } finally {
+ this.inRecursiveFunction = false;
+ }
+ }
+ return super.updateTagBits();
+ }
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java
index 3ff8e77bb..1f859b702 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/WildcardBinding.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2014 IBM Corporation and others.
+ * Copyright (c) 2005, 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
@@ -994,4 +994,22 @@ public class WildcardBinding extends ReferenceBinding {
public boolean acceptsNonNullDefault() {
return false;
}
+
+ @Override
+ public long updateTagBits() {
+ if (!this.inRecursiveFunction) {
+ this.inRecursiveFunction = true;
+ try {
+ if (this.bound != null)
+ this.tagBits |= this.bound.updateTagBits();
+ if (this.otherBounds != null) {
+ for (int i = 0, length = this.otherBounds.length; i < length; i++)
+ this.tagBits |= this.otherBounds[i].updateTagBits();
+ }
+ } finally {
+ this.inRecursiveFunction = false;
+ }
+ }
+ return super.updateTagBits();
+ }
}
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 689813bb3..59c3bcd1d 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
@@ -462,6 +462,7 @@ public static int getIrritant(int problemID) {
case IProblem.IllegalReturnNullityRedefinition:
case IProblem.IllegalReturnNullityRedefinitionFreeTypeVariable:
case IProblem.IllegalRedefinitionToNonNullParameter:
+ case IProblem.IllegalRedefinitionOfTypeVariable:
case IProblem.IllegalDefinitionToNonNullParameter:
case IProblem.ParameterLackingNullableAnnotation:
case IProblem.CannotImplementIncompatibleNullness:
@@ -9532,9 +9533,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:
@@ -9543,8 +9541,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
}
}
}
@@ -13668,9 +13668,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;
}
@@ -13721,9 +13719,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;
}
@@ -13752,7 +13748,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;
}
@@ -13907,7 +13903,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 {
@@ -13919,14 +13915,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;
@@ -14046,13 +14042,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,
@@ -14114,12 +14110,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];
}
}
@@ -14261,6 +14257,27 @@ public void nullityMismatchTypeArgument(TypeBinding typeVariable, TypeBinding ty
location.sourceEnd);
}
+public void cannotRedefineTypeArgumentNullity(TypeBinding typeVariable, Binding superElement, ASTNode location) {
+ String[] arguments = new String[2];
+ String[] shortArguments = new String[2];
+ arguments[0] = String.valueOf(typeVariable.nullAnnotatedReadableName(this.options, false));
+ shortArguments[0] = String.valueOf(typeVariable.nullAnnotatedReadableName(this.options, true));
+ if (superElement instanceof MethodBinding) {
+ ReferenceBinding declaringClass = ((MethodBinding) superElement).declaringClass;
+ arguments[1] = String.valueOf(CharOperation.concat(declaringClass.readableName(), superElement.shortReadableName(), '.'));
+ shortArguments[1] = String.valueOf(CharOperation.concat(declaringClass.shortReadableName(), superElement.shortReadableName(), '.'));
+ } else {
+ arguments[1] = String.valueOf(superElement.readableName());
+ shortArguments[1] = String.valueOf(superElement.shortReadableName());
+ }
+ this.handle(
+ IProblem.IllegalRedefinitionOfTypeVariable,
+ arguments,
+ shortArguments,
+ location.sourceStart,
+ location.sourceEnd);
+}
+
public void implicitObjectBoundNoNullDefault(TypeReference reference) {
this.handle(IProblem.ImplicitObjectBoundNoNullDefault,
NoArgument, NoArgument,
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index c1fdaa424..353784da1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2000, 2014 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
@@ -833,6 +833,7 @@
972 = Illegal redefinition of parameter {0}, inherited method from {1} declares this parameter as ''{2}'' (mismatching null constraints)
973 = Contradictory null annotations: function type was inferred as ''{2} ({4})'', but only one of ''@{0}'' and ''@{1}'' can be effective at any location
974 = The return type is incompatible with the free type variable ''{1}'' returned from {0} (mismatching null constraints)
+975 = Cannot redefine null constraints of type variable ''{0}'' declared in ''{1}''
# Java 8
1001 = Syntax error, modifiers and annotations are not allowed for the lambda parameter {0} as its type is elided

Back to the top