Skip to content

Commit 6cb9198

Browse files
committed
Swift: Control flow through #available.
1 parent 38f4f65 commit 6cb9198

File tree

3 files changed

+227
-2
lines changed

3 files changed

+227
-2
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,8 @@ private predicate inBooleanContext(ControlFlowElement n) {
122122
private predicate astInBooleanContext(AstNode n) {
123123
n = any(ConditionElement condElem).getBoolean().getFullyUnresolved()
124124
or
125+
n = any(ConditionElement condElem).getAvailability().getFullyUnresolved()
126+
or
125127
n = any(StmtCondition stmtCond).getFullyUnresolved()
126128
or
127129
exists(RepeatWhileStmt repeat | n = repeat.getCondition().getFullyConverted())

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

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,15 +249,23 @@ module Stmts {
249249
child.asAstNode() = ast.getAnElement().getPattern().getFullyUnresolved()
250250
or
251251
child.asAstNode() = ast.getAnElement().getBoolean().getFullyConverted()
252+
or
253+
child.asAstNode() = ast.getAnElement().getAvailability().getFullyUnresolved()
252254
}
253255

254256
predicate firstElement(int i, ControlFlowElement first) {
255257
// If there is an initializer in the first element, evaluate that first
256258
astFirst(ast.getElement(i).getInitializer().getFullyConverted(), first)
257259
or
258-
// Otherwise, the first element is a boolean condition.
260+
// Otherwise, the first element is...
259261
not exists(ast.getElement(i).getInitializer()) and
260-
astFirst(ast.getElement(i).getBoolean().getFullyConverted(), first)
262+
(
263+
// ... a boolean condition.
264+
astFirst(ast.getElement(i).getBoolean().getFullyConverted(), first)
265+
or
266+
// ... or an availability check ...
267+
astFirst(ast.getElement(i).getAvailability().getFullyUnresolved(), first)
268+
)
261269
}
262270

263271
predicate succElement(int i, ControlFlowElement pred, ControlFlowElement succ, Completion c) {
@@ -272,6 +280,9 @@ module Stmts {
272280
or
273281
// ... or the boolean ...
274282
astLast(ast.getElement(i).getBoolean().getFullyConverted(), pred, c)
283+
or
284+
// ... or the availability check ...
285+
astLast(ast.getElement(i).getAvailability().getFullyUnresolved(), pred, c)
275286
) and
276287
// We evaluate the next element
277288
c instanceof NormalCompletion and
@@ -287,11 +298,17 @@ module Stmts {
287298
astLast(ast.getAnElement().getPattern().getFullyUnresolved(), last, c) and
288299
not c.(MatchingCompletion).isMatch()
289300
or
301+
// Stop if an availability check failed
302+
astLast(ast.getAnElement().getAvailability().getFullyUnresolved(), last, c) and
303+
c instanceof FalseCompletion
304+
or
290305
// Stop if we successfully evaluated all the conditionals
291306
(
292307
astLast(ast.getLastElement().getBoolean().getFullyConverted(), last, c)
293308
or
294309
astLast(ast.getLastElement().getPattern().getFullyUnresolved(), last, c)
310+
or
311+
astLast(ast.getLastElement().getAvailability().getFullyUnresolved(), last, c)
295312
) and
296313
c instanceof NormalCompletion
297314
}
@@ -1770,6 +1787,20 @@ module Exprs {
17701787
}
17711788
}
17721789

1790+
module AvailabilityInfo {
1791+
private class AvailabilityInfoTree extends AstStandardPostOrderTree {
1792+
override AvailabilityInfo ast;
1793+
1794+
final override ControlFlowElement getChildElement(int i) {
1795+
result.asAstNode() = ast.getSpec(i).getFullyUnresolved()
1796+
}
1797+
}
1798+
1799+
private class AvailabilitySpecTree extends AstLeafTree {
1800+
override AvailabilitySpec ast;
1801+
}
1802+
}
1803+
17731804
private Scope parent(Scope n) {
17741805
result = n.getOuterScope() and
17751806
not n instanceof CfgScope::Range_

swift/ql/test/library-tests/controlflow/graph/Cfg.expected

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6329,6 +6329,11 @@ cfg.swift:
63296329
# 496| enter testAvailable()
63306330
#-----| -> testAvailable()
63316331

6332+
# 496| exit testAvailable()
6333+
6334+
# 496| exit testAvailable() (normal)
6335+
#-----| -> exit testAvailable()
6336+
63326337
# 496| testAvailable()
63336338
#-----| -> x
63346339

@@ -6347,4 +6352,191 @@ cfg.swift:
63476352
# 499| if ... then { ... }
63486353
#-----| -> StmtCondition
63496354

6355+
# 499| #available
6356+
#-----| true -> .+=(_:_:)
6357+
#-----| false -> if ... then { ... }
6358+
63506359
# 499| StmtCondition
6360+
#-----| -> macOS 10
6361+
6362+
# 499| macOS 10
6363+
#-----| -> *
6364+
6365+
# 499| *
6366+
#-----| -> #available
6367+
6368+
# 500| &...
6369+
#-----| -> 1
6370+
6371+
# 500| x
6372+
#-----| -> &...
6373+
6374+
# 500| ... .+=(_:_:) ...
6375+
#-----| -> if ... then { ... }
6376+
6377+
# 500| .+=(_:_:)
6378+
#-----| -> Int.Type
6379+
6380+
# 500| Int.Type
6381+
#-----| -> x
6382+
6383+
# 500| 1
6384+
#-----| -> ... .+=(_:_:) ...
6385+
6386+
# 503| if ... then { ... }
6387+
#-----| -> StmtCondition
6388+
6389+
# 503| #available
6390+
#-----| true -> .+=(_:_:)
6391+
#-----| false -> if ... then { ... }
6392+
6393+
# 503| StmtCondition
6394+
#-----| -> macOS 10.13
6395+
6396+
# 503| macOS 10.13
6397+
#-----| -> *
6398+
6399+
# 503| *
6400+
#-----| -> #available
6401+
6402+
# 504| &...
6403+
#-----| -> 1
6404+
6405+
# 504| x
6406+
#-----| -> &...
6407+
6408+
# 504| ... .+=(_:_:) ...
6409+
#-----| -> if ... then { ... }
6410+
6411+
# 504| .+=(_:_:)
6412+
#-----| -> Int.Type
6413+
6414+
# 504| Int.Type
6415+
#-----| -> x
6416+
6417+
# 504| 1
6418+
#-----| -> ... .+=(_:_:) ...
6419+
6420+
# 507| if ... then { ... }
6421+
#-----| -> StmtCondition
6422+
6423+
# 507| #unavailable
6424+
#-----| true -> .+=(_:_:)
6425+
#-----| false -> guard ... else { ... }
6426+
6427+
# 507| StmtCondition
6428+
#-----| -> iOS 10
6429+
6430+
# 507| iOS 10
6431+
#-----| -> watchOS 10
6432+
6433+
# 507| watchOS 10
6434+
#-----| -> macOS 10
6435+
6436+
# 507| macOS 10
6437+
#-----| -> #unavailable
6438+
6439+
# 508| &...
6440+
#-----| -> 1
6441+
6442+
# 508| x
6443+
#-----| -> &...
6444+
6445+
# 508| ... .+=(_:_:) ...
6446+
#-----| -> guard ... else { ... }
6447+
6448+
# 508| .+=(_:_:)
6449+
#-----| -> Int.Type
6450+
6451+
# 508| Int.Type
6452+
#-----| -> x
6453+
6454+
# 508| 1
6455+
#-----| -> ... .+=(_:_:) ...
6456+
6457+
# 511| guard ... else { ... }
6458+
#-----| -> StmtCondition
6459+
6460+
# 511| #available
6461+
#-----| false -> .+=(_:_:)
6462+
#-----| true -> if ... then { ... }
6463+
6464+
# 511| StmtCondition
6465+
#-----| -> macOS 12
6466+
6467+
# 511| macOS 12
6468+
#-----| -> *
6469+
6470+
# 511| *
6471+
#-----| -> #available
6472+
6473+
# 512| &...
6474+
#-----| -> 1
6475+
6476+
# 512| x
6477+
#-----| -> &...
6478+
6479+
# 512| ... .+=(_:_:) ...
6480+
#-----| -> if ... then { ... }
6481+
6482+
# 512| .+=(_:_:)
6483+
#-----| -> Int.Type
6484+
6485+
# 512| Int.Type
6486+
#-----| -> x
6487+
6488+
# 512| 1
6489+
#-----| -> ... .+=(_:_:) ...
6490+
6491+
# 515| if ... then { ... }
6492+
#-----| -> StmtCondition
6493+
6494+
# 515| #available
6495+
#-----| false, true -> iOS 12
6496+
#-----| false -> x
6497+
6498+
# 515| StmtCondition
6499+
#-----| -> macOS 12
6500+
6501+
# 515| macOS 12
6502+
#-----| -> *
6503+
6504+
# 515| *
6505+
#-----| -> #available
6506+
6507+
# 515| #available
6508+
#-----| true -> .+=(_:_:)
6509+
#-----| false -> x
6510+
6511+
# 515| iOS 12
6512+
#-----| -> *
6513+
6514+
# 515| *
6515+
#-----| -> #available
6516+
6517+
# 516| &...
6518+
#-----| -> 1
6519+
6520+
# 516| x
6521+
#-----| -> &...
6522+
6523+
# 516| ... .+=(_:_:) ...
6524+
#-----| -> x
6525+
6526+
# 516| .+=(_:_:)
6527+
#-----| -> Int.Type
6528+
6529+
# 516| Int.Type
6530+
#-----| -> x
6531+
6532+
# 516| 1
6533+
#-----| -> ... .+=(_:_:) ...
6534+
6535+
# 519| return ...
6536+
#-----| return -> exit testAvailable() (normal)
6537+
6538+
# 519| (Int) ...
6539+
#-----| -> return ...
6540+
6541+
# 519| x
6542+
#-----| -> (Int) ...

0 commit comments

Comments
 (0)