mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
dfa live var analysis should honor variables read in closures (IDEA-131719)
This commit is contained in:
+5
-18
@@ -222,24 +222,11 @@ public class DataFlowRunner {
|
||||
|
||||
protected DfaInstructionState[] acceptInstruction(InstructionVisitor visitor, DfaInstructionState instructionState) {
|
||||
Instruction instruction = instructionState.getInstruction();
|
||||
if (instruction instanceof MethodCallInstruction) {
|
||||
PsiCallExpression anchor = ((MethodCallInstruction)instruction).getCallExpression();
|
||||
if (anchor instanceof PsiNewExpression) {
|
||||
PsiAnonymousClass anonymousClass = ((PsiNewExpression)anchor).getAnonymousClass();
|
||||
if (anonymousClass != null) {
|
||||
registerNestedClosures(instructionState, anonymousClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (instruction instanceof LambdaInstruction) {
|
||||
PsiLambdaExpression lambdaExpression = ((LambdaInstruction)instruction).getLambdaExpression();
|
||||
registerNestedClosures(instructionState, lambdaExpression);
|
||||
}
|
||||
else if (instruction instanceof EmptyInstruction) {
|
||||
PsiElement anchor = ((EmptyInstruction)instruction).getAnchor();
|
||||
if (anchor instanceof PsiClass) {
|
||||
registerNestedClosures(instructionState, (PsiClass)anchor);
|
||||
}
|
||||
PsiElement closure = DfaUtil.getClosureInside(instruction);
|
||||
if (closure instanceof PsiClass) {
|
||||
registerNestedClosures(instructionState, (PsiClass)closure);
|
||||
} else if (closure instanceof PsiLambdaExpression) {
|
||||
registerNestedClosures(instructionState, (PsiLambdaExpression)closure);
|
||||
}
|
||||
|
||||
return instruction.accept(this, instructionState.getMemoryState(), visitor);
|
||||
|
||||
@@ -15,9 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.instructions.AssignInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.PushInstruction;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.openapi.util.MultiValuesMap;
|
||||
@@ -113,6 +111,26 @@ public class DfaUtil {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static PsiElement getClosureInside(Instruction instruction) {
|
||||
if (instruction instanceof MethodCallInstruction) {
|
||||
PsiCallExpression anchor = ((MethodCallInstruction)instruction).getCallExpression();
|
||||
if (anchor instanceof PsiNewExpression) {
|
||||
return ((PsiNewExpression)anchor).getAnonymousClass();
|
||||
}
|
||||
}
|
||||
else if (instruction instanceof LambdaInstruction) {
|
||||
return ((LambdaInstruction)instruction).getLambdaExpression();
|
||||
}
|
||||
else if (instruction instanceof EmptyInstruction) {
|
||||
PsiElement anchor = ((EmptyInstruction)instruction).getAnchor();
|
||||
if (anchor instanceof PsiClass) {
|
||||
return anchor;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class ValuableInstructionVisitor extends StandardInstructionVisitor {
|
||||
final Map<PsiElement, PlaceResult> myResults = ContainerUtil.newHashMap();
|
||||
|
||||
|
||||
+65
-30
@@ -21,10 +21,11 @@ import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
|
||||
import com.intellij.psi.PsiReferenceExpression;
|
||||
import com.intellij.util.PairFunction;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.FilteringIterator;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.containers.*;
|
||||
import com.intellij.util.containers.Queue;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -39,6 +40,26 @@ public class LiveVariablesAnalyzer {
|
||||
private final Instruction[] myInstructions;
|
||||
private final MultiMap<Instruction, Instruction> myForwardMap;
|
||||
private final MultiMap<Instruction, Instruction> myBackwardMap;
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection") private final FactoryMap<PsiElement, List<DfaVariableValue>> myClosureReads = new FactoryMap<PsiElement, List<DfaVariableValue>>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected List<DfaVariableValue> create(PsiElement closure) {
|
||||
final Set<DfaVariableValue> result = ContainerUtil.newLinkedHashSet();
|
||||
closure.accept(new PsiRecursiveElementWalkingVisitor() {
|
||||
@Override
|
||||
public void visitElement(PsiElement element) {
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
DfaValue value = myFactory.createValue((PsiReferenceExpression)element);
|
||||
if (value instanceof DfaVariableValue) {
|
||||
result.add((DfaVariableValue)value);
|
||||
}
|
||||
}
|
||||
super.visitElement(element);
|
||||
}
|
||||
});
|
||||
return ContainerUtil.newArrayList(result);
|
||||
}
|
||||
};
|
||||
|
||||
public LiveVariablesAnalyzer(ControlFlow flow, DfaValueFactory factory) {
|
||||
myFactory = factory;
|
||||
@@ -96,12 +117,36 @@ public class LiveVariablesAnalyzer {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DfaVariableValue getWrittenVariable(Instruction instruction) {
|
||||
if (instruction instanceof AssignInstruction) {
|
||||
DfaValue value = ((AssignInstruction)instruction).getAssignedValue();
|
||||
return value instanceof DfaVariableValue ? (DfaVariableValue)value : null;
|
||||
}
|
||||
if (instruction instanceof FlushVariableInstruction) return ((FlushVariableInstruction)instruction).getVariable();
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<DfaVariableValue> getReadVariables(Instruction instruction) {
|
||||
if (instruction instanceof PushInstruction && !((PushInstruction)instruction).isReferenceWrite()) {
|
||||
DfaValue value = ((PushInstruction)instruction).getValue();
|
||||
if (value instanceof DfaVariableValue) {
|
||||
return Collections.singletonList((DfaVariableValue)value);
|
||||
}
|
||||
} else {
|
||||
PsiElement closure = DfaUtil.getClosureInside(instruction);
|
||||
if (closure != null) {
|
||||
return myClosureReads.get(closure);
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private boolean isInterestingInstruction(Instruction instruction) {
|
||||
if (instruction == myInstructions[0]) return true;
|
||||
if (instruction instanceof PushInstruction) return ((PushInstruction)instruction).getValue() instanceof DfaVariableValue;
|
||||
if (instruction instanceof AssignInstruction) return ((AssignInstruction)instruction).getAssignedValue() != null;
|
||||
if (!getReadVariables(instruction).isEmpty() || getWrittenVariable(instruction) != null) return true;
|
||||
return instruction instanceof FinishElementInstruction ||
|
||||
instruction instanceof FlushVariableInstruction ||
|
||||
instruction instanceof GotoInstruction ||
|
||||
instruction instanceof ConditionalGotoInstruction ||
|
||||
instruction instanceof ReturnInstruction;
|
||||
@@ -124,34 +169,24 @@ public class LiveVariablesAnalyzer {
|
||||
}
|
||||
}
|
||||
|
||||
if (instruction instanceof AssignInstruction) {
|
||||
DfaValue value = ((AssignInstruction)instruction).getAssignedValue();
|
||||
if (value instanceof DfaVariableValue) {
|
||||
liveVars = (BitSet)liveVars.clone();
|
||||
liveVars.clear(value.getID());
|
||||
for (DfaVariableValue var : myFactory.getVarFactory().getAllQualifiedBy((DfaVariableValue)value)) {
|
||||
liveVars.clear(var.getID());
|
||||
}
|
||||
DfaVariableValue written = getWrittenVariable(instruction);
|
||||
if (written != null) {
|
||||
liveVars = (BitSet)liveVars.clone();
|
||||
liveVars.clear(written.getID());
|
||||
for (DfaVariableValue var : myFactory.getVarFactory().getAllQualifiedBy(written)) {
|
||||
liveVars.clear(var.getID());
|
||||
}
|
||||
}
|
||||
|
||||
if (instruction instanceof PushInstruction) {
|
||||
DfaValue value = ((PushInstruction)instruction).getValue();
|
||||
if (value instanceof DfaVariableValue) {
|
||||
if (!((PushInstruction)instruction).isReferenceWrite() && !liveVars.get(value.getID())) {
|
||||
liveVars = (BitSet)liveVars.clone();
|
||||
} else {
|
||||
boolean cloned = false;
|
||||
for (DfaVariableValue value : getReadVariables(instruction)) {
|
||||
if (!liveVars.get(value.getID())) {
|
||||
if (!cloned) {
|
||||
liveVars = (BitSet)liveVars.clone();
|
||||
cloned = true;
|
||||
}
|
||||
liveVars.set(value.getID());
|
||||
}
|
||||
}
|
||||
} else if (instruction instanceof FlushVariableInstruction) {
|
||||
DfaVariableValue variable = ((FlushVariableInstruction)instruction).getVariable();
|
||||
if (variable != null) {
|
||||
liveVars = (BitSet)liveVars.clone();
|
||||
liveVars.clear(variable.getID());
|
||||
for (DfaVariableValue var : myFactory.getVarFactory().getAllQualifiedBy(variable)) {
|
||||
liveVars.clear(var.getID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return liveVars;
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
class Foo {
|
||||
|
||||
void foo(final @Nullable String s, String s2) {
|
||||
if (s2 == null) return;
|
||||
if (s == null) return;
|
||||
|
||||
System.out.println(s2);
|
||||
|
||||
new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
s.hashCode();
|
||||
}
|
||||
}.run();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -242,6 +242,7 @@ public class DataFlowInspectionTest extends LightCodeInsightFixtureTestCase {
|
||||
public void testManyContinuesNotComplex() { doTest(); }
|
||||
public void testFinallyNotComplex() { doTest(); }
|
||||
public void testFlushFurtherUnusedVariables() { doTest(); }
|
||||
public void testDontFlushVariablesUsedInClosures() { doTest(); }
|
||||
|
||||
public void testVariablesDiverge() { doTest(); }
|
||||
public void testMergeByNullability() { doTest(); }
|
||||
|
||||
Reference in New Issue
Block a user