More warnings (under existing irritants):
- directly dereferencing nullable message send results
- comparing nonnull message send result against null
Cleanup messages: don't terminate with full stop.
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java
index 8bd42dc..25d37cc 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/CompilerAdaptation.java
@@ -53,6 +53,7 @@
import base org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import base org.eclipse.jdt.internal.compiler.ast.Annotation;
import base org.eclipse.jdt.internal.compiler.ast.Assignment;
+import base org.eclipse.jdt.internal.compiler.ast.EqualExpression;
import base org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import base org.eclipse.jdt.internal.compiler.ast.MessageSend;
import base org.eclipse.jdt.internal.compiler.ast.ReturnStatement;
@@ -132,15 +133,9 @@
@SuppressWarnings("basecall")
callin int nullStatus(FlowInfo flowInfo) {
- MethodBinding binding = this.getBinding();
- if (binding.isValidBinding()) {
- // try to retrieve null status of this message send from an annotation of the called method:
- long tagBits = binding.getTagBits();
- if ((tagBits & TagBits.AnnotationNonNull) != 0)
- return FlowInfo.NON_NULL;
- if ((tagBits & TagBits.AnnotationNullable) != 0)
- return FlowInfo.POTENTIALLY_NULL;
- }
+ int status = getNullStatus();
+ if (status != FlowInfo.UNKNOWN)
+ return status;
return base.nullStatus(flowInfo);
}
@@ -168,6 +163,61 @@
}
}
}
+
+ checkNPE <- after checkNPE;
+ /** Detect and signal directly dereferencing a nullable message send result. */
+ void checkNPE(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo) {
+ if (getNullStatus() == FlowInfo.POTENTIALLY_NULL)
+ scope.problemReporter().messageSendPotentialNullReference(getBinding(), this);
+ }
+
+ protected int getNullStatus() {
+ MethodBinding binding = this.getBinding();
+ if (binding.isValidBinding()) {
+ // try to retrieve null status of this message send from an annotation of the called method:
+ long tagBits = binding.getTagBits();
+ if ((tagBits & TagBits.AnnotationNonNull) != 0)
+ return FlowInfo.NON_NULL;
+ if ((tagBits & TagBits.AnnotationNullable) != 0)
+ return FlowInfo.POTENTIALLY_NULL;
+ }
+ return FlowInfo.UNKNOWN;
+ }
+ }
+
+ protected class EqualExpression playedBy EqualExpression {
+
+ MessageSend getLeftMessage() -> get Expression left
+ with { result <- (left instanceof MessageSend) ? (MessageSend) left : null }
+ int getLeftNullStatus(FlowInfo info) -> get Expression left
+ with { result <- left.nullStatus(info) }
+ MessageSend getRightMessage() -> get Expression right
+ with { result <- (right instanceof MessageSend) ? (MessageSend) right : null }
+ int getRightNullStatus(FlowInfo info)-> get Expression right
+ with { result <- right.nullStatus(info) }
+
+
+ checkNullComparison <- before checkNullComparison;
+
+ /** Detect and signal when comparing a non-null message send against null. */
+ void checkNullComparison(BlockScope scope, FlowContext flowContext, FlowInfo flowInfo,
+ FlowInfo initsWhenTrue, FlowInfo initsWhenFalse)
+ {
+ MessageSend leftMessage = getLeftMessage();
+ if ( leftMessage != null
+ && leftMessage.getNullStatus() == FlowInfo.NON_NULL
+ && getRightNullStatus(flowInfo) == FlowInfo.NULL)
+ {
+ scope.problemReporter().messageSendRedundantCheckOnNonNull(leftMessage.getBinding(), leftMessage);
+ }
+ MessageSend rightMessage = getRightMessage();
+ if ( rightMessage != null
+ && rightMessage.getNullStatus() == FlowInfo.NON_NULL
+ && getLeftNullStatus(flowInfo) == FlowInfo.NULL)
+ {
+ scope.problemReporter().messageSendRedundantCheckOnNonNull(rightMessage.getBinding(), rightMessage);
+ }
+ }
}
/** Analyse the expression within a return statement, check against method return annotation. */
@@ -296,6 +346,7 @@
boolean isStatic() -> boolean isStatic();
boolean isValidBinding() -> boolean isValidBinding();
AbstractMethodDeclaration sourceMethod()-> AbstractMethodDeclaration sourceMethod();
+ char[] readableName() -> char[] readableName();
/** After method verifier has finished, fill in missing nullness values from the default. */
protected void fillInDefaultNullness(long defaultNullness) {
@@ -333,6 +384,7 @@
void fillInDefaultNullNess() <- after void checkMethods();
void checkNullContractInheritance(MethodBinding currentMethod, MethodBinding[] methods, int length) {
+ // TODO: change traversal: process all methods at once!
for (int i = length; --i >= 0;)
if (!currentMethod.isStatic() && !methods[i].isStatic())
checkNullContractInheritance(currentMethod, methods[i]);
@@ -698,6 +750,10 @@
case IProblem.NonNullParameterInsufficientInfo:
case IProblem.NonNullReturnInsufficientInfo:
return CompilerOptions.NullContractInsufficientInfo;
+ case IProblem.PotentialNullMessageSendReference:
+ return org.eclipse.jdt.internal.compiler.impl.CompilerOptions.PotentialNullReference;
+ case IProblem.RedundantNullCheckOnNonNullMessageSend:
+ return org.eclipse.jdt.internal.compiler.impl.CompilerOptions.RedundantNullCheck;
}
return 0;
}
@@ -771,8 +827,27 @@
new String[] { new String(declaringClass.readableName()), CharOperation.toString(nonNullAnnotationName)},
new String[] { new String(declaringClass.shortReadableName()), new String(nonNullAnnotationName[nonNullAnnotationName.length-1])},
methodDecl.sourceStart,
- methodDecl.sourceEnd);
- }
+ methodDecl.sourceEnd);
+ }
+ public void messageSendPotentialNullReference(MethodBinding method, ASTNode location) {
+ String[] arguments = new String[] {new String(method.readableName())};
+ this.handle(
+ IProblem.PotentialNullMessageSendReference,
+ arguments,
+ arguments,
+ location.sourceStart,
+ location.sourceEnd);
+ }
+ public void messageSendRedundantCheckOnNonNull(MethodBinding method, ASTNode location) {
+ String[] arguments = new String[] {new String(method.readableName()) };
+ this.handle(
+ IProblem.RedundantNullCheckOnNonNullMessageSend,
+ arguments,
+ arguments,
+ location.sourceStart,
+ location.sourceEnd);
+ }
+
public void missingNullAnnotationType(char[][] nullAnnotationName) {
String[] args = { new String(CharOperation.concatWith(nullAnnotationName, '.')) };
this.handle(IProblem.MissingNullAnnotationType, args, args, 0, 0);
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/IConstants.java b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/IConstants.java
index 6a95761..d98a8c5 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/IConstants.java
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/IConstants.java
@@ -63,5 +63,9 @@
int IllegalRedefinitionToNonNullParameter = MethodRelated + 892;
/** @since 3.7 */
int IllegalDefinitionToNonNullParameter = MethodRelated + 893;
+ /** @since 3.7 */
+ int PotentialNullMessageSendReference = Internal + 894;
+ /** @since 3.7 */
+ int RedundantNullCheckOnNonNullMessageSend = 895;
}
}
diff --git a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties
index 11576f7..a4bd5f0 100644
--- a/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties
+++ b/plugins/org.eclipse.objectteams.jdt.nullity/src/org/eclipse/objectteams/internal/jdt/nullity/problem_messages.properties
@@ -11,17 +11,19 @@
### addition for /org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
### NULL ANNOTATIONS
-880 = Null contract violation: returning null from a method declared as @{0}.
-881 = Null contract violation: return value can be null but method is declared as @{0}.
-882 = Potential null contract violation: insufficient nullness information regarding return value while the method is declared as @{0}.
-883 = Null contract violation: passing null to a parameter declared as @{0}.
-884 = Null contract violation: potentially passing null to a parameter declared as @{0}.
-885 = Potential null contract violation: insufficient nullness information regarding a value that is passed to a parameter declared as @{0}.
-886 = Null contract violation: assigning null to local variable {0}, which is declared as @{1}.
-887 = Null contract violation: potentially assigning null to local variable {0}, which is declared as @{1}.
-888 = Potential null contract violation: insufficient nullness information regarding a value that is assigned to local variable {0}, which is declared as @{1}.
-889 = Buildpath problem: emulation of type {0} is requested (for null annotations) but a type of this name exists on the build path.
-890 = Buildpath problem: the type {0} which is configured as a null annotation type cannot be resolved.
-891 = Cannot relax null contract for method return, inherited method from {0} is declared as @{1}.
-892 = Cannot tighten null contract for parameter {0}, inherited method from {1} declares this parameter as @{2}.
-893 = Cannot tighten null contract for parameter {0}, inherited method from {1} does not constrain this parameter.
+880 = Null contract violation: returning null from a method declared as @{0}
+881 = Null contract violation: return value can be null but method is declared as @{0}
+882 = Potential null contract violation: insufficient nullness information regarding return value while the method is declared as @{0}
+883 = Null contract violation: passing null to a parameter declared as @{0}
+884 = Null contract violation: potentially passing null to a parameter declared as @{0}
+885 = Potential null contract violation: insufficient nullness information regarding a value that is passed to a parameter declared as @{0}
+886 = Null contract violation: assigning null to local variable {0}, which is declared as @{1}
+887 = Null contract violation: potentially assigning null to local variable {0}, which is declared as @{1}
+888 = Potential null contract violation: insufficient nullness information regarding a value that is assigned to local variable {0}, which is declared as @{1}
+889 = Buildpath problem: emulation of type {0} is requested (for null annotations) but a type of this name exists on the build path
+890 = Buildpath problem: the type {0} which is configured as a null annotation type cannot be resolved
+891 = Cannot relax null contract for method return, inherited method from {0} is declared as @{1}
+892 = Cannot tighten null contract for parameter {0}, inherited method from {1} declares this parameter as @{2}
+893 = Cannot tighten null contract for parameter {0}, inherited method from {1} does not constrain this parameter
+894 = Potential null pointer access: The method {0} may return null
+895 = Redundant null check: The method {0} cannot return null
\ No newline at end of file