apply deferred checking also in assignment local-decl to fix their analysis inside loops etc.
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 a107cb6..6e84e31 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
@@ -115,10 +115,14 @@
// ======================= Statement level analysis ============================
@SuppressWarnings({"abstractrelevantrole", "hidden-lifting-problem"}) // due to abstractness of this role failed lifting could theoretically block callin triggers
- @Instantiation(InstantiationPolicy.ALWAYS)
protected abstract class Statement playedBy Statement {
abstract Expression getExpression();
+ FlowContext flowContext;
+ void storeFlowContext(BlockScope scope, FlowContext flowContext) {
+ this.flowContext = flowContext;
+ }
+
// use custom hook from JDT/Core (https://bugs.eclipse.org/335093)
// TODO(SH): should calls to this method be guarded by "if (flowInfo.reachMode() == FlowInfo.REACHABLE)"?
// otherwise we'll proceed with incomplete information (normal null analysis avoids dead code).
@@ -132,7 +136,7 @@
&& (local.tagBits & TagBits.AnnotationNonNull) != 0
&& nullStatus != FlowInfo.NON_NULL)
{
- currentScope.problemReporter().nullityMismatch(getExpression(), local.type,
+ recordNullityMismatch(currentScope, flowContext, getExpression(), local.type,
nullStatus, currentScope.environment().getNonNullAnnotationName());
nullStatus=FlowInfo.NON_NULL;
}
@@ -140,15 +144,17 @@
}
}
- @Instantiation(InstantiationPolicy.ALWAYS)
protected class Assignment extends Statement playedBy Assignment {
/** Wire required method of super class. */
- Expression getExpression() -> get Expression expression;
+ Expression getExpression() -> get Expression expression;
+ void storeFlowContext(BlockScope scope, FlowContext flowContext)
+ <- before FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo);
}
- @Instantiation(InstantiationPolicy.ALWAYS)
protected class LocalDeclaration extends Statement playedBy LocalDeclaration {
/** Wire required method of super class. */
Expression getExpression() -> get Expression initialization;
+ void storeFlowContext(BlockScope scope, FlowContext flowContext)
+ <- before FlowInfo analyseCode(BlockScope currentScope, FlowContext flowContext, FlowInfo flowInfo);
}
protected abstract class MessageSendish {
@@ -161,49 +167,48 @@
MethodBinding methodBinding = getBinding();
Expression[] arguments = getArguments();
if (arguments != null && methodBinding.parameterNonNullness != null) {
- int length = arguments.length;
- argumentLoop:
- for (int i = 0; i < length; i++) {
- int nullStatus = arguments[i].nullStatus(flowInfo); // slight loss of precision: should also use the null info from the receiver.
- if ( nullStatus != FlowInfo.NON_NULL
- && methodBinding.parameterNonNullness[i] != null)
- {
- if (methodBinding.parameterNonNullness[i].booleanValue()) // if @NonNull is required
- {
- char[][] annotationName = currentScope.environment().getNonNullAnnotationName();
- LocalVariableBinding local = arguments[i].localVariableBinding();
- if (local != null) {
- while (flowContext != null) {
- // some flow contexts implement deferred checking, should we participate in that?
- if (flowContext instanceof org.eclipse.jdt.internal.compiler.flow.FinallyFlowContext) {
- // cf. decision structure inside FinallyFlowContext.recordUsingNullReference(..)
- if (nullStatus == FlowInfo.UNKNOWN ||
- ((flowContext.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0 && nullStatus != FlowInfo.NULL))
- {
- // deferred reporting
- recordNullReference((org.eclipse.jdt.internal.compiler.flow.FinallyFlowContext)flowContext,
- arguments[i], methodBinding.getParameters()[i], ConditionalFlowContext.ASSIGN_TO_NONNULL);
- continue argumentLoop;
- }
- }
- else if (flowContext instanceof org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext) {
- // deferred reporting
- recordNullReference((org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext)flowContext,
- arguments[i], methodBinding.getParameters()[i], ConditionalFlowContext.ASSIGN_TO_NONNULL);
- continue argumentLoop;
- }
- flowContext = flowContext.parent;
- }
- }
- // other cases report immediately
- currentScope.problemReporter().nullityMismatch(arguments[i], methodBinding.getParameters()[i], nullStatus, annotationName);
- }
+ char[][] annotationName = currentScope.environment().getNonNullAnnotationName();
+ for (int i = 0; i < arguments.length; i++) {
+ if (methodBinding.parameterNonNullness[i] == Boolean.TRUE) {
+ TypeBinding expectedType = methodBinding.getParameters()[i];
+ Expression argument = arguments[i];
+ int nullStatus = argument.nullStatus(flowInfo); // slight loss of precision: should also use the null info from the receiver.
+ if (nullStatus != FlowInfo.NON_NULL) // if required non-null is not provided
+ recordNullityMismatch(currentScope, flowContext, argument, expectedType, nullStatus, annotationName);
}
}
}
}
}
+ void recordNullityMismatch(BlockScope currentScope, FlowContext flowContext, Expression expression, TypeBinding expectedType, int nullStatus, char[][] annotationName) {
+ if (expression.localVariableBinding() != null) { // flowContext cannot yet handle non-localvar expressions (e.g., fields)
+ while (flowContext != null) {
+ // some flow contexts implement deferred checking, should we participate in that?
+ if (flowContext instanceof org.eclipse.jdt.internal.compiler.flow.FinallyFlowContext) {
+ // cf. decision structure inside FinallyFlowContext.recordUsingNullReference(..)
+ if (nullStatus == FlowInfo.UNKNOWN ||
+ ((flowContext.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0 && nullStatus != FlowInfo.NULL))
+ {
+ // deferred reporting
+ recordNullReference((org.eclipse.jdt.internal.compiler.flow.FinallyFlowContext)flowContext,
+ expression, expectedType, ConditionalFlowContext.ASSIGN_TO_NONNULL);
+ return;
+ }
+ }
+ else if (flowContext instanceof org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext) {
+ // deferred reporting
+ recordNullReference((org.eclipse.jdt.internal.compiler.flow.LoopingFlowContext)flowContext,
+ expression, expectedType, ConditionalFlowContext.ASSIGN_TO_NONNULL);
+ return;
+ }
+ flowContext = flowContext.parent;
+ }
+ }
+ // other cases report immediately
+ currentScope.problemReporter().nullityMismatch(expression, expectedType, nullStatus, annotationName);
+ }
+
/** Analyse argument expressions as part of a MessageSend, check against method parameter annotation. */
@Instantiation(InstantiationPolicy.ALWAYS)
protected class MessageSend extends MessageSendish playedBy MessageSend {