mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge branch 'cfg-negative-assert-type'
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user