mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-1430 Recognize that 'assert False' breaks control flow
This commit is contained in:
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user