/******************************************************************************* * Copyright (c) 2000, 2011 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - contribution for Bug 336428 - [compiler][null] bogus warning "redundant null check" in condition of do {} while * () loop *******************************************************************************/ package org.eclipse.che.ide.ext.java.jdt.internal.compiler.flow; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.ASTNode; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.Expression; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.ast.Reference; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.codegen.BranchLabel; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.BlockScope; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.Scope; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.TypeIds; import org.eclipse.che.ide.ext.java.jdt.internal.compiler.lookup.VariableBinding; import java.util.ArrayList; /** Reflects the context of code analysis, keeping track of enclosing try statements, exception handlers, etc... */ public class LoopingFlowContext extends SwitchFlowContext { public BranchLabel continueLabel; public UnconditionalFlowInfo initsOnContinue = FlowInfo.DEAD_END; private UnconditionalFlowInfo upstreamNullFlowInfo; private LoopingFlowContext innerFlowContexts[] = null; private UnconditionalFlowInfo innerFlowInfos[] = null; private int innerFlowContextsCount = 0; private LabelFlowContext breakTargetContexts[] = null; private int breakTargetsCount = 0; Reference finalAssignments[]; VariableBinding finalVariables[]; int assignCount = 0; LocalVariableBinding[] nullLocals; Expression[] nullReferences; int[] nullCheckTypes; int nullCount; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=321926 static public class EscapingExceptionCatchSite { final ReferenceBinding caughtException; final ExceptionHandlingFlowContext catchingContext; public EscapingExceptionCatchSite(ExceptionHandlingFlowContext catchingContext, ReferenceBinding caughtException) { this.catchingContext = catchingContext; this.caughtException = caughtException; } void simulateThrowAfterLoopBack(FlowInfo flowInfo) { this.catchingContext.recordHandlingException(this.caughtException, flowInfo.unconditionalInits(), null, // raised // exception, // irrelevant // here, null, null, /* invocation site, irrelevant here */true // we have no business altering the needed status. ); } } private ArrayList escapingExceptionCatchSites = null; Scope associatedScope; public LoopingFlowContext(FlowContext parent, FlowInfo upstreamNullFlowInfo, ASTNode associatedNode, BranchLabel breakLabel, BranchLabel continueLabel, Scope associatedScope) { super(parent, associatedNode, breakLabel); this.tagBits |= FlowContext.PREEMPT_NULL_DIAGNOSTIC; // children will defer to this, which may defer to its own parent this.continueLabel = continueLabel; this.associatedScope = associatedScope; this.upstreamNullFlowInfo = upstreamNullFlowInfo.unconditionalCopy(); } /** * Perform deferred checks relative to final variables duplicate initialization of lack of initialization. * * @param scope * the scope to which this context is associated * @param flowInfo * the flow info against which checks must be performed */ public void complainOnDeferredFinalChecks(BlockScope scope, FlowInfo flowInfo) { // complain on final assignments in loops for (int i = 0; i < this.assignCount; i++) { VariableBinding variable = this.finalVariables[i]; if (variable == null) continue; boolean complained = false; // remember if have complained on this final assignment if (variable instanceof FieldBinding) { if (flowInfo.isPotentiallyAssigned((FieldBinding)variable)) { complained = true; scope.problemReporter().duplicateInitializationOfBlankFinalField((FieldBinding)variable, this.finalAssignments[i]); } } else { if (flowInfo.isPotentiallyAssigned((LocalVariableBinding)variable)) { complained = true; scope.problemReporter().duplicateInitializationOfFinalLocal((LocalVariableBinding)variable, this.finalAssignments[i]); } } // any reference reported at this level is removed from the parent context where it // could also be reported again if (complained) { FlowContext context = this.parent; while (context != null) { context.removeFinalAssignmentIfAny(this.finalAssignments[i]); context = context.parent; } } } } /** * Perform deferred checks relative to the null status of local variables. * * @param scope * the scope to which this context is associated * @param callerFlowInfo * the flow info against which checks must be performed */ public void complainOnDeferredNullChecks(BlockScope scope, FlowInfo callerFlowInfo) { for (int i = 0; i < this.innerFlowContextsCount; i++) { this.upstreamNullFlowInfo.addPotentialNullInfoFrom(this.innerFlowContexts[i].upstreamNullFlowInfo) .addPotentialNullInfoFrom(this.innerFlowInfos[i]); } this.innerFlowContextsCount = 0; UnconditionalFlowInfo flowInfo = this.upstreamNullFlowInfo.addPotentialNullInfoFrom(callerFlowInfo.unconditionalInitsWithoutSideEffect()); if ((this.tagBits & FlowContext.DEFER_NULL_DIAGNOSTIC) != 0) { // check only immutable null checks on innermost looping context for (int i = 0; i < this.nullCount; i++) { LocalVariableBinding local = this.nullLocals[i]; Expression expression = this.nullReferences[i]; // final local variable switch (this.nullCheckTypes[i]) { case CAN_ONLY_NON_NULL | IN_COMPARISON_NULL: case CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL: if (flowInfo.isDefinitelyNonNull(local)) { this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNonNullComparedToNull(local, expression); } } continue; } break; case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL: if (flowInfo.isDefinitelyNonNull(local)) { this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNonNullComparedToNull(local, expression); } } continue; } if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNullComparedToNonNull(local, expression); } } continue; } break; 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: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; switch (this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); } continue; case FlowContext.IN_COMPARISON_NON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNullComparedToNonNull(local, expression); } continue; case FlowContext.IN_ASSIGNMENT: scope.problemReporter().localVariableRedundantNullAssignment(local, expression); continue; case FlowContext.IN_INSTANCEOF: scope.problemReporter().localVariableNullInstanceof(local, expression); continue; } } else if (flowInfo.isPotentiallyNull(local)) { switch (this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariablePotentialNullReference(local, expression); continue; } break; case FlowContext.IN_COMPARISON_NON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariablePotentialNullReference(local, expression); continue; } break; } } break; case MAY_NULL: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; scope.problemReporter().localVariableNullReference(local, expression); continue; } break; default: // never happens } this.parent.recordUsingNullReference(scope, local, expression, this.nullCheckTypes[i], flowInfo); } } else { // check inconsistent null checks on outermost looping context for (int i = 0; i < this.nullCount; i++) { Expression expression = this.nullReferences[i]; // final local variable LocalVariableBinding local = this.nullLocals[i]; switch (this.nullCheckTypes[i]) { case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL: if (flowInfo.isDefinitelyNonNull(local)) { this.nullReferences[i] = null; if (this.nullCheckTypes[i] == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNonNull(local, expression); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNonNullComparedToNull(local, expression); } } continue; } //$FALL-THROUGH$ 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: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; switch (this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNull(local, expression); } continue; case FlowContext.IN_COMPARISON_NON_NULL: if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariableNullReference(local, expression); continue; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNullComparedToNonNull(local, expression); } continue; case FlowContext.IN_ASSIGNMENT: scope.problemReporter().localVariableRedundantNullAssignment(local, expression); continue; case FlowContext.IN_INSTANCEOF: scope.problemReporter().localVariableNullInstanceof(local, expression); continue; } } else if (flowInfo.isPotentiallyNull(local)) { switch (this.nullCheckTypes[i] & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariablePotentialNullReference(local, expression); continue; } break; case FlowContext.IN_COMPARISON_NON_NULL: this.nullReferences[i] = null; if (((this.nullCheckTypes[i] & CHECK_MASK) == CAN_ONLY_NULL) && (expression.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariablePotentialNullReference(local, expression); continue; } break; } } break; case MAY_NULL: if (flowInfo.isDefinitelyNull(local)) { this.nullReferences[i] = null; scope.problemReporter().localVariableNullReference(local, expression); continue; } if (flowInfo.isPotentiallyNull(local)) { this.nullReferences[i] = null; scope.problemReporter().localVariablePotentialNullReference(local, expression); continue; } break; default: // never happens } } } // propagate breaks this.initsOnBreak.addPotentialNullInfoFrom(flowInfo); for (int i = 0; i < this.breakTargetsCount; i++) { this.breakTargetContexts[i].initsOnBreak.addPotentialNullInfoFrom(flowInfo); } } public BranchLabel continueLabel() { return this.continueLabel; } public String individualToString() { StringBuffer buffer = new StringBuffer("Looping flow context"); //$NON-NLS-1$ buffer.append("[initsOnBreak - ").append(this.initsOnBreak.toString()).append(']'); //$NON-NLS-1$ buffer.append("[initsOnContinue - ").append(this.initsOnContinue.toString()).append(']'); //$NON-NLS-1$ buffer.append("[finalAssignments count - ").append(this.assignCount).append(']'); //$NON-NLS-1$ buffer.append("[nullReferences count - ").append(this.nullCount).append(']'); //$NON-NLS-1$ return buffer.toString(); } public boolean isContinuable() { return true; } public boolean isContinuedTo() { return this.initsOnContinue != FlowInfo.DEAD_END; } public void recordBreakTo(FlowContext targetContext) { if (targetContext instanceof LabelFlowContext) { int current; if ((current = this.breakTargetsCount++) == 0) { this.breakTargetContexts = new LabelFlowContext[2]; } else if (current == this.breakTargetContexts.length) { System.arraycopy(this.breakTargetContexts, 0, this.breakTargetContexts = new LabelFlowContext[current + 2], 0, current); } this.breakTargetContexts[current] = (LabelFlowContext)targetContext; } } public void recordContinueFrom(FlowContext innerFlowContext, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) { if ((this.initsOnContinue.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) == 0) { this.initsOnContinue = this.initsOnContinue.mergedWith(flowInfo.unconditionalInitsWithoutSideEffect()); } else { this.initsOnContinue = flowInfo.unconditionalCopy(); } FlowContext inner = innerFlowContext; while (inner != this && !(inner instanceof LoopingFlowContext)) { inner = inner.parent; } if (inner == this) { this.upstreamNullFlowInfo.addPotentialNullInfoFrom(flowInfo.unconditionalInitsWithoutSideEffect()); } else { int length = 0; if (this.innerFlowContexts == null) { this.innerFlowContexts = new LoopingFlowContext[5]; this.innerFlowInfos = new UnconditionalFlowInfo[5]; } else if (this.innerFlowContextsCount == (length = this.innerFlowContexts.length) - 1) { System.arraycopy(this.innerFlowContexts, 0, (this.innerFlowContexts = new LoopingFlowContext[length + 5]), 0, length); System.arraycopy(this.innerFlowInfos, 0, (this.innerFlowInfos = new UnconditionalFlowInfo[length + 5]), 0, length); } this.innerFlowContexts[this.innerFlowContextsCount] = (LoopingFlowContext)inner; this.innerFlowInfos[this.innerFlowContextsCount++] = flowInfo.unconditionalInitsWithoutSideEffect(); } } } protected boolean recordFinalAssignment(VariableBinding binding, Reference finalAssignment) { // do not consider variables which are defined inside this loop if (binding instanceof LocalVariableBinding) { Scope scope = ((LocalVariableBinding)binding).declaringScope; while ((scope = scope.parent) != null) { if (scope == this.associatedScope) return false; } } if (this.assignCount == 0) { this.finalAssignments = new Reference[5]; this.finalVariables = new VariableBinding[5]; } else { if (this.assignCount == this.finalAssignments.length) System.arraycopy(this.finalAssignments, 0, (this.finalAssignments = new Reference[this.assignCount * 2]), 0, this.assignCount); System.arraycopy(this.finalVariables, 0, (this.finalVariables = new VariableBinding[this.assignCount * 2]), 0, this.assignCount); } this.finalAssignments[this.assignCount] = finalAssignment; this.finalVariables[this.assignCount++] = binding; return true; } protected void recordNullReference(LocalVariableBinding local, Expression expression, int status) { if (this.nullCount == 0) { this.nullLocals = new LocalVariableBinding[5]; this.nullReferences = new Expression[5]; this.nullCheckTypes = new int[5]; } else if (this.nullCount == this.nullLocals.length) { System.arraycopy(this.nullLocals, 0, this.nullLocals = new LocalVariableBinding[this.nullCount * 2], 0, this.nullCount); System.arraycopy(this.nullReferences, 0, this.nullReferences = new Expression[this.nullCount * 2], 0, this.nullCount); System.arraycopy(this.nullCheckTypes, 0, this.nullCheckTypes = new int[this.nullCount * 2], 0, this.nullCount); } this.nullLocals[this.nullCount] = local; this.nullReferences[this.nullCount] = expression; this.nullCheckTypes[this.nullCount++] = status; } public void recordUsingNullReference(Scope scope, LocalVariableBinding local, Expression reference, int checkType, FlowInfo flowInfo) { if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) != 0 || flowInfo.isDefinitelyUnknown(local)) { return; } switch (checkType) { case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL: case CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL: if (flowInfo.isDefinitelyNonNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNonNullComparedToNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); } } } else if (flowInfo.isDefinitelyNull(local)) { if (checkType == (CAN_ONLY_NULL_NON_NULL | IN_COMPARISON_NULL)) { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); } } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenTrue().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); } } } else if (this.upstreamNullFlowInfo.isDefinitelyNonNull(local) && !flowInfo.isPotentiallyNull(local) && !flowInfo.isPotentiallyUnknown(local)) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=291418 flowInfo.markAsDefinitelyNonNull(local); if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { recordNullReference(local, reference, checkType); } } else if (flowInfo.cannotBeDefinitelyNullOrNonNull(local)) { return; // no reason to complain, since there is definitely some uncertainty making the comparison relevant. } else { if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { // note: pot non-null & pot null is already captured by cannotBeDefinitelyNullOrNonNull() if (flowInfo.isPotentiallyNonNull(local)) { // knowing 'local' can be non-null, we're only interested in seeing whether it can *only* be non-null recordNullReference(local, reference, CAN_ONLY_NON_NULL | checkType & CONTEXT_MASK); } else if (flowInfo.isPotentiallyNull(local)) { // knowing 'local' can be null, we're only interested in seeing whether it can *only* be null recordNullReference(local, reference, CAN_ONLY_NULL | checkType & CONTEXT_MASK); } else { recordNullReference(local, reference, checkType); } } } 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: if (flowInfo.isPotentiallyNonNull(local) || flowInfo.isPotentiallyUnknown(local) || flowInfo.isProtectedNonNull(local)) { // if variable is not null, we are not interested in recording null reference for deferred checks. // This is because CAN_ONLY_NULL means we're only interested in cases when variable can be null. return; } if (flowInfo.isDefinitelyNull(local)) { switch (checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableRedundantCheckOnNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { flowInfo.initsWhenFalse().setReachMode(FlowInfo.UNREACHABLE_BY_NULLANALYSIS); } return; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariableNullReference(local, reference); return; } if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0) { scope.problemReporter().localVariableNullComparedToNonNull(local, reference); } if (!flowInfo.isMarkedAsNullOrNonNullInAssertExpression(local)) { 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 (checkType & CONTEXT_MASK) { case FlowContext.IN_COMPARISON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariablePotentialNullReference(local, reference); return; } break; case FlowContext.IN_COMPARISON_NON_NULL: if (((checkType & CHECK_MASK) == CAN_ONLY_NULL) && (reference.implicitConversion & TypeIds.UNBOXING) != 0) { // check for auto-unboxing first and report appropriate warning scope.problemReporter().localVariablePotentialNullReference(local, reference); return; } break; } } // if the contention is inside assert statement, we want to avoid null warnings only in case of // comparisons and not in case of assignment and instanceof if ((this.tagBits & FlowContext.HIDE_NULL_COMPARISON_WARNING) == 0 || (checkType & CONTEXT_MASK) == FlowContext.IN_ASSIGNMENT || (checkType & CONTEXT_MASK) == FlowContext.IN_INSTANCEOF) { recordNullReference(local, reference, checkType); } return; case MAY_NULL: if (flowInfo.isDefinitelyNonNull(local)) { return; } if (flowInfo.isDefinitelyNull(local)) { scope.problemReporter().localVariableNullReference(local, reference); return; } if (flowInfo.isPotentiallyNull(local)) { scope.problemReporter().localVariablePotentialNullReference(local, reference); return; } recordNullReference(local, reference, checkType); return; default: // never happens } } void removeFinalAssignmentIfAny(Reference reference) { for (int i = 0; i < this.assignCount; i++) { if (this.finalAssignments[i] == reference) { this.finalAssignments[i] = null; this.finalVariables[i] = null; return; } } } /* * Simulate a throw of an exception from inside a loop in its second or subsequent iteration. See * https://bugs.eclipse.org/bugs/show_bug.cgi?id=321926 */ public void simulateThrowAfterLoopBack(FlowInfo flowInfo) { if (this.escapingExceptionCatchSites != null) { for (int i = 0, exceptionCount = this.escapingExceptionCatchSites.size(); i < exceptionCount; i++) { ((EscapingExceptionCatchSite)this.escapingExceptionCatchSites.get(i)).simulateThrowAfterLoopBack(flowInfo); } this.escapingExceptionCatchSites = null; // don't care for it anymore. } } /* * Record the fact that some exception thrown by code within this loop is caught by an outer catch block. This is used to * propagate data flow along the edge back to the next iteration. See simulateThrowAfterLoopBack */ public void recordCatchContextOfEscapingException(ExceptionHandlingFlowContext catchingContext, ReferenceBinding caughtException) { if (this.escapingExceptionCatchSites == null) { this.escapingExceptionCatchSites = new ArrayList(5); } this.escapingExceptionCatchSites.add(new EscapingExceptionCatchSite(catchingContext, caughtException)); } public boolean hasEscapingExceptions() { return this.escapingExceptionCatchSites != null; } }