[java-dfa] Enhance DfaBoxedValue to wrap any special field

Allows putting on the stack values with special field bound to variable.

GitOrigin-RevId: 3fac435de1b77c8e5185b356ff187cfe2b990554
This commit is contained in:
Tagir Valeev
2021-02-15 06:47:01 +00:00
committed by intellij-monorepo-bot
parent 0effec1ee0
commit 0a196e727f
15 changed files with 167 additions and 101 deletions
@@ -3,10 +3,7 @@ package com.intellij.codeInspection.dataFlow;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.codeInspection.dataFlow.value.DfaTypeValue;
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.codeInspection.util.InspectionMessage;
import com.intellij.java.analysis.JavaAnalysisBundle;
import com.intellij.psi.*;
@@ -121,6 +118,9 @@ public abstract class ContractReturnValue {
memState.meetDfType(newValue, result);
return newValue;
}
if (defaultValue instanceof DfaBoxedValue && newType.isSuperType(defaultValue.getDfType())) {
return defaultValue;
}
if (defaultValue instanceof DfaVariableValue) {
memState.meetDfType(defaultValue, result);
return defaultValue;
@@ -6,6 +6,7 @@ import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
import com.intellij.codeInspection.dataFlow.types.DfIntType;
import com.intellij.codeInspection.dataFlow.types.DfLongType;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.value.DfaBinOpValue;
import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
import com.intellij.codeInspection.dataFlow.value.RelationType;
@@ -66,57 +67,79 @@ public final class CustomMethodHandlers {
interface CustomMethodHandler {
@Nullable
DfaValue getMethodResultValue(DfaCallArguments callArguments,
DfaMemoryState memState,
DfaValueFactory factory,
PsiMethod method);
default CustomMethodHandler compose(CustomMethodHandler other) {
if (other == null) return this;
return (args, memState, factory, method) -> {
DfaValue result = this.getMethodResultValue(args, memState, factory, method);
return result == null ? other.getMethodResultValue(args, memState, factory, method) : result;
};
}
}
interface DfTypeCustomMethodHandler extends CustomMethodHandler {
@NotNull
DfType getMethodResult(DfaCallArguments callArguments,
DfaMemoryState memState,
DfaValueFactory factory,
PsiMethod method);
default CustomMethodHandler compose(CustomMethodHandler other) {
if (other == null) return this;
return (args, memState, factory, method) -> {
DfType result = this.getMethodResult(args, memState, factory, method);
return result == TOP ? other.getMethodResult(args, memState, factory, method) : result;
};
@Override
@Nullable
default DfaValue getMethodResultValue(DfaCallArguments callArguments,
DfaMemoryState memState,
DfaValueFactory factory,
PsiMethod method) {
DfType dfType = getMethodResult(callArguments, memState, factory, method);
return dfType == TOP ? null : factory.fromDfType(dfType);
}
}
private static CustomMethodHandler toValue(DfTypeCustomMethodHandler handler) {
return handler;
}
private static final CallMapper<CustomMethodHandler> CUSTOM_METHOD_HANDLERS = new CallMapper<CustomMethodHandler>()
.register(instanceCall(JAVA_LANG_STRING, "indexOf", "lastIndexOf"),
(args, memState, factory, method) -> indexOf(args.myQualifier, memState, factory, STRING_LENGTH))
toValue((args, memState, factory, method) -> indexOf(args.myQualifier, memState, factory, STRING_LENGTH)))
.register(instanceCall(JAVA_UTIL_LIST, "indexOf", "lastIndexOf"),
(args, memState, factory, method) -> indexOf(args.myQualifier, memState, factory, COLLECTION_SIZE))
toValue((args, memState, factory, method) -> indexOf(args.myQualifier, memState, factory, COLLECTION_SIZE)))
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("int"),
(args, memState, factory, method) -> mathAbs(args.myArguments, memState, false))
toValue((args, memState, factory, method) -> mathAbs(args.myArguments, memState, false)))
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("long"),
(args, memState, factory, method) -> mathAbs(args.myArguments, memState, true))
toValue((args, memState, factory, method) -> mathAbs(args.myArguments, memState, true)))
.register(exactInstanceCall(JAVA_LANG_STRING, "substring"),
(args, memState, factory, method) -> substring(args, memState, factory, method.getReturnType()))
.register(OptionalUtil.OPTIONAL_OF_NULLABLE,
(args, memState, factory, method) -> OPTIONAL_VALUE.asDfType(memState.getDfType(args.myArguments[0]), method.getReturnType()))
toValue((args, memState, factory, method) -> OPTIONAL_VALUE.asDfType(memState.getDfType(args.myArguments[0]))))
.register(instanceCall(JAVA_UTIL_CALENDAR, "get").parameterTypes("int"),
(args, memState, factory, method) -> calendarGet(args.myArguments, memState))
toValue((args, memState, factory, method) -> calendarGet(args.myArguments, memState)))
.register(anyOf(instanceCall("java.io.InputStream", "skip").parameterTypes("long"),
instanceCall("java.io.Reader", "skip").parameterTypes("long")),
(args, memState, factory, method) -> skip(args.myArguments, memState))
toValue((args, memState, factory, method) -> skip(args.myArguments, memState)))
.register(staticCall(JAVA_LANG_INTEGER, "toHexString").parameterCount(1),
(args, memState, factory, method) -> numberAsString(args, memState, 4, Integer.SIZE))
toValue((args, memState, factory, method) -> numberAsString(args, memState, 4, Integer.SIZE)))
.register(staticCall(JAVA_LANG_INTEGER, "toOctalString").parameterCount(1),
(args, memState, factory, method) -> numberAsString(args, memState, 3, Integer.SIZE))
toValue((args, memState, factory, method) -> numberAsString(args, memState, 3, Integer.SIZE)))
.register(staticCall(JAVA_LANG_INTEGER, "toBinaryString").parameterCount(1),
(args, memState, factory, method) -> numberAsString(args, memState, 1, Integer.SIZE))
toValue((args, memState, factory, method) -> numberAsString(args, memState, 1, Integer.SIZE)))
.register(staticCall(JAVA_LANG_LONG, "toHexString").parameterCount(1),
(args, memState, factory, method) -> numberAsString(args, memState, 4, Long.SIZE))
toValue((args, memState, factory, method) -> numberAsString(args, memState, 4, Long.SIZE)))
.register(staticCall(JAVA_LANG_LONG, "toOctalString").parameterCount(1),
(args, memState, factory, method) -> numberAsString(args, memState, 3, Long.SIZE))
toValue((args, memState, factory, method) -> numberAsString(args, memState, 3, Long.SIZE)))
.register(staticCall(JAVA_LANG_LONG, "toBinaryString").parameterCount(1),
(args, memState, factory, method) -> numberAsString(args, memState, 1, Long.SIZE))
toValue((args, memState, factory, method) -> numberAsString(args, memState, 1, Long.SIZE)))
.register(instanceCall(JAVA_LANG_ENUM, "name").parameterCount(0),
(args, memState, factory, method) -> enumName(args.myQualifier, memState, method.getReturnType()))
toValue((args, memState, factory, method) -> enumName(args.myQualifier, memState, method.getReturnType())))
.register(staticCall(JAVA_UTIL_COLLECTIONS, "emptyList", "emptySet", "emptyMap").parameterCount(0),
(args, memState, factory, method) -> getEmptyCollectionConstant(method))
toValue((args, memState, factory, method) -> getEmptyCollectionConstant(method)))
.register(exactInstanceCall(JAVA_LANG_CLASS, "getName", "getSimpleName", "getCanonicalName").parameterCount(0),
(args, memState, factory, method) -> className(memState, args.myQualifier, method.getName(), method.getReturnType()))
toValue((args, memState, factory, method) -> className(memState, args.myQualifier, method.getName(), method.getReturnType())))
.register(anyOf(
staticCall(JAVA_UTIL_COLLECTIONS, "singleton", "singletonList", "singletonMap"),
staticCall(JAVA_UTIL_LIST, "of"),
@@ -128,11 +151,11 @@ public final class CustomMethodHandlers {
staticCall(JAVA_LANG_LONG, "compare").parameterTypes("long", "long"),
staticCall(JAVA_LANG_BYTE, "compare").parameterTypes("byte", "byte"),
staticCall(JAVA_LANG_SHORT, "compare").parameterTypes("short", "short")),
(args, state, factory, method) -> compareInteger(args, state))
toValue((args, state, factory, method) -> compareInteger(args, state)))
.register(anyOf(
instanceCall("java.util.Random", "nextInt").parameterTypes("int"),
instanceCall("java.util.SplittableRandom", "nextInt").parameterTypes("int"),
instanceCall("java.util.SplittableRandom", "nextInt").parameterTypes("int", "int")), CustomMethodHandlers::randomNextInt)
instanceCall("java.util.SplittableRandom", "nextInt").parameterTypes("int", "int")), toValue(CustomMethodHandlers::randomNextInt))
.register(staticCall(JAVA_UTIL_ARRAYS, "copyOf"), CustomMethodHandlers::copyOfArray)
.register(instanceCall(JAVA_UTIL_COLLECTION, "toArray").parameterTypes("T[]"), CustomMethodHandlers::collectionToArray)
.register(instanceCall(JAVA_UTIL_COLLECTION, "toArray").parameterCount(0), CustomMethodHandlers::collectionToArray)
@@ -141,7 +164,7 @@ public final class CustomMethodHandlers {
public static CustomMethodHandler find(PsiMethod method) {
CustomMethodHandler handler = null;
if (isConstantCall(method)) {
handler = (arguments, state, factory, m) -> handleConstantCall(arguments, state, m);
handler = toValue((arguments, state, factory, m) -> handleConstantCall(arguments, state, m));
}
CustomMethodHandler handler2 = CUSTOM_METHOD_HANDLERS.mapFirst(method);
return handler == null ? handler2 : handler.compose(handler2);
@@ -256,32 +279,32 @@ public final class CustomMethodHandlers {
return intRange(LongRangeSet.range(-1, range.max() - 1));
}
private static @NotNull DfType copyOfArray(DfaCallArguments arguments, DfaMemoryState state, DfaValueFactory factory, PsiMethod method) {
if (arguments.myArguments.length < 2) return TOP;
DfType size = state.getDfType(arguments.myArguments[1]).meet(intRange(LongRangeSet.indexRange()));
if (size == BOTTOM) return FAIL;
return typedObject(method.getReturnType(), Nullability.NOT_NULL).meet(ARRAY_LENGTH.asDfType(size)).meet(LOCAL_OBJECT);
private static @Nullable DfaValue copyOfArray(DfaCallArguments arguments, DfaMemoryState state, DfaValueFactory factory, PsiMethod method) {
if (arguments.myArguments.length < 2) return null;
return factory.getBoxedFactory().createBoxed(typedObject(method.getReturnType(), Nullability.NOT_NULL).meet(LOCAL_OBJECT),
ARRAY_LENGTH, arguments.myArguments[1]);
}
private static @NotNull DfType collectionFactory(DfaCallArguments args,
private static @Nullable DfaValue collectionFactory(DfaCallArguments args,
DfaMemoryState memState, DfaValueFactory factory,
PsiMethod method) {
PsiType type = method.getReturnType();
if (!(type instanceof PsiClassType)) return TOP;
if (!(type instanceof PsiClassType)) return null;
int factor = PsiTypesUtil.classNameEquals(type, JAVA_UTIL_MAP) ? 2 : 1;
DfType size;
DfaValue size;
if (method.isVarArgs()) {
size = memState.getDfType(ARRAY_LENGTH.createValue(factory, args.myArguments[0]));
size = ARRAY_LENGTH.createValue(factory, args.myArguments[0]);
}
else {
size = intValue(args.myArguments.length / factor);
size = factory.fromDfType(intValue(args.myArguments.length / factor));
}
boolean asList = method.getName().equals("asList");
Mutability mutability = asList ? Mutability.MUTABLE : Mutability.UNMODIFIABLE;
DfType result = typedObject(type, Nullability.NOT_NULL)
.meet(COLLECTION_SIZE.asDfType(size))
.meet(mutability.asDfType());
return asList ? result.meet(LOCAL_OBJECT) : result;
DfType result = typedObject(type, Nullability.NOT_NULL).meet(mutability.asDfType());
if (asList) {
result = result.meet(LOCAL_OBJECT);
}
return factory.getBoxedFactory().createBoxed(result, COLLECTION_SIZE, size);
}
private static DfType getEmptyCollectionConstant(PsiMethod method) {
@@ -293,21 +316,19 @@ public final class CustomMethodHandlers {
return constant(field, field.getType());
}
private static @NotNull DfType substring(DfaCallArguments args, DfaMemoryState state, DfaValueFactory factory, PsiType stringType) {
if (stringType == null || !stringType.equalsToText(JAVA_LANG_STRING)) return TOP;
private static @Nullable DfaValue substring(DfaCallArguments args, DfaMemoryState state, DfaValueFactory factory, PsiType stringType) {
if (stringType == null || !stringType.equalsToText(JAVA_LANG_STRING)) return null;
DfaValue qualifier = args.myQualifier;
DfaValue[] arguments = args.myArguments;
if (arguments.length < 1 || arguments.length > 2 || arguments[0] == null) return TOP;
if (arguments.length < 1 || arguments.length > 2 || arguments[0] == null) return null;
DfaValue from = arguments[0];
DfaValue lenVal = STRING_LENGTH.createValue(factory, qualifier);
DfaValue to = arguments.length == 1 ? lenVal : arguments[1];
DfaValue resultLenVal = factory.getBinOpFactory().create(to, from, state, false, JavaTokenType.MINUS);
DfType resultLen = state.getDfType(resultLenVal);
if (!(resultLen instanceof DfIntType)) return FAIL;
resultLen = ((DfIntType)resultLen).meetRelation(RelationType.GE, intValue(0));
if (!(resultLen instanceof DfIntType)) return FAIL;
resultLen = ((DfIntType)resultLen).meetRelation(RelationType.LE, state.getDfType(lenVal));
return STRING_LENGTH.asDfType(resultLen, stringType);
DfaValue resultLen = factory.getBinOpFactory().create(to, from, state, false, JavaTokenType.MINUS);
if (resultLen instanceof DfaBinOpValue) {
resultLen = factory.fromDfType(state.getDfType(resultLen));
}
return factory.getBoxedFactory().createBoxed(typedObject(stringType, Nullability.NOT_NULL), STRING_LENGTH, resultLen);
}
private static @NotNull DfType mathAbs(DfaValue[] args, DfaMemoryState memState, boolean isLong) {
@@ -446,11 +467,12 @@ public final class CustomMethodHandlers {
return TOP;
}
private static @NotNull DfType collectionToArray(DfaCallArguments arguments, DfaMemoryState state, DfaValueFactory factory, PsiMethod method) {
private static @NotNull DfaValue collectionToArray(DfaCallArguments arguments, DfaMemoryState state, DfaValueFactory factory, PsiMethod method) {
DfType result = NOT_NULL_OBJECT;
LongRangeSet finalRange;
DfaValue collection = arguments.myQualifier;
LongRangeSet collectionSizeRange = DfIntType.extractRange(state.getDfType(COLLECTION_SIZE.createValue(factory, collection)));
DfaValue collectionSize = COLLECTION_SIZE.createValue(factory, collection);
LongRangeSet collectionSizeRange = DfIntType.extractRange(state.getDfType(collectionSize));
DfaValue finalSize = collectionSize;
if (arguments.myArguments.length == 1) {
DfaValue array = arguments.myArguments[0];
DfType arrType = state.getDfType(array);
@@ -460,20 +482,20 @@ public final class CustomMethodHandlers {
}
// Array size is max of collection size and argument array size
LongRangeSet arraySizeRange = DfIntType.extractRange(state.getDfType(ARRAY_LENGTH.createValue(factory, array)));
LongRangeSet biggerArrays = collectionSizeRange.fromRelation(RelationType.GE).intersect(arraySizeRange);
LongRangeSet biggerArrays = collectionSizeRange.fromRelation(RelationType.GT).intersect(arraySizeRange);
LongRangeSet biggerCollections = arraySizeRange.fromRelation(RelationType.GE).intersect(collectionSizeRange);
finalRange = biggerArrays.unite(biggerCollections);
} else {
finalRange = collectionSizeRange;
if (!biggerArrays.isEmpty()) {
finalSize = factory.fromDfType(intRange(biggerArrays.unite(biggerCollections)));
}
}
return result.meet(ARRAY_LENGTH.asDfType(intRange(finalRange)));
return factory.getBoxedFactory().createBoxed(result, ARRAY_LENGTH, finalSize);
}
private static @NotNull DfType stringToCharArray(DfaCallArguments arguments, DfaMemoryState state, DfaValueFactory factory,
private static @NotNull DfaValue stringToCharArray(DfaCallArguments arguments, DfaMemoryState state, DfaValueFactory factory,
PsiMethod method) {
DfaValue string = arguments.myQualifier;
DfType stringSizeRange = state.getDfType(STRING_LENGTH.createValue(factory, string));
return typedObject(PsiType.CHAR.createArrayType(), Nullability.NOT_NULL)
.meet(LOCAL_OBJECT).meet(ARRAY_LENGTH.asDfType(stringSizeRange));
DfaValue stringLength = STRING_LENGTH.createValue(factory, string);
return factory.getBoxedFactory().createBoxed(typedObject(PsiType.CHAR.createArrayType(), Nullability.NOT_NULL)
.meet(LOCAL_OBJECT), ARRAY_LENGTH, stringLength);
}
}
@@ -1219,7 +1219,8 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
if (value instanceof DfaBoxedValue) {
DfaBoxedValue boxedValue = (DfaBoxedValue)value;
DfaValue canonicalized = canonicalize(boxedValue.getWrappedValue());
return Objects.requireNonNull(myFactory.getBoxedFactory().createBoxed(canonicalized, boxedValue.getDfType()));
if (canonicalized == boxedValue.getWrappedValue()) return boxedValue;
return myFactory.getBoxedFactory().createBoxed(boxedValue.getDfType(), boxedValue.getSpecialField(), canonicalized);
}
return value;
}
@@ -359,7 +359,7 @@ public final class DfaUtil {
public static DfaValue boxUnbox(DfaValue value, @Nullable PsiType type) {
if (TypeConversionUtil.isPrimitiveWrapper(type)) {
if (TypeConversionUtil.isPrimitiveAndNotNull(value.getType())) {
return value.getFactory().getBoxedFactory().createBoxed(value, DfTypes.typedObject(type, Nullability.NOT_NULL));
return value.getFactory().getBoxedFactory().createBoxed(DfTypes.typedObject(type, Nullability.NOT_NULL), SpecialField.UNBOX, value);
}
}
if (TypeConversionUtil.isPrimitiveAndNotNull(type)) {
@@ -159,7 +159,7 @@ public abstract class InstructionVisitor {
if (value instanceof DfaBinOpValue) {
value = factory.fromDfType(state.getDfType(value));
}
state.push(factory.getBoxedFactory().createBoxed(value, instruction.getTargetType()));
state.push(factory.getBoxedFactory().createBoxed(instruction.getTargetType(), SpecialField.UNBOX, value));
return nextInstruction(instruction, runner, state);
}
@@ -175,15 +175,6 @@ public enum SpecialField implements VariableDescriptor {
return DfTypes.TOP;
}
@NotNull
@Override
public DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, boolean forAccessor) {
if (qualifier instanceof DfaBoxedValue) {
return ((DfaBoxedValue)qualifier).getWrappedValue();
}
return super.createValue(factory, qualifier, forAccessor);
}
@Override
boolean isMyQualifierType(PsiType type) {
return TypeConversionUtil.isPrimitiveWrapper(type);
@@ -300,6 +291,9 @@ public enum SpecialField implements VariableDescriptor {
@NotNull
@Override
public DfaValue createValue(@NotNull DfaValueFactory factory, @Nullable DfaValue qualifier, boolean forAccessor) {
if (qualifier instanceof DfaBoxedValue && ((DfaBoxedValue)qualifier).getSpecialField() == this) {
return ((DfaBoxedValue)qualifier).getWrappedValue();
}
if (qualifier instanceof DfaVariableValue) {
DfaVariableValue variableValue = (DfaVariableValue)qualifier;
PsiModifierListOwner psiVariable = variableValue.getPsiVariable();
@@ -660,9 +660,9 @@ public class StandardInstructionVisitor extends InstructionVisitor {
if (method != null) {
CustomMethodHandlers.CustomMethodHandler handler = CustomMethodHandlers.find(method);
if (handler != null) {
DfType dfType = handler.getMethodResult(callArguments, state, factory, method);
if (dfType != TOP) {
return factory.fromDfType(dfType);
DfaValue value = handler.getMethodResultValue(callArguments, state, factory, method);
if (value != null) {
return value;
}
}
}
@@ -3,25 +3,35 @@ package com.intellij.codeInspection.dataFlow.value;
import com.intellij.codeInspection.dataFlow.SpecialField;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.codeInspection.dataFlow.types.DfTypes;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiType;
import gnu.trove.TIntObjectHashMap;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import static com.intellij.psi.CommonClassNames.JAVA_LANG_STRING;
public final class DfaBoxedValue extends DfaValue {
private final @NotNull DfaVariableValue myWrappedValue;
private final @NotNull SpecialField mySpecialField;
private final @NotNull DfType myType;
private DfaBoxedValue(@NotNull DfaVariableValue valueToWrap, @NotNull DfaValueFactory factory, @NotNull DfType type) {
super(factory);
private DfaBoxedValue(@NotNull DfaVariableValue valueToWrap,
@NotNull SpecialField field,
@NotNull DfType type) {
super(valueToWrap.getFactory());
myWrappedValue = valueToWrap;
mySpecialField = field;
myType = type;
}
@NonNls
public String toString() {
return "Boxed "+myWrappedValue.toString();
return myType + ";" + mySpecialField + "=" + myWrappedValue;
}
@NotNull
@@ -29,6 +39,11 @@ public final class DfaBoxedValue extends DfaValue {
return myWrappedValue;
}
@NotNull
public SpecialField getSpecialField() {
return mySpecialField;
}
@Nullable
@Override
public PsiType getType() {
@@ -42,7 +57,7 @@ public final class DfaBoxedValue extends DfaValue {
}
public static class Factory {
private final TIntObjectHashMap<DfaBoxedValue> cachedValues = new TIntObjectHashMap<>();
private final HashMap<Object, DfaBoxedValue> cachedValues = new HashMap<>();
private final DfaValueFactory myFactory;
@@ -51,26 +66,31 @@ public final class DfaBoxedValue extends DfaValue {
}
@NotNull
public DfaValue createBoxed(@NotNull DfaValue valueToWrap, @NotNull DfType type) {
if (valueToWrap instanceof DfaVariableValue && ((DfaVariableValue)valueToWrap).getDescriptor() == SpecialField.UNBOX) {
DfaVariableValue qualifier = ((DfaVariableValue)valueToWrap).getQualifier();
if (qualifier != null && type.equals(qualifier.getDfType())) {
public DfaValue createBoxed(@NotNull DfType qualifierType,
@NotNull SpecialField specialField, @NotNull DfaValue specialFieldValue) {
if (specialFieldValue instanceof DfaVariableValue && ((DfaVariableValue)specialFieldValue).getDescriptor() == specialField) {
DfaVariableValue qualifier = ((DfaVariableValue)specialFieldValue).getQualifier();
if (qualifier != null && qualifierType.isSuperType(qualifier.getDfType())) {
return qualifier;
}
}
if (valueToWrap instanceof DfaTypeValue) {
DfType dfType = SpecialField.UNBOX.asDfType(valueToWrap.getDfType()).meet(type);
if (specialFieldValue instanceof DfaTypeValue) {
DfType dfType;
DfType fieldValue = specialFieldValue.getDfType();
if (specialField == SpecialField.STRING_LENGTH && fieldValue.isConst(0)) {
dfType = DfTypes.constant("", JavaPsiFacade.getElementFactory(specialFieldValue.getFactory().getProject())
.createTypeByFQClassName(JAVA_LANG_STRING));
}
else {
dfType = qualifierType.meet(specialField.asDfType(fieldValue));
}
return myFactory.fromDfType(dfType);
}
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;
if (specialFieldValue instanceof DfaVariableValue) {
return cachedValues.computeIfAbsent(Arrays.asList(specialFieldValue, specialField, qualifierType),
k -> new DfaBoxedValue((DfaVariableValue)specialFieldValue, specialField, qualifierType));
}
return myFactory.fromDfType(type);
return myFactory.fromDfType(qualifierType);
}
}
}
@@ -234,7 +234,8 @@ public class DfaExpressionFactory {
PsiType type = expression.getType();
if (expression instanceof PsiArrayInitializerExpression) {
int length = ((PsiArrayInitializerExpression)expression).getInitializers().length;
return myFactory.fromDfType(SpecialField.ARRAY_LENGTH.asDfType(DfTypes.intValue(length), type));
return myFactory.fromDfType(SpecialField.ARRAY_LENGTH.asDfType(DfTypes.intValue(length))
.meet(DfTypes.typedObject(type, Nullability.NOT_NULL)));
}
DfType dfType = DfTypes.typedObject(type, NullabilityUtil.getExpressionNullability(expression));
if (type instanceof PsiPrimitiveType && targetType instanceof PsiPrimitiveType && !type.equals(targetType)) {
@@ -144,7 +144,7 @@ public class DfaValueFactory {
Boolean boo = computeJavaLangBooleanFieldReference(variable);
if (boo != null) {
DfaValue unboxed = getConstant(boo, PsiType.BOOLEAN);
return getBoxedFactory().createBoxed(unboxed, DfTypes.typedObject(type, Nullability.NOT_NULL));
return getBoxedFactory().createBoxed(DfTypes.typedObject(type, Nullability.NOT_NULL), SpecialField.UNBOX, unboxed);
}
if (DfaUtil.isEmptyCollectionConstantField(variable)) {
return getConstant(variable, type);
@@ -12,6 +12,11 @@ class Foo {
if (<warning descr="Condition 'list2.size() == 1' is always 'true'">list2.size() == 1</warning>) {}
if (<warning descr="Condition 'list3.size() == 3' is always 'true'">list3.size() == 3</warning>) {}
}
void test2(String[] arr) {
List<String> list = Arrays.asList(arr);
if (<warning descr="Condition 'list.size() == arr.length' is always 'true'">list.size() == arr.length</warning>) {}
}
native void unknown();
}
@@ -31,4 +31,9 @@ public class ArraysCopyOf {
if (<warning descr="Condition 'copy3.length == 0' is always 'true'">copy3.length == 0</warning>) {}
int[] copy4 = Arrays.<warning descr="The call to 'copyOf' always fails as index is out of bounds">copyOf</warning>(arr, copy3.length - 1);
}
void test2(int[] arr, int size) {
int[] copy = Arrays.copyOf(arr, size);
if (<warning descr="Condition 'copy.length == size' is always 'true'">copy.length == size</warning>) {}
}
}
@@ -75,4 +75,14 @@ public class CollectionToArray {
assert arr.length == 0;
return list.toArray(arr);
}
void testSizeEquality(List<String> list, int x) {
String[] arr = list.toArray(new String[0]);
if (<warning descr="Condition 'x == 1 && list.get(arr.length).isEmpty()' is always 'false'">x == 1 && list.<warning descr="The call to 'get' always fails as index is out of bounds">get</warning>(arr.length).isEmpty()</warning>) {
}
if (<warning descr="Condition 'x == 2 && arr[list.size()].isEmpty()' is always 'false'">x == 2 && arr[<warning descr="Array index is out of bounds">list.size()</warning>].isEmpty()</warning>) {
}
if (list.isEmpty()) return;
if (<warning descr="Condition 'arr.length == 0' is always 'false'">arr.length == 0</warning>) return;
}
}
@@ -88,4 +88,11 @@ class StringSubstring {
if (<warning descr="Condition 's2.length() != 4' is always 'false'">s2.length() != 4</warning>) {}
}
}
void testSubstringVarRef(String s, int from, int length) {
String s1 = s.substring(0, length);
if (<warning descr="Condition 's1.length() == length' is always 'true'">s1.length() == length</warning>) {}
String s2 = s.substring(from, from + length);
if (<warning descr="Condition 's2.length() == length' is always 'true'">s2.length() == length</warning>) {}
}
}
@@ -9,6 +9,7 @@ public class StringToCharArray {
if (s.startsWith("--")) {
char[] arr = s.toCharArray();
if (<warning descr="Condition 'arr.length > 1' is always 'true'">arr.length > 1</warning>) {}
if (<warning descr="Condition 'arr.length == s.length()' is always 'true'">arr.length == s.length()</warning>) {}
}
}
}