Skip to content

Commit 3b686c0

Browse files
committed
Python: Port ContainsNonContainer.ql
Uses the new `DuckTyping` module to handle recognising whether a class is a container or not. Only trivial test changes (one version uses "class", the other "Class"). Note that the ported query has no understanding of built-in classes. At some point we'll likely want to replace `hasUnresolvedBase` (which will hold for any class that extends a built-in) with something that's aware of the built-in classes.
1 parent 40a8e22 commit 3b686c0

File tree

2 files changed

+12
-16
lines changed

2 files changed

+12
-16
lines changed

python/ql/src/Expressions/ContainsNonContainer.ql

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,21 @@
1212
*/
1313

1414
import python
15-
private import LegacyPointsTo
15+
import semmle.python.dataflow.new.DataFlow
16+
private import semmle.python.dataflow.new.internal.DataFlowDispatch
1617

17-
predicate rhs_in_expr(ControlFlowNode rhs, Compare cmp) {
18-
exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs.getNode() |
18+
predicate rhs_in_expr(Expr rhs, Compare cmp) {
19+
exists(Cmpop op, int i | cmp.getOp(i) = op and cmp.getComparator(i) = rhs |
1920
op instanceof In or op instanceof NotIn
2021
)
2122
}
2223

23-
from
24-
ControlFlowNodeWithPointsTo non_seq, Compare cmp, Value v, ClassValue cls, ControlFlowNode origin
24+
from Compare cmp, DataFlow::LocalSourceNode origin, DataFlow::Node rhs, Class cls
2525
where
26-
rhs_in_expr(non_seq, cmp) and
27-
non_seq.pointsTo(_, v, origin) and
28-
v.getClass() = cls and
29-
not Types::failedInference(cls, _) and
30-
not cls.hasAttribute("__contains__") and
31-
not cls.hasAttribute("__iter__") and
32-
not cls.hasAttribute("__getitem__") and
33-
not cls = ClassValue::nonetype() and
34-
not cls = Value::named("types.MappingProxyType")
26+
origin = classInstanceTracker(cls) and
27+
origin.flowsTo(rhs) and
28+
not DuckTyping::isContainer(cls) and
29+
not DuckTyping::hasUnresolvedBase(getADirectSuperclass*(cls)) and
30+
rhs_in_expr(rhs.asExpr(), cmp)
3531
select cmp, "This test may raise an Exception as the $@ may be of non-container class $@.", origin,
3632
"target", cls, cls.getName()
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
| expressions_test.py:89:8:89:15 | Compare | This test may raise an Exception as the $@ may be of non-container class $@. | expressions_test.py:88:11:88:17 | ControlFlowNode for XIter() | target | expressions_test.py:77:1:77:20 | class XIter | XIter |
2-
| expressions_test.py:91:8:91:19 | Compare | This test may raise an Exception as the $@ may be of non-container class $@. | expressions_test.py:88:11:88:17 | ControlFlowNode for XIter() | target | expressions_test.py:77:1:77:20 | class XIter | XIter |
1+
| expressions_test.py:89:8:89:15 | Compare | This test may raise an Exception as the $@ may be of non-container class $@. | expressions_test.py:88:11:88:17 | ControlFlowNode for XIter() | target | expressions_test.py:77:1:77:20 | Class XIter | XIter |
2+
| expressions_test.py:91:8:91:19 | Compare | This test may raise an Exception as the $@ may be of non-container class $@. | expressions_test.py:88:11:88:17 | ControlFlowNode for XIter() | target | expressions_test.py:77:1:77:20 | Class XIter | XIter |

0 commit comments

Comments
 (0)