Fixed false positive in unreachable code inspection for 'raise' inside 'with' that suppressed errors (PY-7420)

We have to be more conservative and assume that any context manager can suppress exceptions. Otherwise we will get false positives.
This commit is contained in:
Andrey Vlasovskikh
2012-09-12 18:46:07 +04:00
parent b3a190e134
commit 1df7a007a9
3 changed files with 40 additions and 48 deletions
@@ -3,14 +3,11 @@ package com.jetbrains.python.codeInsight.controlflow;
import com.intellij.codeInsight.controlflow.ControlFlow;
import com.intellij.codeInsight.controlflow.ControlFlowBuilder;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.lang.ASTNode;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.PsiTreeUtil;
import com.jetbrains.python.PyElementTypes;
import com.jetbrains.python.PyNames;
import com.jetbrains.python.psi.*;
import com.jetbrains.python.psi.impl.ParamHelper;
@@ -26,9 +23,6 @@ import java.util.List;
* @author oleg
*/
public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
public static final TokenSet CALL_OR_REF_EXPR = TokenSet.create(PyElementTypes.CALL_EXPRESSION, PyElementTypes.REFERENCE_EXPRESSION);
public static final String SELF_ASSERT_RAISES = "self.assertRaises";
private final ControlFlowBuilder myBuilder = new ControlFlowBuilder();
public ControlFlow buildControlFlow(@NotNull final ScopeOwner owner) {
@@ -711,42 +705,18 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
@Override
public void visitPyWithStatement(final PyWithStatement node) {
boolean withSelfAssertRaises = false;
final PyWithItem[] items = node.getWithItems();
if (items.length == 1){
final PyWithItem item = items[0];
final ASTNode callNode = item.getNode().findChildByType(CALL_OR_REF_EXPR);
if (callNode != null) {
final PsiElement element = callNode.getPsi();
if (element instanceof PyCallExpression) {
final PyExpression callee = ((PyCallExpression)element).getCallee();
if (callee != null) {
final String repr = PyUtil.getReadableRepr(callee, true);
withSelfAssertRaises = SELF_ASSERT_RAISES.equals(repr);
}
}
if (element instanceof PyReferenceExpression){
withSelfAssertRaises = SELF_ASSERT_RAISES.equals(element.getText());
}
}
}
super.visitPyWithStatement(node);
if (withSelfAssertRaises){
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
public void process(final PsiElement pendingScope, final Instruction instruction) {
final PsiElement element = instruction.getElement();
if (element == null){
myBuilder.addPendingEdge(pendingScope, instruction);
}
else if (PsiTreeUtil.getParentOfType(element, PyRaiseStatement.class) != null){
myBuilder.addPendingEdge(node, instruction);
}
else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
public void process(final PsiElement pendingScope, final Instruction instruction) {
final PsiElement element = instruction.getElement();
if (PsiTreeUtil.getParentOfType(element, PyRaiseStatement.class) != null) {
myBuilder.addPendingEdge(node, instruction);
}
else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
});
}
});
}
private void abruptFlow(final PsiElement node) {
@@ -0,0 +1,12 @@
class C(object):
def __enter__(self):
return self
def __exit__(self, exc, value, traceback):
return True
def f():
with C():
raise Exception()
x = 1 #pass
@@ -9,19 +9,29 @@ import com.jetbrains.python.psi.LanguageLevel;
public class PyUnreachableCodeInspectionTest extends PyTestCase {
private static final String TEST_DIRECTORY = "inspections/PyUnreachableCodeInspection/";
// All previous unreachable tests
// All previous unreachable tests, feel free to split them
public void testUnreachable() {
doTest();
}
private void doTest() {
runWithLanguageLevel(LanguageLevel.PYTHON27, new Runnable() {
runWithLanguageLevel(LanguageLevel.PYTHON26, new Runnable() {
@Override
public void run() {
myFixture.configureByFile(TEST_DIRECTORY + getTestName(false) + ".py");
myFixture.enableInspections(PyUnreachableCodeInspection.class);
myFixture.checkHighlighting(true, false, false);
doTest();
}
});
}
// PY-7420
public void testWithSuppressedExceptions() {
runWithLanguageLevel(LanguageLevel.PYTHON26, new Runnable() {
@Override
public void run() {
doTest();
}
});
}
private void doTest() {
myFixture.configureByFile(TEST_DIRECTORY + getTestName(false) + ".py");
myFixture.enableInspections(PyUnreachableCodeInspection.class);
myFixture.checkHighlighting(true, false, false);
}
}