Skip to content

Commit bedd44a

Browse files
Update query and add case for iter(self.__next__, None)
1 parent e4b7b91 commit bedd44a

File tree

1 file changed

+35
-8
lines changed

1 file changed

+35
-8
lines changed

python/ql/src/Functions/IterReturnsNonSelf.ql

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,54 @@
44
* @kind problem
55
* @tags reliability
66
* correctness
7+
* quality
78
* @problem.severity error
89
* @sub-severity low
910
* @precision high
1011
* @id py/iter-returns-non-self
1112
*/
1213

1314
import python
15+
import semmle.python.dataflow.new.DataFlow
16+
import semmle.python.ApiGraphs
1417

15-
Function iter_method(ClassValue t) { result = t.lookup("__iter__").(FunctionValue).getScope() }
18+
/** Holds if `f` is a method of the class `c`. */
19+
private predicate methodOfClass(Function f, Class c) {
20+
exists(FunctionDef d | d.getDefinedFunction() = f and d.getScope() = c)
21+
}
22+
23+
Function iterMethod(Class c) { methodOfClass(result, c) and result.getName() = "__iter__" }
24+
25+
Function nextMethod(Class c) { methodOfClass(result, c) and result.getName() = "__next__" }
1626

17-
predicate is_self(Name value, Function f) { value.getVariable() = f.getArg(0).(Name).getVariable() }
27+
predicate isSelfVar(Function f, Name var) { var.getVariable() = f.getArg(0).(Name).getVariable() }
28+
29+
predicate isGoodReturn(Function f, Expr e) {
30+
isSelfVar(f, e)
31+
or
32+
exists(DataFlow::CallCfgNode call, DataFlow::AttrRead read, DataFlow::Node selfNode |
33+
e = call.asExpr()
34+
|
35+
call = API::builtin("iter").getACall() and
36+
call.getArg(0) = read and
37+
read.accesses(selfNode, "__next__") and
38+
isSelfVar(f, selfNode.asExpr()) and
39+
call.getArg(1).asExpr() instanceof None
40+
)
41+
}
1842

19-
predicate returns_non_self(Function f) {
43+
predicate returnsNonSelf(Function f) {
2044
exists(f.getFallthroughNode())
2145
or
22-
exists(Return r | r.getScope() = f and not is_self(r.getValue(), f))
46+
exists(Return r | r.getScope() = f and not isGoodReturn(f, r.getValue()))
2347
or
2448
exists(Return r | r.getScope() = f and not exists(r.getValue()))
2549
}
2650

27-
from ClassValue t, Function iter
28-
where t.isIterator() and iter = iter_method(t) and returns_non_self(iter)
29-
select t, "Class " + t.getName() + " is an iterator but its $@ method does not return 'self'.",
30-
iter, iter.getName()
51+
from Class c, Function iter
52+
where
53+
exists(nextMethod(c)) and
54+
iter = iterMethod(c) and
55+
returnsNonSelf(iter)
56+
select iter, "Iter method of iterator $@ does not return `" + iter.getArg(0).getName() + "`.", c,
57+
c.getName()

0 commit comments

Comments
 (0)