From 2ee1bec87dbfe390bf8a6665e3e00f84b1dba342 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Thu, 19 Oct 2017 13:59:45 +0700 Subject: [PATCH] DfaTypeValue -> DfaFactMapValue --- .../dataFlow/ControlFlowAnalyzer.java | 2 +- .../codeInspection/dataFlow/DfaFactMap.java | 30 +++--- .../codeInspection/dataFlow/DfaFactType.java | 54 +++++----- .../dataFlow/DfaMemoryState.java | 2 + .../dataFlow/DfaMemoryStateImpl.java | 99 +++++++++---------- .../dataFlow/DfaVariableState.java | 16 +-- .../dataFlow/MethodContract.java | 4 +- .../dataFlow/StandardInstructionVisitor.java | 33 ++++--- .../dataFlow/TypeConstraint.java | 8 +- .../dataFlow/value/DfaFactMapValue.java | 5 + .../dataFlow/value/DfaRelationValue.java | 7 +- .../dataFlow/value/DfaTypeValue.java | 95 ------------------ .../dataFlow/value/DfaValueFactory.java | 26 ++--- 13 files changed, 158 insertions(+), 223 deletions(-) delete mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index b34b87fd5c04..049dbba485df 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -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 { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactMap.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactMap.java index ca58b8c05d7d..dbfd8b385d9c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactMap.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactMap.java @@ -65,7 +65,7 @@ public final class DfaFactMap { */ @NotNull public DfaFactMap with(@NotNull DfaFactType 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 type = (DfaFactType)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 DfaFactMap invert(@NotNull DfaFactType 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 * diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactType.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactType.java index 0a903cf79a65..314611c81483 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactType.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaFactType.java @@ -44,6 +44,15 @@ public abstract class DfaFactType extends Key { 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 extends Key { 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 extends Key { */ public static final DfaFactType OPTIONAL_PRESENCE = new DfaFactType("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 extends Key { 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 extends Key { * {@link TypeConstraint#EMPTY} value is equivalent to absent fact (not constrained) */ public static final DfaFactType TYPE_CONSTRAINT = new DfaFactType("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 extends Key { 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 extends Key { 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. * diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java index dcc8debb0ac0..ddcebef17e34 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java @@ -55,6 +55,8 @@ public interface DfaMemoryState { @Nullable T getValueFact(@NotNull DfaFactType factType, @NotNull DfaValue value); + void forceNotNull(DfaVariableValue var); + void flushFields(); void flushVariable(DfaVariableValue variable); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index cbf0b61ddd18..244cb4e69c71 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -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; } - boolean applyFact(DfaVariableValue target, DfaFactType factType, T range) { - if (!isUnknownState(target) && range != null) { + boolean applyFact(DfaVariableValue target, DfaFactType 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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java index 04c6d8afeb55..77a002dbdffa 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java @@ -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 DfaVariableState withFact(DfaFactType type, T value) { - DfaFactMap factMap = myFactMap.with(type, value); - return myFactMap.equals(factMap) ? this : createCopy(factMap); + return withFacts(myFactMap.with(type, value)); } DfaVariableState withoutFact(DfaFactType type) { @@ -109,13 +109,17 @@ class DfaVariableState { @Nullable DfaVariableState intersectFact(DfaFactType 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 diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java index f80e104c69ef..0ab517e3976b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java @@ -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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 43ef9f65c6cf..489585dbec57 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -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; } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java index 7d828b8d23b0..ebba36d2bed4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/TypeConstraint.java @@ -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 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) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaFactMapValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaFactMapValue.java index e7e68bb187c4..6386c193e0d8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaFactMapValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaFactMapValue.java @@ -17,10 +17,15 @@ public class DfaFactMapValue extends DfaValue { myFacts = facts; } + public DfaValue withFact(@NotNull DfaFactType factType, @Nullable T value) { + return getFactory().getFactFactory().createValue(myFacts.with(factType, value)); + } + public DfaFactMap getFacts() { return myFacts; } + @Nullable public T get(@NotNull DfaFactType factType) { return myFacts.get(factType); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java index 9d421900c146..f93f9a2ed075 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java deleted file mode 100644 index 8f663a3a04d8..000000000000 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaTypeValue.java +++ /dev/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> myCache = ContainerUtil.newHashMap(); - @NotNull - private final DfaValueFactory myFactory; - - Factory(@NotNull DfaValueFactory factory) { - myFactory = factory; - } - - @NotNull - DfaTypeValue createTypeValue(@NotNull DfaPsiType type, @NotNull Nullness nullness) { - ArrayList 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; - } - -} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java index 95993aaac811..0c0f8a960811 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java @@ -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;