Skip to content

Commit 11b5c54

Browse files
authored
Merge pull request #2820 from RasmusWL/python-modernise-statements
Python: modernise Statements/ queries
2 parents e3fed39 + b1d1974 commit 11b5c54

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+672
-347
lines changed

python/ql/src/Statements/AssertLiteralConstant.ql

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,5 @@ where
2626
value = test.(NameConstant).toString()
2727
) and
2828
/* Exclude asserts appearing at the end of a chain of `elif`s */
29-
not exists(If i |
30-
i.getElif().getAnOrelse() = a
31-
)
29+
not exists(If i | i.getElif().getAnOrelse() = a)
3230
select a, "Assert of literal constant " + value + "."

python/ql/src/Statements/AssertOnTuple.ql

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
import python
1515

1616
from Assert a, string b, string non
17-
where a.getTest() instanceof Tuple and
18-
(if exists(((Tuple)a.getTest()).getAnElt()) then
19-
(b = "True" and non = "non-")
20-
else
21-
(b = "False" and non = "")
22-
)
17+
where
18+
a.getTest() instanceof Tuple and
19+
(
20+
if exists(a.getTest().(Tuple).getAnElt())
21+
then (
22+
b = "True" and non = "non-"
23+
) else (
24+
b = "False" and non = ""
25+
)
26+
)
2327
select a, "Assertion of " + non + "empty tuple is always " + b + "."
24-

python/ql/src/Statements/BreakOrReturnInFinally.ql

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
import python
1616

1717
from Stmt s, string kind
18-
where
19-
s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s))
20-
or
21-
s instanceof Break and kind = "break" and
22-
exists(Try t | t.getFinalbody().contains(s) |
23-
not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop))
24-
and
25-
not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop))
26-
)
18+
where
19+
s instanceof Return and kind = "return" and exists(Try t | t.getFinalbody().contains(s))
20+
or
21+
s instanceof Break and
22+
kind = "break" and
23+
exists(Try t | t.getFinalbody().contains(s) |
24+
not exists(For loop | loop.contains(s) and t.getFinalbody().contains(loop)) and
25+
not exists(While loop | loop.contains(s) and t.getFinalbody().contains(loop))
26+
)
2727
select s, "'" + kind + "' in a finally block will swallow any exceptions raised."

python/ql/src/Statements/C_StyleParentheses.ql

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,22 @@
1414
import python
1515

1616
from Expr e, Location l, string kind, string what
17-
where e.isParenthesized() and
18-
not e instanceof Tuple and
19-
(
20-
exists(If i | i.getTest() = e) and kind = "if" and what = "condition"
21-
or
22-
exists(While w | w.getTest() = e) and kind = "while" and what = "condition"
23-
or
24-
exists(Return r | r.getValue() = e) and kind = "return" and what = "value"
25-
or
26-
exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and kind = "assert" and what = "test"
27-
)
28-
and
29-
// These require parentheses
30-
(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and
31-
l = e.getLocation() and l.getStartLine() = l.getEndLine()
17+
where
18+
e.isParenthesized() and
19+
not e instanceof Tuple and
20+
(
21+
exists(If i | i.getTest() = e) and kind = "if" and what = "condition"
22+
or
23+
exists(While w | w.getTest() = e) and kind = "while" and what = "condition"
24+
or
25+
exists(Return r | r.getValue() = e) and kind = "return" and what = "value"
26+
or
27+
exists(Assert a | a.getTest() = e and not exists(a.getMsg())) and
28+
kind = "assert" and
29+
what = "test"
30+
) and
31+
// These require parentheses
32+
(not e instanceof Yield and not e instanceof YieldFrom and not e instanceof GeneratorExp) and
33+
l = e.getLocation() and
34+
l.getStartLine() = l.getEndLine()
3235
select e, "Parenthesized " + what + " in '" + kind + "' statement."

python/ql/src/Statements/ConstantInConditional.ql

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,15 @@
1515

1616
import python
1717

18-
1918
predicate is_condition(Expr cond) {
20-
exists(If i | i.getTest() = cond) or
21-
exists(IfExp ie | ie.getTest() = cond)
19+
exists(If i | i.getTest() = cond) or
20+
exists(IfExp ie | ie.getTest() = cond)
2221
}
2322

2423
/* Treat certain unmodified builtins as constants as well. */
2524
predicate effective_constant(Name cond) {
2625
exists(GlobalVariable var | var = cond.getVariable() and not exists(NameNode f | f.defines(var)) |
27-
var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented"
26+
var.getId() = "True" or var.getId() = "False" or var.getId() = "NotImplemented"
2827
)
2928
}
3029

@@ -34,9 +33,10 @@ predicate test_makes_code_unreachable(Expr cond) {
3433
exists(While w | w.getTest() = cond and w.getStmt(0).isUnreachable())
3534
}
3635

37-
3836
from Expr cond
39-
where is_condition(cond) and (cond.isConstant() or effective_constant(cond)) and
40-
/* Ignore cases where test makes code unreachable, as that is handled in different query */
41-
not test_makes_code_unreachable(cond)
37+
where
38+
is_condition(cond) and
39+
(cond.isConstant() or effective_constant(cond)) and
40+
/* Ignore cases where test makes code unreachable, as that is handled in different query */
41+
not test_makes_code_unreachable(cond)
4242
select cond, "Testing a constant will always give the same result."

python/ql/src/Statements/DocStrings.ql

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
* @precision medium
1010
* @id py/missing-docstring
1111
*/
12-
/* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule.
12+
13+
/*
14+
* NOTE: precision of 'medium' reflects the lack of precision in the underlying rule.
1315
* Do we care whether a function has a docstring? That often depends on the reader of that docstring.
1416
*/
1517

@@ -18,25 +20,26 @@ import python
1820
predicate needs_docstring(Scope s) {
1921
s.isPublic() and
2022
(
21-
not s instanceof Function
22-
or
23-
function_needs_docstring(s)
23+
not s instanceof Function
24+
or
25+
function_needs_docstring(s)
2426
)
2527
}
2628

2729
predicate function_needs_docstring(Function f) {
28-
not exists(FunctionObject fo, FunctionObject base | fo.overrides(base) and fo.getFunction() = f |
29-
not function_needs_docstring(base.getFunction())) and
30+
not exists(FunctionValue fo, FunctionValue base | fo.overrides(base) and fo.getScope() = f |
31+
not function_needs_docstring(base.getScope())
32+
) and
3033
f.getName() != "lambda" and
31-
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2
32-
and not exists(PythonPropertyObject p |
34+
(f.getMetrics().getNumberOfLinesOfCode() - count(f.getADecorator())) > 2 and
35+
not exists(PythonPropertyObject p |
3336
p.getGetter().getFunction() = f or
3437
p.getSetter().getFunction() = f
3538
)
3639
}
3740

3841
string scope_type(Scope s) {
39-
result = "Module" and s instanceof Module and not ((Module)s).isPackage()
42+
result = "Module" and s instanceof Module and not s.(Module).isPackage()
4043
or
4144
result = "Class" and s instanceof Class
4245
or
@@ -46,5 +49,3 @@ string scope_type(Scope s) {
4649
from Scope s
4750
where needs_docstring(s) and not exists(s.getDocString())
4851
select s, scope_type(s) + " " + s.getName() + " does not have a docstring"
49-
50-

python/ql/src/Statements/ExecUsed.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ string message() {
1919
}
2020

2121
predicate exec_function_call(Call c) {
22-
exists(GlobalVariable exec | exec = ((Name)c.getFunc()).getVariable() and exec.getId() = "exec")
22+
exists(GlobalVariable exec | exec = c.getFunc().(Name).getVariable() and exec.getId() = "exec")
2323
}
2424

2525
from AstNode exec
2626
where exec_function_call(exec) or exec instanceof Exec
27-
select exec, message()
27+
select exec, message()

python/ql/src/Statements/IterableStringOrSequence.ql

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@
1313

1414
import python
1515

16-
predicate is_a_string_type(ClassObject seqtype) {
17-
seqtype = theBytesType() and major_version() = 2
18-
or
19-
seqtype = theUnicodeType()
16+
predicate has_string_type(Value v) {
17+
v.getClass() = ClassValue::str()
18+
or
19+
v.getClass() = ClassValue::unicode() and major_version() = 2
2020
}
2121

22-
from For loop, ControlFlowNode iter, Object str, Object seq, ControlFlowNode seq_origin, ClassObject strtype, ClassObject seqtype, ControlFlowNode str_origin
23-
where loop.getIter().getAFlowNode() = iter and
24-
iter.refersTo(str, strtype, str_origin) and
25-
iter.refersTo(seq, seqtype, seq_origin) and
26-
is_a_string_type(strtype) and
27-
seqtype.isIterable() and
28-
not is_a_string_type(seqtype)
29-
30-
select loop, "Iteration over $@, of class " + seqtype.getName() + ", may also iterate over $@.", seq_origin, "sequence", str_origin, "string"
22+
from
23+
For loop, ControlFlowNode iter, Value str, Value seq, ControlFlowNode seq_origin, ControlFlowNode str_origin
24+
where
25+
loop.getIter().getAFlowNode() = iter and
26+
iter.pointsTo(str, str_origin) and
27+
iter.pointsTo(seq, seq_origin) and
28+
has_string_type(str) and
29+
seq.getClass().isIterable() and
30+
not has_string_type(seq)
31+
select loop, "Iteration over $@, of class " + seq.getClass().getName() + ", may also iterate over $@.",
32+
seq_origin, "sequence", str_origin, "string"

python/ql/src/Statements/MismatchInMultipleAssignment.ql

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@
1414

1515
import python
1616

17-
private int len(ExprList el) {
18-
result = count(el.getAnItem())
19-
}
17+
private int len(ExprList el) { result = count(el.getAnItem()) }
2018

2119
predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequenceType) {
2220
exists(ExprList l, ExprList r |
23-
(a.getATarget().(Tuple).getElts() = l or
24-
a.getATarget().(List).getElts() = l)
25-
and
26-
((a.getValue().(Tuple).getElts() = r and sequenceType = "tuple") or
27-
(a.getValue().(List).getElts() = r and sequenceType = "list"))
28-
and
21+
(
22+
a.getATarget().(Tuple).getElts() = l or
23+
a.getATarget().(List).getElts() = l
24+
) and
25+
(
26+
a.getValue().(Tuple).getElts() = r and sequenceType = "tuple"
27+
or
28+
a.getValue().(List).getElts() = r and sequenceType = "list"
29+
) and
2930
loc = a.getValue().getLocation() and
3031
lcount = len(l) and
3132
rcount = len(r) and
@@ -35,24 +36,26 @@ predicate mismatched(Assign a, int lcount, int rcount, Location loc, string sequ
3536
}
3637

3738
predicate mismatched_tuple_rhs(Assign a, int lcount, int rcount, Location loc) {
38-
exists(ExprList l, TupleObject r, AstNode origin |
39-
(a.getATarget().(Tuple).getElts() = l or
40-
a.getATarget().(List).getElts() = l)
41-
and
42-
a.getValue().refersTo(r, origin) and
39+
exists(ExprList l, TupleValue r, AstNode origin |
40+
(
41+
a.getATarget().(Tuple).getElts() = l or
42+
a.getATarget().(List).getElts() = l
43+
) and
44+
a.getValue().pointsTo(r, origin) and
4345
loc = origin.getLocation() and
4446
lcount = len(l) and
45-
rcount = r.getLength() and
47+
rcount = r.length() and
4648
lcount != rcount and
4749
not exists(Starred s | l.getAnItem() = s)
4850
)
4951
}
5052

51-
5253
from Assign a, int lcount, int rcount, Location loc, string sequenceType
5354
where
5455
mismatched(a, lcount, rcount, loc, sequenceType)
5556
or
5657
mismatched_tuple_rhs(a, lcount, rcount, loc) and
5758
sequenceType = "tuple"
58-
select a, "Left hand side of assignment contains " + lcount + " variables, but right hand side is a $@ of length " + rcount + "." , loc, sequenceType
59+
select a,
60+
"Left hand side of assignment contains " + lcount +
61+
" variables, but right hand side is a $@ of length " + rcount + ".", loc, sequenceType

python/ql/src/Statements/ModificationOfLocals.ql

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,18 @@
1212

1313
import python
1414

15-
Object aFunctionLocalsObject() {
16-
exists(Call c, Name n, GlobalVariable v |
17-
c = result.getOrigin() and
18-
n = c.getFunc() and
19-
n.getVariable() = v and
20-
v.getId() = "locals" and
21-
c.getScope() instanceof FastLocalsFunction
22-
)
15+
predicate originIsLocals(ControlFlowNode n) {
16+
n.pointsTo(_, _, Value::named("locals").getACall())
2317
}
2418

25-
26-
2719
predicate modification_of_locals(ControlFlowNode f) {
28-
f.(SubscriptNode).getObject().refersTo(aFunctionLocalsObject()) and (f.isStore() or f.isDelete())
20+
originIsLocals(f.(SubscriptNode).getObject()) and
21+
(f.isStore() or f.isDelete())
2922
or
3023
exists(string mname, AttrNode attr |
3124
attr = f.(CallNode).getFunction() and
32-
attr.getObject(mname).refersTo(aFunctionLocalsObject(), _) |
25+
originIsLocals(attr.getObject(mname))
26+
|
3327
mname = "pop" or
3428
mname = "popitem" or
3529
mname = "update" or
@@ -39,5 +33,4 @@ predicate modification_of_locals(ControlFlowNode f) {
3933

4034
from AstNode a, ControlFlowNode f
4135
where modification_of_locals(f) and a = f.getNode()
42-
4336
select a, "Modification of the locals() dictionary will have no effect on the local variables."

0 commit comments

Comments
 (0)