Skip to content

Commit 7b452a1

Browse files
Add case for wrappers
1 parent bedd44a commit 7b452a1

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

python/ql/src/Functions/IterReturnsNonSelf.ql

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,16 @@ private predicate methodOfClass(Function f, Class c) {
2020
exists(FunctionDef d | d.getDefinedFunction() = f and d.getScope() = c)
2121
}
2222

23+
/** Gets the __iter__ method of `c`. */
2324
Function iterMethod(Class c) { methodOfClass(result, c) and result.getName() = "__iter__" }
2425

26+
/** Gets the `__next__` method of `c`. */
2527
Function nextMethod(Class c) { methodOfClass(result, c) and result.getName() = "__next__" }
2628

29+
/** Holds if `var` is a variable referring to the `self` parameter of `f`. */
2730
predicate isSelfVar(Function f, Name var) { var.getVariable() = f.getArg(0).(Name).getVariable() }
2831

32+
/** Holds if `e` is an expression that an iter function `f` should return. */
2933
predicate isGoodReturn(Function f, Expr e) {
3034
isSelfVar(f, e)
3135
or
@@ -40,6 +44,7 @@ predicate isGoodReturn(Function f, Expr e) {
4044
)
4145
}
4246

47+
/** Holds if the iter method `f` does not return `self` or an equivalent. */
4348
predicate returnsNonSelf(Function f) {
4449
exists(f.getFallthroughNode())
4550
or
@@ -48,10 +53,46 @@ predicate returnsNonSelf(Function f) {
4853
exists(Return r | r.getScope() = f and not exists(r.getValue()))
4954
}
5055

51-
from Class c, Function iter
56+
/** Holds if `iter` and `next` methods are wrappers around some field. */
57+
predicate iterWrapperMethods(Function iter, Function next) {
58+
exists(string field |
59+
exists(Return r, DataFlow::Node self, DataFlow::AttrRead read |
60+
r.getScope() = iter and
61+
r.getValue() = iterCall(read).asExpr() and
62+
read.accesses(self, field) and
63+
isSelfVar(iter, self.asExpr())
64+
) and
65+
exists(Return r, DataFlow::Node self, DataFlow::AttrRead read |
66+
r.getScope() = next and
67+
r.getValue() = nextCall(read).asExpr() and
68+
read.accesses(self, field) and
69+
isSelfVar(next, self.asExpr())
70+
)
71+
)
72+
}
73+
74+
DataFlow::CallCfgNode iterCall(DataFlow::Node arg) {
75+
result.(DataFlow::MethodCallNode).calls(arg, "__iter__")
76+
or
77+
result = API::builtin("iter").getACall() and
78+
arg = result.getArg(0) and
79+
not exists(result.getArg(1))
80+
or
81+
result = arg // assume the wrapping field is already an iterator
82+
}
83+
84+
DataFlow::CallCfgNode nextCall(DataFlow::Node arg) {
85+
result.(DataFlow::MethodCallNode).calls(arg, "__next__")
86+
or
87+
result = API::builtin("next").getACall() and
88+
arg = result.getArg(0)
89+
}
90+
91+
from Class c, Function iter, Function next
5292
where
53-
exists(nextMethod(c)) and
93+
next = nextMethod(c) and
5494
iter = iterMethod(c) and
55-
returnsNonSelf(iter)
95+
returnsNonSelf(iter) and
96+
not iterWrapperMethods(iter, next)
5697
select iter, "Iter method of iterator $@ does not return `" + iter.getArg(0).getName() + "`.", c,
5798
c.getName()

0 commit comments

Comments
 (0)