Allow multiple outcoming edges from if conditions (PY-25974)

This commit is contained in:
Semyon Proshev
2017-12-25 14:23:49 +03:00
parent ede06bbf4b
commit 1d8fda0172
2 changed files with 43 additions and 18 deletions
@@ -18,8 +18,9 @@ package com.jetbrains.python.codeInsight.controlflow;
import com.google.common.collect.Lists;
import com.intellij.codeInsight.controlflow.ControlFlowBuilder;
import com.intellij.codeInsight.controlflow.Instruction;
import com.jetbrains.python.psi.PyReferenceExpression;
import com.intellij.psi.util.QualifiedName;
import com.jetbrains.python.psi.PyReferenceExpression;
import org.jetbrains.annotations.NotNull;
import java.util.List;
@@ -30,9 +31,11 @@ public class InstructionBuilder {
private InstructionBuilder() {
}
public static List<Instruction> buildInstructions(ControlFlowBuilder builder, List<PyTypeAssertionEvaluator.Assertion> assertions) {
List<Instruction> result = Lists.newArrayList();
for (PyTypeAssertionEvaluator.Assertion def: assertions) {
@NotNull
public static List<Instruction> buildInstructions(@NotNull ControlFlowBuilder builder,
@NotNull List<PyTypeAssertionEvaluator.Assertion> assertions) {
final List<Instruction> result = Lists.newArrayList();
for (PyTypeAssertionEvaluator.Assertion def : assertions) {
final PyReferenceExpression e = def.getElement();
final QualifiedName qname = e.asQualifiedName();
final String name = qname != null ? qname.toString() : e.getName();
@@ -41,9 +44,13 @@ public class InstructionBuilder {
return result;
}
public static void addAssertInstructions(ControlFlowBuilder builder, PyTypeAssertionEvaluator assertionEvaluator) {
for (Instruction instr : buildInstructions(builder, assertionEvaluator.getDefinitions())) {
@NotNull
public static List<Instruction> addAssertInstructions(@NotNull ControlFlowBuilder builder,
@NotNull PyTypeAssertionEvaluator assertionEvaluator) {
final List<Instruction> instructions = buildInstructions(builder, assertionEvaluator.getDefinitions());
for (Instruction instr : instructions) {
builder.addNode(instr);
}
return instructions;
}
}
@@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.QualifiedName;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.psi.*;
@@ -278,17 +279,18 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
}
}
private Instruction getPrevInstruction(final PyElement condition) {
final Ref<Instruction> head = new Ref<>(myBuilder.prevInstruction);
@NotNull
private List<Pair<PsiElement, Instruction>> getPrevInstructions(final PyElement condition) {
final List<Pair<PsiElement, Instruction>> result = ContainerUtil.newArrayList(Pair.create(condition, myBuilder.prevInstruction));
myBuilder.processPending((pendingScope, instruction) -> {
if (pendingScope != null && PsiTreeUtil.isAncestor(condition, pendingScope, false)) {
head.set(instruction);
result.add(Pair.create(pendingScope, instruction));
}
else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
});
return head.get();
return result;
}
@Override
@@ -327,8 +329,11 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
}
// Set the head as the last instruction of condition
PyElement lastCondition = condition;
Instruction lastBranchingPoint = getPrevInstruction(condition);
myBuilder.prevInstruction = lastBranchingPoint;
List<Pair<PsiElement, Instruction>> lastBranchingPoints = getPrevInstructions(condition);
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
myBuilder.prevInstruction = null;
final PyStatementList thenStatements = ifPart.getStatementList();
myBuilder.startConditionalNode(thenStatements, condition, true);
InstructionBuilder.addAssertInstructions(myBuilder, assertionEvaluator);
@@ -344,18 +349,22 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
for (final PyIfPart part : node.getElifParts()) {
// Set the head as the false branch
myBuilder.prevInstruction = lastBranchingPoint;
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
myBuilder.prevInstruction = null;
myBuilder.startConditionalNode(part, lastCondition, false);
condition = part.getCondition();
assertionEvaluator = new PyTypeAssertionEvaluator();
if (condition != null) {
lastCondition = condition;
lastBranchingPoint = getPrevInstruction(lastCondition);
lastBranchingPoints = getPrevInstructions(lastCondition);
condition.accept(this);
condition.accept(assertionEvaluator);
}
// Set the head as the last instruction of condition
myBuilder.prevInstruction = getPrevInstruction(lastCondition);
getPrevInstructions(lastCondition).forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
myBuilder.prevInstruction = null;
myBuilder.startConditionalNode(part, lastCondition, true);
final PyStatementList statementList = part.getStatementList();
InstructionBuilder.addAssertInstructions(myBuilder, assertionEvaluator);
@@ -379,15 +388,24 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
final PyElsePart elseBranch = node.getElsePart();
if (elseBranch != null) {
// Set the head as the false branch
myBuilder.prevInstruction = lastBranchingPoint;
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
myBuilder.prevInstruction = null;
myBuilder.startConditionalNode(elseBranch, lastCondition, false);
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
elseBranch.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
else {
myBuilder.prevInstruction = lastBranchingPoint;
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
myBuilder.prevInstruction = null;
final Instruction instruction = ContainerUtil.getFirstItem(InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator));
if (instruction != null) {
lastBranchingPoints.forEach(p -> myBuilder.addEdge(p.getSecond(), instruction));
}
else {
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
}
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
}