mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-dfa] IDEA-361818 Make LiveVariableAnalyzer descriptor-based, rather than variable-based
GitOrigin-RevId: c3a8a029fe4e6f4c3ef7bdd67726ec4993cb3c80
This commit is contained in:
committed by
intellij-monorepo-bot
parent
402acf04ff
commit
0ca49293fa
+11
-7
@@ -14,10 +14,7 @@ import com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfTypes;
|
||||
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.RelationType;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.debugger.engine.DebuggerUtils;
|
||||
import com.intellij.debugger.engine.evaluation.EvaluateException;
|
||||
import com.intellij.debugger.impl.DebuggerUtilsEx;
|
||||
@@ -150,7 +147,7 @@ public class DebuggerDfaRunner {
|
||||
long modificationStamp = PsiModificationTracker.getInstance(project).getModificationCount();
|
||||
int offset = flow.getStartOffset(anchor).getInstructionOffset();
|
||||
if (offset < 0) return null;
|
||||
Map<Value, List<DfaVariableValue>> jdiToDfa = createPreliminaryJdiMap(provider, anchor, factory, proxy);
|
||||
Map<Value, List<DfaVariableValue>> jdiToDfa = createPreliminaryJdiMap(provider, anchor, flow, proxy);
|
||||
if (jdiToDfa.isEmpty()) return null;
|
||||
return new Larva(project, anchor, body, flow, factory, modificationStamp, provider, jdiToDfa, proxy, offset);
|
||||
}
|
||||
@@ -158,11 +155,18 @@ public class DebuggerDfaRunner {
|
||||
@NotNull
|
||||
private static Map<Value, List<DfaVariableValue>> createPreliminaryJdiMap(@NotNull DfaAssistProvider provider,
|
||||
@NotNull PsiElement anchor,
|
||||
@NotNull DfaValueFactory factory,
|
||||
@NotNull ControlFlow flow,
|
||||
@NotNull StackFrameProxyEx proxy) throws EvaluateException {
|
||||
DfaValueFactory factory = flow.getFactory();
|
||||
Set<VariableDescriptor> descriptors = StreamEx.of(flow.getInstructions()).flatCollection(inst -> inst.getRequiredDescriptors(factory))
|
||||
.toSet();
|
||||
Map<Value, List<DfaVariableValue>> myMap = new HashMap<>();
|
||||
for (DfaValue dfaValue : factory.getValues().toArray(DfaValue.EMPTY_ARRAY)) {
|
||||
if (dfaValue instanceof DfaVariableValue dfaVar) {
|
||||
StreamEx<DfaVariableValue> stream = StreamEx.of(descriptors)
|
||||
.map(desc -> desc.createValue(factory, dfaValue))
|
||||
.append(dfaValue)
|
||||
.select(DfaVariableValue.class);
|
||||
for (DfaVariableValue dfaVar : stream) {
|
||||
Value jdiValue = resolveJdiValue(provider, anchor, proxy, dfaVar);
|
||||
if (jdiValue != null) {
|
||||
myMap.computeIfAbsent(jdiValue, v -> new ArrayList<>()).add(dfaVar);
|
||||
|
||||
@@ -380,12 +380,10 @@ public final class DfaPsiUtil {
|
||||
@Override
|
||||
protected DfaInstructionState @NotNull [] acceptInstruction(@NotNull DfaInstructionState instructionState) {
|
||||
Instruction instruction = instructionState.getInstruction();
|
||||
if (instruction instanceof FinishElementInstruction) {
|
||||
Set<DfaVariableValue> vars = ((FinishElementInstruction)instruction).getVarsToFlush();
|
||||
vars.removeIf(v -> {
|
||||
PsiElement variable = v.getPsiVariable();
|
||||
return variable instanceof PsiField && ((PsiField)variable).getContainingClass() == containingClass;
|
||||
});
|
||||
if (instruction instanceof FinishElementInstruction finishInstruction) {
|
||||
finishInstruction.removeFromFlushList(desc ->
|
||||
desc instanceof PlainDescriptor plainDescriptor && plainDescriptor.getPsiElement() instanceof PsiField field &&
|
||||
field.getContainingClass() == containingClass);
|
||||
}
|
||||
if ((isCallExposingNonInitializedFields(instruction) || instruction instanceof ReturnInstruction)) {
|
||||
for (PsiField field : containingClass.getFields()) {
|
||||
|
||||
+8
-10
@@ -41,8 +41,8 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.augment.PsiAugmentProvider;
|
||||
import com.intellij.psi.impl.source.tree.java.PsiEmptyExpressionImpl;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -177,9 +177,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
myCurrentFlow.finishElement(element);
|
||||
if (element instanceof PsiField || (element instanceof PsiStatement && !(element instanceof PsiReturnStatement) &&
|
||||
!(element instanceof PsiSwitchLabeledRuleStatement))) {
|
||||
List<DfaVariableValue> synthetics = myCurrentFlow.getSynthetics(element);
|
||||
List<VariableDescriptor> synthetics = myCurrentFlow.getSynthetics(element);
|
||||
FinishElementInstruction instruction = new FinishElementInstruction(element);
|
||||
instruction.getVarsToFlush().addAll(synthetics);
|
||||
instruction.flushVars(synthetics);
|
||||
addInstruction(instruction);
|
||||
}
|
||||
}
|
||||
@@ -231,8 +231,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
if (arrayStore != null) {
|
||||
DfaControlTransferValue transfer = createTransfer("java.lang.ArrayIndexOutOfBoundsException");
|
||||
var staticVariable = ObjectUtils.tryCast(JavaDfaValueFactory.getExpressionDfaValue(myFactory, arrayStore), DfaVariableValue.class);
|
||||
addInstruction(new JavaArrayStoreInstruction(arrayStore, rExpr, transfer, staticVariable));
|
||||
VariableDescriptor staticDescriptor = ArrayElementDescriptor.fromArrayAccess(arrayStore);
|
||||
addInstruction(new JavaArrayStoreInstruction(arrayStore, rExpr, transfer, staticDescriptor));
|
||||
} else {
|
||||
addInstruction(new AssignInstruction(rExpr, JavaDfaValueFactory.getExpressionDfaValue(myFactory, lExpr)));
|
||||
}
|
||||
@@ -1681,10 +1681,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
private void readArrayElement(@NotNull PsiArrayAccessExpression expression) {
|
||||
DfaControlTransferValue transfer = createTransfer("java.lang.ArrayIndexOutOfBoundsException");
|
||||
DfaVariableValue staticValue =
|
||||
ObjectUtils.tryCast(JavaDfaValueFactory.getExpressionDfaValue(myFactory, expression), DfaVariableValue.class);
|
||||
addInstruction(new ArrayAccessInstruction(new JavaExpressionAnchor(expression), new ArrayIndexProblem(expression), transfer,
|
||||
staticValue));
|
||||
ArrayElementDescriptor.fromArrayAccess(expression)));
|
||||
}
|
||||
|
||||
private @Nullable DfaVariableValue getTargetVariable(PsiExpression expression) {
|
||||
@@ -2446,8 +2444,8 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
}
|
||||
DfaValue dest = JavaDfaValueFactory.getExpressionDfaValue(myFactory, operand);
|
||||
if (arrayStore != null) {
|
||||
addInstruction(new JavaArrayStoreInstruction(arrayStore, null, null,
|
||||
ObjectUtils.tryCast(dest, DfaVariableValue.class)));
|
||||
VariableDescriptor staticDescriptor = ArrayElementDescriptor.fromArrayAccess(arrayStore);
|
||||
addInstruction(new JavaArrayStoreInstruction(arrayStore, null, null, staticDescriptor));
|
||||
} else {
|
||||
addInstruction(new AssignInstruction(operand, null, dest));
|
||||
}
|
||||
|
||||
+7
-7
@@ -4,6 +4,7 @@ package com.intellij.codeInspection.dataFlow.java.inst;
|
||||
import com.intellij.codeInspection.dataFlow.TypeConstraint;
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter;
|
||||
import com.intellij.codeInspection.dataFlow.java.JavaDfaHelpers;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.descriptors.ArrayElementDescriptor;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.problems.IndexOutOfBoundsProblem;
|
||||
import com.intellij.codeInspection.dataFlow.lang.DfaAnchor;
|
||||
@@ -15,7 +16,6 @@ import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfIntType;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -25,12 +25,12 @@ import java.util.List;
|
||||
public class ArrayAccessInstruction extends ExpressionPushingInstruction {
|
||||
private final @Nullable DfaControlTransferValue myOutOfBoundsTransfer;
|
||||
private final @NotNull IndexOutOfBoundsProblem myProblem;
|
||||
private final @Nullable DfaVariableValue myStaticValue;
|
||||
private final @Nullable VariableDescriptor myStaticValue;
|
||||
|
||||
public ArrayAccessInstruction(@Nullable DfaAnchor anchor,
|
||||
@NotNull IndexOutOfBoundsProblem indexProblem,
|
||||
@Nullable DfaControlTransferValue outOfBoundsTransfer,
|
||||
@Nullable DfaVariableValue staticValue) {
|
||||
@Nullable VariableDescriptor staticValue) {
|
||||
super(anchor);
|
||||
myOutOfBoundsTransfer = outOfBoundsTransfer;
|
||||
myProblem = indexProblem;
|
||||
@@ -40,8 +40,7 @@ public class ArrayAccessInstruction extends ExpressionPushingInstruction {
|
||||
@Override
|
||||
public @NotNull Instruction bindToFactory(@NotNull DfaValueFactory factory) {
|
||||
DfaControlTransferValue newTransfer = myOutOfBoundsTransfer == null ? null : myOutOfBoundsTransfer.bindToFactory(factory);
|
||||
DfaVariableValue newStaticValue = myStaticValue == null ? null : myStaticValue.bindToFactory(factory);
|
||||
return new ArrayAccessInstruction(getDfaAnchor(), myProblem, newTransfer, newStaticValue);
|
||||
return new ArrayAccessInstruction(getDfaAnchor(), myProblem, newTransfer, myStaticValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,8 +77,9 @@ public class ArrayAccessInstruction extends ExpressionPushingInstruction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getRequiredVariables(DfaValueFactory factory) {
|
||||
return ContainerUtil.createMaybeSingletonList(myStaticValue);
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return myStaticValue == null ? List.of(SpecialField.ARRAY_LENGTH) :
|
||||
List.of(myStaticValue, SpecialField.ARRAY_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+7
-11
@@ -3,6 +3,7 @@ package com.intellij.codeInspection.dataFlow.java.inst;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter;
|
||||
import com.intellij.codeInspection.dataFlow.java.JavaDfaHelpers;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.SpecialField;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.descriptors.ArrayElementDescriptor;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.problems.IndexOutOfBoundsProblem;
|
||||
import com.intellij.codeInspection.dataFlow.lang.DfaAnchor;
|
||||
@@ -12,11 +13,7 @@ import com.intellij.codeInspection.dataFlow.lang.ir.Instruction;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfIntType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaControlTransferValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -30,12 +27,12 @@ import java.util.Set;
|
||||
public class ArrayStoreInstruction extends ExpressionPushingInstruction {
|
||||
protected final @Nullable DfaControlTransferValue myOutOfBoundsTransfer;
|
||||
protected final @NotNull IndexOutOfBoundsProblem myIndexProblem;
|
||||
protected final @Nullable DfaVariableValue myStaticVariable;
|
||||
protected final @Nullable VariableDescriptor myStaticVariable;
|
||||
|
||||
public ArrayStoreInstruction(@Nullable DfaAnchor anchor,
|
||||
@NotNull IndexOutOfBoundsProblem problem,
|
||||
@Nullable DfaControlTransferValue outOfBoundsTransfer,
|
||||
@Nullable DfaVariableValue variable) {
|
||||
@Nullable VariableDescriptor variable) {
|
||||
super(anchor);
|
||||
myIndexProblem = problem;
|
||||
myOutOfBoundsTransfer = outOfBoundsTransfer;
|
||||
@@ -45,8 +42,7 @@ public class ArrayStoreInstruction extends ExpressionPushingInstruction {
|
||||
@Override
|
||||
public @NotNull Instruction bindToFactory(@NotNull DfaValueFactory factory) {
|
||||
DfaControlTransferValue transfer = myOutOfBoundsTransfer == null ? null : myOutOfBoundsTransfer.bindToFactory(factory);
|
||||
DfaVariableValue staticVariable = myStaticVariable == null ? null : myStaticVariable.bindToFactory(factory);
|
||||
return new ArrayStoreInstruction(getDfaAnchor(), myIndexProblem, transfer, staticVariable);
|
||||
return new ArrayStoreInstruction(getDfaAnchor(), myIndexProblem, transfer, myStaticVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,8 +83,8 @@ public class ArrayStoreInstruction extends ExpressionPushingInstruction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getWrittenVariables(DfaValueFactory factory) {
|
||||
return ContainerUtil.createMaybeSingletonList(myStaticVariable);
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return List.of(SpecialField.ARRAY_LENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-8
@@ -19,13 +19,10 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class AssignInstruction extends ExpressionPushingInstruction {
|
||||
@@ -57,11 +54,6 @@ public class AssignInstruction extends ExpressionPushingInstruction {
|
||||
return new AssignInstruction(myLExpression, myRExpression, myAssignedValue.bindToFactory(factory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getWrittenVariables(DfaValueFactory factory) {
|
||||
return ContainerUtil.createMaybeSingletonList(tryCast(myAssignedValue, DfaVariableValue.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] accept(@NotNull DataFlowInterpreter interpreter, @NotNull DfaMemoryState stateBefore) {
|
||||
DfaValue dfaSource = stateBefore.pop();
|
||||
|
||||
+5
-3
@@ -9,7 +9,8 @@ import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.psi.PsiMember;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@@ -26,9 +27,10 @@ public class EndOfInstanceInitializerInstruction extends Instruction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getRequiredVariables(DfaValueFactory factory) {
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return StreamEx.of(factory.getValues()).select(DfaVariableValue.class)
|
||||
.filter(var -> var.getPsiVariable() instanceof PsiMember).toList();
|
||||
.map(DfaVariableValue::getDescriptor)
|
||||
.filter(var -> !PsiUtil.isJvmLocalVariable(var.getPsiElement())).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
-2
@@ -8,7 +8,9 @@ import com.intellij.codeInspection.dataFlow.lang.ir.Instruction;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
@@ -32,8 +34,12 @@ public class EscapeInstruction extends Instruction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getRequiredVariables(DfaValueFactory factory) {
|
||||
return List.copyOf(myEscapedVars);
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return StreamEx.of(myEscapedVars)
|
||||
.flatMap(v -> StreamEx.of(v.getDependentVariables()).append(v))
|
||||
.map(DfaVariableValue::getDescriptor)
|
||||
.distinct()
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+15
-4
@@ -4,21 +4,22 @@ package com.intellij.codeInspection.dataFlow.java.inst;
|
||||
import com.intellij.codeInsight.Nullability;
|
||||
import com.intellij.codeInspection.dataFlow.DfaNullability;
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter;
|
||||
import com.intellij.codeInspection.dataFlow.jvm.descriptors.GetterDescriptor;
|
||||
import com.intellij.codeInspection.dataFlow.lang.DfaAnchor;
|
||||
import com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState;
|
||||
import com.intellij.codeInspection.dataFlow.lang.ir.ExpressionPushingInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaCondition;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.RelationType;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.codeInspection.dataFlow.types.DfTypes.*;
|
||||
|
||||
@@ -83,6 +84,16 @@ public class InstanceofInstruction extends ExpressionPushingInstruction {
|
||||
return states.toArray(DfaInstructionState.EMPTY_ARRAY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return StreamEx.of(factory.getValues())
|
||||
.select(DfaVariableValue.class)
|
||||
.map(DfaVariableValue::getDescriptor)
|
||||
.filter(desc -> desc instanceof GetterDescriptor getterDescriptor &&
|
||||
PsiTypesUtil.isGetClass(getterDescriptor.getPsiElement()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "INSTANCE_OF " + (myClassObjectCheck ? "(CLASS)" : "");
|
||||
|
||||
+3
-4
@@ -12,7 +12,7 @@ import com.intellij.codeInspection.dataFlow.types.DfType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaControlTransferValue;
|
||||
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.VariableDescriptor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiArrayAccessExpression;
|
||||
import com.intellij.psi.PsiAssignmentExpression;
|
||||
@@ -36,7 +36,7 @@ public class JavaArrayStoreInstruction extends ArrayStoreInstruction {
|
||||
public JavaArrayStoreInstruction(@NotNull PsiArrayAccessExpression expression,
|
||||
@Nullable PsiExpression valueExpression,
|
||||
@Nullable DfaControlTransferValue outOfBoundsTransfer,
|
||||
@Nullable DfaVariableValue staticVariable) {
|
||||
@Nullable VariableDescriptor staticVariable) {
|
||||
super(createAnchor(expression), new ArrayIndexProblem(expression), outOfBoundsTransfer, staticVariable);
|
||||
myExpression = expression;
|
||||
myValueExpression = valueExpression;
|
||||
@@ -50,8 +50,7 @@ public class JavaArrayStoreInstruction extends ArrayStoreInstruction {
|
||||
@Override
|
||||
public @NotNull Instruction bindToFactory(@NotNull DfaValueFactory factory) {
|
||||
DfaControlTransferValue transfer = myOutOfBoundsTransfer == null ? null : myOutOfBoundsTransfer.bindToFactory(factory);
|
||||
DfaVariableValue staticVariable = myStaticVariable == null ? null : myStaticVariable.bindToFactory(factory);
|
||||
return new JavaArrayStoreInstruction(myExpression, myValueExpression, transfer, staticVariable);
|
||||
return new JavaArrayStoreInstruction(myExpression, myValueExpression, transfer, myStaticVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -26,7 +26,6 @@ import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.MethodCallUtils;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -538,7 +537,8 @@ public class MethodCallInstruction extends ExpressionPushingInstruction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getRequiredVariables(DfaValueFactory factory) {
|
||||
return ContainerUtil.createMaybeSingletonList(tryCast(myPrecalculatedReturnValue, DfaVariableValue.class));
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return myPrecalculatedReturnValue instanceof DfaVariableValue var ?
|
||||
List.of(var.getDescriptor()) : List.of();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,6 +325,11 @@ public enum SpecialField implements DerivedVariableDescriptor {
|
||||
return myFinal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isImplicitReadPossible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public abstract boolean isMyQualifierType(DfType type);
|
||||
|
||||
/**
|
||||
|
||||
+16
@@ -22,6 +22,11 @@ import org.jetbrains.annotations.Nullable;
|
||||
public final class ArrayElementDescriptor extends JvmVariableDescriptor {
|
||||
private final int myIndex;
|
||||
|
||||
/**
|
||||
* Creates a descriptor that represents an array element with a fixed index
|
||||
*
|
||||
* @param index index of an array element
|
||||
*/
|
||||
private ArrayElementDescriptor(int index) {
|
||||
myIndex = index;
|
||||
}
|
||||
@@ -199,4 +204,15 @@ public final class ArrayElementDescriptor extends JvmVariableDescriptor {
|
||||
}
|
||||
return componentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param arrayAccess expression to create a descriptor for
|
||||
* @return an array element descriptor that describes a specified array access expression;
|
||||
* null if it's not possible to describe a given array access expression with a single
|
||||
* {@code ArrayElementDescriptor}
|
||||
*/
|
||||
public static @Nullable ArrayElementDescriptor fromArrayAccess(@NotNull PsiArrayAccessExpression arrayAccess) {
|
||||
return ExpressionUtils.computeConstantExpression(arrayAccess.getIndexExpression()) instanceof Integer index ?
|
||||
new ArrayElementDescriptor(index) : null;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-6
@@ -27,7 +27,6 @@ import com.intellij.java.analysis.JavaAnalysisBundle;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.ControlFlowUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
@@ -198,11 +197,14 @@ public final class SuspiciousInvocationHandlerImplementationInspection extends A
|
||||
List<DfaInstructionState> result = new ArrayList<>();
|
||||
Instruction instruction = flow.getInstruction(0);
|
||||
for (Instruction inst : flow.getInstructions()) {
|
||||
if (inst instanceof FinishElementInstruction) {
|
||||
Set<DfaVariableValue> flush = ((FinishElementInstruction)inst).getVarsToFlush();
|
||||
flush.remove(myDfaMethodDeclaringClass);
|
||||
flush.remove(myDfaMethodName);
|
||||
flush.remove(myDfaMethodName.getQualifier());
|
||||
if (inst instanceof FinishElementInstruction finishInstruction) {
|
||||
DfaVariableValue qualifier = myDfaMethodName.getQualifier();
|
||||
finishInstruction.removeFromFlushList(
|
||||
desc ->
|
||||
desc.equals(myDfaMethodDeclaringClass.getDescriptor()) ||
|
||||
desc.equals(myDfaMethodName.getDescriptor()) ||
|
||||
(qualifier != null && desc.equals(qualifier.getDescriptor()))
|
||||
);
|
||||
}
|
||||
}
|
||||
for (DfaMemoryState state : memStates) {
|
||||
|
||||
-3
@@ -149,9 +149,6 @@ final class OverwrittenFieldAnalyzer {
|
||||
.filter(value -> value.getPsiVariable() instanceof PsiField field &&
|
||||
field.hasModifierProperty(PsiModifier.STATIC));
|
||||
}
|
||||
else if (instruction instanceof FinishElementInstruction finishElementInstruction) {
|
||||
readVariables = StreamEx.of(finishElementInstruction.getVarsToFlush());
|
||||
}
|
||||
else {
|
||||
readVariables = getReadVariables(instruction);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,16 @@ class App {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
System.out.println(a.field);
|
||||
}
|
||||
|
||||
void test1(A a, A b) {
|
||||
if(a.field != b.field) {
|
||||
// Not supported anymore, as `field` is flushed :(
|
||||
if(a == b) {
|
||||
System.out.println("Impossible");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -0,0 +1,384 @@
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Should not take more than 1-2 seconds to analyze
|
||||
*/
|
||||
public class ManyObjectEquals2 {
|
||||
|
||||
private String test0;
|
||||
private String test1;
|
||||
private String test2;
|
||||
private String test3;
|
||||
private String test4;
|
||||
private String test5;
|
||||
private String test6;
|
||||
private String test7;
|
||||
private String test8;
|
||||
private String test9;
|
||||
private String test10;
|
||||
private String test11;
|
||||
private String test12;
|
||||
private String test13;
|
||||
private String test14;
|
||||
private String test15;
|
||||
private String test16;
|
||||
private String test17;
|
||||
private String test18;
|
||||
private String test19;
|
||||
private String test20;
|
||||
private String test21;
|
||||
private String test22;
|
||||
private String test23;
|
||||
private String test24;
|
||||
private String test25;
|
||||
private String test26;
|
||||
private String test27;
|
||||
private String test28;
|
||||
private String test29;
|
||||
private String test30;
|
||||
private String test31;
|
||||
private String test32;
|
||||
private String test33;
|
||||
private String test34;
|
||||
private String test35;
|
||||
private String test36;
|
||||
private String test37;
|
||||
private String test38;
|
||||
private String test39;
|
||||
private String test40;
|
||||
private String test41;
|
||||
private String test42;
|
||||
private String test43;
|
||||
private String test44;
|
||||
private String test45;
|
||||
private String test46;
|
||||
private String test47;
|
||||
private String test48;
|
||||
private String test49;
|
||||
private String test50;
|
||||
private String test51;
|
||||
private String test52;
|
||||
private String test53;
|
||||
private String test54;
|
||||
private String test55;
|
||||
private String test56;
|
||||
private String test57;
|
||||
private String test58;
|
||||
private String test59;
|
||||
private String test60;
|
||||
private String test61;
|
||||
private String test62;
|
||||
private String test63;
|
||||
private String test64;
|
||||
private String test65;
|
||||
private String test66;
|
||||
private String test67;
|
||||
private String test68;
|
||||
private String test69;
|
||||
private String test70;
|
||||
private String test71;
|
||||
private String test72;
|
||||
private String test73;
|
||||
private String test74;
|
||||
private String test75;
|
||||
private String test76;
|
||||
private String test77;
|
||||
private String test78;
|
||||
private String test79;
|
||||
private String test80;
|
||||
private String test81;
|
||||
private String test82;
|
||||
private String test83;
|
||||
private String test84;
|
||||
private String test85;
|
||||
private String test86;
|
||||
private String test87;
|
||||
private String test88;
|
||||
private String test89;
|
||||
private String test90;
|
||||
private String test91;
|
||||
private String test92;
|
||||
private String test93;
|
||||
private String test94;
|
||||
private String test95;
|
||||
private String test96;
|
||||
private String test97;
|
||||
private String test98;
|
||||
private String test99;
|
||||
private String test100;
|
||||
private String test101;
|
||||
private String test102;
|
||||
private String test103;
|
||||
private String test104;
|
||||
private String test105;
|
||||
private String test106;
|
||||
private String test107;
|
||||
private String test108;
|
||||
private String test109;
|
||||
private String test110;
|
||||
private String test111;
|
||||
private String test112;
|
||||
private String test113;
|
||||
private String test114;
|
||||
private String test115;
|
||||
private String test116;
|
||||
private String test117;
|
||||
private String test118;
|
||||
private String test119;
|
||||
private String test120;
|
||||
private String test121;
|
||||
private String test122;
|
||||
private String test123;
|
||||
private String test124;
|
||||
private String test125;
|
||||
private String test126;
|
||||
private String test127;
|
||||
private String test128;
|
||||
private String test129;
|
||||
private String test130;
|
||||
private String test131;
|
||||
private String test132;
|
||||
private String test133;
|
||||
private String test134;
|
||||
private String test135;
|
||||
private String test136;
|
||||
private String test137;
|
||||
private String test138;
|
||||
private String test139;
|
||||
private String test140;
|
||||
private String test141;
|
||||
private String test142;
|
||||
private String test143;
|
||||
private String test144;
|
||||
private String test145;
|
||||
private String test146;
|
||||
private String test147;
|
||||
private String test148;
|
||||
private String test149;
|
||||
private String test150;
|
||||
private String test151;
|
||||
private String test152;
|
||||
private String test153;
|
||||
private String test154;
|
||||
private String test155;
|
||||
private String test156;
|
||||
private String test157;
|
||||
private String test158;
|
||||
private String test159;
|
||||
private String test160;
|
||||
private String test161;
|
||||
private String test162;
|
||||
private String test163;
|
||||
private String test164;
|
||||
private String test165;
|
||||
private String test166;
|
||||
private String test167;
|
||||
private String test168;
|
||||
private String test169;
|
||||
private String test170;
|
||||
private String test171;
|
||||
private String test172;
|
||||
private String test173;
|
||||
private String test174;
|
||||
private String test175;
|
||||
private String test176;
|
||||
private String test177;
|
||||
private String test178;
|
||||
private String test179;
|
||||
private String test180;
|
||||
private String test181;
|
||||
private String test182;
|
||||
private String test183;
|
||||
|
||||
@Override
|
||||
public boolean <weak_warning descr="Method 'equals' is complex: data flow results could be imprecise">equals</weak_warning>(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ManyObjectEquals2 test = (ManyObjectEquals2) o;
|
||||
return Objects.equals(this.test0, test.test0) &&
|
||||
Objects.equals(this.test1, test.test1) &&
|
||||
Objects.equals(this.test2, test.test2) &&
|
||||
Objects.equals(this.test3, test.test3) &&
|
||||
Objects.equals(this.test4, test.test4) &&
|
||||
Objects.equals(this.test5, test.test5) &&
|
||||
Objects.equals(this.test6, test.test6) &&
|
||||
Objects.equals(this.test7, test.test7) &&
|
||||
Objects.equals(this.test8, test.test8) &&
|
||||
Objects.equals(this.test9, test.test9) &&
|
||||
Objects.equals(this.test10, test.test10) &&
|
||||
Objects.equals(this.test11, test.test11) &&
|
||||
Objects.equals(this.test12, test.test12) &&
|
||||
Objects.equals(this.test13, test.test13) &&
|
||||
Objects.equals(this.test14, test.test14) &&
|
||||
Objects.equals(this.test15, test.test15) &&
|
||||
Objects.equals(this.test16, test.test16) &&
|
||||
Objects.equals(this.test17, test.test17) &&
|
||||
Objects.equals(this.test18, test.test18) &&
|
||||
Objects.equals(this.test19, test.test19) &&
|
||||
Objects.equals(this.test20, test.test20) &&
|
||||
Objects.equals(this.test21, test.test21) &&
|
||||
Objects.equals(this.test22, test.test22) &&
|
||||
Objects.equals(this.test23, test.test23) &&
|
||||
Objects.equals(this.test24, test.test24) &&
|
||||
Objects.equals(this.test25, test.test25) &&
|
||||
Objects.equals(this.test26, test.test26) &&
|
||||
Objects.equals(this.test27, test.test27) &&
|
||||
Objects.equals(this.test28, test.test28) &&
|
||||
Objects.equals(this.test29, test.test29) &&
|
||||
Objects.equals(this.test30, test.test30) &&
|
||||
Objects.equals(this.test31, test.test31) &&
|
||||
Objects.equals(this.test32, test.test32) &&
|
||||
Objects.equals(this.test33, test.test33) &&
|
||||
Objects.equals(this.test34, test.test34) &&
|
||||
Objects.equals(this.test35, test.test35) &&
|
||||
Objects.equals(this.test36, test.test36) &&
|
||||
Objects.equals(this.test37, test.test37) &&
|
||||
Objects.equals(this.test38, test.test38) &&
|
||||
Objects.equals(this.test39, test.test39) &&
|
||||
Objects.equals(this.test40, test.test40) &&
|
||||
Objects.equals(this.test41, test.test41) &&
|
||||
Objects.equals(this.test42, test.test42) &&
|
||||
Objects.equals(this.test43, test.test43) &&
|
||||
Objects.equals(this.test44, test.test44) &&
|
||||
Objects.equals(this.test45, test.test45) &&
|
||||
Objects.equals(this.test46, test.test46) &&
|
||||
Objects.equals(this.test47, test.test47) &&
|
||||
Objects.equals(this.test48, test.test48) &&
|
||||
Objects.equals(this.test49, test.test49) &&
|
||||
Objects.equals(this.test50, test.test50) &&
|
||||
Objects.equals(this.test51, test.test51) &&
|
||||
Objects.equals(this.test52, test.test52) &&
|
||||
Objects.equals(this.test53, test.test53) &&
|
||||
Objects.equals(this.test54, test.test54) &&
|
||||
Objects.equals(this.test55, test.test55) &&
|
||||
Objects.equals(this.test56, test.test56) &&
|
||||
Objects.equals(this.test57, test.test57) &&
|
||||
Objects.equals(this.test58, test.test58) &&
|
||||
Objects.equals(this.test59, test.test59) &&
|
||||
Objects.equals(this.test60, test.test60) &&
|
||||
Objects.equals(this.test61, test.test61) &&
|
||||
Objects.equals(this.test62, test.test62) &&
|
||||
Objects.equals(this.test63, test.test63) &&
|
||||
Objects.equals(this.test64, test.test64) &&
|
||||
Objects.equals(this.test65, test.test65) &&
|
||||
Objects.equals(this.test66, test.test66) &&
|
||||
Objects.equals(this.test67, test.test67) &&
|
||||
Objects.equals(this.test68, test.test68) &&
|
||||
Objects.equals(this.test69, test.test69) &&
|
||||
Objects.equals(this.test70, test.test70) &&
|
||||
Objects.equals(this.test71, test.test71) &&
|
||||
Objects.equals(this.test72, test.test72) &&
|
||||
Objects.equals(this.test73, test.test73) &&
|
||||
Objects.equals(this.test74, test.test74) &&
|
||||
Objects.equals(this.test75, test.test75) &&
|
||||
Objects.equals(this.test76, test.test76) &&
|
||||
Objects.equals(this.test77, test.test77) &&
|
||||
Objects.equals(this.test78, test.test78) &&
|
||||
Objects.equals(this.test79, test.test79) &&
|
||||
Objects.equals(this.test80, test.test80) &&
|
||||
Objects.equals(this.test81, test.test81) &&
|
||||
Objects.equals(this.test82, test.test82) &&
|
||||
Objects.equals(this.test83, test.test83) &&
|
||||
Objects.equals(this.test84, test.test84) &&
|
||||
Objects.equals(this.test85, test.test85) &&
|
||||
Objects.equals(this.test86, test.test86) &&
|
||||
Objects.equals(this.test87, test.test87) &&
|
||||
Objects.equals(this.test88, test.test88) &&
|
||||
Objects.equals(this.test89, test.test89) &&
|
||||
Objects.equals(this.test90, test.test90) &&
|
||||
Objects.equals(this.test91, test.test91) &&
|
||||
Objects.equals(this.test92, test.test92) &&
|
||||
Objects.equals(this.test93, test.test93) &&
|
||||
Objects.equals(this.test94, test.test94) &&
|
||||
Objects.equals(this.test95, test.test95) &&
|
||||
Objects.equals(this.test96, test.test96) &&
|
||||
Objects.equals(this.test97, test.test97) &&
|
||||
Objects.equals(this.test98, test.test98) &&
|
||||
Objects.equals(this.test99, test.test99) &&
|
||||
Objects.equals(this.test100, test.test100) &&
|
||||
Objects.equals(this.test101, test.test101) &&
|
||||
Objects.equals(this.test102, test.test102) &&
|
||||
Objects.equals(this.test103, test.test103) &&
|
||||
Objects.equals(this.test104, test.test104) &&
|
||||
Objects.equals(this.test105, test.test105) &&
|
||||
Objects.equals(this.test106, test.test106) &&
|
||||
Objects.equals(this.test107, test.test107) &&
|
||||
Objects.equals(this.test108, test.test108) &&
|
||||
Objects.equals(this.test109, test.test109) &&
|
||||
Objects.equals(this.test101, test.test101) &&
|
||||
Objects.equals(this.test111, test.test111) &&
|
||||
Objects.equals(this.test112, test.test112) &&
|
||||
Objects.equals(this.test113, test.test113) &&
|
||||
Objects.equals(this.test114, test.test114) &&
|
||||
Objects.equals(this.test115, test.test115) &&
|
||||
Objects.equals(this.test116, test.test116) &&
|
||||
Objects.equals(this.test117, test.test117) &&
|
||||
Objects.equals(this.test118, test.test118) &&
|
||||
Objects.equals(this.test119, test.test119) &&
|
||||
Objects.equals(this.test120, test.test120) &&
|
||||
Objects.equals(this.test121, test.test121) &&
|
||||
Objects.equals(this.test122, test.test122) &&
|
||||
Objects.equals(this.test123, test.test123) &&
|
||||
Objects.equals(this.test124, test.test124) &&
|
||||
Objects.equals(this.test125, test.test125) &&
|
||||
Objects.equals(this.test126, test.test126) &&
|
||||
Objects.equals(this.test127, test.test127) &&
|
||||
Objects.equals(this.test128, test.test128) &&
|
||||
Objects.equals(this.test129, test.test129) &&
|
||||
Objects.equals(this.test130, test.test130) &&
|
||||
Objects.equals(this.test131, test.test131) &&
|
||||
Objects.equals(this.test132, test.test132) &&
|
||||
Objects.equals(this.test133, test.test133) &&
|
||||
Objects.equals(this.test134, test.test134) &&
|
||||
Objects.equals(this.test135, test.test135) &&
|
||||
Objects.equals(this.test136, test.test136) &&
|
||||
Objects.equals(this.test137, test.test137) &&
|
||||
Objects.equals(this.test138, test.test138) &&
|
||||
Objects.equals(this.test139, test.test139) &&
|
||||
Objects.equals(this.test140, test.test140) &&
|
||||
Objects.equals(this.test141, test.test141) &&
|
||||
Objects.equals(this.test142, test.test142) &&
|
||||
Objects.equals(this.test143, test.test143) &&
|
||||
Objects.equals(this.test144, test.test144) &&
|
||||
Objects.equals(this.test145, test.test145) &&
|
||||
Objects.equals(this.test146, test.test146) &&
|
||||
Objects.equals(this.test147, test.test147) &&
|
||||
Objects.equals(this.test148, test.test148) &&
|
||||
Objects.equals(this.test149, test.test149) &&
|
||||
Objects.equals(this.test150, test.test150) &&
|
||||
Objects.equals(this.test151, test.test151) &&
|
||||
Objects.equals(this.test152, test.test152) &&
|
||||
Objects.equals(this.test153, test.test153) &&
|
||||
Objects.equals(this.test154, test.test154) &&
|
||||
Objects.equals(this.test155, test.test155) &&
|
||||
Objects.equals(this.test156, test.test156) &&
|
||||
Objects.equals(this.test157, test.test157) &&
|
||||
Objects.equals(this.test158, test.test158) &&
|
||||
Objects.equals(this.test159, test.test159) &&
|
||||
Objects.equals(this.test160, test.test160) &&
|
||||
Objects.equals(this.test161, test.test161) &&
|
||||
Objects.equals(this.test162, test.test162) &&
|
||||
Objects.equals(this.test163, test.test163) &&
|
||||
Objects.equals(this.test164, test.test164) &&
|
||||
Objects.equals(this.test165, test.test165) &&
|
||||
Objects.equals(this.test166, test.test166) &&
|
||||
Objects.equals(this.test167, test.test167) &&
|
||||
Objects.equals(this.test168, test.test168) &&
|
||||
Objects.equals(this.test169, test.test169) &&
|
||||
Objects.equals(this.test170, test.test170) &&
|
||||
Objects.equals(this.test171, test.test171) &&
|
||||
Objects.equals(this.test172, test.test172) &&
|
||||
Objects.equals(this.test173, test.test173) &&
|
||||
Objects.equals(this.test174, test.test174) &&
|
||||
Objects.equals(this.test175, test.test175) &&
|
||||
Objects.equals(this.test176, test.test176) &&
|
||||
Objects.equals(this.test177, test.test177) &&
|
||||
Objects.equals(this.test178, test.test178) &&
|
||||
Objects.equals(this.test179, test.test179) &&
|
||||
Objects.equals(this.test180, test.test180) &&
|
||||
Objects.equals(this.test181, test.test181) &&
|
||||
Objects.equals(this.test182, test.test182) &&
|
||||
Objects.equals(this.test183, test.test183);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,8 @@ import org.jetbrains.annotations.NotNull;
|
||||
class NullableReturn {
|
||||
@NotNull Object test(Object o, Object o2, Object o3) {
|
||||
Object x = o == null ? o3 : o2;
|
||||
return x == null ? o3 : x;
|
||||
// probably ephemeral (e.g. o3 could be never null; o2 only), but it's ok to warn in the absence of nullity annotations
|
||||
return x == null ? <warning descr="Expression 'o3' might evaluate to null but is returned by the method declared as @NotNull">o3</warning> : x;
|
||||
}
|
||||
|
||||
interface Context {}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
/*
|
||||
Value is always false (x == null; line#11)
|
||||
'x' was assigned (=; line#10)
|
||||
Primitive value was boxed ("0".equals(key) ? -1 : m.get(key); line#10)
|
||||
Value is always false (x == null; line#13)
|
||||
'x' was assigned (=; line#12)
|
||||
One of the following happens:
|
||||
Primitive value was boxed ("0".equals(key) ? -1 : m.get(key); line#12)
|
||||
or expression cannot be null as it's a value of primitive type 'int' (-1; line#12)
|
||||
*/
|
||||
import java.util.*;
|
||||
|
||||
|
||||
@@ -253,6 +253,7 @@ public class DataFlowInspection8Test extends DataFlowInspectionTestCase {
|
||||
}
|
||||
public void testObjectsEquals() { doTest(); }
|
||||
public void testManyObjectEquals() { doTest(); }
|
||||
public void testManyObjectEquals2() { doTest(); }
|
||||
public void testLambdaAfterNullCheck() { doTest(); }
|
||||
public void testFlatMapSideEffect() { doTest(); }
|
||||
public void testOptionalValueTracking() { doTest(); }
|
||||
|
||||
@@ -1921,8 +1921,9 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.FinishElementInstruction
|
||||
- <init>(com.intellij.psi.PsiElement):V
|
||||
- accept(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[]
|
||||
- bindToFactory(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- getVarsToFlush():java.util.Set
|
||||
- getWrittenVariables(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
- flushVars(java.util.Collection):V
|
||||
- mayFlushSomething():Z
|
||||
- removeFromFlushList(java.util.function.Predicate):V
|
||||
c:com.intellij.codeInspection.dataFlow.lang.ir.FlushFieldsInstruction
|
||||
- com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- <init>():V
|
||||
@@ -1933,7 +1934,6 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.FlushVariableInstruction
|
||||
- accept(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[]
|
||||
- bindToFactory(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- getVariable():com.intellij.codeInspection.dataFlow.value.DfaVariableValue
|
||||
- getWrittenVariables(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
c:com.intellij.codeInspection.dataFlow.lang.ir.GotoInstruction
|
||||
- com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- <init>(com.intellij.codeInspection.dataFlow.lang.ir.ControlFlow$ControlFlowOffset):V
|
||||
@@ -1948,9 +1948,8 @@ a:com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- a:accept(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[]
|
||||
- bindToFactory(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- getIndex():I
|
||||
- getRequiredVariables(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
- getRequiredDescriptors(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
- getSuccessorIndexes():I[]
|
||||
- getWrittenVariables(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
- isLinear():Z
|
||||
- pf:nextState(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState
|
||||
- pf:nextStates(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[]
|
||||
@@ -1964,7 +1963,7 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.PushInstruction
|
||||
- <init>(com.intellij.codeInspection.dataFlow.value.DfaValue,com.intellij.codeInspection.dataFlow.lang.DfaAnchor):V
|
||||
- bindToFactory(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- eval(com.intellij.codeInspection.dataFlow.value.DfaValueFactory,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState,com.intellij.codeInspection.dataFlow.value.DfaValue[]):com.intellij.codeInspection.dataFlow.value.DfaValue
|
||||
- getRequiredVariables(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
- getRequiredDescriptors(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
- getValue():com.intellij.codeInspection.dataFlow.value.DfaValue
|
||||
c:com.intellij.codeInspection.dataFlow.lang.ir.PushValueInstruction
|
||||
- com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction
|
||||
@@ -1988,7 +1987,6 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.SimpleAssignmentInstruction
|
||||
- accept(com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState):com.intellij.codeInspection.dataFlow.lang.ir.DfaInstructionState[]
|
||||
- bindToFactory(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- getDestination():com.intellij.codeInspection.dataFlow.value.DfaVariableValue
|
||||
- getWrittenVariables(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
c:com.intellij.codeInspection.dataFlow.lang.ir.SpliceInstruction
|
||||
- com.intellij.codeInspection.dataFlow.lang.ir.Instruction
|
||||
- <init>(I,I[]):V
|
||||
@@ -2001,6 +1999,7 @@ c:com.intellij.codeInspection.dataFlow.lang.ir.UnwrapDerivedVariableInstruction
|
||||
- com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction
|
||||
- <init>(com.intellij.codeInspection.dataFlow.value.DerivedVariableDescriptor):V
|
||||
- eval(com.intellij.codeInspection.dataFlow.value.DfaValueFactory,com.intellij.codeInspection.dataFlow.memory.DfaMemoryState,com.intellij.codeInspection.dataFlow.value.DfaValue[]):com.intellij.codeInspection.dataFlow.value.DfaValue
|
||||
- getRequiredDescriptors(com.intellij.codeInspection.dataFlow.value.DfaValueFactory):java.util.List
|
||||
c:com.intellij.codeInspection.dataFlow.lang.ir.WrapDerivedVariableInstruction
|
||||
- com.intellij.codeInspection.dataFlow.lang.ir.EvalInstruction
|
||||
- <init>(com.intellij.codeInspection.dataFlow.types.DfType,com.intellij.codeInspection.dataFlow.value.DerivedVariableDescriptor):V
|
||||
@@ -2019,7 +2018,8 @@ com.intellij.codeInspection.dataFlow.memory.DfaMemoryState
|
||||
- a:flushFieldsQualifiedBy(java.util.Set):V
|
||||
- a:flushVariable(com.intellij.codeInspection.dataFlow.value.DfaVariableValue):V
|
||||
- a:flushVariable(com.intellij.codeInspection.dataFlow.value.DfaVariableValue,Z):V
|
||||
- a:flushVariables(java.util.function.Predicate):V
|
||||
- flushVariables(java.util.function.Predicate):V
|
||||
- a:flushVariables(java.util.function.Predicate,Z):V
|
||||
- a:getDfType(com.intellij.codeInspection.dataFlow.value.DfaValue):com.intellij.codeInspection.dataFlow.types.DfType
|
||||
- a:getDfTypeIncludingDerived(com.intellij.codeInspection.dataFlow.value.DfaValue):com.intellij.codeInspection.dataFlow.types.DfType
|
||||
- a:getMergeabilityKey():java.lang.Object
|
||||
@@ -2061,7 +2061,7 @@ c:com.intellij.codeInspection.dataFlow.memory.DfaMemoryStateImpl
|
||||
- flushFieldsQualifiedBy(java.util.Set):V
|
||||
- flushVariable(com.intellij.codeInspection.dataFlow.value.DfaVariableValue):V
|
||||
- flushVariable(com.intellij.codeInspection.dataFlow.value.DfaVariableValue,Z):V
|
||||
- flushVariables(java.util.function.Predicate):V
|
||||
- flushVariables(java.util.function.Predicate,Z):V
|
||||
- forRecordedVariableTypes(java.util.function.BiConsumer):V
|
||||
- getBinOpRange(com.intellij.codeInspection.dataFlow.value.DfaBinOpValue):com.intellij.codeInspection.dataFlow.types.DfType
|
||||
- getDfType(com.intellij.codeInspection.dataFlow.value.DfaValue):com.intellij.codeInspection.dataFlow.types.DfType
|
||||
|
||||
+1
-1
@@ -205,7 +205,7 @@ public class StandardDataFlowInterpreter implements DataFlowInterpreter {
|
||||
.into(joinInstructions);
|
||||
for (int index = 0; index < myInstructions.length; index++) {
|
||||
Instruction instruction = myInstructions[index];
|
||||
if (instruction instanceof FinishElementInstruction && !((FinishElementInstruction)instruction).getVarsToFlush().isEmpty()) {
|
||||
if (instruction instanceof FinishElementInstruction finishInstruction && !finishInstruction.mayFlushSomething()) {
|
||||
// Good chances to squash something after some vars are flushed
|
||||
joinInstructions.add(myInstructions[index + 1]);
|
||||
}
|
||||
|
||||
+7
-10
@@ -2,8 +2,8 @@
|
||||
package com.intellij.codeInspection.dataFlow.lang.ir;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
@@ -67,7 +67,7 @@ public abstract class BaseVariableAnalyzer {
|
||||
/**
|
||||
* @return true if completed, false if "too complex"
|
||||
*/
|
||||
protected boolean runDfa(boolean forward, BiFunction<? super Instruction, ? super BitSet, ? extends BitSet> handleState) {
|
||||
protected boolean runDfa(boolean forward, BiFunction<? super Instruction, ? super Set<VariableDescriptor>, ? extends Set<VariableDescriptor>> handleState) {
|
||||
List<Instruction> entryPoints;
|
||||
if (forward) {
|
||||
entryPoints = List.of(myInstructions[0]);
|
||||
@@ -80,11 +80,11 @@ public abstract class BaseVariableAnalyzer {
|
||||
|
||||
Deque<InstructionState> queue = new ArrayDeque<>(10);
|
||||
for (Instruction i : entryPoints) {
|
||||
queue.addLast(new InstructionState(i, new BitSet()));
|
||||
queue.addLast(new InstructionState(i, new HashSet<>()));
|
||||
}
|
||||
|
||||
int limit = myForwardMap.size() * 100;
|
||||
Map<BitSet, IntSet> processed = new HashMap<>();
|
||||
Map<Set<VariableDescriptor>, IntSet> processed = new HashMap<>();
|
||||
int steps = 0;
|
||||
while (!queue.isEmpty()) {
|
||||
if (steps > limit) {
|
||||
@@ -94,9 +94,9 @@ public abstract class BaseVariableAnalyzer {
|
||||
ProgressManager.checkCanceled();
|
||||
}
|
||||
InstructionState state = queue.removeFirst();
|
||||
Instruction instruction = state.first;
|
||||
Instruction instruction = state.instruction;
|
||||
Collection<Instruction> nextInstructions = forward ? myForwardMap.get(instruction) : myBackwardMap.get(instruction);
|
||||
BitSet nextVars = handleState.apply(instruction, state.second);
|
||||
Set<VariableDescriptor> nextVars = handleState.apply(instruction, state.nextVars);
|
||||
for (Instruction next : nextInstructions) {
|
||||
IntSet instructionSet = processed.computeIfAbsent(nextVars, k -> new IntOpenHashSet());
|
||||
int index = next.getIndex() + 1;
|
||||
@@ -110,9 +110,6 @@ public abstract class BaseVariableAnalyzer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private static class InstructionState extends Pair<Instruction, BitSet> {
|
||||
InstructionState(Instruction first, BitSet second) {
|
||||
super(first, second);
|
||||
}
|
||||
private record InstructionState(Instruction instruction, Set<VariableDescriptor> nextVars) {
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -186,15 +186,14 @@ public final class ControlFlow {
|
||||
return getFactory().getVarFactory().createVariableValue(new Synthetic(getInstructionCount(), dfType));
|
||||
}
|
||||
|
||||
public @NotNull List<DfaVariableValue> getSynthetics(PsiElement element) {
|
||||
public @NotNull List<VariableDescriptor> getSynthetics(PsiElement element) {
|
||||
int startOffset = getStartOffset(element).getInstructionOffset();
|
||||
List<DfaVariableValue> synthetics = new ArrayList<>();
|
||||
List<VariableDescriptor> synthetics = new ArrayList<>();
|
||||
for (DfaValue value : myFactory.getValues()) {
|
||||
if (value instanceof DfaVariableValue var) {
|
||||
VariableDescriptor descriptor = var.getDescriptor();
|
||||
if (descriptor instanceof Synthetic && ((Synthetic)descriptor).myLocation >= startOffset) {
|
||||
synthetics.add(var);
|
||||
}
|
||||
if (value instanceof DfaVariableValue var &&
|
||||
var.getDescriptor() instanceof Synthetic synthetic &&
|
||||
synthetic.myLocation >= startOffset) {
|
||||
synthetics.add(synthetic);
|
||||
}
|
||||
}
|
||||
return synthetics;
|
||||
|
||||
+33
-16
@@ -4,16 +4,17 @@ package com.intellij.codeInspection.dataFlow.lang.ir;
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class FinishElementInstruction extends Instruction {
|
||||
private final Set<DfaVariableValue> myVarsToFlush = new HashSet<>();
|
||||
private final Set<VariableDescriptor> myVarsToFlush = new HashSet<>();
|
||||
private final PsiElement myElement;
|
||||
|
||||
public FinishElementInstruction(PsiElement element) {
|
||||
@@ -23,9 +24,7 @@ public class FinishElementInstruction extends Instruction {
|
||||
@Override
|
||||
public DfaInstructionState[] accept(@NotNull DataFlowInterpreter interpreter, @NotNull DfaMemoryState state) {
|
||||
if (!myVarsToFlush.isEmpty()) {
|
||||
for (DfaVariableValue value : myVarsToFlush) {
|
||||
state.flushVariable(value, false);
|
||||
}
|
||||
state.flushVariables(var -> myVarsToFlush.contains(var.getDescriptor()), false);
|
||||
}
|
||||
return nextStates(interpreter, state);
|
||||
}
|
||||
@@ -33,24 +32,42 @@ public class FinishElementInstruction extends Instruction {
|
||||
@Override
|
||||
public @NotNull Instruction bindToFactory(@NotNull DfaValueFactory factory) {
|
||||
if (myVarsToFlush.isEmpty()) return this;
|
||||
// Derived analysis may change myVarsToFlush
|
||||
// E.g. see com.intellij.codeInspection.dataFlow.DfaPsiUtil#getBlockNotNullFields
|
||||
// (bad idea, but this is how it's done now)
|
||||
// So we still need to copy instruction to detach myVarsToFlush list
|
||||
var instruction = new FinishElementInstruction(myElement);
|
||||
for (DfaVariableValue var : myVarsToFlush) {
|
||||
instruction.myVarsToFlush.add(var.bindToFactory(factory));
|
||||
}
|
||||
instruction.flushVars(myVarsToFlush);
|
||||
return instruction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getWrittenVariables(DfaValueFactory factory) {
|
||||
return List.copyOf(myVarsToFlush);
|
||||
/**
|
||||
* Add variables with given descriptors to the flush list
|
||||
*
|
||||
* @param vars vars to flush
|
||||
*/
|
||||
public void flushVars(@NotNull Collection<@NotNull VariableDescriptor> vars) {
|
||||
myVarsToFlush.addAll(vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes variable descriptors from the flush list that match the given predicate.
|
||||
*
|
||||
* @param predicate the predicate used to determine which variable descriptors to remove
|
||||
*/
|
||||
public void removeFromFlushList(@NotNull Predicate<? super VariableDescriptor> predicate) {
|
||||
myVarsToFlush.removeIf(predicate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this instruction may flush some variables
|
||||
*/
|
||||
public boolean mayFlushSomething() {
|
||||
return !myVarsToFlush.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FINISH " + (myElement == null ? "" : myElement) + (myVarsToFlush.isEmpty() ? "" : "; flushing " + myVarsToFlush);
|
||||
}
|
||||
|
||||
public Set<DfaVariableValue> getVarsToFlush() {
|
||||
return myVarsToFlush;
|
||||
}
|
||||
}
|
||||
|
||||
-7
@@ -8,8 +8,6 @@ import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Flush single variable
|
||||
*/
|
||||
@@ -32,11 +30,6 @@ public class FlushVariableInstruction extends Instruction {
|
||||
return myVariable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getWrittenVariables(DfaValueFactory factory) {
|
||||
return List.of(myVariable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaInstructionState[] accept(@NotNull DataFlowInterpreter interpreter, @NotNull DfaMemoryState stateBefore) {
|
||||
stateBefore.flushVariable(getVariable());
|
||||
|
||||
+5
-13
@@ -5,7 +5,7 @@ package com.intellij.codeInspection.dataFlow.lang.ir;
|
||||
import com.intellij.codeInspection.dataFlow.interpreter.DataFlowInterpreter;
|
||||
import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -57,19 +57,11 @@ public abstract class Instruction {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of variables that are known to be always written by this instruction
|
||||
* @return list of variable descriptors that must stay reachable at this instruction.
|
||||
* The variables that are not required to be reachable by any instructions
|
||||
* until the end of the interpretation could be flushed automatically.
|
||||
*/
|
||||
public List<DfaVariableValue> getWrittenVariables(DfaValueFactory factory) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list of variables that must stay reachable at this instruction.
|
||||
* Variables that are not required to be reachable by any instructions
|
||||
* until the next write or the interpretation end,
|
||||
* could be flushed automatically.
|
||||
*/
|
||||
public List<DfaVariableValue> getRequiredVariables(DfaValueFactory factory) {
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
+34
-52
@@ -1,9 +1,8 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.dataFlow.lang.ir;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
@@ -17,79 +16,62 @@ final class LiveVariablesAnalyzer extends BaseVariableAnalyzer {
|
||||
@Override
|
||||
protected boolean isInterestingInstruction(Instruction instruction) {
|
||||
if (instruction == myInstructions[0]) return true;
|
||||
if (!instruction.getRequiredVariables(myFactory).isEmpty() || !instruction.getWrittenVariables(myFactory).isEmpty()) return true;
|
||||
if (!instruction.getRequiredDescriptors(myFactory).isEmpty()) return true;
|
||||
return !instruction.isLinear() || instruction instanceof FinishElementInstruction;
|
||||
}
|
||||
|
||||
private @Nullable Map<FinishElementInstruction, BitSet> findLiveVars() {
|
||||
final Map<FinishElementInstruction, BitSet> result = new HashMap<>();
|
||||
private @Nullable Map<FinishElementInstruction, Set<VariableDescriptor>> findLiveVars() {
|
||||
final Map<FinishElementInstruction, Set<VariableDescriptor>> result = new HashMap<>();
|
||||
|
||||
boolean ok = runDfa(false, (instruction, liveVars) -> {
|
||||
if (instruction instanceof FinishElementInstruction) {
|
||||
BitSet set = result.get(instruction);
|
||||
if (instruction instanceof FinishElementInstruction finishInstruction) {
|
||||
Set<VariableDescriptor> set = result.get(instruction);
|
||||
if (set != null) {
|
||||
set.or(liveVars);
|
||||
return (BitSet)set.clone();
|
||||
set.addAll(liveVars);
|
||||
return new HashSet<>(set);
|
||||
}
|
||||
else if (!liveVars.isEmpty()) {
|
||||
result.put((FinishElementInstruction)instruction, (BitSet)liveVars.clone());
|
||||
result.put(finishInstruction, new HashSet<>(liveVars));
|
||||
}
|
||||
}
|
||||
|
||||
List<DfaVariableValue> writtenVariables = instruction.getWrittenVariables(myFactory);
|
||||
if (!writtenVariables.isEmpty()) {
|
||||
BitSet newVars = (BitSet)liveVars.clone();
|
||||
for (DfaVariableValue written : writtenVariables) {
|
||||
newVars.clear(written.getID());
|
||||
for (DfaVariableValue var : written.getDependentVariables()) {
|
||||
newVars.clear(var.getID());
|
||||
}
|
||||
}
|
||||
return newVars;
|
||||
} else {
|
||||
var processor = new Consumer<DfaVariableValue>() {
|
||||
boolean cloned = false;
|
||||
BitSet newVars = liveVars;
|
||||
var processor = new Consumer<VariableDescriptor>() {
|
||||
boolean cloned = false;
|
||||
Set<VariableDescriptor> newVars = liveVars;
|
||||
|
||||
@Override
|
||||
public void accept(DfaVariableValue value) {
|
||||
if (!newVars.get(value.getID())) {
|
||||
if (!cloned) {
|
||||
newVars = (BitSet)newVars.clone();
|
||||
cloned = true;
|
||||
}
|
||||
newVars.set(value.getID());
|
||||
@Override
|
||||
public void accept(VariableDescriptor value) {
|
||||
if (!newVars.contains(value)) {
|
||||
if (!cloned) {
|
||||
newVars = new HashSet<>(newVars);
|
||||
cloned = true;
|
||||
}
|
||||
newVars.add(value);
|
||||
}
|
||||
};
|
||||
StreamEx.of(instruction.getRequiredVariables(myFactory))
|
||||
.flatMap(v -> StreamEx.of(v.getDependentVariables()).prepend(v)).distinct().forEach(processor);
|
||||
return processor.newVars;
|
||||
}
|
||||
}
|
||||
};
|
||||
instruction.getRequiredDescriptors(myFactory).forEach(processor);
|
||||
return processor.newVars;
|
||||
});
|
||||
return ok ? result : null;
|
||||
}
|
||||
|
||||
void flushDeadVariablesOnStatementFinish() {
|
||||
final Map<FinishElementInstruction, BitSet> liveVars = findLiveVars();
|
||||
final Map<FinishElementInstruction, Set<VariableDescriptor>> liveVars = findLiveVars();
|
||||
if (liveVars == null) return;
|
||||
|
||||
final MultiMap<FinishElementInstruction, DfaVariableValue> toFlush = MultiMap.createSet();
|
||||
final MultiMap<FinishElementInstruction, VariableDescriptor> toFlush = MultiMap.createSet();
|
||||
|
||||
boolean ok = runDfa(true, (instruction, prevLiveVars) -> {
|
||||
if (instruction instanceof FinishElementInstruction) {
|
||||
BitSet currentlyLive = liveVars.get(instruction);
|
||||
if (instruction instanceof FinishElementInstruction finishInstruction) {
|
||||
Set<VariableDescriptor> currentlyLive = liveVars.get(instruction);
|
||||
if (currentlyLive == null) {
|
||||
currentlyLive = new BitSet();
|
||||
currentlyLive = new HashSet<>();
|
||||
}
|
||||
int index = 0;
|
||||
while (true) {
|
||||
int setBit = prevLiveVars.nextSetBit(index);
|
||||
if (setBit < 0) break;
|
||||
if (!currentlyLive.get(setBit)) {
|
||||
toFlush.putValue((FinishElementInstruction)instruction, (DfaVariableValue)myFactory.getValue(setBit));
|
||||
for (VariableDescriptor var : prevLiveVars) {
|
||||
if (!currentlyLive.contains(var)) {
|
||||
toFlush.putValue(finishInstruction, var);
|
||||
}
|
||||
index = setBit + 1;
|
||||
}
|
||||
return currentlyLive;
|
||||
}
|
||||
@@ -99,9 +81,9 @@ final class LiveVariablesAnalyzer extends BaseVariableAnalyzer {
|
||||
|
||||
if (ok) {
|
||||
for (FinishElementInstruction instruction : toFlush.keySet()) {
|
||||
Collection<DfaVariableValue> values = toFlush.get(instruction);
|
||||
values.removeIf(var -> var.getDescriptor().isImplicitReadPossible());
|
||||
instruction.getVarsToFlush().addAll(values);
|
||||
Collection<VariableDescriptor> values = toFlush.get(instruction);
|
||||
values.removeIf(var -> var.isImplicitReadPossible());
|
||||
instruction.flushVars(values);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -6,13 +6,11 @@ import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
/**
|
||||
* An instruction that pushes given value to the stack
|
||||
*/
|
||||
@@ -30,8 +28,8 @@ public class PushInstruction extends EvalInstruction {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getRequiredVariables(DfaValueFactory factory) {
|
||||
return ContainerUtil.createMaybeSingletonList(tryCast(myValue, DfaVariableValue.class));
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return getValue() instanceof DfaVariableValue var ? List.of(var.getDescriptor()) : List.of();
|
||||
}
|
||||
|
||||
public @NotNull DfaValue getValue() {
|
||||
|
||||
-7
@@ -10,8 +10,6 @@ import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Assign the value from the stack to a specified static destination
|
||||
*/
|
||||
@@ -42,11 +40,6 @@ public class SimpleAssignmentInstruction extends ExpressionPushingInstruction {
|
||||
return nextStates(interpreter, stateBefore);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DfaVariableValue> getWrittenVariables(DfaValueFactory factory) {
|
||||
return List.of(myDestination);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ASSIGN_TO " + myDestination;
|
||||
|
||||
+8
@@ -5,8 +5,11 @@ import com.intellij.codeInspection.dataFlow.memory.DfaMemoryState;
|
||||
import com.intellij.codeInspection.dataFlow.value.DerivedVariableDescriptor;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.VariableDescriptor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Instruction to push a field qualified by the value on the stack
|
||||
*/
|
||||
@@ -23,6 +26,11 @@ public class UnwrapDerivedVariableInstruction extends EvalInstruction {
|
||||
return myDerivedVariableDescriptor.createValue(factory, arguments[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<VariableDescriptor> getRequiredDescriptors(@NotNull DfaValueFactory factory) {
|
||||
return List.of(myDerivedVariableDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "UNWRAP " + myDerivedVariableDescriptor;
|
||||
|
||||
+13
-1
@@ -200,7 +200,19 @@ public interface DfaMemoryState {
|
||||
*
|
||||
* @param filter filter to check whether the variable should be flushed
|
||||
*/
|
||||
void flushVariables(@NotNull Predicate<? super @NotNull DfaVariableValue> filter);
|
||||
default void flushVariables(@NotNull Predicate<? super @NotNull DfaVariableValue> filter) {
|
||||
flushVariables(filter, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Flush all the variables for which filter returns true
|
||||
*
|
||||
* @param filter filter to check whether the variable should be flushed
|
||||
* @param canonicalize whether to canonicalize the variable before flushing. Flushing canonical variable allows to forget
|
||||
* about all known aliases as well. Flushing without canonicalization could be necessary only
|
||||
* to simplify memory state, if it's known that given variable is never used anymore.
|
||||
*/
|
||||
void flushVariables(@NotNull Predicate<? super @NotNull DfaVariableValue> filter, boolean canonicalize);
|
||||
|
||||
/**
|
||||
* Mark this state as ephemeral. See {@link #isEphemeral()} for details.
|
||||
|
||||
+2
-2
@@ -1416,7 +1416,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flushVariables(@NotNull Predicate<? super @NotNull DfaVariableValue> filter) {
|
||||
public void flushVariables(@NotNull Predicate<? super @NotNull DfaVariableValue> filter, boolean canonicalize) {
|
||||
Set<DfaVariableValue> vars = new HashSet<>();
|
||||
for (EqClass aClass : myEqClasses) {
|
||||
if (aClass != null) {
|
||||
@@ -1427,7 +1427,7 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
vars.addAll(myVariableTypes.keySet());
|
||||
vars.removeIf(filter.negate());
|
||||
vars.forEach(this::flushVariable);
|
||||
vars.forEach(variable -> flushVariable(variable, canonicalize));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user