package org.eclipse.jdt.internal.compiler.flow;
import java.util.ArrayList;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FakedTrackingVariable;
import org.eclipse.jdt.internal.compiler.ast.LabeledStatement;
import org.eclipse.jdt.internal.compiler.ast.LambdaExpression;
import org.eclipse.jdt.internal.compiler.ast.NullAnnotationMatching;
import org.eclipse.jdt.internal.compiler.ast.Reference;
import org.eclipse.jdt.internal.compiler.ast.SingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.SubRoutineStatement;
import org.eclipse.jdt.internal.compiler.ast.ThrowStatement;
import org.eclipse.jdt.internal.compiler.ast.TryStatement;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.codegen.BranchLabel;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.CatchParameterBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.lookup.VariableBinding;
@SuppressWarnings({"rawtypes", "unchecked"})
public class FlowContext implements TypeConstants {
public final static FlowContext NotContinuableContext = new FlowContext(null, null, true);
public ASTNode associatedNode;
public FlowContext parent;
public FlowInfo initsOnFinally;
public int conditionalLevel = -1;
public int tagBits;
public TypeBinding[][] providedExpectedTypes = null;
private Reference[] nullCheckedFieldReferences = null;
private int[] timesToLiveForNullCheckInfo = null;
public static final int DEFER_NULL_DIAGNOSTIC = 0x1;
public static final int PREEMPT_NULL_DIAGNOSTIC = 0x2;
public static final int INSIDE_NEGATION = 0x4;
public static final int HIDE_NULL_COMPARISON_WARNING = 0x1000;
public static final int HIDE_NULL_COMPARISON_WARNING_MASK = 0xF000;
public static final int CAN_ONLY_NULL_NON_NULL = 0x0000;
public static final int CAN_ONLY_NULL = 0x0001;
public static final int CAN_ONLY_NON_NULL = 0x0002;
public static final int MAY_NULL = 0x0003;
public final static int ASSIGN_TO_NONNULL = 0x0080;
public static final int IN_UNBOXING = 0x0010;
public static final int EXIT_RESOURCE = 0x0800;
public static final int CHECK_MASK = 0x00FF;
public static final int IN_COMPARISON_NULL = 0x0100;
public static final int IN_COMPARISON_NON_NULL = 0x0200;
public static final int IN_ASSIGNMENT = 0x0300;
public static final int IN_INSTANCEOF = 0x0400;
public static final int CONTEXT_MASK = ~CHECK_MASK & ~HIDE_NULL_COMPARISON_WARNING_MASK;
public FlowContext(FlowContext parent, ASTNode associatedNode, boolean inheritNullFieldChecks) {
this.parent = parent;
this.associatedNode = associatedNode;
if (parent != null) {
if ((parent.tagBits & (FlowContext.DEFER_NULL_DIAGNOSTIC | FlowContext.PREEMPT_NULL_DIAGNOSTIC)) != 0) {
this.tagBits |= FlowContext.DEFER_NULL_DIAGNOSTIC;
}
this.initsOnFinally = parent.initsOnFinally;
this.conditionalLevel = parent.conditionalLevel;
if (inheritNullFieldChecks)
copyNullCheckedFieldsFrom(parent);
}
}
public void copyNullCheckedFieldsFrom(FlowContext other) {
Reference[] fieldReferences = other.nullCheckedFieldReferences;
if (fieldReferences != null && fieldReferences.length > 0 && fieldReferences[0] != null) {
this.nullCheckedFieldReferences = other.nullCheckedFieldReferences;
this.timesToLiveForNullCheckInfo = other.timesToLiveForNullCheckInfo;
}
}
public void recordNullCheckedFieldReference(Reference reference, int timeToLive) {
if (this.nullCheckedFieldReferences == null) {
this.nullCheckedFieldReferences = new Reference[] { reference, null };
this.timesToLiveForNullCheckInfo = new int[] { timeToLive, -1 };
} else {
int len = this.nullCheckedFieldReferences.length;
for (int i=0; i<len; i++) {
if (this.nullCheckedFieldReferences[i] == null) {
this.nullCheckedFieldReferences[i] = reference;
this.timesToLiveForNullCheckInfo[i] = timeToLive;
return;
}
}
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;
}
}
public void extendTimeToLiveForNullCheckedField(int t) {
if (this.timesToLiveForNullCheckInfo != null) {
for (int i = 0; i < this.timesToLiveForNullCheckInfo.length; i++)
if (this.timesToLiveForNullCheckInfo[i] > 0)
this.timesToLiveForNullCheckInfo[i] += t;
}
}
public void expireNullCheckedFieldInfo() {
if (this.nullCheckedFieldReferences != null) {
for (int i = 0; i < this.nullCheckedFieldReferences.length; i++) {
if (--this.timesToLiveForNullCheckInfo[i] == 0)
this.nullCheckedFieldReferences[i] = null;
}
}
}
public boolean isNullcheckedFieldAccess(Reference reference) {
if (this.nullCheckedFieldReferences == null)
return false;
int len = this.nullCheckedFieldReferences.length;
for (int i=0; i<len; i++) {
Reference checked = this.nullCheckedFieldReferences[i];
if (checked == null) {
continue;
}
if (checked.isEquivalent(reference)) {
return true;
}
}
return false;
}
public BranchLabel breakLabel() {
return null;
}
public void checkExceptionHandlers(TypeBinding raisedException, ASTNode location, FlowInfo flowInfo, BlockScope scope) {
checkExceptionHandlers(raisedException, location, flowInfo, scope, false);
}
public void checkExceptionHandlers(TypeBinding raisedException, ASTNode location, FlowInfo flowInfo, BlockScope scope, boolean isExceptionOnAutoClose) {
FlowContext traversedContext = this;
ArrayList abruptlyExitedLoops = null;
if (scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_7 && location instanceof ThrowStatement) {
Expression throwExpression = ((ThrowStatement)location).exception;
LocalVariableBinding throwArgBinding = throwExpression.localVariableBinding();
if (throwExpression instanceof SingleNameReference
&& throwArgBinding instanceof CatchParameterBinding && throwArgBinding.isEffectivelyFinal()) {
CatchParameterBinding parameter = (CatchParameterBinding) throwArgBinding;
checkExceptionHandlers(parameter.getPreciseTypes(), location, flowInfo, scope);
return;
}
}
while (traversedContext != null) {
SubRoutineStatement sub;
if (((sub = traversedContext.subroutine()) != null) && sub.isSubRoutineEscaping()) {
return;
}
if (traversedContext instanceof ExceptionHandlingFlowContext) {
ExceptionHandlingFlowContext exceptionContext =
(ExceptionHandlingFlowContext) traversedContext;
ReferenceBinding[] caughtExceptions;
if ((caughtExceptions = exceptionContext.handledExceptions) != Binding.NO_EXCEPTIONS) {
boolean definitelyCaught = false;
for (int caughtIndex = 0, caughtCount = caughtExceptions.length;
caughtIndex < caughtCount;
caughtIndex++) {
ReferenceBinding caughtException = caughtExceptions[caughtIndex];
FlowInfo exceptionFlow = flowInfo;
int state = caughtException == null
? Scope.EQUAL_OR_MORE_SPECIFIC
: Scope.compareTypes(raisedException, caughtException);
if (abruptlyExitedLoops != null && caughtException != null && state != Scope.NOT_RELATED) {
for (int i = 0, abruptlyExitedLoopsCount = abruptlyExitedLoops.size(); i < abruptlyExitedLoopsCount; i++) {
LoopingFlowContext loop = (LoopingFlowContext) abruptlyExitedLoops.get(i);
loop.recordCatchContextOfEscapingException(exceptionContext, caughtException, flowInfo);
}
exceptionFlow = FlowInfo.DEAD_END;
}
switch (state) {
case Scope.EQUAL_OR_MORE_SPECIFIC :
exceptionContext.recordHandlingException(
caughtException,
exceptionFlow.unconditionalInits(),
raisedException,
raisedException,
location,
definitelyCaught);
definitelyCaught = true;
break;
case Scope.MORE_GENERIC :
exceptionContext.recordHandlingException(
caughtException,
exceptionFlow.unconditionalInits(),
raisedException,
caughtException,
location,
false);
}
}
if (definitelyCaught)
return;
}
if (exceptionContext.isMethodContext) {
if (raisedException.isUncheckedException(false))
return;
boolean shouldMergeUnhandledExceptions = exceptionContext instanceof ExceptionInferenceFlowContext;
if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
if (method.isConstructor() && method.binding.declaringClass.isAnonymousType())
shouldMergeUnhandledExceptions = true;
}
if (shouldMergeUnhandledExceptions) {
exceptionContext.mergeUnhandledException(raisedException);
return;
}
break;
}
} else if (traversedContext instanceof LoopingFlowContext) {
if (abruptlyExitedLoops == null) {
abruptlyExitedLoops = new ArrayList(5);
}
abruptlyExitedLoops.add(traversedContext);
}
traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
if (!isExceptionOnAutoClose) {
if (traversedContext instanceof InsideSubRoutineFlowContext) {
ASTNode node = traversedContext.associatedNode;
if (node instanceof TryStatement) {
TryStatement tryStatement = (TryStatement) node;
flowInfo.addInitializationsFrom(tryStatement.subRoutineInits);
}
}
}
traversedContext = traversedContext.getLocalParent();
}
if (isExceptionOnAutoClose) {
scope.problemReporter().unhandledExceptionFromAutoClose(raisedException, location);
} else {
scope.problemReporter().unhandledException(raisedException, location);
}
}
public void checkExceptionHandlers(TypeBinding[] raisedExceptions, ASTNode location, FlowInfo flowInfo, BlockScope scope) {
int remainingCount;
int raisedCount;
if ((raisedExceptions == null)
|| ((raisedCount = raisedExceptions.length) == 0))
return;
remainingCount = raisedCount;
System.arraycopy(
raisedExceptions,
0,
(raisedExceptions = new TypeBinding[raisedCount]),
0,
raisedCount);
FlowContext traversedContext = this;
ArrayList abruptlyExitedLoops = null;
while (traversedContext != null) {
SubRoutineStatement sub;
if (((sub = traversedContext.subroutine()) != null) && sub.isSubRoutineEscaping()) {
return;
}
if (traversedContext instanceof ExceptionHandlingFlowContext) {
ExceptionHandlingFlowContext exceptionContext =
(ExceptionHandlingFlowContext) traversedContext;
ReferenceBinding[] caughtExceptions;
if ((caughtExceptions = exceptionContext.handledExceptions) != Binding.NO_EXCEPTIONS) {
int caughtCount = caughtExceptions.length;
boolean[] locallyCaught = new boolean[raisedCount];
for (int caughtIndex = 0; caughtIndex < caughtCount; caughtIndex++) {
ReferenceBinding caughtException = caughtExceptions[caughtIndex];
for (int raisedIndex = 0; raisedIndex < raisedCount; raisedIndex++) {
TypeBinding raisedException;
if ((raisedException = raisedExceptions[raisedIndex]) != null) {
FlowInfo exceptionFlow = flowInfo;
int state = caughtException == null
? Scope.EQUAL_OR_MORE_SPECIFIC
: Scope.compareTypes(raisedException, caughtException);
if (abruptlyExitedLoops != null && caughtException != null && state != Scope.NOT_RELATED) {
for (int i = 0, abruptlyExitedLoopsCount = abruptlyExitedLoops.size(); i < abruptlyExitedLoopsCount; i++) {
LoopingFlowContext loop = (LoopingFlowContext) abruptlyExitedLoops.get(i);
loop.recordCatchContextOfEscapingException(exceptionContext, caughtException, flowInfo);
}
exceptionFlow = FlowInfo.DEAD_END;
}
switch (state) {
case Scope.EQUAL_OR_MORE_SPECIFIC :
exceptionContext.recordHandlingException(
caughtException,
exceptionFlow.unconditionalInits(),
raisedException,
raisedException,
location,
locallyCaught[raisedIndex]);
if (!locallyCaught[raisedIndex]) {
locallyCaught[raisedIndex] = true;
remainingCount--;
}
break;
case Scope.MORE_GENERIC :
exceptionContext.recordHandlingException(
caughtException,
exceptionFlow.unconditionalInits(),
raisedException,
caughtException,
location,
false);
}
}
}
}
for (int i = 0; i < raisedCount; i++) {
if (locallyCaught[i]) {
raisedExceptions[i] = null;
}
}
}
if (exceptionContext.isMethodContext) {
for (int i = 0; i < raisedCount; i++) {
TypeBinding raisedException;
if ((raisedException = raisedExceptions[i]) != null) {
if (raisedException.isUncheckedException(false)) {
remainingCount--;
raisedExceptions[i] = null;
}
}
}
boolean shouldMergeUnhandledException = exceptionContext instanceof ExceptionInferenceFlowContext;
if (exceptionContext.associatedNode instanceof AbstractMethodDeclaration) {
AbstractMethodDeclaration method = (AbstractMethodDeclaration)exceptionContext.associatedNode;
if (method.isConstructor() && method.binding.declaringClass.isAnonymousType())
shouldMergeUnhandledException = true;
}
if (shouldMergeUnhandledException) {
for (int i = 0; i < raisedCount; i++) {
TypeBinding raisedException;
if ((raisedException = raisedExceptions[i]) != null) {
exceptionContext.mergeUnhandledException(raisedException);
}
}
return;
}
break;
}
} else if (traversedContext instanceof LoopingFlowContext) {
if (abruptlyExitedLoops == null) {
abruptlyExitedLoops = new ArrayList(5);
}
abruptlyExitedLoops.add(traversedContext);
}
if (remainingCount == 0)
return;
traversedContext.recordReturnFrom(flowInfo.unconditionalInits());
if (traversedContext instanceof InsideSubRoutineFlowContext) {
ASTNode node = traversedContext.associatedNode;
if (node instanceof TryStatement) {
TryStatement tryStatement = (TryStatement) node;
flowInfo.addInitializationsFrom(tryStatement.subRoutineInits);
}
}
traversedContext = traversedContext.getLocalParent();
}
nextReport: for (int i = 0; i < raisedCount; i++) {
TypeBinding exception;
if ((exception = raisedExceptions[i]) != null) {
for (int j = 0; j < i; j++) {
if (TypeBinding.equalsEquals(raisedExceptions[j], exception)) continue nextReport;
}
scope.problemReporter().unhandledException(exception, location);
}
}
}
public BranchLabel continueLabel() {
return null;
}
public FlowInfo getInitsForFinalBlankInitializationCheck(TypeBinding declaringType, FlowInfo flowInfo) {
FlowContext current = this;
FlowInfo inits = flowInfo;
do {
if (current instanceof InitializationFlowContext) {
InitializationFlowContext initializationContext = (InitializationFlowContext) current;
if (TypeBinding.equalsEquals(((TypeDeclaration)initializationContext.associatedNode).binding, declaringType)) {
return inits;
}
inits = initializationContext.initsBeforeContext;
current = initializationContext.initializationParent;
} else if (current instanceof ExceptionHandlingFlowContext) {
if(current instanceof FieldInitsFakingFlowContext) {
return FlowInfo.DEAD_END;
}
ExceptionHandlingFlowContext exceptionContext = (ExceptionHandlingFlowContext) current;
current = exceptionContext.initializationParent == null ? exceptionContext.parent : exceptionContext.initializationParent;
} else {
current = current.getLocalParent();
}
} while (current != null);
throw new IllegalStateException(declaringType.debugName());
}
public FlowContext getTargetContextForBreakLabel(char[] labelName) {
FlowContext current = this, lastNonReturningSubRoutine = null;
while (current != null) {
if (current.isNonReturningContext()) {
lastNonReturningSubRoutine = current;
}
char[] currentLabelName;
if (((currentLabelName = current.labelName()) != null)
&& CharOperation.equals(currentLabelName, labelName)) {
((LabeledStatement)current.associatedNode).bits |= ASTNode.LabelUsed;
if (lastNonReturningSubRoutine == null)
return current;
return lastNonReturningSubRoutine;
}
current = current.getLocalParent();
}
return null;
}
public FlowContext getTargetContextForContinueLabel(char[] labelName) {
FlowContext current = this;
FlowContext lastContinuable = null;
FlowContext lastNonReturningSubRoutine = null;
while (current != null) {
if (current.isNonReturningContext()) {
lastNonReturningSubRoutine = current;
} else {
if (current.isContinuable()) {
lastContinuable = current;
}
}
char[] currentLabelName;
if ((currentLabelName = current.labelName()) != null && CharOperation.equals(currentLabelName, labelName)) {
((LabeledStatement)current.associatedNode).bits |= ASTNode.LabelUsed;
if ((lastContinuable != null)
&& (current.associatedNode.concreteStatement() == lastContinuable.associatedNode)) {
if (lastNonReturningSubRoutine == null) return lastContinuable;
return lastNonReturningSubRoutine;
}
return FlowContext.NotContinuableContext;
}
current = current.getLocalParent();
}
return null;
}
public FlowContext getTargetContextForDefaultBreak() {
FlowContext current = this, lastNonReturningSubRoutine = null;
while (current != null) {
if (current.isNonReturningContext()) {
lastNonReturningSubRoutine = current;
}
if (current.isBreakable() && current.labelName() == null) {
if (lastNonReturningSubRoutine == null) return current;
return lastNonReturningSubRoutine;
}
current = current.getLocalParent();
}
return null;
}
public FlowContext getTargetContextForDefaultYield() {
FlowContext current = this, lastNonReturningSubRoutine = null;
while (current != null) {
if (current.isNonReturningContext()) {
lastNonReturningSubRoutine = current;
}
if (current.isBreakable() && current.labelName() == null && ((SwitchFlowContext) current).isExpression){
if (lastNonReturningSubRoutine == null) return current;
return lastNonReturningSubRoutine;
}
current = current.getLocalParent();
}
return null;
}
public FlowContext getTargetContextForDefaultContinue() {
FlowContext current = this, lastNonReturningSubRoutine = null;
while (current != null) {
if (current.isNonReturningContext()) {
lastNonReturningSubRoutine = current;
}
if (current.isContinuable()) {
if (lastNonReturningSubRoutine == null)
return current;
return lastNonReturningSubRoutine;
}
current = current.getLocalParent();
}
return null;
}
public FlowContext getInitializationContext() {
return null;
}
public FlowContext getLocalParent() {
if (this.associatedNode instanceof AbstractMethodDeclaration || this.associatedNode instanceof TypeDeclaration || this.associatedNode instanceof LambdaExpression)
return null;
return this.parent;
}
public String individualToString() {
return "Flow context";
}
public FlowInfo initsOnBreak() {
return FlowInfo.DEAD_END;
}
public UnconditionalFlowInfo initsOnReturn() {
return FlowInfo.DEAD_END;
}
public boolean isBreakable() {
return false;
}
public boolean isContinuable() {
return false;
}
public boolean isNonReturningContext() {
return false;
}
public boolean isSubRoutine() {
return false;
}
public char[] labelName() {
return null;
}
public void markFinallyNullStatus(LocalVariableBinding local, int nullStatus) {
if (this.initsOnFinally == null) return;
if (this.conditionalLevel == -1) return;
if (this.conditionalLevel == 0) {
this.initsOnFinally.markNullStatus(local, nullStatus);
return;
}
UnconditionalFlowInfo newInfo = this.initsOnFinally.unconditionalCopy();
newInfo.markNullStatus(local, nullStatus);
this.initsOnFinally = this.initsOnFinally.mergedWith(newInfo);
}
public void mergeFinallyNullInfo(FlowInfo flowInfo) {
if (this.initsOnFinally == null) return;
if (this.conditionalLevel == -1) return;
if (this.conditionalLevel == 0) {
this.initsOnFinally.addNullInfoFrom(flowInfo);
return;
}
this.initsOnFinally = this.initsOnFinally.mergedWith(flowInfo.unconditionalCopy());
}
public void recordAbruptExit() {
if (this.conditionalLevel > -1) {
this.conditionalLevel++;
if (!(this instanceof ExceptionHandlingFlowContext) && this.parent != null) {
this.parent.recordAbruptExit();
}
}
}
public void recordBreakFrom(FlowInfo flowInfo) {
}
public void recordBreakTo(FlowContext targetContext) {
}
public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) {
}
public boolean recordExitAgainstResource(BlockScope scope, FlowInfo flowInfo, FakedTrackingVariable trackingVar, ASTNode reference) {
return false;
}
protected void recordProvidedExpectedTypes(TypeBinding providedType, TypeBinding expectedType, int nullCount) {
if (nullCount == 0) {
this.providedExpectedTypes = new TypeBinding[5][];
} else if (this.providedExpectedTypes == null) {
int size = 5;
while (size <= nullCount) size *= 2;
this.providedExpectedTypes = new TypeBinding[size][];
}
else if (nullCount >= this.providedExpectedTypes.length) {
int oldLen = this.providedExpectedTypes.length;
System.arraycopy(this.providedExpectedTypes, 0,
this.providedExpectedTypes = new TypeBinding[nullCount * 2][], 0, oldLen);
}
this.providedExpectedTypes[nullCount] = new TypeBinding[]{providedType, expectedType};
}
protected boolean recordFinalAssignment(VariableBinding variable, Reference finalReference) {
return true;
}
protected void recordNullReference(LocalVariableBinding local,
ASTNode location, int checkType, FlowInfo nullInfo) {
}
public void recordUnboxing(Scope scope, Expression expression, int nullStatus, FlowInfo flowInfo) {
checkUnboxing(scope, expression, flowInfo);
}
protected void checkUnboxing(Scope scope, Expression expression, FlowInfo flowInfo) {
int status = expression.nullStatus(flowInfo, this);
if ((status & FlowInfo.NULL) != 0) {
scope.problemReporter().nullUnboxing(expression, expression.resolvedType);
return;
} else if ((status & FlowInfo.POTENTIALLY_NULL) != 0) {
scope.problemReporter().potentialNullUnboxing(expression, expression.resolvedType);
return;
} else if ((status & FlowInfo.NON_NULL) != 0) {
return;
}
if (this.parent != null) {
this.parent.recordUnboxing(scope, expression, FlowInfo.UNKNOWN, flowInfo);
}
}
public void recordReturnFrom(UnconditionalFlowInfo flowInfo) {
}
public void recordSettingFinal(VariableBinding variable, Reference finalReference, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) {
FlowContext context = this;
while (context != null) {
if (!context.recordFinalAssignment(variable, finalReference)) {
break;
}
context = context.getLocalParent();
}
}
}
public void recordUsingNullReference(Scope scope, LocalVariableBinding local,
ASTNode location, int checkType, FlowInfo flowInfo) {
if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 ||
flowInfo.isDefinitelyUnknown(local)) {
return;
}
checkType |= (this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING);
int checkTypeWithoutHideNullWarning = checkType & ~FlowContext.HIDE_NULL_COMPARISON_WARNING_MASK;
switch (checkTypeWithoutHideNullWarning) {
case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL:
case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL:
if (flowInfo.isDefinitelyNonNull(local)) {
if (checkTypeWithoutHideNullWarning == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) {
if ((checkType & HIDE_NULL_COMPARISON_WARNING) == 0) {
scope.problemReporter().localVariableRedundantCheckOnNonNull(local, location);
}
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
} else {
scope.problemReporter().localVariableNonNullComparedToNull(local, location);
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
}
return;
}
else if (flowInfo.cannotBeDefinitelyNullOrNonNull(local)) {
return;
}
case CAN_ONLY_NULL | IN_COMPARISON_NULL:
case CAN_ONLY_NULL | IN_COMPARISON_NON_NULL:
case CAN_ONLY_NULL | IN_ASSIGNMENT:
case CAN_ONLY_NULL | IN_INSTANCEOF:
Expression reference = (Expression)location;
if (flowInfo.isDefinitelyNull(local)) {
switch(checkTypeWithoutHideNullWarning & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) {
scope.problemReporter().localVariableNullReference(local, reference);
return;
}
if ((checkType & HIDE_NULL_COMPARISON_WARNING) == 0) {
scope.problemReporter().localVariableRedundantCheckOnNull(local, reference);
}
flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
return;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) {
scope.problemReporter().localVariableNullReference(local, reference);
return;
}
scope.problemReporter().localVariableNullComparedToNonNull(local, reference);
flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS);
return;
case FlowContext.IN_ASSIGNMENT:
scope.problemReporter().localVariableRedundantNullAssignment(local, reference);
return;
case FlowContext.IN_INSTANCEOF:
scope.problemReporter().localVariableNullInstanceof(local, reference);
return;
}
} else if (flowInfo.isPotentiallyNull(local)) {
switch(checkTypeWithoutHideNullWarning & CONTEXT_MASK) {
case FlowContext.IN_COMPARISON_NULL:
if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) {
scope.problemReporter().localVariablePotentialNullReference(local, reference);
return;
}
break;
case FlowContext.IN_COMPARISON_NON_NULL:
if (((checkTypeWithoutHideNullWarning & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) {
scope.problemReporter().localVariablePotentialNullReference(local, reference);
return;
}
break;
}
} else if (flowInfo.cannotBeDefinitelyNullOrNonNull(local)) {
return;
}
break;
case MAY_NULL :
if (flowInfo.isDefinitelyNull(local)) {
scope.problemReporter().localVariableNullReference(local, location);
return;
}
if (flowInfo.isPotentiallyNull(local)) {
if(local.type.isFreeTypeVariable()) {
scope.problemReporter().localVariableFreeTypeVariableReference(local, location);
return;
}
scope.problemReporter().localVariablePotentialNullReference(local, location);
return;
}
break;
default:
}
if (this.parent != null) {
this.parent.recordUsingNullReference(scope, local, location, checkType,
flowInfo);
}
}
void removeFinalAssignmentIfAny(Reference reference) {
}
public SubRoutineStatement subroutine() {
return null;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
FlowContext current = this;
int parentsCount = 0;
while ((current = current.parent) != null) {
parentsCount++;
}
FlowContext[] parents = new FlowContext[parentsCount + 1];
current = this;
int index = parentsCount;
while (index >= 0) {
parents[index--] = current;
current = current.parent;
}
for (int i = 0; i < parentsCount; i++) {
for (int j = 0; j < i; j++)
buffer.append('\t');
buffer.append(parents[i].individualToString()).append('\n');
}
buffer.append('*');
for (int j = 0; j < parentsCount + 1; j++)
buffer.append('\t');
buffer.append(individualToString()).append('\n');
return buffer.toString();
}
public void recordNullityMismatch(BlockScope currentScope, Expression expression, TypeBinding providedType, TypeBinding expectedType, FlowInfo flowInfo, int nullStatus, NullAnnotationMatching annotationStatus) {
if (providedType == null) {
return;
}
if (expression.localVariableBinding() != null) {
FlowContext currentContext = this;
while (currentContext != null) {
int isInsideAssert = 0x0;
if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) != 0) {
isInsideAssert = FlowContext.HIDE_NULL_COMPARISON_WARNING;
}
if (currentContext.internalRecordNullityMismatch(expression, providedType, flowInfo, nullStatus, expectedType, ASSIGN_TO_NONNULL | isInsideAssert))
return;
currentContext = currentContext.parent;
}
}
if (annotationStatus != null)
currentScope.problemReporter().nullityMismatchingTypeAnnotation(expression, providedType, expectedType, annotationStatus);
else
currentScope.problemReporter().nullityMismatch(expression, providedType, expectedType, nullStatus,
currentScope.environment().getNonNullAnnotationName());
}
protected boolean internalRecordNullityMismatch(Expression expression, TypeBinding providedType, FlowInfo flowInfo, int nullStatus, TypeBinding expectedType, int checkType) {
return false;
}
}