DFA instruction visitor refactoring wave#5 fixes

ResultOfInstruction
This commit is contained in:
Tagir Valeev
2018-06-25 15:48:10 +07:00
parent 3d241e6410
commit a4a84553e4
12 changed files with 92 additions and 9 deletions
@@ -207,6 +207,16 @@ public class CFGBuilder {
return add(new ObjectOfInstruction()); return add(new ObjectOfInstruction());
} }
/**
* Generate instructions to bind top-of-stack value to the given expression. Stack remains unchanged.
*
* @param expression expression to bind top-of-stack value to
* @return this builder
*/
public CFGBuilder resultOf(PsiExpression expression) {
return add(new ResultOfInstruction(expression));
}
/** /**
* Generate instructions to perform an Class.isInstance operation * Generate instructions to perform an Class.isInstance operation
* <p> * <p>
@@ -1512,6 +1512,9 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
addInstruction(new GotoInstruction(getEndOffset(operands[operands.length - 1]))); addInstruction(new GotoInstruction(getEndOffset(operands[operands.length - 1])));
} }
} }
if (shortCircuit) {
addInstruction(new ResultOfInstruction(expression));
}
} }
@Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) { @Override public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
@@ -243,6 +243,7 @@ final class DataFlowInstructionVisitor extends StandardInstructionVisitor {
} }
private static boolean shouldCollectBooleanResult(PsiExpression expression) { private static boolean shouldCollectBooleanResult(PsiExpression expression) {
if (expression instanceof PsiLiteralExpression) return false;
PsiType type = expression.getType(); PsiType type = expression.getType();
if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false; if (type == null || !PsiType.BOOLEAN.isAssignableFrom(type)) return false;
if (expression instanceof PsiPrefixExpression || expression instanceof PsiPolyadicExpression) { if (expression instanceof PsiPrefixExpression || expression instanceof PsiPolyadicExpression) {
@@ -147,6 +147,11 @@ public abstract class InstructionVisitor {
return nextInstruction(instruction, runner, state); return nextInstruction(instruction, runner, state);
} }
public DfaInstructionState[] visitResultOf(ResultOfInstruction instruction, DataFlowRunner runner, DfaMemoryState state) {
pushExpressionResult(state.pop(), instruction, state);
return nextInstruction(instruction, runner, state);
}
protected static DfaInstructionState[] nextInstruction(Instruction instruction, DataFlowRunner runner, DfaMemoryState memState) { protected static DfaInstructionState[] nextInstruction(Instruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
return new DfaInstructionState[]{new DfaInstructionState(runner.getInstruction(instruction.getIndex() + 1), memState)}; return new DfaInstructionState[]{new DfaInstructionState(runner.getInstruction(instruction.getIndex() + 1), memState)};
} }
@@ -101,7 +101,7 @@ public class StandardInstructionVisitor extends InstructionVisitor {
checkNotNullable(memState, dfaSource, kind.problem(rValue)); checkNotNullable(memState, dfaSource, kind.problem(rValue));
} }
memState.push(dfaDest); pushExpressionResult(dfaDest, instruction, memState);
flushArrayOnUnknownAssignment(instruction, runner.getFactory(), dfaDest, memState); flushArrayOnUnknownAssignment(instruction, runner.getFactory(), dfaDest, memState);
return nextInstruction(instruction, runner, memState); return nextInstruction(instruction, runner, memState);
@@ -81,11 +81,10 @@ public class OptionalChainInliner implements CallInliner {
.ifNotNull() .ifNotNull()
.swap() // stack: .. optValue, elseValue .swap() // stack: .. optValue, elseValue
.end() .end()
.pop(); .pop()
}) .resultOf(call);
.register(OPTIONAL_OR_NULL, (builder, call) -> {
// no op!
}) })
.register(OPTIONAL_OR_NULL, CFGBuilder::resultOf)
.register(OPTIONAL_OR_ELSE_GET, (builder, call) -> { .register(OPTIONAL_OR_ELSE_GET, (builder, call) -> {
PsiExpression fn = call.getArgumentList().getExpressions()[0]; PsiExpression fn = call.getArgumentList().getExpressions()[0];
builder builder
@@ -94,7 +93,8 @@ public class OptionalChainInliner implements CallInliner {
.ifNull() .ifNull()
.pop() .pop()
.invokeFunction(0, fn) .invokeFunction(0, fn)
.end(); .end()
.resultOf(call);
}) })
.register(OPTIONAL_IF_PRESENT, (builder, call) -> { .register(OPTIONAL_IF_PRESENT, (builder, call) -> {
PsiExpression fn = call.getArgumentList().getExpressions()[0]; PsiExpression fn = call.getArgumentList().getExpressions()[0];
@@ -106,7 +106,8 @@ public class OptionalChainInliner implements CallInliner {
.elseBranch() .elseBranch()
.pop() .pop()
.pushUnknown() .pushUnknown()
.end(); .end()
.resultOf(call);
}); });
private static final CallMapper<BiConsumer<CFGBuilder, PsiExpression>> INTERMEDIATE_MAPPER = private static final CallMapper<BiConsumer<CFGBuilder, PsiExpression>> INTERMEDIATE_MAPPER =
@@ -24,10 +24,11 @@ import com.intellij.codeInspection.dataFlow.value.DfaValue;
import com.intellij.psi.PsiAssignmentExpression; import com.intellij.psi.PsiAssignmentExpression;
import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiVariable; import com.intellij.psi.PsiVariable;
import com.intellij.util.ObjectUtils;
import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class AssignInstruction extends Instruction { public class AssignInstruction extends Instruction implements ExpressionPushingInstruction {
private final PsiExpression myRExpression; private final PsiExpression myRExpression;
private final PsiExpression myLExpression; private final PsiExpression myLExpression;
@Nullable private final DfaValue myAssignedValue; @Nullable private final DfaValue myAssignedValue;
@@ -70,6 +71,13 @@ public class AssignInstruction extends Instruction {
return "ASSIGN"; return "ASSIGN";
} }
@Nullable
@Override
public PsiAssignmentExpression getExpression() {
if(myRExpression== null) return null;
return ObjectUtils.tryCast(myRExpression.getParent(), PsiAssignmentExpression.class);
}
@Contract("null -> null") @Contract("null -> null")
@Nullable @Nullable
private static PsiExpression getLeftHandOfAssignment(PsiExpression rExpression) { private static PsiExpression getLeftHandOfAssignment(PsiExpression rExpression) {
@@ -0,0 +1,34 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.codeInspection.dataFlow.instructions;
import com.intellij.codeInspection.dataFlow.DataFlowRunner;
import com.intellij.codeInspection.dataFlow.DfaInstructionState;
import com.intellij.codeInspection.dataFlow.DfaMemoryState;
import com.intellij.codeInspection.dataFlow.InstructionVisitor;
import com.intellij.psi.PsiExpression;
import org.jetbrains.annotations.NotNull;
public class ResultOfInstruction extends Instruction implements ExpressionPushingInstruction {
@NotNull
private final PsiExpression myExpression;
public ResultOfInstruction(@NotNull PsiExpression expression) {
myExpression = expression;
}
@Override
public DfaInstructionState[] accept(DataFlowRunner runner, DfaMemoryState stateBefore, InstructionVisitor visitor) {
return visitor.visitResultOf(this, runner, stateBefore);
}
public String toString() {
return "RESULT_OF "+myExpression.getText();
}
@NotNull
@Override
public PsiExpression getExpression() {
return myExpression;
}
}
@@ -0,0 +1,9 @@
import java.util.List;
class Test {
void test(String type) {
if(type != null && (type.equals("foo") | type.equals("bar"))) {
System.out.println("Who knows");
}
}
}
@@ -41,7 +41,7 @@ class S {
} }
public void te5(boolean b){ public void te5(boolean b){
Boolean c = Boolean.TRUE; Boolean c = Boolean.TRUE;
boolean o = <warning descr="Condition 'b||c' is always 'true'">b||<warning descr="Condition 'c' is always 'true' when reached">c</warning></warning>; boolean o = b||<warning descr="Condition 'c' is always 'true' when reached">c</warning>;
if (<warning descr="Condition 'o' is always 'true'">o</warning>) { if (<warning descr="Condition 'o' is always 'true'">o</warning>) {
} }
} }
@@ -0,0 +1,10 @@
import java.util.List;
class Test {
void test(String type) {
boolean uint = false;
if ("int".equals(type) || (uint = "uint".equals(type))) {
System.out.println("possible");
}
}
}
@@ -622,4 +622,6 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase {
public void testEqualsInLoopNotTooComplex() { doTest(); } public void testEqualsInLoopNotTooComplex() { doTest(); }
public void testEqualsWithItself() { doTest(); } public void testEqualsWithItself() { doTest(); }
public void testBoxingBoolean() { doTest(); } public void testBoxingBoolean() { doTest(); }
public void testOrWithAssignment() { doTest(); }
public void testAndAndWithOr() { doTest(); }
} }