PY-1430 Recognize that 'assert False' breaks control flow

This commit is contained in:
Oleg Shpynov
2010-07-27 14:45:25 +04:00
parent 3422c0e859
commit 3ba7d3971c
11 changed files with 80 additions and 38 deletions
@@ -10,19 +10,19 @@ import java.util.List;
* @author traff
*/
public class InstructionBuilder {
public static List<Instruction> buildInstructions(ControlFlowBuilder builder, List<PyAssertionEvaluator.Assertion> assertions) {
public static List<Instruction> buildInstructions(ControlFlowBuilder builder, List<PyTypeAssertionEvaluator.Assertion> assertions) {
List<Instruction> result = CollectionFactory.arrayList();
for (PyAssertionEvaluator.Assertion def: assertions) {
for (PyTypeAssertionEvaluator.Assertion def: assertions) {
processDef(builder, def, result);
}
return result;
}
private static void processDef(ControlFlowBuilder builder, PyAssertionEvaluator.Assertion def, List<Instruction> result) {
private static void processDef(ControlFlowBuilder builder, PyTypeAssertionEvaluator.Assertion def, List<Instruction> result) {
result.add(ReadWriteInstruction.writeType(builder, def.getElement(), def.getName()));
}
public static void addAssertInstructions(ControlFlowBuilder builder, PyAssertionEvaluator assertionEvaluator) {
public static void addAssertInstructions(ControlFlowBuilder builder, PyTypeAssertionEvaluator assertionEvaluator) {
for (Instruction instr : buildInstructions(builder, assertionEvaluator.getDefinitions())) {
builder.addNode(instr);
}
@@ -13,6 +13,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator;
import com.jetbrains.python.psi.impl.PyConstantExpressionEvaluator;
import com.jetbrains.python.psi.impl.PyImportStatementNavigator;
import org.jetbrains.annotations.NotNull;
@@ -56,24 +57,12 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
@Override
public void visitPyCallExpression(final PyCallExpression node) {
if ("exit".equals(node.getCallee().getText())) {
// Flow abrupted
if (node.isCalleeText("exit")) {
for (PyExpression expression : node.getArguments()) {
expression.accept(this);
}
// Here we process pending instructions!!!
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
public void process(final PsiElement pendingScope, final Instruction instruction) {
if (pendingScope != null && PsiTreeUtil.isAncestor(node, pendingScope, false)) {
myBuilder.addPendingEdge(null, instruction);
}
else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
}
});
myBuilder.addPendingEdge(null, myBuilder.prevInstruction);
myBuilder.flowAbrupted();
abruptFlow(node);
}
else {
super.visitPyCallExpression(node);
@@ -209,7 +198,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
myBuilder.startNode(node);
final PyIfPart ifPart = node.getIfPart();
PyExpression condition = ifPart.getCondition();
PyAssertionEvaluator assertionEvaluator = new PyAssertionEvaluator();
PyTypeAssertionEvaluator assertionEvaluator = new PyTypeAssertionEvaluator();
if (condition != null) {
condition.accept(this);
condition.accept(assertionEvaluator);
@@ -417,19 +406,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
if (expression != null) {
expression.accept(this);
}
// Here we process pending instructions!!!
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
public void process(final PsiElement pendingScope, final Instruction instruction) {
if (pendingScope != null && PsiTreeUtil.isAncestor(node, pendingScope, false)) {
myBuilder.addPendingEdge(null, instruction);
}
else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
}
});
myBuilder.addPendingEdge(null, myBuilder.prevInstruction);
myBuilder.flowAbrupted();
abruptFlow(node);
}
@Override
@@ -563,7 +540,13 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
public void visitPyAssertStatement(final PyAssertStatement node) {
super.visitPyAssertStatement(node);
PyAssertionEvaluator evaluator = new PyAssertionEvaluator();
final PyExpression[] args = node.getArguments();
// assert False
if (args.length == 1 && PyConstantExpressionEvaluator.evaluate(args[0]) == Boolean.FALSE) {
abruptFlow(node);
return;
}
PyTypeAssertionEvaluator evaluator = new PyTypeAssertionEvaluator();
node.acceptChildren(evaluator);
InstructionBuilder.addAssertInstructions(myBuilder, evaluator);
}
@@ -602,7 +585,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
if (callNode != null) {
final PsiElement element = callNode.getPsi();
if (element instanceof PyCallExpression) {
withSelfAssertRaises = SELF_ASSERT_RAISES.equals(((PyCallExpression)element).getCallee().getText());
withSelfAssertRaises = ((PyCallExpression)element).isCalleeText(SELF_ASSERT_RAISES);
}
if (element instanceof PyReferenceExpression){
withSelfAssertRaises = SELF_ASSERT_RAISES.equals(element.getText());
@@ -627,4 +610,21 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
});
}
}
private void abruptFlow(final PsiElement node) {
// Here we process pending instructions!!!
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
public void process(final PsiElement pendingScope, final Instruction instruction) {
if (pendingScope != null && PsiTreeUtil.isAncestor(node, pendingScope, false)) {
myBuilder.addPendingEdge(null, instruction);
}
else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
}
});
myBuilder.addPendingEdge(null, myBuilder.prevInstruction);
myBuilder.flowAbrupted();
}
}
@@ -10,11 +10,11 @@ import java.util.Stack;
/**
* @author traff
*/
public class PyAssertionEvaluator extends PyRecursiveElementVisitor {
public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
private Stack<Assertion> myStack = CollectionFactory.stack();
public PyAssertionEvaluator() {
public PyTypeAssertionEvaluator() {
}
@@ -4,4 +4,5 @@ package com.jetbrains.python.psi;
* @author yole
*/
public interface PyAssertStatement extends PyStatement {
PyExpression[] getArguments();
}
@@ -4,4 +4,5 @@ package com.jetbrains.python.psi;
* @author yole
*/
public interface PyBoolLiteralExpression extends PyLiteralExpression {
boolean getValue();
}
@@ -1,8 +1,10 @@
package com.jetbrains.python.psi.impl;
import com.intellij.lang.ASTNode;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.psi.PyAssertStatement;
import com.jetbrains.python.psi.PyElementVisitor;
import com.jetbrains.python.psi.PyExpression;
/**
* @author yole
@@ -15,4 +17,9 @@ public class PyAssertStatementImpl extends PyElementImpl implements PyAssertStat
protected void acceptPyVisitor(PyElementVisitor pyVisitor) {
pyVisitor.visitPyAssertStatement(this);
}
@Override
public PyExpression[] getArguments() {
return childrenToPsi(PyElementTypes.EXPRESSIONS, PyExpression.EMPTY_ARRAY);
}
}
@@ -17,4 +17,9 @@ public class PyBoolLiteralExpressionImpl extends PyElementImpl implements PyBool
public PyType getType(@NotNull TypeEvalContext context) {
return PyBuiltinCache.getInstance(this).getBoolType();
}
@Override
public boolean getValue() {
return "True".equals(getText());
}
}
@@ -1,7 +1,9 @@
package com.jetbrains.python.psi.impl;
import com.jetbrains.python.psi.PyBoolLiteralExpression;
import com.jetbrains.python.psi.PyExpression;
import com.jetbrains.python.psi.PyNumericLiteralExpression;
import com.jetbrains.python.psi.PyReferenceExpression;
import org.jetbrains.annotations.Nullable;
import java.math.BigInteger;
@@ -14,7 +16,7 @@ public class PyConstantExpressionEvaluator {
}
@Nullable
public static Object evaluate(PyExpression expr) {
public static Object evaluate(final PyExpression expr) {
if (expr instanceof PyNumericLiteralExpression) {
final PyNumericLiteralExpression numericLiteral = (PyNumericLiteralExpression)expr;
if (numericLiteral.isIntegerLiteral()) {
@@ -24,6 +26,18 @@ public class PyConstantExpressionEvaluator {
}
}
}
if (expr instanceof PyBoolLiteralExpression){
return ((PyBoolLiteralExpression)expr).getValue();
}
if (expr instanceof PyReferenceExpression){
final String text = expr.getText();
if ("true".equals(text) || "True".equals(text)){
return true;
}
if ("false".equals(text) || "False".equals(text)){
return false;
}
}
return null;
}
@@ -0,0 +1,4 @@
assert false
print("Unreachable")
assert False
print("Unreachable2")
@@ -0,0 +1,6 @@
0(1) element: null
1(5) READ ACCESS: false
2(3) element: PyPrintStatement
3(5) READ ACCESS: False
4(5) element: PyPrintStatement
5() element: null
@@ -35,6 +35,10 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
doTest();
}
public void testAssertFalse() throws Exception {
doTest();
}
public void testFile() throws Exception {
doTest();
}