Resolved Bug 334457 - [compiler][null] check compatibility of inherited null contracts
diff --git a/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java b/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java
index 18c076f..78fd279 100644
--- a/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java
+++ b/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java
@@ -46,6 +46,7 @@
import org.eclipse.jdt.internal.compiler.flow.InsideSubRoutineFlowContext;
import org.eclipse.jdt.internal.compiler.flow.UnconditionalFlowInfo;
import org.eclipse.jdt.internal.compiler.impl.IrritantSet;
+import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
@@ -598,6 +599,11 @@
<- after
void checkAgainstInheritedMethods(MethodBinding currentMethod, MethodBinding[] methods, int length, MethodBinding[] allInheritedMethods);
+ void checkNullContractInheritance(MethodBinding currentMethod, MethodBinding[] methods, int length)
+ <- after
+ void checkConcreteInheritedMethod(MethodBinding concreteMethod, MethodBinding[] abstractMethods)
+ with { currentMethod <- concreteMethod, methods <- abstractMethods, length <- abstractMethods.length }
+
void checkNullContractInheritance(MethodBinding currentMethod, MethodBinding[] methods, int length) {
// TODO: change traversal: process all methods at once!
for (int i = length; --i >= 0;)
@@ -609,23 +615,31 @@
long inheritedBits = inheritedMethod.getTagBits();
long currentBits = currentMethod.getTagBits();
LookupEnvironment environment = this.getEnvironment();
+ AbstractMethodDeclaration srcMethod = null;
+ if (getType().isSame(currentMethod.getDeclaringClass())) // is currentMethod from the current type?
+ srcMethod = currentMethod.sourceMethod();
// return type:
if ((inheritedBits & TagBits.AnnotationNonNull) != 0) {
long currentNullBits = currentBits & (TagBits.AnnotationNonNull|TagBits.AnnotationNullable);
- if (currentNullBits != TagBits.AnnotationNonNull) {
- AbstractMethodDeclaration methodDecl = currentMethod.sourceMethod();
- getType().problemReporter().illegalReturnRedefinition(methodDecl, inheritedMethod,
- environment.getNonNullAnnotationName());
+ if (currentNullBits != TagBits.AnnotationNonNull) {
+ if (srcMethod != null) {
+ getType().problemReporter().illegalReturnRedefinition(srcMethod, inheritedMethod,
+ environment.getNonNullAnnotationName());
+ } else {
+ getType().problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ return;
+ }
}
}
// parameters:
- Argument[] currentArguments = currentMethod.sourceMethod().getArguments();
+ Argument[] currentArguments = srcMethod == null ? null : srcMethod.getArguments();
if (inheritedMethod.parameterNonNullness != null) {
// inherited method has null-annotations, check compatibility:
for (int i = 0; i < inheritedMethod.parameterNonNullness.length; i++) {
+ Argument currentArgument = currentArguments == null ? null : currentArguments[i];
Boolean inheritedNonNullNess = inheritedMethod.parameterNonNullness[i];
Boolean currentNonNullNess = (currentMethod.parameterNonNullness == null)
@@ -640,23 +654,30 @@
} else {
annotationName = environment.getNullableAnnotationName();
}
-
- getType().problemReporter().parameterLackingNonNullAnnotation(
- currentArguments[i],
- inheritedMethod.getDeclaringClass(),
- needNonNull,
- annotationName);
- continue;
+ if (currentArgument != null) {
+ getType().problemReporter().parameterLackingNonNullAnnotation(
+ currentArgument,
+ inheritedMethod.getDeclaringClass(),
+ needNonNull,
+ annotationName);
+ continue;
+ } else {
+ getType().problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ break;
+ }
}
}
if (inheritedNonNullNess != Boolean.TRUE) { // super parameter is not restricted to @NonNull
if (currentNonNullNess == Boolean.TRUE) { // current parameter is restricted to @NonNull
- getType().problemReporter().illegalRedefinitionToNonNullParameter(
- currentArguments[i],
+ if (currentArgument != null)
+ getType().problemReporter().illegalRedefinitionToNonNullParameter(
+ currentArgument,
inheritedMethod.getDeclaringClass(),
inheritedNonNullNess == null
? null
: environment.getNullableAnnotationName());
+ else
+ getType().problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
}
}
}
@@ -664,10 +685,15 @@
// super method has no annotations but current has
for (int i = 0; i < currentMethod.parameterNonNullness.length; i++) {
if (currentMethod.parameterNonNullness[i] == Boolean.TRUE) { // tightening from unconstrained to @NonNull
- getType().problemReporter().illegalRedefinitionToNonNullParameter(
- currentArguments[i],
- inheritedMethod.getDeclaringClass(),
- null);
+ if (currentArguments != null) {
+ getType().problemReporter().illegalRedefinitionToNonNullParameter(
+ currentArguments[i],
+ inheritedMethod.getDeclaringClass(),
+ null);
+ } else {
+ getType().problemReporter().cannotImplementIncompatibleNullness(currentMethod, inheritedMethod);
+ break;
+ }
}
}
}
@@ -730,6 +756,8 @@
long computeTypeAnnotationTagBits() -> long getAnnotationTagBits();
+ boolean isSame(Object other) -> boolean equals(Object other);
+
private TypeBinding nullnessDefaultAnnotation;
private int nullnessDefaultInitialized = 0; // 0: nothing; 1: type; 2: package
@@ -1211,6 +1239,8 @@
*/
protected class ProblemReporter playedBy ProblemReporter {
+ ReferenceContext getReferenceContext() -> get ReferenceContext referenceContext;
+
@SuppressWarnings("decapsulation")
void handle(int problemId, String[] problemArguments, String[] messageArguments, int severity,
int problemStartPosition, int problemEndPosition)
@@ -1394,6 +1424,31 @@
String[] args = { new String(CharOperation.concatWith(nullAnnotationName, '.')) };
this.handle(IProblem.MissingNullAnnotationType, args, args, 0, 0);
}
+
+ public void cannotImplementIncompatibleNullness(MethodBinding currentMethod, MethodBinding inheritedMethod) {
+ ReferenceContext ctx = getReferenceContext();
+ int sourceStart = 0, sourceEnd = 0;
+ if (ctx instanceof TypeDeclaration) {
+ sourceStart = ((TypeDeclaration) ctx).sourceStart;
+ sourceEnd = ((TypeDeclaration) ctx).sourceEnd;
+ }
+ String[] problemArguments = {
+ new String(currentMethod.readableName()),
+ new String(currentMethod.getDeclaringClass().readableName()),
+ new String(inheritedMethod.getDeclaringClass().readableName())
+ };
+ String[] messageArguments = {
+ new String(currentMethod.shortReadableName()),
+ new String(currentMethod.getDeclaringClass().shortReadableName()),
+ new String(inheritedMethod.getDeclaringClass().shortReadableName())
+ };
+ this.handle(
+ IProblem.CannotImplementIncompatibleNullness,
+ problemArguments,
+ messageArguments,
+ sourceStart,
+ sourceEnd);
+ }
// NOTE: adaptation of toString() omitted
}
diff --git a/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/Constants.java b/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/Constants.java
index 8487411..4f464f2 100644
--- a/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/Constants.java
+++ b/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/Constants.java
@@ -70,6 +70,8 @@
int PotentialNullMessageSendReference = Internal + 919;
/** @since 3.7 */
int RedundantNullCheckOnNonNullMessageSend = Internal + 920;
+ /** @since 3.7 */
+ int CannotImplementIncompatibleNullness = Internal + 921;
}
/** Translate from a nullness annotation to the corresponding tag bit or 0L. */
diff --git a/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties b/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties
index 4871e6c..a4b0918 100644
--- a/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties
+++ b/contrib/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties
@@ -22,3 +22,4 @@
918 = Missing null annotation: inherited method from {1} declares this parameter as @{2}
919 = Potential null pointer access: The method {0} may return null
920 = Redundant null check: The method {0} cannot return null
+921 = The method {0} from class {1} cannot implement the corresponding method from type {2} due to incompatible nullness constraints.
\ No newline at end of file