mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
PY-20889 Fixed: Unresolved attribute after isinstance check in boolean expression
Consider `and` and `or` expressions in PyControlFlowBuilder and use PyTypeAssertionEvaluator to make assertion for right expression.
This commit is contained in:
@@ -25,6 +25,7 @@ import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
import com.jetbrains.python.psi.*;
|
||||
import com.jetbrains.python.psi.impl.ParamHelper;
|
||||
import com.jetbrains.python.psi.impl.PyAugAssignmentStatementNavigator;
|
||||
@@ -390,6 +391,29 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyBinaryExpression(PyBinaryExpression node) {
|
||||
final PyElementType operator = node.getOperator();
|
||||
if (operator == PyTokenTypes.AND_KEYWORD || operator == PyTokenTypes.OR_KEYWORD) {
|
||||
myBuilder.startNode(node);
|
||||
final PyExpression left = node.getLeftExpression();
|
||||
final PyTypeAssertionEvaluator assertionEvaluator = new PyTypeAssertionEvaluator(operator == PyTokenTypes.AND_KEYWORD);
|
||||
if (left != null) {
|
||||
left.accept(this);
|
||||
left.accept(assertionEvaluator);
|
||||
}
|
||||
final PyExpression right = node.getRightExpression();
|
||||
if (right != null) {
|
||||
InstructionBuilder.addAssertInstructions(myBuilder, assertionEvaluator);
|
||||
right.accept(this);
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.visitPyBinaryExpression(node);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyWhileStatement(final PyWhileStatement node) {
|
||||
final Instruction instruction = myBuilder.startNode(node);
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
var = object()
|
||||
if isinstance(var, A) and var:
|
||||
pass
|
||||
@@ -0,0 +1,17 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyClass: A
|
||||
2(3) WRITE ACCESS: A
|
||||
3(4) element: PyAssignmentStatement
|
||||
4(5) READ ACCESS: object
|
||||
5(6) WRITE ACCESS: var
|
||||
6(7) element: PyIfStatement
|
||||
7(8) element: PyBinaryExpression
|
||||
8(9) READ ACCESS: isinstance
|
||||
9(10) READ ACCESS: var
|
||||
10(11) READ ACCESS: A
|
||||
11(12) ASSERTTYPE ACCESS: var
|
||||
12(13,15) READ ACCESS: var
|
||||
13(14) element: PyStatementList. Condition: isinstance(var, A) and var:true
|
||||
14(16) ASSERTTYPE ACCESS: var
|
||||
15(16) ASSERTTYPE ACCESS: var
|
||||
16() element: null
|
||||
@@ -1,16 +1,17 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyForStatement
|
||||
2(3,15) READ ACCESS: self
|
||||
2(3,16) READ ACCESS: self
|
||||
3(4) element: PyTupleExpression
|
||||
4(5) WRITE ACCESS: start
|
||||
5(6) WRITE ACCESS: end
|
||||
6(7) WRITE ACCESS: name
|
||||
7(8) element: PyIfStatement
|
||||
8(9) READ ACCESS: hour
|
||||
9(10) READ ACCESS: start
|
||||
10(11) READ ACCESS: hour
|
||||
11(12,3,15) READ ACCESS: end
|
||||
12(13) element: PyStatementList. Condition: hour >= start and hour < end:true
|
||||
13(14) element: PyReturnStatement
|
||||
14(15) READ ACCESS: name
|
||||
15() element: null
|
||||
8(9) element: PyBinaryExpression
|
||||
9(10) READ ACCESS: hour
|
||||
10(11) READ ACCESS: start
|
||||
11(12) READ ACCESS: hour
|
||||
12(13,3,16) READ ACCESS: end
|
||||
13(14) element: PyStatementList. Condition: hour >= start and hour < end:true
|
||||
14(15) element: PyReturnStatement
|
||||
15(16) READ ACCESS: name
|
||||
16() element: null
|
||||
@@ -20,22 +20,24 @@
|
||||
19(11,20) READ ACCESS: method
|
||||
20(21) WRITE ACCESS: methodList
|
||||
21(22) element: PyAssignmentStatement
|
||||
22(23) READ ACCESS: collapse
|
||||
23(24) element: PyLambdaExpression
|
||||
24(25) element: PyLambdaExpression
|
||||
25(26) WRITE ACCESS: processFunc
|
||||
26(27) element: PyPrintStatement
|
||||
27(28) element: PyListCompExpression
|
||||
28(29) element: PyReferenceExpression: methodList
|
||||
29(30,40) READ ACCESS: methodList
|
||||
30(31) element: PyTargetExpression: method
|
||||
31(32) WRITE ACCESS: method
|
||||
32(33) element: PyBinaryExpression
|
||||
33(34) READ ACCESS: method
|
||||
34(35) READ ACCESS: spacing
|
||||
35(36) READ ACCESS: processFunc
|
||||
36(37) READ ACCESS: str
|
||||
37(38) READ ACCESS: getattr
|
||||
38(39) READ ACCESS: object
|
||||
39(30,40) READ ACCESS: method
|
||||
40() element: null
|
||||
22(23) element: PyBinaryExpression
|
||||
23(24) element: PyBinaryExpression
|
||||
24(25) READ ACCESS: collapse
|
||||
25(26) element: PyLambdaExpression
|
||||
26(27) element: PyLambdaExpression
|
||||
27(28) WRITE ACCESS: processFunc
|
||||
28(29) element: PyPrintStatement
|
||||
29(30) element: PyListCompExpression
|
||||
30(31) element: PyReferenceExpression: methodList
|
||||
31(32,42) READ ACCESS: methodList
|
||||
32(33) element: PyTargetExpression: method
|
||||
33(34) WRITE ACCESS: method
|
||||
34(35) element: PyBinaryExpression
|
||||
35(36) READ ACCESS: method
|
||||
36(37) READ ACCESS: spacing
|
||||
37(38) READ ACCESS: processFunc
|
||||
38(39) READ ACCESS: str
|
||||
39(40) READ ACCESS: getattr
|
||||
40(41) READ ACCESS: object
|
||||
41(32,42) READ ACCESS: method
|
||||
42() element: null
|
||||
@@ -0,0 +1,11 @@
|
||||
class A:
|
||||
pass
|
||||
|
||||
|
||||
class B:
|
||||
pass
|
||||
|
||||
|
||||
if isinstance(var, A):
|
||||
if isinstance(var, B) or var:
|
||||
pass
|
||||
@@ -0,0 +1,23 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyClass: A
|
||||
2(3) WRITE ACCESS: A
|
||||
3(4) element: PyClass: B
|
||||
4(5) WRITE ACCESS: B
|
||||
5(6) element: PyIfStatement
|
||||
6(7) READ ACCESS: isinstance
|
||||
7(8) READ ACCESS: var
|
||||
8(9,21) READ ACCESS: A
|
||||
9(10) element: PyStatementList. Condition: isinstance(var, A):true
|
||||
10(11) ASSERTTYPE ACCESS: var
|
||||
11(12) element: PyIfStatement
|
||||
12(13) element: PyBinaryExpression
|
||||
13(14) READ ACCESS: isinstance
|
||||
14(15) READ ACCESS: var
|
||||
15(16) READ ACCESS: B
|
||||
16(17) ASSERTTYPE ACCESS: var
|
||||
17(18,20) READ ACCESS: var
|
||||
18(19) element: PyStatementList. Condition: isinstance(var, B) or var:true
|
||||
19(22) ASSERTTYPE ACCESS: var
|
||||
20(22) ASSERTTYPE ACCESS: var
|
||||
21(22) ASSERTTYPE ACCESS: var
|
||||
22() element: null
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
class A:
|
||||
def foo(self):
|
||||
pass
|
||||
|
||||
|
||||
class B:
|
||||
def bar(self):
|
||||
pass
|
||||
|
||||
|
||||
var = object()
|
||||
if isinstance(var, A) and var.foo():
|
||||
pass
|
||||
|
||||
|
||||
if isinstance(var, A):
|
||||
if isinstance(var, B) or var.<warning descr="Unresolved attribute reference 'bar' for class 'A'">bar</warning>():
|
||||
pass
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -248,6 +248,16 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20889
|
||||
public void testAndBooleanExpression() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20889
|
||||
public void testOrBooleanExpression() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTestFirstStatement() {
|
||||
final String testName = getTestName(false).toLowerCase();
|
||||
configureByFile(testName + ".py");
|
||||
|
||||
+5
@@ -647,6 +647,11 @@ public class PyUnresolvedReferencesInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-20889
|
||||
public void testTypeAssertionInBooleanOperations() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user