mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[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:
committed by
intellij-monorepo-bot
parent
0effec1ee0
commit
0a196e727f
+4
-4
@@ -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;
|
||||
|
||||
+83
-61
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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)) {
|
||||
|
||||
+1
-1
@@ -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();
|
||||
|
||||
+3
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+39
-19
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -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)) {
|
||||
|
||||
+1
-1
@@ -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>) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user