Skip to content

Commit d0eaa13

Browse files
committed
Python: Magic -> Special and reaarange classes
1 parent b9bf11a commit d0eaa13

File tree

3 files changed

+132
-127
lines changed

3 files changed

+132
-127
lines changed

python/ql/src/experimental/dataflow/internal/DataFlowPrivate.qll

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
private import python
22
private import DataFlowPublic
3-
import semmle.python.Magic
3+
import semmle.python.SpecialMethods
44

55
//--------
66
// Data flow graph
@@ -160,7 +160,7 @@ class DataFlowClassValue extends DataFlowCallable, TClassValue {
160160

161161
newtype TDataFlowCall =
162162
TCallNode(CallNode call) or
163-
TMagicCall(MagicMethod::Actual magic)
163+
TSpecialCall(SpecialMethodCallNode special)
164164

165165
abstract class DataFlowCall extends TDataFlowCall {
166166
/** Gets a textual representation of this element. */
@@ -179,7 +179,6 @@ abstract class DataFlowCall extends TDataFlowCall {
179179
abstract DataFlowCallable getEnclosingCallable();
180180
}
181181

182-
183182
/** Represents a call to a callable. */
184183
class CallNodeCall extends DataFlowCall, TCallNode {
185184
CallNode call;
@@ -192,9 +191,7 @@ class CallNodeCall extends DataFlowCall, TCallNode {
192191

193192
override string toString() { result = call.toString() }
194193

195-
override ControlFlowNode getArg(int n) {
196-
result = call.getArg(n)
197-
}
194+
override ControlFlowNode getArg(int n) { result = call.getArg(n) }
198195

199196
override ControlFlowNode getNode() { result = call }
200197

@@ -203,22 +200,24 @@ class CallNodeCall extends DataFlowCall, TCallNode {
203200
override DataFlowCallable getEnclosingCallable() { result.getScope() = call.getNode().getScope() }
204201
}
205202

206-
class MagicCall extends DataFlowCall, TMagicCall {
207-
MagicMethod::Actual magic;
203+
class SpecialCall extends DataFlowCall, TSpecialCall {
204+
SpecialMethodCallNode special;
208205

209-
MagicCall() { this = TMagicCall(magic) }
206+
SpecialCall() { this = TSpecialCall(special) }
210207

211-
override string toString() { result = magic.toString() }
208+
override string toString() { result = special.toString() }
212209

213-
override ControlFlowNode getArg(int n) {
214-
result = magic.(MagicMethod::Potential).getArg(n)
215-
}
210+
override ControlFlowNode getArg(int n) { result = special.(SpecialMethod::Potential).getArg(n) }
216211

217-
override ControlFlowNode getNode() { result = magic }
212+
override ControlFlowNode getNode() { result = special }
218213

219-
override DataFlowCallable getCallable() { result = TCallableValue(magic.getResolvedMagicMethod()) }
214+
override DataFlowCallable getCallable() {
215+
result = TCallableValue(special.getResolvedSpecialMethod())
216+
}
220217

221-
override DataFlowCallable getEnclosingCallable() { result.getScope() = magic.getNode().getScope() }
218+
override DataFlowCallable getEnclosingCallable() {
219+
result.getScope() = special.getNode().getScope()
220+
}
222221
}
223222

224223
/** A data flow node that represents a call argument. */

python/ql/src/semmle/python/Magic.qll

Lines changed: 0 additions & 111 deletions
This file was deleted.
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/**
2+
* Provides support for special methods.
3+
* This is done in two steps:
4+
* - A subset of `ControlFlowNode`s are labelled as potentially corresponding to
5+
* a special method call (by being an instance of `SpecialMethod::Potential`).
6+
* - A subset of the potential special method calls are labelled as being actual
7+
* special method calls (`SpecialMethodCallNode`) if the appropriate method is defined.
8+
* Extend `SpecialMethod::Potential` to capture more cases.
9+
*/
10+
11+
import python
12+
13+
14+
/** A control flow node which might correpsond to a special method call. */
15+
class PotentialSpecialMethodCallNode extends ControlFlowNode {
16+
PotentialSpecialMethodCallNode() { this instanceof SpecialMethod::Potential}
17+
}
18+
19+
/**
20+
* Machinery for detecting special method calls.
21+
* Extend `SpecialMethod::Potential` to capture more cases.
22+
*/
23+
module SpecialMethod {
24+
/** A control flow node which might correpsond to a special method call. */
25+
abstract class Potential extends ControlFlowNode {
26+
/** Gets the name of the method that would be called. */
27+
abstract string getSpecialMethodName();
28+
29+
/** Gets the controlflow node that would be passed as the specified argument. */
30+
abstract ControlFlowNode getArg(int n);
31+
32+
/**
33+
* Gets the control flow node corresponding to the instance
34+
* that would define the special method.
35+
*/
36+
ControlFlowNode getSelf() { result = this.getArg(0) }
37+
}
38+
39+
/** A binary expression node that might correspond to a special method call. */
40+
class SpecialBinOp extends Potential, BinaryExprNode {
41+
Operator operator;
42+
43+
SpecialBinOp() { this.getOp() = operator }
44+
45+
override string getSpecialMethodName() { result = operator.getSpecialMethodName() }
46+
47+
override ControlFlowNode getArg(int n) {
48+
n = 0 and result = this.getLeft()
49+
or
50+
n = 1 and result = this.getRight()
51+
}
52+
}
53+
54+
/** A subscript expression node that might correspond to a special method call. */
55+
abstract class SpecialSubscript extends Potential, SubscriptNode {
56+
override ControlFlowNode getArg(int n) {
57+
n = 0 and result = this.getObject()
58+
or
59+
n = 1 and result = this.getIndex()
60+
}
61+
}
62+
63+
/** A subscript expression node that might correspond to a call to __getitem__. */
64+
class SpecialGetItem extends SpecialSubscript {
65+
SpecialGetItem() { this.isLoad() }
66+
67+
override string getSpecialMethodName() { result = "__getitem__" }
68+
}
69+
70+
/** A subscript expression node that might correspond to a call to __setitem__. */
71+
class SpecialSetItem extends SpecialSubscript {
72+
SpecialSetItem() { this.isStore() }
73+
74+
override string getSpecialMethodName() { result = "__setitem__" }
75+
76+
override ControlFlowNode getArg(int n) {
77+
n = 0 and result = this.getObject()
78+
or
79+
n = 1 and result = this.getIndex()
80+
or
81+
n = 2 and result = this.getValueNode()
82+
}
83+
84+
private ControlFlowNode getValueNode() {
85+
exists(AssignStmt a |
86+
a.getATarget() = this.getNode() and
87+
result.getNode() = a.getValue()
88+
)
89+
or
90+
exists(AugAssign a |
91+
a.getTarget() = this.getNode() and
92+
result.getNode() = a.getValue()
93+
)
94+
}
95+
}
96+
97+
/** A subscript expression node that might correspond to a call to __delitem__. */
98+
class SpecialDelItem extends SpecialSubscript {
99+
SpecialDelItem() { this.isDelete() }
100+
101+
override string getSpecialMethodName() { result = "__delitem__" }
102+
}
103+
}
104+
105+
class SpecialMethodCallNode extends PotentialSpecialMethodCallNode {
106+
Value resolvedSpecialMethod;
107+
108+
SpecialMethodCallNode() {
109+
exists(SpecialMethod::Potential pot |
110+
this.(SpecialMethod::Potential) = pot and
111+
pot.getSelf().pointsTo().getClass().lookup(pot.getSpecialMethodName()) = resolvedSpecialMethod
112+
)
113+
}
114+
115+
/** The method that is called. */
116+
Value getResolvedSpecialMethod() { result = resolvedSpecialMethod }
117+
}

0 commit comments

Comments
 (0)