Merge branch 'cfg-negative-assert-type'

This commit is contained in:
Andrey Vlasovskikh
2013-04-22 18:06:20 +04:00
14 changed files with 130 additions and 33 deletions
@@ -299,15 +299,43 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
});
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
boolean noPendingInScopeEdges = false;
if (!assertionEvaluator.getDefinitions().isEmpty()) {
final Ref<Boolean> pendingInScopeEdges = Ref.create(false);
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
@Override
public void process(PsiElement pendingScope, Instruction instruction) {
if (pendingScope != null && PsiTreeUtil.isAncestor(node, pendingScope, false)) {
pendingInScopeEdges.set(true);
}
myBuilder.addPendingEdge(pendingScope, instruction);
}
});
noPendingInScopeEdges = !pendingInScopeEdges.get();
}
final PyTypeAssertionEvaluator negativeAssertionEvaluator = new PyTypeAssertionEvaluator(false);
final PyExpression ifCondition = ifPart.getCondition();
// TODO: Add support for 'elif'
if (ifCondition != null) {
ifCondition.accept(negativeAssertionEvaluator);
}
final PyElsePart elseBranch = node.getElsePart();
if (elseBranch != null) {
// Set the head as the false branch
myBuilder.prevInstruction = lastBranchingPoint;
myBuilder.startConditionalNode(elseBranch, lastCondition, false);
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
elseBranch.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
} else {
myBuilder.addPendingEdge(node, lastBranchingPoint);
if (noPendingInScopeEdges) {
myBuilder.prevInstruction = lastBranchingPoint;
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
else {
myBuilder.addPendingEdge(node, lastBranchingPoint);
}
}
}
@@ -16,7 +16,15 @@ import java.util.Stack;
*/
public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
private Stack<Assertion> myStack = new Stack<Assertion>();
private boolean myPositive = true;
private boolean myPositive;
public PyTypeAssertionEvaluator() {
this(true);
}
public PyTypeAssertionEvaluator(boolean positive) {
myPositive = positive;
}
public List<Assertion> getDefinitions() {
return myStack;
@@ -24,7 +32,7 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
@Override
public void visitPyPrefixExpression(PyPrefixExpression node) {
if (myPositive && node.getOperator() == PyTokenTypes.NOT_KEYWORD) {
if (node.getOperator() == PyTokenTypes.NOT_KEYWORD) {
myPositive = !myPositive;
super.visitPyPrefixExpression(node);
myPositive = !myPositive;
@@ -74,12 +82,13 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
@Override
public void visitPyReferenceExpression(final PyReferenceExpression node) {
if (node.getParent() instanceof PyIfPart) {
final boolean positive = myPositive;
pushAssertion(node, new InstructionTypeCallback() {
@Override
public PyType getType(TypeEvalContext context, PsiElement anchor) {
final List<PyType> types = new ArrayList<PyType>();
types.add(PyNoneType.INSTANCE);
return createAssertionType(context.getType(node), types, false, context);
return createAssertionType(context.getType(node), types, !positive, context);
}
});
return;
@@ -95,12 +104,13 @@ public class PyTypeAssertionEvaluator extends PyRecursiveElementVisitor {
if (lhs instanceof PyReferenceExpression && rhs instanceof PyReferenceExpression) {
final PyReferenceExpression target = (PyReferenceExpression)lhs;
if (PyNames.NONE.equals(rhs.getName())) {
final boolean positive = myPositive;
pushAssertion(target, new InstructionTypeCallback() {
@Override
public PyType getType(TypeEvalContext context, @Nullable PsiElement anchor) {
final List<PyType> types = new ArrayList<PyType>();
types.add(PyNoneType.INSTANCE);
return createAssertionType(context.getType(target), types, false, context);
return createAssertionType(context.getType(target), types, !positive, context);
}
});
return;
@@ -1,11 +1,12 @@
0(1) element: null
1(2) element: PyWhileStatement
2(3,10) READ ACCESS: foo
2(3,11) READ ACCESS: foo
3(4) element: PyStatementList. Condition: foo:true
4(5) element: PyIfStatement
5(6,9) READ ACCESS: condition
6(7) element: PyStatementList. Condition: condition:true
7(8) ASSERTTYPE ACCESS: condition
8(10) element: PyBreakStatement
9(1) element: PyPrintStatement
10() element: null
8(11) element: PyBreakStatement
9(10) ASSERTTYPE ACCESS: condition
10(1) element: PyPrintStatement
11() element: null
@@ -1,11 +1,12 @@
0(1) element: null
1(2) element: PyWhileStatement
2(3,10) READ ACCESS: foo
2(3,11) READ ACCESS: foo
3(4) element: PyStatementList. Condition: foo:true
4(5) element: PyIfStatement
5(6,9) READ ACCESS: condition
6(7) element: PyStatementList. Condition: condition:true
7(8) ASSERTTYPE ACCESS: condition
8(1) element: PyContinueStatement
9(1) element: PyPrintStatement
10() element: null
9(10) ASSERTTYPE ACCESS: condition
10(1) element: PyPrintStatement
11() element: null
@@ -6,12 +6,13 @@
5(6) element: PyTryExceptStatement
6(7,9) element: PyTryPart
7(8,9) element: PyReturnStatement
8(9,16) READ ACCESS: renderer
8(9,17) READ ACCESS: renderer
9(10) element: PyExceptPart
10(11) READ ACCESS: KeyError
11(12) element: PyRaiseStatement
12(16) READ ACCESS: Error
12(17) READ ACCESS: Error
13(14) element: PyElsePart. Condition: markup:false
14(15) element: PyReturnStatement
15(16) READ ACCESS: body
16() element: null
14(15) ASSERTTYPE ACCESS: markup
15(16) element: PyReturnStatement
16(17) READ ACCESS: body
17() element: null
@@ -9,9 +9,10 @@
8(9) ASSERTTYPE ACCESS: s
9(10) element: PyAssignmentStatement
10(11) READ ACCESS: s
11(16) WRITE ACCESS: s2
11(17) WRITE ACCESS: s2
12(13) element: PyElsePart. Condition: (isinstance(s, basestring)):false
13(14) element: PyAssignmentStatement
14(15) READ ACCESS: s
15(16) WRITE ACCESS: x
16() element: null
13(14) ASSERTTYPE ACCESS: s
14(15) element: PyAssignmentStatement
15(16) READ ACCESS: s
16(17) WRITE ACCESS: x
17() element: null
@@ -1,11 +1,12 @@
0(1) element: null
1(2) element: PyWhileStatement
2(3,10) READ ACCESS: foo
2(3,11) READ ACCESS: foo
3(4) element: PyStatementList. Condition: foo:true
4(5) element: PyIfStatement
5(6,9) READ ACCESS: condition
6(7) element: PyStatementList. Condition: condition:true
7(8) ASSERTTYPE ACCESS: condition
8(10) element: PyReturnStatement
9(1) element: PyPrintStatement
10() element: null
8(11) element: PyReturnStatement
9(10) ASSERTTYPE ACCESS: condition
10(1) element: PyPrintStatement
11() element: null
@@ -17,7 +17,7 @@
16(17) element: PyAssignmentStatement
17(18) WRITE ACCESS: c
18(19) element: PyWhileStatement
19(20,30) READ ACCESS: c3
19(20,31) READ ACCESS: c3
20(21) element: PyStatementList. Condition: c3:true
21(22) element: PyAssignmentStatement
22(23) WRITE ACCESS: m
@@ -25,9 +25,10 @@
24(25,28) READ ACCESS: m
25(26) element: PyStatementList. Condition: m:true
26(27) ASSERTTYPE ACCESS: m
27(32) element: PyBreakStatement
28(29) element: PyAssignmentStatement
29(18) WRITE ACCESS: n
30(31) element: PyAssignmentStatement
31(32) WRITE ACCESS: o
32() element: null
27(33) element: PyBreakStatement
28(29) ASSERTTYPE ACCESS: m
29(30) element: PyAssignmentStatement
30(18) WRITE ACCESS: n
31(32) element: PyAssignmentStatement
32(33) WRITE ACCESS: o
33() element: null
@@ -0,0 +1,19 @@
def method_a():
"""
:rtype: dict or int
"""
pass
def method_b(d):
"""
:type d: dict
"""
pass
def f():
var = method_a()
if isinstance(var, int):
return var
method_b(var) # pass
@@ -0,0 +1,5 @@
def f(c):
x = 'foo' if c else ['f', 'o', 'o']
if not isinstance(x, str):
raise TypeError('foo')
return x.<warning descr="Unresolved attribute reference 'pop' for class 'str'">pop</warning>() # should warn about using 'pop()' on a 'str' instance
@@ -0,0 +1,4 @@
def f(x):
if x:
return x
return x.upper(), 2 + x
@@ -486,6 +486,17 @@ public class PyTypeTest extends PyTestCase {
"expr = f()\n");
}
// PY-5084
public void testIfIsInstanceElse() {
doTest("str",
"def test(c):\n" +
" x = 'foo' if c else 42\n" +
" if isinstance(x, int):\n" +
" print(x)\n" +
" else:\n" +
" expr = x\n");
}
private PyExpression parseExpr(String text) {
myFixture.configureByText(PythonFileType.INSTANCE, text);
return myFixture.findElementByText("expr", PyExpression.class);
@@ -160,4 +160,9 @@ public class PyTypeCheckerInspectionTest extends PyTestCase {
public void testBytesSubclassAsStr() {
doTest();
}
// PY-9118
public void testNegativeIsInstance() {
doTest();
}
}
@@ -267,6 +267,15 @@ public class PyUnresolvedReferencesInspectionTest extends PyTestCase {
doTest();
}
// PY-7694
public void testNegativeAssertType() {
doTest();
}
public void testNegativeIf() {
doTest();
}
private void doTest() {
myFixture.configureByFile(TEST_DIRECTORY + getTestName(true) + ".py");
myFixture.enableInspections(PyUnresolvedReferencesInspection.class);