org.eclipse.jdt.internal.compiler.flow.FlowInfo Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (c) 2000, 2016 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
* bug 292478 - Report potentially null across variable assignment
* bug 332637 - Dead Code detection removing code that isn't dead
* bug 394768 - [compiler][resource] Incorrect resource leak warning when creating stream in conditional
* Bug 411964 - [1.8][null] leverage null type annotation in foreach statement
* Bug 421035 - [resource] False alarm of resource leak warning when casting a closeable in its assignment
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.flow;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.IfStatement;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
public abstract class FlowInfo {
public int tagBits; // REACHABLE by default
public final static int REACHABLE = 0;
/* unreachable code
* eg. while (true);
* i++; --> unreachable code
*/
public final static int UNREACHABLE_OR_DEAD = 1;
/* unreachable code as inferred by null analysis
* eg. str = null;
* if (str != null) {
* // dead code
* }
*/
public final static int UNREACHABLE_BY_NULLANALYSIS = 2;
/*
* code unreachable in any fashion
*/
public final static int UNREACHABLE = UNREACHABLE_OR_DEAD | UNREACHABLE_BY_NULLANALYSIS;
public final static int NULL_FLAG_MASK = 4;
public final static int UNKNOWN = 1;
public final static int NULL = 2;
public final static int NON_NULL = 4;
public final static int POTENTIALLY_UNKNOWN = 8;
public final static int POTENTIALLY_NULL = 16;
public final static int POTENTIALLY_NON_NULL = 32;
public final static int UNROOTED = 64; // marks a flowInfo that may be appended to another flowInfo (accepting incoming nulls/nonnulls, see UFI.iNBit/iNNBit).
public static final int FREE_TYPEVARIABLE = FlowInfo.POTENTIALLY_NULL | FlowInfo.POTENTIALLY_NON_NULL;
public static final UnconditionalFlowInfo DEAD_END; // Represents a dead branch status of initialization
static {
DEAD_END = new UnconditionalFlowInfo();
DEAD_END.tagBits = UNREACHABLE;
}
/**
* Add other inits to this flow info, then return this. The operation semantics
* are to match as closely as possible the application to this flow info of all
* the operations that resulted into otherInits.
* @param otherInits other inits to add to this
* @return this, modified according to otherInits information
*/
abstract public FlowInfo addInitializationsFrom(FlowInfo otherInits);
/**
* Add all null information from otherInits to this flow info and return this.
* The operation models the effect of an unconditional sequence of this flow info
* and otherInits.
*/
abstract public FlowInfo addNullInfoFrom(FlowInfo otherInits);
/**
* Compose other inits over this flow info, then return this. The operation
* semantics are to wave into this flow info the consequences of a possible
* path into the operations that resulted into otherInits. The fact that this
* path may be left unexecuted under peculiar conditions results into less
* specific results than {@link #addInitializationsFrom(FlowInfo)
* addInitializationsFrom}.
* @param otherInits other inits to compose over this
* @return this, modified according to otherInits information
*/
abstract public FlowInfo addPotentialInitializationsFrom(FlowInfo otherInits);
public FlowInfo asNegatedCondition() {
return this;
}
public static FlowInfo conditional(FlowInfo initsWhenTrue, FlowInfo initsWhenFalse){
if (initsWhenTrue == initsWhenFalse) return initsWhenTrue;
// if (initsWhenTrue.equals(initsWhenFalse)) return initsWhenTrue; -- could optimize if #equals is defined
return new ConditionalFlowInfo(initsWhenTrue, initsWhenFalse);
}
/**
* Check whether a given local variable is known to be unable to gain a definite
* non null or definite null status by the use of an enclosing flow info. The
* semantics are that if the current flow info marks the variable as potentially
* unknown or else as being both potentially null and potentially non null,
* then it won't ever be promoted as definitely null or definitely non null. (It
* could still get promoted to definite unknown).
* @param local the variable to check
* @return true iff this flow info prevents local from being promoted to
* definite non null or definite null against an enclosing flow info
*/
public boolean cannotBeDefinitelyNullOrNonNull(LocalVariableBinding local) {
return isPotentiallyUnknown(local) ||
isPotentiallyNonNull(local) && isPotentiallyNull(local);
}
/**
* Check whether a given local variable is known to be non null, either because
* it is definitely non null, or because is has been tested against non null.
* @param local the variable to ckeck
* @return true iff local cannot be null for this flow info
*/
public boolean cannotBeNull(LocalVariableBinding local) {
return isDefinitelyNonNull(local) || isProtectedNonNull(local);
}
/**
* Check whether a given local variable is known to be null, either because it
* is definitely null, or because is has been tested against null.
* @param local the variable to ckeck
* @return true iff local can only be null for this flow info
*/
public boolean canOnlyBeNull(LocalVariableBinding local) {
return isDefinitelyNull(local) || isProtectedNull(local);
}
/**
* Return a deep copy of the current instance.
* @return a deep copy of this flow info
*/
abstract public FlowInfo copy();
public static UnconditionalFlowInfo initial(int maxFieldCount) {
UnconditionalFlowInfo info = new UnconditionalFlowInfo();
info.maxFieldCount = maxFieldCount;
return info;
}
/**
* Return the flow info that would result from the path associated to the
* value false for the condition expression that generated this flow info.
* May be this flow info if it is not an instance of {@link
* ConditionalFlowInfo}. May have a side effect on subparts of this flow
* info (subtrees get merged).
* @return the flow info associated to the false branch of the condition
* that generated this flow info
*/
abstract public FlowInfo initsWhenFalse();
/**
* Return the flow info that would result from the path associated to the
* value true for the condition expression that generated this flow info.
* May be this flow info if it is not an instance of {@link
* ConditionalFlowInfo}. May have a side effect on subparts of this flow
* info (subtrees get merged).
* @return the flow info associated to the true branch of the condition
* that generated this flow info
*/
abstract public FlowInfo initsWhenTrue();
/**
* Check status of definite assignment for a field.
*/
abstract public boolean isDefinitelyAssigned(FieldBinding field);
/**
* Check status of definite assignment for a local.
*/
public abstract boolean isDefinitelyAssigned(LocalVariableBinding local);
/**
* Check status of definite non-null value for a given local variable.
* @param local the variable to ckeck
* @return true iff local is definitely non null for this flow info
*/
public abstract boolean isDefinitelyNonNull(LocalVariableBinding local);
/**
* Check status of definite null value for a given local variable.
* @param local the variable to ckeck
* @return true iff local is definitely null for this flow info
*/
public abstract boolean isDefinitelyNull(LocalVariableBinding local);
/**
* Check status of definite unknown value for a given local variable.
* @param local the variable to ckeck
* @return true iff local is definitely unknown for this flow info
*/
public abstract boolean isDefinitelyUnknown(LocalVariableBinding local);
/**
* Check if any null info has been recorded for a given local variable.
* Here even recording of 'UNKNOWN' is considered as null info.
*/
public abstract boolean hasNullInfoFor(LocalVariableBinding local);
/**
* Check status of potential assignment for a field.
*/
abstract public boolean isPotentiallyAssigned(FieldBinding field);
/**
* Check status of potential assignment for a local variable.
*/
abstract public boolean isPotentiallyAssigned(LocalVariableBinding field);
/**
* Check status of potential null assignment for a local. Return true if there
* is a reasonable expectation that the variable be non null at this point.
* @param local LocalVariableBinding - the binding for the checked local
* @return true if there is a reasonable expectation that local be non null at
* this point
*/
public abstract boolean isPotentiallyNonNull(LocalVariableBinding local);
/**
* Check status of potential null assignment for a local. Return true if there
* is a reasonable expectation that the variable be null at this point. This
* includes the protected null case, so as to augment diagnostics, but does not
* really check that someone deliberately assigned to null on any specific
* path
* @param local LocalVariableBinding - the binding for the checked local
* @return true if there is a reasonable expectation that local be null at
* this point
*/
public abstract boolean isPotentiallyNull(LocalVariableBinding local);
/**
* Return true if the given local may have been assigned to an unknown value.
* @param local the local to check
* @return true if the given local may have been assigned to an unknown value
*/
public abstract boolean isPotentiallyUnknown(LocalVariableBinding local);
/**
* Return true if the given local is protected by a test against a non null
* value.
* @param local the local to check
* @return true if the given local is protected by a test against a non null
*/
public abstract boolean isProtectedNonNull(LocalVariableBinding local);
/**
* Return true if the given local is protected by a test against null.
* @param local the local to check
* @return true if the given local is protected by a test against null
*/
public abstract boolean isProtectedNull(LocalVariableBinding local);
/**
* Record that a local variable got checked to be non null.
* @param local the checked local variable
*/
abstract public void markAsComparedEqualToNonNull(LocalVariableBinding local);
/**
* Record that a local variable got checked to be null.
* @param local the checked local variable
*/
abstract public void markAsComparedEqualToNull(LocalVariableBinding local);
/**
* Record a field got definitely assigned.
*/
abstract public void markAsDefinitelyAssigned(FieldBinding field);
/**
* Record a local got definitely assigned to a non-null value.
*/
abstract public void markAsDefinitelyNonNull(LocalVariableBinding local);
/**
* Record a local got definitely assigned to null.
*/
abstract public void markAsDefinitelyNull(LocalVariableBinding local);
/**
* Reset all null-information about a given local.
*/
abstract public void resetNullInfo(LocalVariableBinding local);
/**
* Record a local may have got assigned to unknown (set the bit on existing info).
*/
abstract public void markPotentiallyUnknownBit(LocalVariableBinding local);
/**
* Record a local may have got assigned to null (set the bit on existing info).
*/
abstract public void markPotentiallyNullBit(LocalVariableBinding local);
/**
* Record a local may have got assigned to non-null (set the bit on existing info).
*/
abstract public void markPotentiallyNonNullBit(LocalVariableBinding local);
/**
* Record a local got definitely assigned.
*/
abstract public void markAsDefinitelyAssigned(LocalVariableBinding local);
/**
* Record a local got definitely assigned to an unknown value.
*/
abstract public void markAsDefinitelyUnknown(LocalVariableBinding local);
/**
* Mark the null status of the given local according to the given status
* @param local
* @param nullStatus bitset of FLowInfo.UNKNOWN ... FlowInfo.POTENTIALLY_NON_NULL
*/
public void markNullStatus(LocalVariableBinding local, int nullStatus) {
switch(nullStatus) {
// definite status?
case FlowInfo.UNKNOWN :
markAsDefinitelyUnknown(local);
break;
case FlowInfo.NULL :
markAsDefinitelyNull(local);
break;
case FlowInfo.NON_NULL :
markAsDefinitelyNonNull(local);
break;
default:
// collect potential status:
resetNullInfo(local);
if ((nullStatus & FlowInfo.POTENTIALLY_UNKNOWN) != 0)
markPotentiallyUnknownBit(local);
if ((nullStatus & FlowInfo.POTENTIALLY_NULL) != 0)
markPotentiallyNullBit(local);
if ((nullStatus & FlowInfo.POTENTIALLY_NON_NULL) != 0)
markPotentiallyNonNullBit(local);
if ((nullStatus & (FlowInfo.POTENTIALLY_NULL|FlowInfo.POTENTIALLY_NON_NULL|FlowInfo.POTENTIALLY_UNKNOWN)) == 0)
markAsDefinitelyUnknown(local);
}
}
/**
* Answer the null status of the given local
* @param local
* @return bitset of FlowInfo.UNKNOWN ... FlowInfo.POTENTIALLY_NON_NULL
*/
public int nullStatus(LocalVariableBinding local) {
if (isDefinitelyUnknown(local))
return FlowInfo.UNKNOWN;
if (isDefinitelyNull(local))
return FlowInfo.NULL;
if (isDefinitelyNonNull(local))
return FlowInfo.NON_NULL;
int status = 0;
if (isPotentiallyUnknown(local))
status |= FlowInfo.POTENTIALLY_UNKNOWN;
if (isPotentiallyNull(local))
status |= FlowInfo.POTENTIALLY_NULL;
if (isPotentiallyNonNull(local))
status |= FlowInfo.POTENTIALLY_NON_NULL;
if (status > 0)
return status;
return FlowInfo.UNKNOWN;
}
/**
* Merge two single bits (NULL, NON_NULL, POTENTIALLY*..) into one.
* This method implements a simpler logic than the 4-bit encoding used in FlowInfo instances.
*/
public static int mergeNullStatus(int nullStatus1, int nullStatus2) {
boolean canBeNull = false;
boolean canBeNonNull = false;
switch (nullStatus1) {
case POTENTIALLY_NULL:
canBeNonNull = true;
//$FALL-THROUGH$
case NULL:
canBeNull = true;
break;
case POTENTIALLY_NON_NULL:
canBeNull = true;
//$FALL-THROUGH$
case NON_NULL:
canBeNonNull = true;
break;
}
switch (nullStatus2) {
case POTENTIALLY_NULL:
canBeNonNull = true;
//$FALL-THROUGH$
case NULL:
canBeNull = true;
break;
case POTENTIALLY_NON_NULL:
canBeNull = true;
//$FALL-THROUGH$
case NON_NULL:
canBeNonNull = true;
break;
}
if (canBeNull) {
if (canBeNonNull)
return POTENTIALLY_NULL;
else
return NULL;
} else {
if (canBeNonNull)
return NON_NULL;
else
return UNKNOWN;
}
}
/**
* Merge branches using optimized boolean conditions
*/
public static UnconditionalFlowInfo mergedOptimizedBranches(
FlowInfo initsWhenTrue, boolean isOptimizedTrue,
FlowInfo initsWhenFalse, boolean isOptimizedFalse,
boolean allowFakeDeadBranch) {
UnconditionalFlowInfo mergedInfo;
if (isOptimizedTrue){
if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) {
mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD).
unconditionalInits();
}
else {
mergedInfo =
initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse.
nullInfoLessUnconditionalCopy()).
unconditionalInits();
}
}
else if (isOptimizedFalse) {
if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) {
mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD).
unconditionalInits();
}
else {
mergedInfo =
initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue.
nullInfoLessUnconditionalCopy()).
unconditionalInits();
}
}
else {
mergedInfo = initsWhenTrue.
mergedWith(initsWhenFalse.unconditionalInits());
}
return mergedInfo;
}
/**
* Merge if-else branches using optimized boolean conditions
*/
public static UnconditionalFlowInfo mergedOptimizedBranchesIfElse(
FlowInfo initsWhenTrue, boolean isOptimizedTrue,
FlowInfo initsWhenFalse, boolean isOptimizedFalse,
boolean allowFakeDeadBranch, FlowInfo flowInfo, IfStatement ifStatement,
boolean reportDeadCodeInKnownPattern) {
UnconditionalFlowInfo mergedInfo;
if (isOptimizedTrue){
if (initsWhenTrue == FlowInfo.DEAD_END && allowFakeDeadBranch) {
if (!reportDeadCodeInKnownPattern) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=256796
// do not report code even after if-else as dead as a consequence of analysis done in known dead code pattern
// when the CompilerOptions$reportDeadCodeInTrivialIfStatement option is disabled
if (ifStatement.elseStatement == null) {
mergedInfo = flowInfo.unconditionalInits();
} else {
mergedInfo = initsWhenFalse.unconditionalInits();
if (initsWhenFalse != FlowInfo.DEAD_END) {
// let the definitely true status of known dead code pattern not affect the reachability
mergedInfo.setReachMode(flowInfo.reachMode());
}
}
} else {
mergedInfo = initsWhenFalse.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD).
unconditionalInits();
}
}
else {
mergedInfo =
initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse.
nullInfoLessUnconditionalCopy()).
unconditionalInits();
}
}
else if (isOptimizedFalse) {
if (initsWhenFalse == FlowInfo.DEAD_END && allowFakeDeadBranch) {
if (!reportDeadCodeInKnownPattern) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=256796
// do not report code even after if-else as dead as a consequence of analysis done in known dead code pattern
// when the CompilerOptions$reportDeadCodeInTrivialIfStatement option is disabled
if (ifStatement.thenStatement == null) {
mergedInfo = flowInfo.unconditionalInits();
} else {
mergedInfo = initsWhenTrue.unconditionalInits();
if (initsWhenTrue != FlowInfo.DEAD_END) {
// let the definitely false status of known dead code pattern not affect the reachability
mergedInfo.setReachMode(flowInfo.reachMode());
}
}
} else {
mergedInfo = initsWhenTrue.setReachMode(FlowInfo.UNREACHABLE_OR_DEAD).
unconditionalInits();
}
}
else {
mergedInfo =
initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue.
nullInfoLessUnconditionalCopy()).
unconditionalInits();
}
}
else if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 &&
(ifStatement.bits & ASTNode.IsElseStatementUnreachable) != 0 &&
initsWhenTrue != FlowInfo.DEAD_END &&
initsWhenFalse != FlowInfo.DEAD_END) {
// Done when the then branch will always be executed but the condition does not have a boolean
// true or false (i.e if(true), etc) for sure
// We don't do this if both if and else branches themselves are in an unreachable code
// or if any of them is a DEAD_END (e.g. contains 'return' or 'throws')
mergedInfo =
initsWhenTrue.addPotentialInitializationsFrom(initsWhenFalse.
nullInfoLessUnconditionalCopy()).
unconditionalInits();
// if a variable is only initialized in one branch and not initialized in the other,
// then we need to cast a doubt on its initialization in the merged info
mergedInfo.mergeDefiniteInitsWith(initsWhenFalse.unconditionalCopy());
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=415997, classify unreachability precisely, IsElseStatementUnreachable could be due to null analysis
if ((mergedInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0 && (initsWhenFalse.tagBits & FlowInfo.UNREACHABLE) == FlowInfo.UNREACHABLE_BY_NULLANALYSIS) {
mergedInfo.tagBits &= ~UNREACHABLE_OR_DEAD;
mergedInfo.tagBits |= UNREACHABLE_BY_NULLANALYSIS;
}
}
else if ((flowInfo.tagBits & FlowInfo.UNREACHABLE) == 0 &&
(ifStatement.bits & ASTNode.IsThenStatementUnreachable) != 0 && initsWhenTrue != FlowInfo.DEAD_END
&& initsWhenFalse != FlowInfo.DEAD_END) {
// Done when the else branch will always be executed but the condition does not have a boolean
// true or false (i.e if(true), etc) for sure
// We don't do this if both if and else branches themselves are in an unreachable code
// or if any of them is a DEAD_END (e.g. contains 'return' or 'throws')
mergedInfo =
initsWhenFalse.addPotentialInitializationsFrom(initsWhenTrue.
nullInfoLessUnconditionalCopy()).
unconditionalInits();
// if a variable is only initialized in one branch and not initialized in the other,
// then we need to cast a doubt on its initialization in the merged info
mergedInfo.mergeDefiniteInitsWith(initsWhenTrue.unconditionalCopy());
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=415997, classify unreachability precisely, IsThenStatementUnreachable could be due to null analysis
if ((mergedInfo.tagBits & FlowInfo.UNREACHABLE_OR_DEAD) != 0 && (initsWhenTrue.tagBits & FlowInfo.UNREACHABLE) == FlowInfo.UNREACHABLE_BY_NULLANALYSIS) {
mergedInfo.tagBits &= ~UNREACHABLE_OR_DEAD;
mergedInfo.tagBits |= UNREACHABLE_BY_NULLANALYSIS;
}
}
else {
mergedInfo = initsWhenTrue.
mergedWith(initsWhenFalse.unconditionalInits());
}
return mergedInfo;
}
/**
* Find out the reachability mode of this flowInfo.
* @return REACHABLE if this flow info is reachable, otherwise
* either UNREACHABLE_OR_DEAD or UNREACHABLE_BY_NULLANALYSIS.
*/
public int reachMode() {
return this.tagBits & UNREACHABLE;
}
/**
* Return a flow info that carries the same information as the result of
* {@link #initsWhenTrue() initsWhenTrue}, but warrantied to be different
* from this.
* Caveat: side effects on the result may affect components of this.
* @return the result of initsWhenTrue or a copy of it
*/
abstract public FlowInfo safeInitsWhenTrue();
/**
* Set this flow info reach mode and return this.
* @param reachMode one of {@link #REACHABLE REACHABLE}, {@link #UNREACHABLE_OR_DEAD UNREACHABLE_OR_DEAD},
* {@link #UNREACHABLE_BY_NULLANALYSIS UNREACHABLE_BY_NULLANALYSIS} or {@link #UNREACHABLE UNREACHABLE}
* @return this, with the reach mode set to reachMode
*/
abstract public FlowInfo setReachMode(int reachMode);
/**
* Return the intersection of this and otherInits, that is
* one of:
* - the receiver updated in the following way:
* - intersection of definitely assigned variables,
*
- union of potentially assigned variables,
*
- similar operations for null,
* - or the receiver or otherInits if the other one is non
* reachable.
* otherInits is not affected, and is not returned either (no
* need to protect the result).
* @param otherInits the flow info to merge with this
* @return the intersection of this and otherInits.
*/
abstract public UnconditionalFlowInfo mergedWith(
UnconditionalFlowInfo otherInits);
abstract public UnconditionalFlowInfo mergeDefiniteInitsWith(UnconditionalFlowInfo otherInits);
/**
* Return a copy of this unconditional flow info, deprived from its null
* info. {@link #DEAD_END DEAD_END} is returned unmodified.
* @return a copy of this unconditional flow info deprived from its null info
*/
abstract public UnconditionalFlowInfo nullInfoLessUnconditionalCopy();
@Override
public String toString(){
if (this == DEAD_END){
return "FlowInfo.DEAD_END"; //$NON-NLS-1$
}
return super.toString();
}
/**
* Return a new flow info that holds the same information as this would after
* a call to unconditionalInits, but leaving this info unaffected. Moreover,
* the result can be modified without affecting this.
* @return a new flow info carrying this unconditional flow info
*/
abstract public UnconditionalFlowInfo unconditionalCopy();
/**
* Return a new flow info that holds the same information as this would after
* a call to {@link #unconditionalInits() unconditionalInits} followed by the
* erasure of fields specific information, but leaving this flow info unaffected.
* @return a new flow info carrying the unconditional flow info for local variables
*/
abstract public UnconditionalFlowInfo unconditionalFieldLessCopy();
/**
* Return a flow info that merges the possible paths of execution described by
* this flow info. In case of an unconditional flow info, return this. In case
* of a conditional flow info, merge branches recursively. Caveat: this may
* be affected, and modifying the result may affect this.
* @return a flow info that merges the possible paths of execution described by
* this
*/
abstract public UnconditionalFlowInfo unconditionalInits();
/**
* Return a new flow info that holds the same information as this would after
* a call to {@link #unconditionalInits() unconditionalInits}, but leaving
* this info unaffected. Side effects on the result might affect this though
* (consider it as read only).
* @return a flow info carrying this unconditional flow info
*/
abstract public UnconditionalFlowInfo unconditionalInitsWithoutSideEffect();
/**
* Resets the definite and potential initialization info for the given local variable
* @param local
*/
abstract public void resetAssignmentInfo(LocalVariableBinding local);
/**
* Check whether 'tagBits' contains either {@link TagBits#AnnotationNonNull} or {@link TagBits#AnnotationNullable},
* and answer the corresponding null status ({@link #NON_NULL} etc.).
*/
public static int tagBitsToNullStatus(long tagBits) {
if ((tagBits & TagBits.AnnotationNonNull) != 0)
return NON_NULL;
if ((tagBits & TagBits.AnnotationNullable) != 0)
return POTENTIALLY_NULL | POTENTIALLY_NON_NULL;
return UNKNOWN;
}
}