Skip to content

Commit 81e27aa

Browse files
committed
Python: Modernise py/unused-loop-variable
1 parent 8f7ba0a commit 81e27aa

3 files changed

Lines changed: 31 additions & 18 deletions

File tree

python/ql/src/Variables/SuspiciousUnusedLoopIterationVariable.ql

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Definition
1515

1616
predicate is_increment(Stmt s) {
1717
/* x += n */
18-
s.(AugAssign).getValue() instanceof IntegerLiteral
18+
s.(AugAssign).getValue() instanceof IntegerLiteral
1919
or
2020
/* x = x + n */
2121
exists(Name t, BinaryExpr add |
@@ -36,7 +36,7 @@ predicate empty_loop(For f) {
3636

3737
predicate one_item_only(For f) {
3838
not exists(Continue c | f.contains(c)) and
39-
exists(Stmt s |
39+
exists(Stmt s |
4040
s = f.getBody().getLastItem() |
4141
s instanceof Return
4242
or
@@ -45,13 +45,13 @@ predicate one_item_only(For f) {
4545
}
4646

4747
predicate points_to_call_to_range(ControlFlowNode f) {
48-
/* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */
49-
exists(Object range, Object call |
50-
range = Object::builtin("range") or
51-
range = Object::builtin("xrange")
48+
/* (x)range is a function in Py2 and a class in Py3, so we must treat it as a plain object */
49+
exists(Value range, Value call |
50+
range = Value::named("range") or
51+
range = Value::named("xrange")
5252
|
53-
f.refersTo(call) and
54-
call.(CallNode).getFunction().refersTo(range)
53+
f.pointsTo(call) and
54+
call.getACall().getFunction().pointsTo(range)
5555
)
5656
or
5757
/* In case points-to fails due to 'from six.moves import range' or similar. */
@@ -60,11 +60,10 @@ predicate points_to_call_to_range(ControlFlowNode f) {
6060
range = "range" or range = "xrange"
6161
)
6262
or
63-
/* If range is wrapped in a list it is still a range */
64-
exists(CallNode call |
65-
f.refersTo(call) and
66-
call = theListType().getACall() and
67-
points_to_call_to_range(call.getArg(0))
63+
/* Handle list(range(...)) and list(list(range(...))) */
64+
(
65+
f.(CallNode).pointsTo().getClass() = ClassValue::list() and
66+
points_to_call_to_range(f.(CallNode).getArg(0))
6867
)
6968
}
7069

@@ -100,7 +99,7 @@ predicate implicit_repeat(For f) {
10099
* E.g. gets `x` from `{ y for y in x }`.
101100
*/
102101
ControlFlowNode get_comp_iterable(For f) {
103-
exists(Comp c |
102+
exists(Comp c |
104103
c.getFunction().getStmt(0) = f |
105104
c.getAFlowNode().getAPredecessor() = result
106105
)

python/ql/src/semmle/python/objects/ObjectAPI.qll

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ module Value {
211211
}
212212

213213
/** Gets the `Value` for the integer constant `i`, if it exists.
214-
* There will be no `Value` for most integers, but the following are
214+
* There will be no `Value` for most integers, but the following are
215215
* guaranteed to exist:
216216
* * From zero to 511 inclusive.
217217
* * All powers of 2 (up to 2**30)
@@ -634,6 +634,11 @@ module ClassValue {
634634
result = TBuiltinClassObject(Builtin::special("float"))
635635
}
636636

637+
/** Get the `ClassValue` for the `list` class. */
638+
ClassValue list() {
639+
result = TBuiltinClassObject(Builtin::special("list"))
640+
}
641+
637642
/** Get the `ClassValue` for the `bytes` class (also called `str` in Python 2). */
638643
ClassValue bytes() {
639644
result = TBuiltinClassObject(Builtin::special("bytes"))

python/ql/test/query-tests/Variables/unused/test.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def OK1(seq):
1010
for _ in seq:
1111
do_something()
1212
print("Hi")
13-
13+
1414
#OK counting
1515
def OK2(seq):
1616
i = 3
@@ -29,7 +29,7 @@ def OK4(n):
2929
r = range(n)
3030
for i in r:
3131
print("x")
32-
32+
3333
#OK named as unused
3434
def OK5(seq):
3535
for unused_x in seq:
@@ -77,7 +77,7 @@ def fail4(coll, sequence):
7777
x = coll.pop()
7878
for s in sequence:
7979
do_something(x+1)
80-
80+
8181
#OK See ODASA-4153 and ODASA-4533
8282
def fail5(t):
8383
x, y = t
@@ -106,3 +106,12 @@ def cleanup(sessions):
106106
for sess in sessions:
107107
# Original code had some comment about deleting sessions
108108
del sess
109+
110+
# For SuspiciousUnusedLoopIterationVariable.ql
111+
# ok
112+
for x in list(range(100)):
113+
print('hi')
114+
115+
# ok
116+
for y in list(list(range(100))):
117+
print('hi')

0 commit comments

Comments
 (0)