Skip to content

Commit cac7758

Browse files
authored
Merge pull request #1839 from markshannon/python-rationalize-library
Python: rationalize library a bit.
2 parents 602b99e + f64f6e6 commit cac7758

File tree

8 files changed

+267
-282
lines changed

8 files changed

+267
-282
lines changed

python/ql/src/python.qll

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import semmle.python.Operations
33
import semmle.python.Variables
44
import semmle.python.AstGenerated
55
import semmle.python.AstExtended
6-
import semmle.python.AST
76
import semmle.python.Function
87
import semmle.python.Module
98
import semmle.python.Class
@@ -12,7 +11,6 @@ import semmle.python.Stmts
1211
import semmle.python.Exprs
1312
import semmle.python.Keywords
1413
import semmle.python.Comprehensions
15-
import semmle.python.Lists
1614
import semmle.python.Flow
1715
import semmle.python.Metrics
1816
import semmle.python.Constants
@@ -28,7 +26,6 @@ import semmle.python.types.Version
2826
import semmle.python.types.Descriptors
2927
import semmle.python.protocols
3028
import semmle.python.SSA
31-
import semmle.python.Assigns
3229
import semmle.python.SelfAttribute
3330
import semmle.python.types.Properties
3431
import semmle.python.xml.XML

python/ql/src/semmle/python/AST.qll

Lines changed: 0 additions & 57 deletions
This file was deleted.

python/ql/src/semmle/python/Assigns.qll

Lines changed: 0 additions & 19 deletions
This file was deleted.

python/ql/src/semmle/python/AstExtended.qll

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
11
import python
22

3+
/** Syntactic node (Class, Function, Module, Expr, Stmt or Comprehension) corresponding to a flow node */
4+
abstract class AstNode extends AstNode_ {
5+
6+
/** Gets the scope that this node occurs in */
7+
abstract Scope getScope();
8+
9+
/** Gets a flow node corresponding directly to this node.
10+
* NOTE: For some statements and other purely syntactic elements,
11+
* there may not be a `ControlFlowNode` */
12+
ControlFlowNode getAFlowNode() {
13+
py_flow_bb_node(result, this, _, _)
14+
}
15+
16+
/** Gets the location for this AST node */
17+
Location getLocation() {
18+
none()
19+
}
20+
21+
/** Whether this syntactic element is artificial, that is it is generated
22+
* by the compiler and is not present in the source */
23+
predicate isArtificial() {
24+
none()
25+
}
26+
27+
/** Gets a child node of this node in the AST. This predicate exists to aid exploration of the AST
28+
* and other experiments. The child-parent relation may not be meaningful.
29+
* For a more meaningful relation in terms of dependency use
30+
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
31+
* Scope.getAStmt().
32+
*/
33+
abstract AstNode getAChildNode();
34+
35+
/** Gets the parent node of this node in the AST. This predicate exists to aid exploration of the AST
36+
* and other experiments. The child-parent relation may not be meaningful.
37+
* For a more meaningful relation in terms of dependency use
38+
* Expr.getASubExpression(), Stmt.getASubStatement(), Stmt.getASubExpression() or
39+
* Scope.getAStmt() applied to the parent.
40+
*/
41+
AstNode getParentNode() {
42+
result.getAChildNode() = this
43+
}
44+
45+
/** Whether this contains `inner` syntactically */
46+
predicate contains(AstNode inner) {
47+
this.getAChildNode+() = inner
48+
}
49+
50+
/** Whether this contains `inner` syntactically and `inner` has the same scope as `this` */
51+
predicate containsInScope(AstNode inner) {
52+
this.contains(inner) and
53+
this.getScope() = inner.getScope() and
54+
not inner instanceof Scope
55+
}
56+
57+
}
58+
359
/* Parents */
460

561
/** Internal implementation class */
@@ -116,3 +172,59 @@ class StringPartList extends StringPartList_ {
116172

117173
}
118174

175+
/* **** Lists ***/
176+
177+
/** A parameter list */
178+
class ParameterList extends @py_parameter_list {
179+
180+
Function getParent() {
181+
py_parameter_lists(this, result)
182+
}
183+
184+
/** Gets a parameter */
185+
Parameter getAnItem() {
186+
/* Item can be a Name or a Tuple, both of which are expressions */
187+
py_exprs(result, _, this, _)
188+
}
189+
190+
/** Gets the nth parameter */
191+
Parameter getItem(int index) {
192+
/* Item can be a Name or a Tuple, both of which are expressions */
193+
py_exprs(result, _, this, index)
194+
}
195+
196+
string toString() {
197+
result = "ParameterList"
198+
}
199+
}
200+
201+
/** A list of Comprehensions (for generating parts of a set, list or dictionary comprehension) */
202+
class ComprehensionList extends ComprehensionList_ {
203+
204+
}
205+
206+
/** A list of expressions */
207+
class ExprList extends ExprList_ {
208+
209+
}
210+
211+
212+
library class DictItemList extends DictItemList_ {
213+
214+
}
215+
216+
library class DictItemListParent extends DictItemListParent_ {
217+
218+
}
219+
220+
/** A list of strings (the primitive type string not Bytes or Unicode) */
221+
class StringList extends StringList_ {
222+
223+
}
224+
225+
/** A list of aliases in an import statement */
226+
class AliasList extends AliasList_ {
227+
228+
}
229+
230+

python/ql/src/semmle/python/Flow.qll

Lines changed: 143 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import python
2-
import semmle.python.flow.NameNode
32
private import semmle.python.pointsto.PointsTo
43

54
/* Note about matching parent and child nodes and CFG splitting:
@@ -961,10 +960,149 @@ class RaiseStmtNode extends ControlFlowNode {
961960

962961
}
963962

964-
private
965-
predicate defined_by(NameNode def, Variable v) {
966-
def.defines(v) or
967-
exists(NameNode p | defined_by(p, v) and p.getASuccessor() = def and not p.defines(v))
963+
/** A control flow node corresponding to a (plain variable) name expression, such as `var`.
964+
* `None`, `True` and `False` are excluded.
965+
*/
966+
class NameNode extends ControlFlowNode {
967+
968+
NameNode() {
969+
exists(Name n | py_flow_bb_node(this, n, _, _))
970+
or
971+
exists(PlaceHolder p | py_flow_bb_node(this, p, _, _))
972+
}
973+
974+
/** Whether this flow node defines the variable `v`. */
975+
predicate defines(Variable v) {
976+
exists(Name d | this.getNode() = d and d.defines(v))
977+
and not this.isLoad()
978+
}
979+
980+
/** Whether this flow node deletes the variable `v`. */
981+
predicate deletes(Variable v) {
982+
exists(Name d | this.getNode() = d and d.deletes(v))
983+
}
984+
985+
/** Whether this flow node uses the variable `v`. */
986+
predicate uses(Variable v) {
987+
this.isLoad() and exists(Name u | this.getNode() = u and u.uses(v))
988+
or
989+
exists(PlaceHolder u | this.getNode() = u and u.getVariable() = v and u.getCtx() instanceof Load)
990+
or
991+
Scopes::use_of_global_variable(this, v.getScope(), v.getId())
992+
}
993+
994+
string getId() {
995+
result = this.getNode().(Name).getId()
996+
or
997+
result = this.getNode().(PlaceHolder).getId()
998+
}
999+
1000+
/** Whether this is a use of a local variable. */
1001+
predicate isLocal() {
1002+
Scopes::local(this)
1003+
}
1004+
1005+
/** Whether this is a use of a non-local variable. */
1006+
predicate isNonLocal() {
1007+
Scopes::non_local(this)
1008+
}
1009+
1010+
/** Whether this is a use of a global (including builtin) variable. */
1011+
predicate isGlobal() {
1012+
Scopes::use_of_global_variable(this, _, _)
1013+
}
1014+
1015+
predicate isSelf() {
1016+
exists(SsaVariable selfvar |
1017+
selfvar.isSelf() and selfvar.getAUse() = this
1018+
)
1019+
}
1020+
1021+
}
1022+
1023+
/** A control flow node corresponding to a named constant, one of `None`, `True` or `False`. */
1024+
class NameConstantNode extends NameNode {
1025+
1026+
NameConstantNode() {
1027+
exists(NameConstant n | py_flow_bb_node(this, n, _, _))
1028+
}
1029+
1030+
override deprecated predicate defines(Variable v) { none() }
1031+
1032+
override deprecated predicate deletes(Variable v) { none() }
1033+
1034+
/* We ought to override uses as well, but that has
1035+
* a serious performance impact.
1036+
deprecated predicate uses(Variable v) { none() }
1037+
*/
1038+
}
1039+
1040+
private module Scopes {
1041+
1042+
private predicate fast_local(NameNode n) {
1043+
exists(FastLocalVariable v |
1044+
n.uses(v) and
1045+
v.getScope() = n.getScope()
1046+
)
1047+
}
1048+
1049+
predicate local(NameNode n) {
1050+
fast_local(n)
1051+
or
1052+
exists(SsaVariable var |
1053+
var.getAUse() = n and
1054+
n.getScope() instanceof Class and
1055+
exists(var.getDefinition())
1056+
)
1057+
}
1058+
1059+
predicate non_local(NameNode n) {
1060+
exists(FastLocalVariable flv |
1061+
flv.getALoad() = n.getNode() and
1062+
not flv.getScope() = n.getScope()
1063+
)
1064+
}
1065+
1066+
// magic is fine, but we get questionable join-ordering of it
1067+
pragma [nomagic]
1068+
predicate use_of_global_variable(NameNode n, Module scope, string name) {
1069+
n.isLoad() and
1070+
not non_local(n)
1071+
and
1072+
not exists(SsaVariable var |
1073+
var.getAUse() = n |
1074+
var.getVariable() instanceof FastLocalVariable
1075+
or
1076+
n.getScope() instanceof Class and
1077+
not maybe_undefined(var)
1078+
)
1079+
and name = n.getId()
1080+
and scope = n.getEnclosingModule()
1081+
}
1082+
1083+
private predicate maybe_defined(SsaVariable var) {
1084+
exists(var.getDefinition()) and not py_ssa_phi(var, _) and not var.getDefinition().isDelete()
1085+
or
1086+
exists(SsaVariable input |
1087+
input = var.getAPhiInput() |
1088+
maybe_defined(input)
1089+
)
1090+
}
1091+
1092+
private predicate maybe_undefined(SsaVariable var) {
1093+
not exists(var.getDefinition()) and not py_ssa_phi(var, _)
1094+
or
1095+
var.getDefinition().isDelete()
1096+
or
1097+
maybe_undefined(var.getAPhiInput())
1098+
or
1099+
exists(BasicBlock incoming |
1100+
exists(var.getAPhiInput()) and
1101+
incoming.getASuccessor() = var.getDefinition().getBasicBlock() and
1102+
not var.getAPhiInput().getDefinition().getBasicBlock().dominates(incoming)
1103+
)
1104+
}
1105+
9681106
}
9691107

9701108
/** A basic block (ignoring exceptional flow edges to scope exit) */

0 commit comments

Comments
 (0)