Skip to content

Commit 436fe65

Browse files
authored
Merge pull request #10155 from MathiasVP/swift-properties-as-callables
Swift: Model property getters, setters and observers as callables
2 parents 20ac15d + 06a39d2 commit 436fe65

File tree

7 files changed

+212
-97
lines changed

7 files changed

+212
-97
lines changed

swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ class CfgNode extends ControlFlowNode, TElementNode {
9393
final Split getASplit() { result = splits.getASplit() }
9494
}
9595

96+
private Expr getAst(ControlFlowElement n) {
97+
result = n.asAstNode()
98+
or
99+
result = n.(PropertyGetterElement).getRef()
100+
or
101+
result = n.(PropertySetterElement).getAssignExpr()
102+
or
103+
result = n.(PropertyObserverElement).getAssignExpr()
104+
or
105+
result = n.(ClosureElement).getAst()
106+
or
107+
result = n.(KeyPathElement).getAst()
108+
}
109+
96110
/** A control-flow node that wraps an AST expression. */
97111
class ExprCfgNode extends CfgNode {
98112
Expr e;
@@ -108,21 +122,43 @@ class PropertyGetterCfgNode extends CfgNode {
108122
override PropertyGetterElement n;
109123

110124
Expr getRef() { result = n.getRef() }
125+
126+
CfgNode getBase() { getAst(result.getNode()) = n.getBase() }
127+
128+
AccessorDecl getAccessorDecl() { result = n.getAccessorDecl() }
111129
}
112130

113131
/** A control-flow node that wraps a property setter. */
114132
class PropertySetterCfgNode extends CfgNode {
115133
override PropertySetterElement n;
116134

117135
AssignExpr getAssignExpr() { result = n.getAssignExpr() }
136+
137+
CfgNode getBase() { getAst(result.getNode()) = n.getBase() }
138+
139+
CfgNode getSource() { getAst(result.getNode()) = n.getAssignExpr().getSource() }
140+
141+
AccessorDecl getAccessorDecl() { result = n.getAccessorDecl() }
142+
}
143+
144+
class PropertyObserverCfgNode extends CfgNode {
145+
override PropertyObserverElement n;
146+
147+
AssignExpr getAssignExpr() { result = n.getAssignExpr() }
148+
149+
CfgNode getBase() { getAst(result.getNode()) = n.getBase() }
150+
151+
CfgNode getSource() { getAst(result.getNode()) = n.getAssignExpr().getSource() }
152+
153+
AccessorDecl getAccessorDecl() { result = n.getObserver() }
118154
}
119155

120156
class ApplyExprCfgNode extends ExprCfgNode {
121157
override ApplyExpr e;
122158

123-
ExprCfgNode getArgument(int index) {
124-
result.getNode().asAstNode() = e.getArgument(index).getExpr()
125-
}
159+
CfgNode getArgument(int index) { getAst(result.getNode()) = e.getArgument(index).getExpr() }
160+
161+
CfgNode getQualifier() { getAst(result.getNode()) = e.getQualifier() }
126162

127163
AbstractFunctionDecl getStaticTarget() { result = e.getStaticTarget() }
128164

swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ class PropertyGetterElement extends ControlFlowElement, TPropertyGetterElement {
123123
Expr getRef() { result = ref }
124124

125125
AccessorDecl getAccessorDecl() { result = accessor }
126+
127+
Expr getBase() { result = ref.(LookupExpr).getBase() }
126128
}
127129

128130
class PropertySetterElement extends ControlFlowElement, TPropertySetterElement {
@@ -138,6 +140,8 @@ class PropertySetterElement extends ControlFlowElement, TPropertySetterElement {
138140
AccessorDecl getAccessorDecl() { result = accessor }
139141

140142
AssignExpr getAssignExpr() { result = assign }
143+
144+
Expr getBase() { result = assign.getDest().(LookupExpr).getBase() }
141145
}
142146

143147
class PropertyObserverElement extends ControlFlowElement, TPropertyObserverElement {
@@ -163,6 +167,8 @@ class PropertyObserverElement extends ControlFlowElement, TPropertyObserverEleme
163167
predicate isDidSet() { observer.isDidSet() }
164168

165169
AssignExpr getAssignExpr() { result = assign }
170+
171+
Expr getBase() { result = assign.getDest().(LookupExpr).getBase() }
166172
}
167173

168174
class FuncDeclElement extends ControlFlowElement, TFuncDeclElement {

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowDispatch.qll

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ class DataFlowCallable extends TDataFlowCallable {
6969
cached
7070
newtype TDataFlowCall =
7171
TNormalCall(ApplyExprCfgNode call) or
72+
TPropertyGetterCall(PropertyGetterCfgNode getter) or
73+
TPropertySetterCall(PropertySetterCfgNode setter) or
74+
TPropertyObserverCall(PropertyObserverCfgNode obserer) or
7275
TSummaryCall(FlowSummaryImpl::Public::SummarizedCallable c, Node receiver) {
7376
FlowSummaryImpl::Private::summaryCallbackRange(c, receiver)
7477
}
@@ -84,6 +87,14 @@ class DataFlowCall extends TDataFlowCall {
8487
/** Gets the underlying source code call, if any. */
8588
ApplyExprCfgNode asCall() { none() }
8689

90+
/**
91+
* Gets the i'th argument of call.class
92+
* The qualifier is considered to have index `-1`.
93+
*/
94+
CfgNode getArgument(int i) { none() }
95+
96+
final CfgNode getAnArgument() { result = this.getArgument(_) }
97+
8798
/** Gets a textual representation of this call. */
8899
string toString() { none() }
89100

@@ -111,13 +122,92 @@ private class NormalCall extends DataFlowCall, TNormalCall {
111122

112123
override ApplyExprCfgNode asCall() { result = apply }
113124

125+
override CfgNode getArgument(int i) {
126+
i = -1 and
127+
result = apply.getQualifier()
128+
or
129+
result = apply.getArgument(i)
130+
}
131+
114132
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(apply.getScope()) }
115133

116134
override string toString() { result = apply.toString() }
117135

118136
override Location getLocation() { result = apply.getLocation() }
119137
}
120138

139+
class PropertyGetterCall extends DataFlowCall, TPropertyGetterCall {
140+
private PropertyGetterCfgNode getter;
141+
142+
PropertyGetterCall() { this = TPropertyGetterCall(getter) }
143+
144+
override CfgNode getArgument(int i) {
145+
i = -1 and
146+
result = getter.getBase()
147+
}
148+
149+
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(getter.getScope()) }
150+
151+
PropertyGetterCfgNode getGetter() { result = getter }
152+
153+
override string toString() { result = getter.toString() }
154+
155+
override Location getLocation() { result = getter.getLocation() }
156+
157+
AccessorDecl getAccessorDecl() { result = getter.getAccessorDecl() }
158+
}
159+
160+
class PropertySetterCall extends DataFlowCall, TPropertySetterCall {
161+
private PropertySetterCfgNode setter;
162+
163+
PropertySetterCall() { this = TPropertySetterCall(setter) }
164+
165+
override CfgNode getArgument(int i) {
166+
i = -1 and
167+
result = setter.getBase()
168+
or
169+
i = 0 and
170+
result = setter.getSource()
171+
}
172+
173+
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(setter.getScope()) }
174+
175+
PropertySetterCfgNode getSetter() { result = setter }
176+
177+
override string toString() { result = setter.toString() }
178+
179+
override Location getLocation() { result = setter.getLocation() }
180+
181+
AccessorDecl getAccessorDecl() { result = setter.getAccessorDecl() }
182+
}
183+
184+
class PropertyObserverCall extends DataFlowCall, TPropertyObserverCall {
185+
private PropertyObserverCfgNode observer;
186+
187+
PropertyObserverCall() { this = TPropertyObserverCall(observer) }
188+
189+
override CfgNode getArgument(int i) {
190+
i = -1 and
191+
result = observer.getBase()
192+
or
193+
// TODO: This is correct for `willSet` (which takes a `newValue` parameter),
194+
// but for `didSet` (which takes an `oldValue` paramter) we need an rvalue
195+
// for `getBase()`.
196+
i = 0 and
197+
result = observer.getSource()
198+
}
199+
200+
override DataFlowCallable getEnclosingCallable() { result = TDataFlowFunc(observer.getScope()) }
201+
202+
PropertyObserverCfgNode getObserver() { result = observer }
203+
204+
override string toString() { result = observer.toString() }
205+
206+
override Location getLocation() { result = observer.getLocation() }
207+
208+
AccessorDecl getAccessorDecl() { result = observer.getAccessorDecl() }
209+
}
210+
121211
class SummaryCall extends DataFlowCall, TSummaryCall {
122212
private FlowSummaryImpl::Public::SummarizedCallable c;
123213
private Node receiver;
@@ -145,6 +235,12 @@ private module Cached {
145235
cached
146236
DataFlowCallable viableCallable(DataFlowCall call) {
147237
result = TDataFlowFunc(call.asCall().getStaticTarget())
238+
or
239+
result = TDataFlowFunc(call.(PropertyGetterCall).getAccessorDecl())
240+
or
241+
result = TDataFlowFunc(call.(PropertySetterCall).getAccessorDecl())
242+
or
243+
result = TDataFlowFunc(call.(PropertyObserverCall).getAccessorDecl())
148244
}
149245

150246
cached

swift/ql/lib/codeql/swift/dataflow/internal/DataFlowPrivate.qll

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ private module ParameterNodes {
154154
predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) { none() }
155155
}
156156

157-
class NormalParameterNode extends ParameterNodeImpl, SsaDefinitionNode {
157+
class NormalParameterNode extends ParameterNodeImpl, SsaDefinitionNodeImpl {
158158
ParamDecl param;
159159

160160
NormalParameterNode() {
@@ -169,10 +169,10 @@ private module ParameterNodes {
169169
override string toStringImpl() { result = param.toString() }
170170

171171
override predicate isParameterOf(DataFlowCallable c, ParameterPosition pos) {
172-
exists(Callable f, int index |
173-
c = TDataFlowFunc(f) and
174-
f.getParam(index) = param and
175-
pos = TPositionalParameter(index)
172+
exists(Callable f | c = TDataFlowFunc(f) |
173+
exists(int index | f.getParam(index) = param and pos = TPositionalParameter(index))
174+
or
175+
f.getSelfParam() = param and pos = TThisParameter()
176176
)
177177
}
178178

@@ -207,11 +207,68 @@ abstract class ArgumentNode extends Node {
207207

208208
private module ArgumentNodes {
209209
class NormalArgumentNode extends ExprNode, ArgumentNode {
210-
NormalArgumentNode() { exists(ApplyExpr call | call.getAnArgument().getExpr() = this.asExpr()) }
210+
NormalArgumentNode() { exists(DataFlowCall call | call.getAnArgument() = this.getCfgNode()) }
211211

212212
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
213-
call.asCall().getArgument(pos.(PositionalArgumentPosition).getIndex()).getExpr() =
214-
this.asExpr()
213+
call.getArgument(pos.(PositionalArgumentPosition).getIndex()) = this.getCfgNode()
214+
or
215+
pos = TThisArgument() and
216+
call.getArgument(-1) = this.getCfgNode()
217+
}
218+
}
219+
220+
class PropertyGetterArgumentNode extends ExprNode, ArgumentNode {
221+
private PropertyGetterCfgNode getter;
222+
223+
PropertyGetterArgumentNode() { getter.getBase() = this.getCfgNode() }
224+
225+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
226+
call.(PropertyGetterCall).getGetter() = getter and
227+
pos = TThisArgument()
228+
}
229+
}
230+
231+
class SetterArgumentNode extends ExprNode, ArgumentNode {
232+
private PropertySetterCfgNode setter;
233+
234+
SetterArgumentNode() {
235+
setter.getBase() = this.getCfgNode() or
236+
setter.getSource() = this.getCfgNode()
237+
}
238+
239+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
240+
call.(PropertySetterCall).getSetter() = setter and
241+
(
242+
pos = TThisArgument() and
243+
setter.getBase() = this.getCfgNode()
244+
or
245+
pos.(PositionalArgumentPosition).getIndex() = 0 and
246+
setter.getSource() = this.getCfgNode()
247+
)
248+
}
249+
}
250+
251+
class ObserverArgumentNode extends ExprNode, ArgumentNode {
252+
private PropertyObserverCfgNode observer;
253+
254+
ObserverArgumentNode() {
255+
observer.getBase() = this.getCfgNode()
256+
or
257+
// TODO: This should be an rvalue representing the `getBase` when
258+
// `observer` a `didSet` observer.
259+
observer.getSource() = this.getCfgNode()
260+
}
261+
262+
override predicate argumentOf(DataFlowCall call, ArgumentPosition pos) {
263+
call.(PropertySetterCall).getSetter() = observer and
264+
(
265+
pos = TThisArgument() and
266+
observer.getBase() = this.getCfgNode()
267+
or
268+
// TODO: See the comment above for `didSet` observers.
269+
pos.(PositionalArgumentPosition).getIndex() = 0 and
270+
observer.getSource() = this.getCfgNode()
271+
)
215272
}
216273
}
217274
}

0 commit comments

Comments
 (0)