DFA: add missing box/unbox conversions

This commit is contained in:
Tagir Valeev
2018-07-12 15:24:18 +07:00
parent 9f92839838
commit 86918930ac
6 changed files with 49 additions and 12 deletions
@@ -358,7 +358,7 @@ public class CFGBuilder {
*
* @return this builder
*/
public CFGBuilder boxUnbox(PsiExpression expression, PsiType expectedType) {
public CFGBuilder boxUnbox(@NotNull PsiExpression expression, PsiType expectedType) {
myAnalyzer.generateBoxingUnboxingInstructionFor(expression, expectedType);
return this;
}
@@ -11,6 +11,7 @@ import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiType;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.ExpectedTypeUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
@@ -67,6 +68,7 @@ public class MapUpdateInliner implements CallInliner {
.pushExpression(key)
.pop()
.pushExpression(value)
.boxUnbox(value, ExpectedTypeUtils.findExpectedType(value, false))
.checkNotNull(value, NullabilityProblemKind.passingNullableToNotNullParameter)
.evaluateFunction(function)
.pushUnknown()
@@ -26,10 +26,7 @@ import com.intellij.openapi.util.registry.Registry;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.impl.JavaConstantExpressionEvaluator;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PropertyUtilBase;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.*;
import com.intellij.util.ObjectUtils;
import com.intellij.util.containers.ContainerUtil;
import com.siyeh.ig.psiutils.ClassUtils;
@@ -283,19 +280,19 @@ public class DfaExpressionFactory {
}
@NotNull
private DfaValue getAdvancedExpressionDfaValue(@Nullable PsiExpression expression) {
private DfaValue getAdvancedExpressionDfaValue(@Nullable PsiExpression expression, @Nullable PsiType targetType) {
if (expression == null) return DfaUnknownValue.getInstance();
DfaValue value = getExpressionDfaValue(expression);
if (value != null) {
return value;
return boxUnbox(value, targetType);
}
if (expression instanceof PsiConditionalExpression) {
return getAdvancedExpressionDfaValue(((PsiConditionalExpression)expression).getThenExpression()).union(
getAdvancedExpressionDfaValue(((PsiConditionalExpression)expression).getElseExpression()));
return getAdvancedExpressionDfaValue(((PsiConditionalExpression)expression).getThenExpression(), targetType).union(
getAdvancedExpressionDfaValue(((PsiConditionalExpression)expression).getElseExpression(), targetType));
}
PsiType type = expression.getType();
if (type instanceof PsiPrimitiveType) return DfaUnknownValue.getInstance();
return myFactory.createTypeValue(type, NullabilityUtil.getExpressionNullability(expression));
return boxUnbox(myFactory.createTypeValue(type, NullabilityUtil.getExpressionNullability(expression)), targetType);
}
@NotNull
@@ -311,18 +308,37 @@ public class DfaExpressionFactory {
DfaVariableValue arrayDfaVar = (DfaVariableValue)array;
PsiModifierListOwner arrayPsiVar = arrayDfaVar.getPsiVariable();
if (!(arrayPsiVar instanceof PsiVariable)) return DfaUnknownValue.getInstance();
PsiType arrayType = ((PsiVariable)arrayPsiVar).getType();
PsiType targetType = arrayType instanceof PsiArrayType ? ((PsiArrayType)arrayType).getComponentType() : null;
PsiExpression[] elements = ExpressionUtils.getConstantArrayElements((PsiVariable)arrayPsiVar);
if (elements == null || elements.length == 0) return DfaUnknownValue.getInstance();
indexSet = indexSet.intersect(LongRangeSet.range(0, elements.length - 1));
if (indexSet.isEmpty() || indexSet.max() - indexSet.min() > 100) return DfaUnknownValue.getInstance();
return LongStreamEx.of(indexSet.stream())
.mapToObj(idx -> getAdvancedExpressionDfaValue(elements[(int)idx]))
.mapToObj(idx -> getAdvancedExpressionDfaValue(elements[(int)idx], targetType))
.prefix(DfaValue::union)
.takeWhileInclusive(value -> value != DfaUnknownValue.getInstance())
.reduce((a, b) -> b)
.orElse(DfaUnknownValue.getInstance());
}
private DfaValue boxUnbox(DfaValue value, PsiType type) {
if (TypeConversionUtil.isPrimitiveWrapper(type)) {
if (value instanceof DfaConstValue || value instanceof DfaUnboxedValue ||
(value instanceof DfaVariableValue && TypeConversionUtil.isPrimitiveAndNotNull(((DfaVariableValue)value).getVariableType()))) {
DfaValue boxed = myFactory.getBoxedFactory().createBoxed(value);
return boxed == null ? DfaUnknownValue.getInstance() : boxed;
}
}
if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
if (value instanceof DfaBoxedValue ||
(value instanceof DfaVariableValue && TypeConversionUtil.isPrimitiveWrapper(((DfaVariableValue)value).getVariableType()))) {
return myFactory.getBoxedFactory().createUnboxed(value);
}
}
return value;
}
@Contract("null, _ -> null")
@Nullable
public DfaValue getArrayElementValue(DfaValue array, int index) {
@@ -335,7 +351,7 @@ public class DfaExpressionFactory {
if (arrayPsiVar instanceof PsiVariable) {
PsiExpression constantArrayElement = ExpressionUtils.getConstantArrayElement((PsiVariable)arrayPsiVar, index);
if (constantArrayElement != null) {
return getAdvancedExpressionDfaValue(constantArrayElement);
return getAdvancedExpressionDfaValue(constantArrayElement, componentType);
}
}
ArrayElementSource indexVariable = getArrayIndexVariable(index);
@@ -0,0 +1,14 @@
class Test {
private final static Integer[] DATA = {119, 7, 3};
private final static int[] DATA2 = {119, 7, 3};
void test() {
for(int i=0; i<DATA.length; i++) {
Integer val = DATA[i];
int val2 = DATA2[i];
if (val != val2) {
System.out.println("Trouble");
}
}
}
}
@@ -59,4 +59,8 @@ class MapUpdateInlining {
System.out.println("impossible");
}
}
void testBoxing(Map<String, Integer> map, String key) {
map.merge(key, 1, (i1, i2) -> i1+1);
}
}
@@ -620,4 +620,5 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testNanComparisonWrong() { doTest(); }
public void testConstantMethods() { doTest(); }
public void testPolyadicEquality() { doTest(); }
public void testBoxUnboxArrayElement() { doTest(); }
}