mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] Fixes for new widening procedure
GitOrigin-RevId: 615c6f1cd89002a56a65bc8f886e60e1c74c099f
This commit is contained in:
committed by
intellij-monorepo-bot
parent
11fa1e5d48
commit
ae140ee21e
@@ -167,5 +167,8 @@ public interface DfaMemoryState {
|
||||
*/
|
||||
boolean shouldCompareByEquals(DfaValue dfaLeft, DfaValue dfaRight);
|
||||
|
||||
/**
|
||||
* Widen this memory state on back-branches
|
||||
*/
|
||||
void widen();
|
||||
}
|
||||
|
||||
+1
-1
@@ -895,7 +895,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
String rightString = memState.getDfType(right).getConstantOfType(String.class);
|
||||
if (leftString != null && rightString != null &&
|
||||
leftString.length() + rightString.length() <= CustomMethodHandlers.MAX_STRING_CONSTANT_LENGTH_TO_TRACK) {
|
||||
return factory.getConstant(leftString + rightString, stringType);
|
||||
return factory.fromDfType(concatenationResult(leftString + rightString, stringType));
|
||||
}
|
||||
DfaValue leftLength = SpecialField.STRING_LENGTH.createValue(factory, left);
|
||||
DfaValue rightLength = SpecialField.STRING_LENGTH.createValue(factory, right);
|
||||
|
||||
+3
-12
@@ -2,9 +2,7 @@
|
||||
package com.intellij.codeInspection.dataFlow.instructions;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfConstantType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfLongType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfPrimitiveType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaBinOpValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
@@ -17,8 +15,6 @@ import com.intellij.psi.util.TypeConversionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.intellij.codeInspection.dataFlow.types.DfTypes.rangeClamped;
|
||||
|
||||
/**
|
||||
* A unary instruction that converts a primitive value from the stack to the desired type
|
||||
*/
|
||||
@@ -47,13 +43,8 @@ public class PrimitiveConversionInstruction extends EvalInstruction {
|
||||
}
|
||||
|
||||
DfType dfType = state.getDfType(value);
|
||||
if (dfType instanceof DfConstantType && type != null) {
|
||||
Object casted = TypeConversionUtil.computeCastTo(((DfConstantType<?>)dfType).getValue(), type);
|
||||
return factory.getConstant(casted, type);
|
||||
}
|
||||
if (TypeConversionUtil.isIntegralNumberType(type)) {
|
||||
LongRangeSet range = DfLongType.extractRange(dfType);
|
||||
return factory.fromDfType(rangeClamped(range.castTo(type), PsiType.LONG.equals(type)));
|
||||
if (dfType instanceof DfPrimitiveType && type != null) {
|
||||
return factory.fromDfType(((DfPrimitiveType)dfType).castTo(type));
|
||||
}
|
||||
return factory.getUnknown();
|
||||
}
|
||||
|
||||
+7
-1
@@ -22,11 +22,17 @@ public interface DfIntType extends DfIntegralType {
|
||||
getWideRange().contains(intType.getWideRange());
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean containsConstant(@NotNull DfConstantType<?> constant) {
|
||||
Integer value = constant.getConstantOfType(Integer.class);
|
||||
return value != null && getRange().contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
default @NotNull DfType eval(@NotNull DfType other, @NotNull LongRangeBinOp op) {
|
||||
if (!(other instanceof DfIntType)) return DfTypes.INT;
|
||||
LongRangeSet result = op.eval(getRange(), ((DfIntType)other).getRange(), false);
|
||||
LongRangeSet wideResult = op.evalWide(getRange(), ((DfIntType)other).getRange(), false);
|
||||
LongRangeSet wideResult = op.evalWide(getWideRange(), ((DfIntType)other).getWideRange(), false);
|
||||
return DfTypes.intRange(result, wideResult);
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -4,6 +4,9 @@ package com.intellij.codeInspection.dataFlow.types;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeBinOp;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.RelationType;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -16,7 +19,13 @@ public interface DfIntegralType extends DfPrimitiveType {
|
||||
default @NotNull LongRangeSet getWideRange() {
|
||||
return getRange();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform binary operation between this type and other type
|
||||
* @param other other operand
|
||||
* @param op operation to perform
|
||||
* @return result of the operation
|
||||
*/
|
||||
@NotNull DfType eval(@NotNull DfType other, @NotNull LongRangeBinOp op);
|
||||
|
||||
@NotNull
|
||||
@@ -29,4 +38,18 @@ public interface DfIntegralType extends DfPrimitiveType {
|
||||
}
|
||||
|
||||
@NotNull DfType meetRange(@NotNull LongRangeSet range);
|
||||
|
||||
/**
|
||||
* Cast this type to the specified primitive type
|
||||
* @param type target type
|
||||
* @return result of the cast
|
||||
*/
|
||||
default @NotNull DfType castTo(@NotNull PsiPrimitiveType type) {
|
||||
if (!TypeConversionUtil.isIntegralNumberType(type)) {
|
||||
return DfPrimitiveType.super.castTo(type);
|
||||
}
|
||||
LongRangeSet range = getRange().castTo(type);
|
||||
LongRangeSet wideRange = getWideRange().castTo(type);
|
||||
return type.equals(PsiType.LONG) ? DfTypes.longRange(range, wideRange) : DfTypes.intRange(range, wideRange);
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -22,11 +22,17 @@ public interface DfLongType extends DfIntegralType {
|
||||
getWideRange().contains(longType.getWideRange());
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean containsConstant(@NotNull DfConstantType<?> constant) {
|
||||
Long value = constant.getConstantOfType(Long.class);
|
||||
return value != null && getRange().contains(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
default @NotNull DfType eval(@NotNull DfType other, @NotNull LongRangeBinOp op) {
|
||||
if (!(other instanceof DfLongType)) return DfTypes.LONG;
|
||||
LongRangeSet result = op.eval(getRange(), ((DfLongType)other).getRange(), true);
|
||||
LongRangeSet wideResult = op.evalWide(getRange(), ((DfLongType)other).getRange(), true);
|
||||
LongRangeSet wideResult = op.evalWide(getWideRange(), ((DfLongType)other).getWideRange(), true);
|
||||
return DfTypes.longRange(result, wideResult);
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -2,6 +2,7 @@
|
||||
package com.intellij.codeInspection.dataFlow.types;
|
||||
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
@@ -10,4 +11,17 @@ import org.jetbrains.annotations.NotNull;
|
||||
public interface DfPrimitiveType extends DfType {
|
||||
@NotNull
|
||||
PsiPrimitiveType getPsiType();
|
||||
|
||||
/**
|
||||
* Cast this type to the specified primitive type
|
||||
* @param type target type
|
||||
* @return result of the cast
|
||||
*/
|
||||
default @NotNull DfType castTo(@NotNull PsiPrimitiveType type) {
|
||||
Object value = TypeConversionUtil.computeCastTo(getConstantOfType(Object.class), type);
|
||||
if (value != null) {
|
||||
return DfTypes.constant(value, type);
|
||||
}
|
||||
return DfTypes.TOP;
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -19,14 +19,25 @@ public class DfReferenceConstantType extends DfConstantType<Object> implements D
|
||||
private final @NotNull Mutability myMutability;
|
||||
private final @Nullable SpecialField mySpecialField;
|
||||
private final @NotNull DfType mySpecialFieldType;
|
||||
private final boolean myDropConstantOnWiden;
|
||||
|
||||
DfReferenceConstantType(@NotNull Object constant, @NotNull PsiType psiType, @NotNull TypeConstraint type) {
|
||||
DfReferenceConstantType(@NotNull Object constant, @NotNull PsiType psiType, @NotNull TypeConstraint type, boolean dropConstantOnWiden) {
|
||||
super(constant);
|
||||
myPsiType = psiType;
|
||||
myConstraint = type;
|
||||
myMutability = constant instanceof PsiModifierListOwner ? Mutability.getMutability((PsiModifierListOwner)constant) : Mutability.UNKNOWN;
|
||||
mySpecialField = SpecialField.fromQualifierType(psiType);
|
||||
mySpecialFieldType = mySpecialField == null ? BOTTOM : mySpecialField.fromConstant(constant);
|
||||
myDropConstantOnWiden = dropConstantOnWiden;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfType widen() {
|
||||
if (myDropConstantOnWiden) {
|
||||
return new DfGenericObjectType(Set.of(), myConstraint, DfaNullability.NOT_NULL, myMutability,
|
||||
mySpecialField, mySpecialFieldType.widen(), false);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -39,7 +50,7 @@ public class DfReferenceConstantType extends DfConstantType<Object> implements D
|
||||
if (type.isSuperType(this)) return this;
|
||||
TypeConstraint constraint = type.getConstraint().meet(myConstraint);
|
||||
if (constraint != TypeConstraints.BOTTOM) {
|
||||
DfReferenceConstantType subConstant = new DfReferenceConstantType(getValue(), myPsiType, constraint);
|
||||
DfReferenceConstantType subConstant = new DfReferenceConstantType(getValue(), myPsiType, constraint, myDropConstantOnWiden);
|
||||
if (type.isSuperType(subConstant)) return subConstant;
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -123,6 +123,11 @@ public interface DfReferenceType extends DfType {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean containsConstant(@NotNull DfConstantType<?> constant) {
|
||||
return dropTypeConstraint().isSuperType(constant);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NlsSafe String toString();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,14 @@ public interface DfType {
|
||||
* @return true if this type is the supertype of other.
|
||||
*/
|
||||
boolean isSuperType(@NotNull DfType other);
|
||||
|
||||
/**
|
||||
* @param constant
|
||||
* @return true given constant value may be contained by this supertype
|
||||
*/
|
||||
default boolean containsConstant(@NotNull DfConstantType<?> constant) {
|
||||
return isSuperType(constant);
|
||||
}
|
||||
|
||||
default boolean isMergeable(@NotNull DfType other) {
|
||||
return isSuperType(other);
|
||||
|
||||
+13
-9
@@ -219,13 +219,10 @@ public final class DfTypes {
|
||||
}
|
||||
|
||||
static @NotNull DfType intRange(@NotNull LongRangeSet range, @Nullable LongRangeSet wideRange) {
|
||||
if (wideRange == null || wideRange.equals(range)) return intRange(range);
|
||||
if (wideRange == null || wideRange.equals(range) || wideRange.isEmpty()) return intRange(range);
|
||||
if (range.isEmpty()) {
|
||||
return BOTTOM;
|
||||
}
|
||||
if (wideRange.isEmpty() || wideRange.min() > range.min() || wideRange.max() < range.max()) {
|
||||
throw new IllegalArgumentException("Wide range " + wideRange + " must contain range " + range);
|
||||
}
|
||||
Long value = range.getConstantValue();
|
||||
if (value != null) {
|
||||
return new DfIntConstantType(Math.toIntExact(value), wideRange);
|
||||
@@ -265,13 +262,10 @@ public final class DfTypes {
|
||||
}
|
||||
|
||||
static @NotNull DfType longRange(@NotNull LongRangeSet range, @Nullable LongRangeSet wideRange) {
|
||||
if (wideRange == null || wideRange.equals(range)) return longRange(range);
|
||||
if (wideRange == null || wideRange.equals(range) || wideRange.isEmpty()) return longRange(range);
|
||||
if (range.isEmpty()) {
|
||||
return BOTTOM;
|
||||
}
|
||||
if (wideRange.isEmpty() || wideRange.min() > range.min() || wideRange.max() < range.max()) {
|
||||
throw new IllegalArgumentException("Wide range " + wideRange + " must contain range " + range);
|
||||
}
|
||||
Long value = range.getConstantValue();
|
||||
if (value != null) {
|
||||
return new DfLongConstantType(value, wideRange);
|
||||
@@ -451,7 +445,17 @@ public final class DfTypes {
|
||||
if (constant instanceof Double) {
|
||||
return doubleValue((Double)constant);
|
||||
}
|
||||
return new DfReferenceConstantType(constant, type, TypeConstraints.instanceOf(type));
|
||||
return new DfReferenceConstantType(constant, type, TypeConstraints.instanceOf(type), false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param constant string constant
|
||||
* @param stringType string type
|
||||
* @return concatenation result string
|
||||
*/
|
||||
@NotNull
|
||||
public static DfConstantType<?> concatenationResult(@NotNull String constant, @NotNull PsiType stringType) {
|
||||
return new DfReferenceConstantType(constant, stringType, TypeConstraints.exact(stringType), true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+8
-8
@@ -2,7 +2,10 @@
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.DfaUtil;
|
||||
import com.intellij.codeInspection.dataFlow.types.*;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfConstantType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfIntegralType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfTypes;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -98,17 +101,18 @@ public abstract class DfaCondition {
|
||||
|
||||
if (relationType == RelationType.EQ || relationType == RelationType.NE) {
|
||||
if (leftType instanceof DfConstantType) {
|
||||
DfConstantType<?> leftConst = (DfConstantType<?>)leftType;
|
||||
if (rightType instanceof DfConstantType) {
|
||||
return fromBoolean(leftType.isConst(((DfConstantType<?>)rightType).getValue()) ^
|
||||
!DfaUtil.isNaN(((DfConstantType<?>)leftType).getValue()) ^
|
||||
!DfaUtil.isNaN(leftConst.getValue()) ^
|
||||
relationType == RelationType.EQ);
|
||||
}
|
||||
if (!couldBeEqualToConstant(rightType, leftType)) {
|
||||
if (!rightType.containsConstant(leftConst)) {
|
||||
return fromBoolean(relationType == RelationType.NE);
|
||||
}
|
||||
}
|
||||
else if (rightType instanceof DfConstantType) {
|
||||
if (!couldBeEqualToConstant(leftType, rightType)) {
|
||||
if (!leftType.containsConstant((DfConstantType<?>)rightType)) {
|
||||
return fromBoolean(relationType == RelationType.NE);
|
||||
}
|
||||
}
|
||||
@@ -138,9 +142,5 @@ public abstract class DfaCondition {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean couldBeEqualToConstant(DfType type, DfType constantType) {
|
||||
return (type instanceof DfReferenceType ? ((DfReferenceType)type).dropTypeConstraint() : type).isSuperType(constantType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
public class DontWidenPlusInLoop {
|
||||
public static int parse(String value, int i) {
|
||||
do {
|
||||
i = value.indexOf('{', i);
|
||||
if (i != -1 && i == value.indexOf("{{", i)) {
|
||||
i += 2;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (<warning descr="Condition 'i != -1' is always 'true'">i != -1</warning>);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import java.util.List;
|
||||
|
||||
public class WidenMismatch {
|
||||
void listSize(WidenMismatch m) {
|
||||
int N = 200;
|
||||
while (m.makeList().size() == N + 1 + N) {
|
||||
System.gc();
|
||||
}
|
||||
}
|
||||
|
||||
native List<String> makeList();
|
||||
|
||||
int test(int value) {
|
||||
if (value < 100) {
|
||||
return value;
|
||||
}
|
||||
else if (value % 10 < 5) {
|
||||
return value - value % 10;
|
||||
}
|
||||
else {
|
||||
return value + 10 - value % 10;
|
||||
}
|
||||
}
|
||||
|
||||
void test2(int n) {
|
||||
int members = 5 * n;
|
||||
int y = 100 + members * 40 * 3;
|
||||
}
|
||||
|
||||
int myRelativeLevel;
|
||||
|
||||
private String getDots() {
|
||||
String dots = "";
|
||||
for (int i = 0; i < myRelativeLevel; i += 1) {
|
||||
dots += ".";
|
||||
}
|
||||
return dots;
|
||||
}
|
||||
|
||||
long longDecode(String s) {
|
||||
if (s == null || s.length() == 0) {
|
||||
return 0;
|
||||
}
|
||||
int multiplier;
|
||||
switch (s.charAt(s.length() - 1)) {
|
||||
case 'K':
|
||||
multiplier = 1000;
|
||||
break;
|
||||
case 'M':
|
||||
multiplier = 1000000;
|
||||
break;
|
||||
case 'G':
|
||||
multiplier = 1000000000;
|
||||
break;
|
||||
default:
|
||||
multiplier = 1;
|
||||
}
|
||||
try {
|
||||
if (multiplier == 1) {
|
||||
return Long.decode(s);
|
||||
}
|
||||
else {
|
||||
return Long.decode(s.substring(0, s.length() - 1)).longValue() * multiplier;
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void test(String testName) {
|
||||
int result = 0;
|
||||
int multiplier = 1;
|
||||
|
||||
for(int i = testName.length() - 1; i >=0 ; --i) {
|
||||
final char ch = testName.charAt(i);
|
||||
if (Character.isDigit(ch)) {
|
||||
result += (ch - '0')* multiplier;
|
||||
multiplier *= 10;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int parse(int len, int ret) {
|
||||
int k = ((ret >> 12) & 0xF) * 0x11000000;
|
||||
k |= ((ret >> 8) & 0xF) * 0x110000;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
+2
@@ -74,4 +74,6 @@ public class DataFlowRangeAnalysisTest extends DataFlowInspectionTestCase {
|
||||
public void testReduceBinOpOnCast() { doTest(); }
|
||||
public void testSuppressZeroReport() { doTest(); }
|
||||
public void testCompareMethods() { doTest(); }
|
||||
public void testWidenMismatch() { doTest(); }
|
||||
public void testDontWidenPlusInLoop() { doTest(); }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user