mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
DfaTypeValue -> DfaFactMapValue
This commit is contained in:
+1
-1
@@ -1409,7 +1409,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
if (type instanceof PsiClassType) {
|
||||
type = ((PsiClassType)type).rawType();
|
||||
}
|
||||
addInstruction(new PushInstruction(myFactory.createTypeValue(type, Nullness.UNKNOWN), null));
|
||||
addInstruction(new PushInstruction(myFactory.createTypeValue(type, Nullness.NOT_NULL), null));
|
||||
addInstruction(new InstanceofInstruction(expression, myProject, operand, type));
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -65,7 +65,7 @@ public final class DfaFactMap {
|
||||
*/
|
||||
@NotNull
|
||||
public <T> DfaFactMap with(@NotNull DfaFactType<T> type, @Nullable T value) {
|
||||
KeyFMap newMap = value == null ? myMap.minus(type) : myMap.plus(type, value);
|
||||
KeyFMap newMap = value == null || type.isUnknown(value) ? myMap.minus(type) : myMap.plus(type, value);
|
||||
return newMap == myMap ? this : new DfaFactMap(newMap);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,24 @@ public final class DfaFactMap {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the passed fact map is always distinct from this map (i.e. any exact value
|
||||
* which conforms the passed fact map does not conform this fact map).
|
||||
*
|
||||
* @param otherMap a fact map to check
|
||||
* @return true if this fact map is always distinct from other map.
|
||||
*/
|
||||
public boolean isDistinct(DfaFactMap otherMap) {
|
||||
for (DfaFactType<?> key : DfaFactType.getTypes()) {
|
||||
@SuppressWarnings("unchecked")
|
||||
DfaFactType<Object> type = (DfaFactType<Object>)key;
|
||||
Object thisValue = myMap.get(type);
|
||||
Object other = otherMap.get(type);
|
||||
if(thisValue != null && other != null && type.isDistinct(thisValue, other)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a fact map which is additionally restricted by supplied fact.
|
||||
* The returned map is a sub-state of this map.
|
||||
@@ -127,16 +145,6 @@ public final class DfaFactMap {
|
||||
return result;
|
||||
}
|
||||
|
||||
public DfaFactMap invert() {
|
||||
return StreamEx.of(DfaFactType.getTypes()).foldLeft(this, DfaFactMap::invert);
|
||||
}
|
||||
|
||||
private <TT> DfaFactMap invert(@NotNull DfaFactType<TT> type) {
|
||||
TT fact = get(type);
|
||||
return with(type, type.invert(fact));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a fact map which additionally allows having supplied value for the supplied fact
|
||||
*
|
||||
|
||||
@@ -44,6 +44,15 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
return fact ? "Nullable" : "NotNull";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
Boolean intersectFacts(@NotNull Boolean left, @NotNull Boolean right) {
|
||||
if (Boolean.FALSE.equals(left) || Boolean.FALSE.equals(right)) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
return super.intersectFacts(left, right);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
Boolean fromDfaValue(DfaValue value) {
|
||||
@@ -51,9 +60,6 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
return ((DfaConstValue)value).getValue() == null;
|
||||
}
|
||||
if (value instanceof DfaBoxedValue || value instanceof DfaUnboxedValue) return false;
|
||||
if (value instanceof DfaTypeValue) {
|
||||
return NullnessUtil.toBoolean(((DfaTypeValue)value).getNullness());
|
||||
}
|
||||
if (value instanceof DfaFactMapValue) {
|
||||
DfaFactMapValue factValue = (DfaFactMapValue)value;
|
||||
if (factValue.get(OPTIONAL_PRESENCE) != null || factValue.get(RANGE) != null) return false;
|
||||
@@ -75,8 +81,8 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
*/
|
||||
public static final DfaFactType<Boolean> OPTIONAL_PRESENCE = new DfaFactType<Boolean>("Optional presense") {
|
||||
@Override
|
||||
Boolean invert(Boolean fact) {
|
||||
return fact == null ? null : !fact;
|
||||
public boolean isDistinct(@NotNull Boolean fact, @NotNull Boolean otherFact) {
|
||||
return fact != otherFact;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,6 +101,11 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
return superFact == null || subFact != null && superFact.contains(subFact);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isUnknown(@NotNull LongRangeSet fact) {
|
||||
return LongRangeSet.all().equals(fact);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
LongRangeSet fromDfaValue(DfaValue value) {
|
||||
@@ -142,21 +153,16 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
* {@link TypeConstraint#EMPTY} value is equivalent to absent fact (not constrained)
|
||||
*/
|
||||
public static final DfaFactType<TypeConstraint> TYPE_CONSTRAINT = new DfaFactType<TypeConstraint>("Type") {
|
||||
@Nullable
|
||||
@Override
|
||||
TypeConstraint fromDfaValue(DfaValue value) {
|
||||
if(value instanceof DfaTypeValue) {
|
||||
TypeConstraint constraint = TypeConstraint.EMPTY.withInstanceofValue(((DfaTypeValue)value).getDfaType());
|
||||
return constraint == null || constraint.isEmpty() ? null : constraint;
|
||||
}
|
||||
return super.fromDfaValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isSuper(@Nullable TypeConstraint superFact, @Nullable TypeConstraint subFact) {
|
||||
return superFact == null || (subFact != null && superFact.isSuperStateOf(subFact));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isUnknown(@NotNull TypeConstraint fact) {
|
||||
return fact.equals(TypeConstraint.EMPTY);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
TypeConstraint intersectFacts(@NotNull TypeConstraint left, @NotNull TypeConstraint right) {
|
||||
@@ -201,6 +207,14 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
return Objects.equals(superFact, subFact);
|
||||
}
|
||||
|
||||
boolean isDistinct(@NotNull T fact, @NotNull T otherFact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isUnknown(@NotNull T fact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects two facts of this type.
|
||||
*
|
||||
@@ -213,16 +227,6 @@ public abstract class DfaFactType<T> extends Key<T> {
|
||||
return left.equals(right) ? left : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inverts a fact. This operation might not be reversible.
|
||||
*
|
||||
* @param fact
|
||||
* @return an inverted fact
|
||||
*/
|
||||
T invert(T fact) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unites two facts of this type.
|
||||
*
|
||||
|
||||
@@ -55,6 +55,8 @@ public interface DfaMemoryState {
|
||||
@Nullable
|
||||
<T> T getValueFact(@NotNull DfaFactType<T> factType, @NotNull DfaValue value);
|
||||
|
||||
void forceNotNull(DfaVariableValue var);
|
||||
|
||||
void flushFields();
|
||||
|
||||
void flushVariable(DfaVariableValue variable);
|
||||
|
||||
+48
-51
@@ -258,16 +258,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return;
|
||||
}
|
||||
|
||||
setVariableState(var, withValueNullability(value, getVariableState(var).withValue(value)));
|
||||
if (value instanceof DfaTypeValue) {
|
||||
if (((DfaTypeValue)value).isNotNull()) {
|
||||
DfaRelationValue dfaInstanceof = myFactory.getRelationFactory().createRelation(var, RelationType.IS, value);
|
||||
applyCondition(dfaInstanceof);
|
||||
} else {
|
||||
applyInstanceofOrNull(var, ((DfaTypeValue)value).getDfaType());
|
||||
}
|
||||
DfaVariableState state = getVariableState(var).withValue(value);
|
||||
if (value instanceof DfaFactMapValue) {
|
||||
setVariableState(var, state.withFacts(((DfaFactMapValue)value).getFacts()));
|
||||
}
|
||||
else {
|
||||
setVariableState(var, isNull(value) ? state.withFact(DfaFactType.CAN_BE_NULL, true) : state);
|
||||
DfaRelationValue dfaEqual = myFactory.getRelationFactory().createRelation(var, RelationType.EQ, value);
|
||||
if (dfaEqual == null) return;
|
||||
applyCondition(dfaEqual);
|
||||
@@ -283,16 +279,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
}
|
||||
|
||||
private DfaVariableState withValueNullability(DfaValue value, DfaVariableState state) {
|
||||
if (value instanceof DfaTypeValue) {
|
||||
return state.withFact(DfaFactType.CAN_BE_NULL, NullnessUtil.toBoolean(((DfaTypeValue)value).getNullness()));
|
||||
}
|
||||
if (isNull(value)) {
|
||||
return state.withFact(DfaFactType.CAN_BE_NULL, true);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
private DfaValue handleFlush(DfaVariableValue flushed, DfaValue value) {
|
||||
if (value instanceof DfaVariableValue && (value == flushed || myFactory.getVarFactory().getAllQualifiedBy(flushed).contains(value))) {
|
||||
Nullness nullability = isNotNull(value) ? Nullness.NOT_NULL : ((DfaVariableValue)value).getInherentNullability();
|
||||
@@ -649,7 +635,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
public boolean isNotNull(DfaValue dfaVar) {
|
||||
if (dfaVar instanceof DfaConstValue) return ((DfaConstValue)dfaVar).getValue() != null;
|
||||
if (dfaVar instanceof DfaBoxedValue) return true;
|
||||
if (dfaVar instanceof DfaTypeValue) return ((DfaTypeValue)dfaVar).isNotNull();
|
||||
if (dfaVar instanceof DfaFactMapValue) return Boolean.FALSE.equals(((DfaFactMapValue)dfaVar).get(DfaFactType.CAN_BE_NULL));
|
||||
if (dfaVar instanceof DfaVariableValue) {
|
||||
if (getVariableState((DfaVariableValue)dfaVar).isNotNull()) return true;
|
||||
|
||||
@@ -721,19 +707,22 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean applyFacts(DfaValue dfaLeft, DfaFactMap facts) {
|
||||
if (dfaLeft instanceof DfaVariableValue && !isUnknownState(dfaLeft)) {
|
||||
DfaVariableState state = getVariableState((DfaVariableValue)dfaLeft).intersectMap(facts);
|
||||
private boolean applyFacts(DfaValue value, DfaFactMap facts) {
|
||||
if (value instanceof DfaVariableValue && !isUnknownState(value)) {
|
||||
DfaVariableState state = getVariableState((DfaVariableValue)value).intersectMap(facts);
|
||||
if (state == null) return false;
|
||||
setVariableState((DfaVariableValue)dfaLeft, state);
|
||||
setVariableState((DfaVariableValue)value, state);
|
||||
if (Boolean.FALSE.equals(facts.get(DfaFactType.CAN_BE_NULL))) {
|
||||
return applyRelation(value, getFactory().getConstFactory().getNull(), true);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
<T> boolean applyFact(DfaVariableValue target, DfaFactType<T> factType, T range) {
|
||||
if (!isUnknownState(target) && range != null) {
|
||||
<T> boolean applyFact(DfaVariableValue target, DfaFactType<T> factType, T value) {
|
||||
if (!isUnknownState(target) && value != null) {
|
||||
DfaVariableState state = getVariableState(target);
|
||||
DfaVariableState newState = state.intersectFact(factType, range);
|
||||
DfaVariableState newState = state.intersectFact(factType, value);
|
||||
if (newState == null) return false;
|
||||
setVariableState(target, newState);
|
||||
}
|
||||
@@ -814,39 +803,38 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
}
|
||||
|
||||
if (dfaLeft instanceof DfaVariableValue && dfaRight instanceof DfaFactMapValue && relationType == RelationType.IS) {
|
||||
return applyFacts(dfaLeft, ((DfaFactMapValue)dfaRight).getFacts());
|
||||
}
|
||||
if (dfaLeft instanceof DfaVariableValue && dfaRight instanceof DfaFactMapValue && relationType == RelationType.IS_NOT) {
|
||||
return applyFacts(dfaLeft, ((DfaFactMapValue)dfaRight).getFacts().invert());
|
||||
}
|
||||
|
||||
if (dfaRight instanceof DfaTypeValue) {
|
||||
if (dfaRight instanceof DfaFactMapValue) {
|
||||
if (dfaLeft instanceof DfaVariableValue) {
|
||||
DfaVariableValue dfaVar = (DfaVariableValue)dfaLeft;
|
||||
if (isUnknownState(dfaVar)) return true;
|
||||
|
||||
DfaTypeValue typeValue = (DfaTypeValue)dfaRight;
|
||||
DfaFactMapValue factValue = (DfaFactMapValue)dfaRight;
|
||||
switch (relationType) {
|
||||
case EQ:
|
||||
return !(typeValue.isNotNull() && isNull(dfaVar));
|
||||
case IS_NOT: {
|
||||
DfaVariableState newState = getVariableState(dfaVar).withNotInstanceofValue(typeValue.getDfaType());
|
||||
if (newState != null) {
|
||||
setVariableState(dfaVar, newState);
|
||||
return true;
|
||||
}
|
||||
return !getVariableState(dfaVar).isNotNull() && applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false);
|
||||
}
|
||||
return !(Boolean.FALSE.equals(factValue.get(DfaFactType.CAN_BE_NULL)) && isNull(dfaVar));
|
||||
case IS:
|
||||
if (applyRelation(dfaVar, myFactory.getConstFactory().getNull(), true)) {
|
||||
DfaVariableState newState = getVariableState(dfaVar).withInstanceofValue(typeValue.getDfaType());
|
||||
if (newState != null) {
|
||||
setVariableState(dfaVar, newState);
|
||||
return true;
|
||||
return applyFacts(dfaVar, factValue.getFacts());
|
||||
case IS_NOT: {
|
||||
Boolean optionalPresence = factValue.get(DfaFactType.OPTIONAL_PRESENCE);
|
||||
if(optionalPresence != null) {
|
||||
return applyFact(dfaVar, DfaFactType.OPTIONAL_PRESENCE, !optionalPresence);
|
||||
}
|
||||
Boolean canBeNull = factValue.get(DfaFactType.CAN_BE_NULL);
|
||||
TypeConstraint constraint = factValue.get(DfaFactType.TYPE_CONSTRAINT);
|
||||
if (constraint != null && constraint.getNotInstanceofValues().isEmpty()) {
|
||||
DfaVariableState state = getVariableState(dfaVar);
|
||||
for (DfaPsiType type : constraint.getInstanceofValues()) {
|
||||
state = state.withNotInstanceofValue(type);
|
||||
if (state == null) {
|
||||
return Boolean.FALSE.equals(canBeNull) &&
|
||||
!getVariableState(dfaVar).isNotNull() &&
|
||||
applyRelation(dfaVar, myFactory.getConstFactory().getNull(), false);
|
||||
}
|
||||
setVariableState(dfaVar, state);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
@@ -1067,7 +1055,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
@Override
|
||||
public boolean checkNotNullable(DfaValue value) {
|
||||
if (value == myFactory.getConstFactory().getNull()) return false;
|
||||
if (value instanceof DfaTypeValue && ((DfaTypeValue)value).isNullable()) return false;
|
||||
if (value instanceof DfaFactMapValue && Boolean.TRUE.equals(((DfaFactMapValue)value).get(DfaFactType.CAN_BE_NULL))) return false;
|
||||
|
||||
if (value instanceof DfaVariableValue) {
|
||||
DfaVariableValue varValue = (DfaVariableValue)value;
|
||||
@@ -1095,6 +1083,15 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return factType.fromDfaValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forceNotNull(DfaVariableValue var) {
|
||||
if (isUnknownState(var)) return;
|
||||
DfaVariableState state = getVariableState(var);
|
||||
flushVariable(var);
|
||||
setVariableState(var, state.withFact(DfaFactType.CAN_BE_NULL, false));
|
||||
applyRelation(var, getFactory().getConstFactory().getNull(), true);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private DfaValue resolveVariableValue(DfaVariableValue var) {
|
||||
DfaConstValue constValue = getConstantValue(var);
|
||||
|
||||
+10
-6
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -99,8 +100,7 @@ class DfaVariableState {
|
||||
|
||||
@NotNull
|
||||
<T> DfaVariableState withFact(DfaFactType<T> type, T value) {
|
||||
DfaFactMap factMap = myFactMap.with(type, value);
|
||||
return myFactMap.equals(factMap) ? this : createCopy(factMap);
|
||||
return withFacts(myFactMap.with(type, value));
|
||||
}
|
||||
|
||||
<T> DfaVariableState withoutFact(DfaFactType<T> type) {
|
||||
@@ -109,13 +109,17 @@ class DfaVariableState {
|
||||
|
||||
@Nullable
|
||||
<T> DfaVariableState intersectFact(DfaFactType<T> type, T value) {
|
||||
DfaFactMap factMap = myFactMap.intersect(type, value);
|
||||
return factMap == null ? null : myFactMap.equals(factMap) ? this : createCopy(factMap);
|
||||
return withFacts(myFactMap.intersect(type, value));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
DfaVariableState intersectMap(DfaFactMap map) {
|
||||
DfaFactMap factMap = myFactMap.intersect(map);
|
||||
return factMap == null ? null : myFactMap.equals(factMap) ? this : createCopy(factMap);
|
||||
return withFacts(myFactMap.intersect(map));
|
||||
}
|
||||
|
||||
@Contract("null -> null;!null -> !null")
|
||||
public DfaVariableState withFacts(@Nullable DfaFactMap facts) {
|
||||
return facts == null ? null : facts.equals(myFactMap) ? this : createCopy(facts);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+2
-2
@@ -52,8 +52,8 @@ public abstract class MethodContract {
|
||||
switch (getReturnValue()) {
|
||||
case NULL_VALUE: return factory.getConstFactory().getNull();
|
||||
case NOT_NULL_VALUE:
|
||||
return defaultResult instanceof DfaTypeValue
|
||||
? ((DfaTypeValue)defaultResult).withNullness(Nullness.NOT_NULL)
|
||||
return defaultResult instanceof DfaFactMapValue
|
||||
? ((DfaFactMapValue)defaultResult).withFact(DfaFactType.CAN_BE_NULL, false)
|
||||
: DfaUnknownValue.getInstance();
|
||||
case TRUE_VALUE: return factory.getConstFactory().getTrue();
|
||||
case FALSE_VALUE: return factory.getConstFactory().getFalse();
|
||||
|
||||
+18
-15
@@ -95,7 +95,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaMemoryStateImpl stateImpl = (DfaMemoryStateImpl)memState;
|
||||
stateImpl.setVariableState(var, stateImpl.getVariableState(var).withFact(DfaFactType.CAN_BE_NULL, true));
|
||||
}
|
||||
} else if (dfaDest instanceof DfaTypeValue && ((DfaTypeValue)dfaDest).isNotNull()) {
|
||||
} else if (dfaDest instanceof DfaFactMapValue && Boolean.FALSE.equals(((DfaFactMapValue)dfaDest).get(DfaFactType.CAN_BE_NULL))) {
|
||||
checkNotNullable(memState, dfaSource, problem, rValue);
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaValue array = memState.pop();
|
||||
PsiArrayAccessExpression arrayExpression = instruction.getExpression();
|
||||
if (!checkNotNullable(memState, array, NullabilityProblem.fieldAccessNPE, arrayExpression.getArrayExpression())) {
|
||||
forceNotNull(runner, memState, array);
|
||||
forceNotNull(memState, array);
|
||||
}
|
||||
boolean alwaysOutOfBounds = false;
|
||||
if (index != DfaUnknownValue.getInstance()) {
|
||||
@@ -182,7 +182,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
public DfaInstructionState[] visitFieldReference(FieldReferenceInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
final DfaValue qualifier = memState.pop();
|
||||
if (!checkNotNullable(memState, qualifier, NullabilityProblem.fieldAccessNPE, instruction.getElementToAssert())) {
|
||||
forceNotNull(runner, memState, qualifier);
|
||||
forceNotNull(memState, qualifier);
|
||||
}
|
||||
PsiElement parent = instruction.getExpression().getParent();
|
||||
if (parent instanceof PsiMethodReferenceExpression) {
|
||||
@@ -394,7 +394,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaMemoryState memState,
|
||||
boolean contractOnly) {
|
||||
DfaValue[] argValues = popCallArguments(instruction, runner, memState, contractOnly);
|
||||
final DfaValue qualifier = popQualifier(instruction, runner, memState);
|
||||
final DfaValue qualifier = popQualifier(instruction, memState);
|
||||
return new DfaCallArguments(qualifier, argValues);
|
||||
}
|
||||
|
||||
@@ -434,7 +434,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
Nullness requiredNullability = instruction.getArgRequiredNullability(paramIndex);
|
||||
if (requiredNullability == Nullness.NOT_NULL) {
|
||||
if (!checkNotNullable(memState, arg, NullabilityProblem.passingNullableToNotNullParameter, anchor)) {
|
||||
forceNotNull(runner, memState, arg);
|
||||
forceNotNull(memState, arg);
|
||||
}
|
||||
}
|
||||
else if (requiredNullability == Nullness.UNKNOWN) {
|
||||
@@ -444,13 +444,13 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return argValues;
|
||||
}
|
||||
|
||||
private DfaValue popQualifier(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
private DfaValue popQualifier(MethodCallInstruction instruction, DfaMemoryState memState) {
|
||||
@NotNull final DfaValue qualifier = memState.pop();
|
||||
boolean unboxing = instruction.getMethodType() == MethodCallInstruction.MethodType.UNBOXING;
|
||||
NullabilityProblem problem = unboxing ? NullabilityProblem.unboxingNullable : NullabilityProblem.callNPE;
|
||||
PsiElement anchor = instruction.getContext();
|
||||
if (!checkNotNullable(memState, qualifier, problem, anchor)) {
|
||||
forceNotNull(runner, memState, qualifier);
|
||||
forceNotNull(memState, qualifier);
|
||||
}
|
||||
return qualifier;
|
||||
}
|
||||
@@ -503,10 +503,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return falseStates;
|
||||
}
|
||||
|
||||
private static void forceNotNull(DataFlowRunner runner, DfaMemoryState memState, DfaValue arg) {
|
||||
private static void forceNotNull(DfaMemoryState memState, DfaValue arg) {
|
||||
if (arg instanceof DfaVariableValue) {
|
||||
DfaVariableValue var = (DfaVariableValue)arg;
|
||||
memState.setVarValue(var, runner.getFactory().createTypeValue(var.getVariableType(), Nullness.NOT_NULL));
|
||||
memState.forceNotNull(var);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -620,10 +620,10 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaValue arg = memState.peek();
|
||||
if (arg instanceof DfaVariableValue) {
|
||||
DfaVariableValue var = (DfaVariableValue)arg;
|
||||
memState.setVarValue(var, runner.getFactory().createTypeValue(var.getVariableType(), Nullness.NOT_NULL));
|
||||
} else if (arg instanceof DfaTypeValue) {
|
||||
memState.forceNotNull(var);
|
||||
} else if (arg instanceof DfaFactMapValue) {
|
||||
memState.pop();
|
||||
memState.push(((DfaTypeValue)arg).withNullness(Nullness.NOT_NULL));
|
||||
memState.push(((DfaFactMapValue)arg).withFact(DfaFactType.CAN_BE_NULL, false));
|
||||
} else if (memState.isNull(arg) && instruction.getProblem() == NullabilityProblem.nullableFunctionReturn) {
|
||||
memState.pop();
|
||||
memState.push(runner.getFactory().createTypeValue(PsiType.VOID, Nullness.NOT_NULL));
|
||||
@@ -712,12 +712,15 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
|
||||
private void handleInstanceof(InstanceofInstruction instruction, DfaValue dfaRight, DfaValue dfaLeft) {
|
||||
if (dfaLeft instanceof DfaTypeValue && dfaRight instanceof DfaTypeValue) {
|
||||
if (!((DfaTypeValue)dfaLeft).isNotNull()) {
|
||||
if (dfaLeft instanceof DfaFactMapValue && dfaRight instanceof DfaFactMapValue) {
|
||||
DfaFactMapValue left = (DfaFactMapValue)dfaLeft;
|
||||
DfaFactMapValue right = (DfaFactMapValue)dfaRight;
|
||||
|
||||
if (!Boolean.FALSE.equals(left.get(DfaFactType.CAN_BE_NULL))) {
|
||||
myCanBeNullInInstanceof.add(instruction);
|
||||
}
|
||||
|
||||
if (((DfaTypeValue)dfaRight).getDfaType().isAssignableFrom(((DfaTypeValue)dfaLeft).getDfaType())) {
|
||||
if (right.getFacts().with(DfaFactType.CAN_BE_NULL, null).isSuperStateOf(left.getFacts())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -21,6 +21,7 @@ import com.intellij.psi.PsiClassType;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import one.util.streamex.EntryStream;
|
||||
import one.util.streamex.MoreCollectors;
|
||||
import one.util.streamex.StreamEx;
|
||||
@@ -81,7 +82,7 @@ public final class TypeConstraint {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TypeConstraint withInstanceofValue(@NotNull DfaPsiType type) {
|
||||
public TypeConstraint withInstanceofValue(@NotNull DfaPsiType type) {
|
||||
if (type.getPsiType() instanceof PsiPrimitiveType) return this;
|
||||
|
||||
if (!checkInstanceofValue(type)) {
|
||||
@@ -100,11 +101,14 @@ public final class TypeConstraint {
|
||||
Set<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.removeAll(moreGeneric);
|
||||
newInstanceof.add(type);
|
||||
if (newInstanceof.size() == 1 && TypeUtils.isJavaLangObject(newInstanceof.iterator().next().getPsiType())) {
|
||||
newInstanceof = Collections.emptySet();
|
||||
}
|
||||
return create(newInstanceof, myNotInstanceofValues);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
TypeConstraint withNotInstanceofValue(DfaPsiType type) {
|
||||
public TypeConstraint withNotInstanceofValue(DfaPsiType type) {
|
||||
if (myNotInstanceofValues.contains(type)) return this;
|
||||
|
||||
for (DfaPsiType dfaTypeValue : myInstanceofValues) {
|
||||
|
||||
+5
@@ -17,10 +17,15 @@ public class DfaFactMapValue extends DfaValue {
|
||||
myFacts = facts;
|
||||
}
|
||||
|
||||
public <T> DfaValue withFact(@NotNull DfaFactType<T> factType, @Nullable T value) {
|
||||
return getFactory().getFactFactory().createValue(myFacts.with(factType, value));
|
||||
}
|
||||
|
||||
public DfaFactMap getFacts() {
|
||||
return myFacts;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public <T> T get(@NotNull DfaFactType<T> factType) {
|
||||
return myFacts.get(factType);
|
||||
}
|
||||
|
||||
+4
-3
@@ -160,7 +160,8 @@ public class DfaRelationValue extends DfaValue {
|
||||
}
|
||||
|
||||
public DfaRelationValue createRelation(DfaValue dfaLeft, RelationType relationType, DfaValue dfaRight) {
|
||||
if ((relationType == RelationType.IS || relationType == RelationType.IS_NOT) && dfaRight instanceof DfaFactMapValue) {
|
||||
if ((relationType == RelationType.IS || relationType == RelationType.IS_NOT) &&
|
||||
dfaRight instanceof DfaFactMapValue && !(dfaLeft instanceof DfaFactMapValue)) {
|
||||
return createCanonicalRelation(dfaLeft, relationType, dfaRight);
|
||||
}
|
||||
if (dfaLeft instanceof DfaVariableValue || dfaLeft instanceof DfaBoxedValue || dfaLeft instanceof DfaUnboxedValue
|
||||
@@ -171,10 +172,10 @@ public class DfaRelationValue extends DfaValue {
|
||||
}
|
||||
return createCanonicalRelation(dfaLeft, relationType, dfaRight);
|
||||
}
|
||||
if (dfaLeft instanceof DfaTypeValue && dfaRight instanceof DfaConstValue) {
|
||||
if (dfaLeft instanceof DfaFactMapValue && dfaRight instanceof DfaConstValue) {
|
||||
return createCanonicalRelation(DfaUnknownValue.getInstance(), relationType, dfaRight);
|
||||
}
|
||||
else if (dfaRight instanceof DfaTypeValue && dfaLeft instanceof DfaConstValue) {
|
||||
else if (dfaRight instanceof DfaFactMapValue && dfaLeft instanceof DfaConstValue) {
|
||||
return createCanonicalRelation(DfaUnknownValue.getInstance(), relationType, dfaLeft);
|
||||
}
|
||||
return null;
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.Nullness;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
public class DfaTypeValue extends DfaValue {
|
||||
public static class Factory {
|
||||
private final Map<DfaPsiType,ArrayList<DfaTypeValue>> myCache = ContainerUtil.newHashMap();
|
||||
@NotNull
|
||||
private final DfaValueFactory myFactory;
|
||||
|
||||
Factory(@NotNull DfaValueFactory factory) {
|
||||
myFactory = factory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
DfaTypeValue createTypeValue(@NotNull DfaPsiType type, @NotNull Nullness nullness) {
|
||||
ArrayList<DfaTypeValue> conditions = myCache.get(type);
|
||||
if (conditions == null) {
|
||||
conditions = new ArrayList<>();
|
||||
myCache.put(type, conditions);
|
||||
}
|
||||
else {
|
||||
for (DfaTypeValue aType : conditions) {
|
||||
if (aType.myNullness == nullness) return aType;
|
||||
}
|
||||
}
|
||||
|
||||
DfaTypeValue result = new DfaTypeValue(type, nullness, myFactory);
|
||||
conditions.add(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private final DfaPsiType myType;
|
||||
@NotNull
|
||||
private final Nullness myNullness;
|
||||
|
||||
private DfaTypeValue(@NotNull DfaPsiType type, @NotNull Nullness nullness, @NotNull DfaValueFactory factory) {
|
||||
super(factory);
|
||||
myType = type;
|
||||
myNullness = nullness;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaPsiType getDfaType() {
|
||||
return myType;
|
||||
}
|
||||
|
||||
public boolean isNullable() {
|
||||
return myNullness == Nullness.NULLABLE;
|
||||
}
|
||||
|
||||
public boolean isNotNull() {
|
||||
return myNullness == Nullness.NOT_NULL;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public Nullness getNullness() {
|
||||
return myNullness;
|
||||
}
|
||||
|
||||
public DfaTypeValue withNullness(Nullness nullness) {
|
||||
return nullness == myNullness ? this : myFactory.getTypeFactory().createTypeValue(myType, nullness);
|
||||
}
|
||||
|
||||
@NonNls
|
||||
public String toString() {
|
||||
return myType + ", nullable=" + myNullness;
|
||||
}
|
||||
|
||||
}
|
||||
+14
-12
@@ -52,7 +52,6 @@ public class DfaValueFactory {
|
||||
myVarFactory = new DfaVariableValue.Factory(this);
|
||||
myConstFactory = new DfaConstValue.Factory(this);
|
||||
myBoxedFactory = new DfaBoxedValue.Factory(this);
|
||||
myTypeFactory = new DfaTypeValue.Factory(this);
|
||||
myRelationFactory = new DfaRelationValue.Factory(this);
|
||||
myExpressionFactory = new DfaExpressionFactory(this);
|
||||
myFactFactory = new DfaFactMapValue.Factory(this);
|
||||
@@ -78,7 +77,9 @@ public class DfaValueFactory {
|
||||
@NotNull
|
||||
public DfaValue createTypeValue(@Nullable PsiType type, @NotNull Nullness nullability) {
|
||||
if (type == null) return DfaUnknownValue.getInstance();
|
||||
return getTypeFactory().createTypeValue(createDfaType(type), nullability);
|
||||
DfaFactMap facts = DfaFactMap.EMPTY.with(DfaFactType.TYPE_CONSTRAINT, TypeConstraint.EMPTY.withInstanceofValue(createDfaType(type)))
|
||||
.with(DfaFactType.CAN_BE_NULL, NullnessUtil.toBoolean(nullability));
|
||||
return getFactFactory().createValue(facts);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -137,10 +138,12 @@ public class DfaValueFactory {
|
||||
|
||||
@Nullable
|
||||
private DfaConstValue tryEvaluate(DfaValue dfaLeft, RelationType relationType, DfaValue dfaRight) {
|
||||
if(dfaRight instanceof DfaTypeValue && dfaLeft == getConstFactory().getNull()) {
|
||||
if (dfaRight instanceof DfaFactMapValue && dfaLeft == getConstFactory().getNull()) {
|
||||
return tryEvaluate(dfaRight, relationType, dfaLeft);
|
||||
}
|
||||
if (dfaLeft instanceof DfaTypeValue && dfaRight == getConstFactory().getNull() && ((DfaTypeValue)dfaLeft).isNotNull()) {
|
||||
if (dfaLeft instanceof DfaFactMapValue &&
|
||||
dfaRight == getConstFactory().getNull() &&
|
||||
Boolean.FALSE.equals(((DfaFactMapValue)dfaLeft).get(DfaFactType.CAN_BE_NULL))) {
|
||||
if (relationType == RelationType.EQ) {
|
||||
return getConstFactory().getFalse();
|
||||
}
|
||||
@@ -152,8 +155,13 @@ public class DfaValueFactory {
|
||||
if(dfaLeft instanceof DfaFactMapValue && dfaRight instanceof DfaFactMapValue) {
|
||||
if(relationType == RelationType.IS || relationType == RelationType.IS_NOT) {
|
||||
boolean isSuperState = ((DfaFactMapValue)dfaRight).getFacts().isSuperStateOf(((DfaFactMapValue)dfaLeft).getFacts());
|
||||
boolean wantedSuperState = relationType == RelationType.IS;
|
||||
return getBoolean(isSuperState == wantedSuperState);
|
||||
if (isSuperState) {
|
||||
return getBoolean(relationType == RelationType.IS);
|
||||
}
|
||||
boolean isDistinct = ((DfaFactMapValue)dfaRight).getFacts().isDistinct(((DfaFactMapValue)dfaLeft).getFacts());
|
||||
if (isDistinct) {
|
||||
return getBoolean(relationType == RelationType.IS_NOT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -215,7 +223,6 @@ public class DfaValueFactory {
|
||||
private final DfaVariableValue.Factory myVarFactory;
|
||||
private final DfaConstValue.Factory myConstFactory;
|
||||
private final DfaBoxedValue.Factory myBoxedFactory;
|
||||
private final DfaTypeValue.Factory myTypeFactory;
|
||||
private final DfaRelationValue.Factory myRelationFactory;
|
||||
private final DfaExpressionFactory myExpressionFactory;
|
||||
private final DfaFactMapValue.Factory myFactFactory;
|
||||
@@ -234,11 +241,6 @@ public class DfaValueFactory {
|
||||
return myBoxedFactory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaTypeValue.Factory getTypeFactory() {
|
||||
return myTypeFactory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaRelationValue.Factory getRelationFactory() {
|
||||
return myRelationFactory;
|
||||
|
||||
Reference in New Issue
Block a user