DfaTypeValue -> DfaFactMapValue

This commit is contained in:
Tagir Valeev
2017-10-20 15:45:53 +07:00
parent 86ab4c786e
commit 2ee1bec87d
13 changed files with 158 additions and 223 deletions
@@ -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);
@@ -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);
@@ -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
@@ -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();
@@ -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;
}
}
@@ -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) {
@@ -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);
}
@@ -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;
@@ -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;
}
}
@@ -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;