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 bf6f83e5410f..e7b5dc4c5763 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 @@ -45,7 +45,6 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.function.BiConsumer; import java.util.function.Function; -import java.util.stream.Collectors; /** * Invariant: qualifiers of the variables used in myEqClasses or myVariableStates must be canonical variables @@ -402,14 +401,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return false; } EqClass eqClass = getEqClass(dfaValue); - DfaValue constant = eqClass == null ? null : eqClass.findConstant(false); + DfaConstValue constant = eqClass == null ? null : eqClass.findConstant(); return constant == null || isNaN(constant); } private boolean isEffectivelyNaN(@NotNull DfaValue dfaValue) { EqClass eqClass = getEqClass(dfaValue); - return eqClass != null && isNaN(eqClass.findConstant(false)); + return eqClass != null && isNaN(eqClass.findConstant()); } List getEqClasses() { @@ -465,7 +464,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { EqClass c1 = myEqClasses.get(c1Index); EqClass c2 = myEqClasses.get(c2Index); - if (c1.findConstant(true) != null && c2.findConstant(true) != null) return false; + if (c1.findConstant() != null && c2.findConstant() != null) return false; EqClass newClass = new EqClass(c1); @@ -578,14 +577,14 @@ public class DfaMemoryStateImpl implements DfaMemoryState { */ public Set getNonEqualConstants(DfaVariableValue value) { int index = getEqClassIndex(value); - if (index == -1 || myEqClasses.get(index).findConstant(true) != null) return Collections.emptySet(); - return getDistinctClassPairs().stream() + if (index == -1 || myEqClasses.get(index).findConstant() != null) return Collections.emptySet(); + return StreamEx.of(getDistinctClassPairs()) .map(pair -> pair.getOtherClass(index)) - .filter(Objects::nonNull) - .map(otherClass -> otherClass.findConstant(true)) - .filter(Objects::nonNull) - .map(constant -> ((DfaConstValue)unwrap(constant)).getValue()) - .collect(Collectors.toSet()); + .nonNull() + .map(EqClass::findConstant) + .nonNull() + .map(DfaConstValue::getValue) + .toSet(); } @Override @@ -596,8 +595,12 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return (DfaConstValue)value; } if (value instanceof DfaVariableValue) { + PsiPrimitiveType unboxedType = PsiPrimitiveType.getUnboxedType(value.getType()); + if (unboxedType != null) { + value = myFactory.getBoxedFactory().createUnboxed(value, unboxedType); + } EqClass ec = getEqClass(value); - return ec == null ? null : (DfaConstValue)unwrap(ec.findConstant(true)); + return ec == null ? null : ec.findConstant(); } return null; } @@ -619,7 +622,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { @Override public boolean castTopOfStack(@NotNull DfaPsiType type) { - DfaValue value = unwrap(peek()); + DfaValue value = peek(); DfaFactMap facts = null; if (value instanceof DfaVariableValue) { @@ -673,7 +676,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState { for (Iterator iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) { DistinctPairSet.DistinctPair pair = iterator.next(); EqClass otherClass = pair.getOtherClass(index); - if (otherClass != null && otherClass.findConstant(false) != getFactory().getConstFactory().getNull()) { + if (otherClass != null && otherClass.findConstant() != getFactory().getConstFactory().getNull()) { iterator.remove(); } } @@ -717,13 +720,6 @@ public class DfaMemoryStateImpl implements DfaMemoryState { return true; } - static DfaValue unwrap(DfaValue value) { - if (value instanceof DfaBoxedValue) { - return ((DfaBoxedValue)value).getWrappedValue(); - } - return value; - } - @Override public boolean applyContractCondition(DfaValue condition) { if (condition instanceof DfaRelationValue) { @@ -768,13 +764,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState { public boolean applyCondition(DfaValue dfaCond) { if (dfaCond instanceof DfaUnknownValue) return true; if (dfaCond instanceof DfaVariableValue) { - DfaVariableValue dfaVar = (DfaVariableValue)dfaCond; DfaValue dfaTrue = myFactory.getConstFactory().getTrue(); - if (dfaVar.getSource() == SpecialField.UNBOX) { - dfaVar = dfaVar.getQualifier(); - dfaTrue = myFactory.getBoxedFactory().createBoxed(dfaTrue, null); - } - return applyRelationCondition(myFactory.getRelationFactory().createRelation(dfaVar, RelationType.EQ, dfaTrue)); + return applyRelationCondition(myFactory.getRelationFactory().createRelation(dfaCond, RelationType.EQ, dfaTrue)); } if (dfaCond instanceof DfaConstValue) { @@ -815,6 +806,10 @@ public class DfaMemoryStateImpl implements DfaMemoryState { !applyRelation(dfaLeft, getFactory().getConstFactory().getNull(), true)) { return false; } + if ((relationType == RelationType.EQ || relationType.isInequality()) && + !applyUnboxedRelation(dfaLeft, dfaRight, relationType.isInequality())) { + return false; + } if (dfaLeft instanceof DfaVariableValue) { DfaVariableValue dfaVar = (DfaVariableValue)dfaLeft; @@ -962,14 +957,19 @@ public class DfaMemoryStateImpl implements DfaMemoryState { } private boolean applyUnboxedRelation(@NotNull DfaValue dfaLeft, DfaValue dfaRight, boolean negated) { - if (!(dfaLeft instanceof DfaBoxedValue) && !TypeConversionUtil.isPrimitiveWrapper(dfaLeft.getType()) || - !(dfaRight instanceof DfaBoxedValue) && !TypeConversionUtil.isPrimitiveWrapper(dfaRight.getType())) { + if (dfaLeft instanceof DfaVariableValue && !TypeConversionUtil.isPrimitiveWrapper(dfaLeft.getType()) || + dfaRight instanceof DfaVariableValue && !TypeConversionUtil.isPrimitiveWrapper(dfaRight.getType())) { return true; } DfaBoxedValue.Factory boxedFactory = myFactory.getBoxedFactory(); DfaValue unboxedLeft = boxedFactory.createUnboxed(dfaLeft, null); DfaValue unboxedRight = boxedFactory.createUnboxed(dfaRight, null); + DfaConstValue leftConst = getConstantValue(unboxedLeft); + DfaConstValue rightConst = getConstantValue(unboxedRight); + if (leftConst != null && rightConst != null) { + return leftConst.getValue().equals(rightConst.getValue()) != negated; + } if (negated && (PsiType.FLOAT.equals(unboxedLeft.getType()) || PsiType.DOUBLE.equals(unboxedLeft.getType()))) { // If floating point wrappers are not equal, unboxed versions could still be equal if they are 0.0 and -0.0 return true; @@ -1009,8 +1009,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState { for (Iterator iterator = myDistinctClasses.iterator(); iterator.hasNext(); ) { DistinctPairSet.DistinctPair pair = iterator.next(); - DfaConstValue const1 = (DfaConstValue)pair.getFirst().findConstant(false); - DfaConstValue const2 = (DfaConstValue)pair.getSecond().findConstant(false); + DfaConstValue const1 = pair.getFirst().findConstant(); + DfaConstValue const2 = pair.getSecond().findConstant(); if (const1 != null && const2 != null && !preserveConstantDistinction(const1.getValue(), const2.getValue())) { iterator.remove(); } @@ -1089,24 +1089,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState { EqClass ec1 = myEqClasses.get(i1); EqClass ec2 = myEqClasses.get(i2); if (ec1 == null || ec2 == null) return ThreeState.UNSURE; - DfaValue constOrBox1 = ec1.findConstant(true); - DfaValue constOrBox2 = ec2.findConstant(true); - if (constOrBox1 == null || constOrBox2 == null) return ThreeState.UNSURE; - if (constOrBox1 instanceof DfaConstValue && constOrBox2 instanceof DfaConstValue) { - return areConstantsEqual((DfaConstValue)constOrBox1, (DfaConstValue)constOrBox2); - } - if (constOrBox1 instanceof DfaBoxedValue && constOrBox2 instanceof DfaBoxedValue) { - DfaValue wrapped1 = ((DfaBoxedValue)constOrBox1).getWrappedValue(); - DfaValue wrapped2 = ((DfaBoxedValue)constOrBox2).getWrappedValue(); - if (wrapped1 instanceof DfaConstValue && wrapped2 instanceof DfaConstValue && - areConstantsEqual((DfaConstValue)wrapped1, (DfaConstValue)wrapped2) == ThreeState.NO) { - return ThreeState.NO; - } - } - return ThreeState.UNSURE; - } - - private static ThreeState areConstantsEqual(DfaConstValue const1, DfaConstValue const2) { + DfaConstValue const1 = ec1.findConstant(); + DfaConstValue const2 = ec2.findConstant(); + if (const1 == null || const2 == null) return ThreeState.UNSURE; Number value1 = ObjectUtils.tryCast(const1.getValue(), Number.class); Number value2 = ObjectUtils.tryCast(const2.getValue(), Number.class); if (value1 == null || value2 == null) return ThreeState.UNSURE; @@ -1207,16 +1192,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState { } if (value instanceof DfaBoxedValue) { DfaBoxedValue boxedValue = (DfaBoxedValue)value; - if (boxedValue.getWrappedValue() instanceof DfaVariableValue) { - DfaValue canonicalized = canonicalize(boxedValue.getWrappedValue()); - return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalized, boxedValue.getType())); - } - } - if (value instanceof DfaConstValue) { - Object constant = ((DfaConstValue)value).getValue(); - if (Double.valueOf(-0.0).equals(constant)) { - return myFactory.getConstFactory().createFromValue(0.0, PsiType.DOUBLE); - } + DfaValue canonicalized = canonicalize(boxedValue.getWrappedValue()); + return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalized, boxedValue.getType())); } return value; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java index 660146923d96..91029b77d1f6 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/EqClass.java @@ -15,10 +15,7 @@ */ package com.intellij.codeInspection.dataFlow; -import com.intellij.codeInspection.dataFlow.value.DfaConstValue; -import com.intellij.codeInspection.dataFlow.value.DfaValue; -import com.intellij.codeInspection.dataFlow.value.DfaValueFactory; -import com.intellij.codeInspection.dataFlow.value.DfaVariableValue; +import com.intellij.codeInspection.dataFlow.value.*; import com.intellij.openapi.util.Ref; import com.intellij.util.ObjectUtils; import com.intellij.util.containers.ContainerUtil; @@ -74,12 +71,12 @@ class EqClass extends SortedIntSet { List vars = ContainerUtil.newArrayList(); forEach(id -> { DfaValue value = myFactory.getValue(id); - if (unwrap) { - value = DfaMemoryStateImpl.unwrap(value); - } if (value instanceof DfaVariableValue) { vars.add((DfaVariableValue)value); } + else if (unwrap && value instanceof DfaBoxedValue) { + vars.add(((DfaBoxedValue)value).getWrappedValue()); + } return true; }); return vars; @@ -109,12 +106,12 @@ class EqClass extends SortedIntSet { } @Nullable - DfaValue findConstant(boolean wrapped) { - Ref result = new Ref<>(); + DfaConstValue findConstant() { + Ref result = new Ref<>(); forEach(id -> { DfaValue value = myFactory.getValue(id); - if (value instanceof DfaConstValue || wrapped && DfaMemoryStateImpl.unwrap(value) instanceof DfaConstValue) { - result.set(value); + if (value instanceof DfaConstValue) { + result.set((DfaConstValue)value); return false; } return true; @@ -122,20 +119,9 @@ class EqClass extends SortedIntSet { return result.get(); } - @Nullable - private static DfaConstValue asConstantValue(DfaValue value) { - value = DfaMemoryStateImpl.unwrap(value); - return value instanceof DfaConstValue ? (DfaConstValue)value : null; - } - boolean containsConstantsOnly() { - for (int i = 0; i < size(); i++) { - if (asConstantValue(myFactory.getValue(get(i))) == null) { - return false; - } - } - - return true; + int size = size(); + return size <= 1 && (size == 0 || myFactory.getValue(get(0)) instanceof DfaConstValue); } } 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 6f4bf5501527..97387cb6f941 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 @@ -907,7 +907,7 @@ public class StandardInstructionVisitor extends InstructionVisitor { long bLong = b.longValue(); if (aLong != bLong) return aLong > bLong ? 1 : -1; - return Double.compare(a.doubleValue(), b.doubleValue()); + return a.doubleValue() == 0.0 && b.doubleValue() == 0.0 ? 0 : Double.compare(a.doubleValue(), b.doubleValue()); } private DfaInstructionState[] makeBooleanResultArray(BinopInstruction instruction, diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java index 895958614dbc..aa1e833f9cd8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java @@ -606,11 +606,11 @@ class StateMerger { static final class EqClassInfo { final List vars; - final DfaValue constant; + final DfaConstValue constant; EqClassInfo(EqClass eqClass) { vars = eqClass.getVariables(false); - constant = eqClass.findConstant(true); + constant = eqClass.findConstant(); } } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java index ac08aa8ee81d..e33f515009ae 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaBoxedValue.java @@ -15,22 +15,20 @@ */ package com.intellij.codeInspection.dataFlow.value; -import com.intellij.codeInspection.dataFlow.SpecialField; +import com.intellij.codeInspection.dataFlow.*; import com.intellij.psi.PsiPrimitiveType; import com.intellij.psi.PsiType; import com.intellij.psi.util.TypeConversionUtil; +import gnu.trove.TIntObjectHashMap; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.HashMap; -import java.util.Map; - public class DfaBoxedValue extends DfaValue { - private final DfaValue myWrappedValue; + private final @NotNull DfaVariableValue myWrappedValue; private final @Nullable PsiType myType; - private DfaBoxedValue(DfaValue valueToWrap, DfaValueFactory factory, @Nullable PsiType type) { + private DfaBoxedValue(@NotNull DfaVariableValue valueToWrap, DfaValueFactory factory, @Nullable PsiType type) { super(factory); myWrappedValue = valueToWrap; myType = type; @@ -41,7 +39,8 @@ public class DfaBoxedValue extends DfaValue { return "Boxed "+myWrappedValue.toString(); } - public DfaValue getWrappedValue() { + @NotNull + public DfaVariableValue getWrappedValue() { return myWrappedValue; } @@ -52,7 +51,7 @@ public class DfaBoxedValue extends DfaValue { } public static class Factory { - private final Map cachedValues = new HashMap<>(); + private final TIntObjectHashMap cachedValues = new TIntObjectHashMap<>(); private final DfaValueFactory myFactory; @@ -60,8 +59,8 @@ public class DfaBoxedValue extends DfaValue { myFactory = factory; } - public DfaValue getBoxedIfExists(DfaVariableValue variable) { - return cachedValues.get(variable); + public DfaBoxedValue getBoxedIfExists(DfaVariableValue variable) { + return cachedValues.get(variable.getID()); } @Nullable @@ -69,15 +68,23 @@ public class DfaBoxedValue extends DfaValue { if (valueToWrap instanceof DfaVariableValue && ((DfaVariableValue)valueToWrap).getSource() == SpecialField.UNBOX) { return ((DfaVariableValue)valueToWrap).getQualifier(); } - Object o = valueToWrap instanceof DfaConstValue - ? ((DfaConstValue)valueToWrap).getValue() - : valueToWrap instanceof DfaVariableValue ? valueToWrap : null; - if (o == null) return null; - DfaBoxedValue boxedValue = cachedValues.get(o); - if (boxedValue == null) { - cachedValues.put(o, boxedValue = new DfaBoxedValue(valueToWrap, myFactory, type)); + if (valueToWrap instanceof DfaConstValue) { + DfaConstValue constValue = (DfaConstValue)valueToWrap; + DfaFactMap facts = DfaFactMap.EMPTY + .with(DfaFactType.TYPE_CONSTRAINT, type == null ? null : TypeConstraint.exact(myFactory.createDfaType(type))) + .with(DfaFactType.NULLABILITY, DfaNullability.NOT_NULL) + .with(DfaFactType.SPECIAL_FIELD_VALUE, SpecialField.UNBOX.withValue(constValue.getValue(), constValue.getType())); + return myFactory.getFactFactory().createValue(facts); } - return boxedValue; + if (valueToWrap instanceof DfaVariableValue) { + int id = valueToWrap.getID(); + DfaBoxedValue boxedValue = cachedValues.get(id); + if (boxedValue == null) { + cachedValues.put(id, boxedValue = new DfaBoxedValue((DfaVariableValue)valueToWrap, myFactory, type)); + } + return boxedValue; + } + return null; } @NotNull @@ -85,6 +92,12 @@ public class DfaBoxedValue extends DfaValue { if (value instanceof DfaBoxedValue) { return ((DfaBoxedValue)value).getWrappedValue(); } + if (value instanceof DfaFactMapValue) { + SpecialFieldValue sfValue = ((DfaFactMapValue)value).get(DfaFactType.SPECIAL_FIELD_VALUE); + if (sfValue != null && sfValue.getField() == SpecialField.UNBOX) { + return sfValue.toConstant(myFactory); + } + } if (value instanceof DfaConstValue) { return TypeConversionUtil.isPrimitiveAndNotNull(((DfaConstValue)value).getType()) ? value : DfaUnknownValue.getInstance(); } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ArrayStoreProblems.java b/java/java-tests/testData/inspection/dataFlow/fixture/ArrayStoreProblems.java index d2c7439bcda4..b88942c5b99c 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/ArrayStoreProblems.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ArrayStoreProblems.java @@ -4,12 +4,12 @@ import org.jetbrains.annotations.NotNull; class ArrayStoreProblems { void test(String[] args, Integer[] args2) { Object[] arr = args; - arr[0] = 123; + arr[0] = 123; arr = args2; arr[1] = 124; arr[2] = "foo"; arr = args; arr[3] = "bar"; - arr[4] = 125; + arr[4] = 125; } } \ No newline at end of file