Skip to content

Commit 124e4dd

Browse files
authored
Merge pull request github#12150 from geoffw0/cfg2
Swift: control flow for #available
2 parents 55aacd6 + 8624098 commit 124e4dd

File tree

12 files changed

+441
-8
lines changed

12 files changed

+441
-8
lines changed

swift/ql/.generated.list

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,7 @@ ql/lib/codeql/swift/elements/type/WeakStorageTypeConstructor.qll d88b031ef44d6de
371371
ql/lib/codeql/swift/elements.qll 82b69a48b7afffeb97cafd9fdc57af96b672e21879580a6cfc3bae2a49bc2c40 82b69a48b7afffeb97cafd9fdc57af96b672e21879580a6cfc3bae2a49bc2c40
372372
ql/lib/codeql/swift/generated/AstNode.qll 02ca56d82801f942ae6265c6079d92ccafdf6b532f6bcebd98a04029ddf696e4 6216fda240e45bd4302fa0cf0f08f5f945418b144659264cdda84622b0420aa2
373373
ql/lib/codeql/swift/generated/AvailabilityInfo.qll 996a5cfadf7ca049122a1d1a1a9eb680d6a625ce28ede5504b172eabe7640fd2 4fe6e0325ff021a576fcd004730115ffaa60a2d9020420c7d4a1baa498067b60
374-
ql/lib/codeql/swift/generated/AvailabilitySpec.qll b7b1d14410927b80d11c80a507b6dfec72da01c403ee5db8ed2fe62efb821187 3aa96ba7cc7d8bf26a0c622130dcb79d24fcd346f9a3134afb08862c0fc18a64
374+
ql/lib/codeql/swift/generated/AvailabilitySpec.qll fb1255f91bb5e41ad4e9c675a2efbc50d0fb366ea2de68ab7eebd177b0795309 144e0c2e7d6c62ecee43325f7f26dcf437881edf0b75cc1bc898c6c4b61fdeaf
375375
ql/lib/codeql/swift/generated/Callable.qll 042b4f975f1e416c48b5bf26bee257549eec13fb262f11025375560f75a73582 0434788243bc54e48fec49e4cce93509b9a2333f2079dacb6ffc12c972853540
376376
ql/lib/codeql/swift/generated/Comment.qll f58b49f6e68c21f87c51e2ff84c8a64b09286d733e86f70d67d3a98fe6260bd6 975bbb599a2a7adc35179f6ae06d9cbc56ea8a03b972ef2ee87604834bc6deb1
377377
ql/lib/codeql/swift/generated/DbFile.qll a49b2a2cb2788cb49c861ebcd458b8daead7b15adb19c3a9f4db3bf39a0051fc a49b2a2cb2788cb49c861ebcd458b8daead7b15adb19c3a9f4db3bf39a0051fc

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/lib/codeql/swift/elements/stmt/ConditionElement.qll

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,7 @@ class ConditionElement extends Generated::ConditionElement {
66
result = this.getBoolean().toString()
77
or
88
result = this.getPattern().toString() + " = ... "
9+
or
10+
result = this.getAvailability().toString()
911
}
1012
}

swift/ql/lib/codeql/swift/generated/AvailabilitySpec.qll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,11 @@ private import codeql.swift.generated.Raw
44
import codeql.swift.elements.AstNode
55

66
module Generated {
7+
/**
8+
* An availability spec, that is, part of an `AvailabilityInfo` condition. For example `iOS 12` and `*` in:
9+
* ```
10+
* if #available(iOS 12, *)
11+
* ```
12+
*/
713
class AvailabilitySpec extends Synth::TAvailabilitySpec, AstNode { }
814
}

swift/ql/test/library-tests/ast/PrintAst.expected

Lines changed: 103 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3071,6 +3071,107 @@ cfg.swift:
30713071
# 489| getElement(7): [IntegerLiteralExpr] 11
30723072
# 490| getElement(8): [IntegerLiteralExpr] 12
30733073
# 493| getElement(9): [IntegerLiteralExpr] 13
3074+
# 496| [ConcreteFuncDecl] testAvailable()
3075+
# 496| InterfaceType = () -> Int
3076+
# 496| getBody(): [BraceStmt] { ... }
3077+
# 497| getElement(0): [PatternBindingDecl] var ... = ...
3078+
# 497| getInit(0): [IntegerLiteralExpr] 0
3079+
# 497| getPattern(0): [NamedPattern] x
3080+
# 497| getElement(1): [ConcreteVarDecl] x
3081+
# 497| Type = Int
3082+
# 499| getElement(2): [IfStmt] if ... then { ... }
3083+
# 499| getCondition(): [StmtCondition] StmtCondition
3084+
# 499| getElement(0): [ConditionElement] #available
3085+
# 499| getAvailability(): [AvailabilityInfo] #available
3086+
# 499| getSpec(0): [PlatformVersionAvailabilitySpec] macOS 10
3087+
# 499| getSpec(1): [OtherAvailabilitySpec] *
3088+
# 499| getThen(): [BraceStmt] { ... }
3089+
# 500| getElement(0): [BinaryExpr] ... .+=(_:_:) ...
3090+
# 500| getFunction(): [MethodLookupExpr] .+=(_:_:)
3091+
# 500| getBase(): [TypeExpr] Int.Type
3092+
# 500| getTypeRepr(): [TypeRepr] Int
3093+
# 500| getMethodRef(): [DeclRefExpr] +=(_:_:)
3094+
# 500| getArgument(0): [Argument] : &...
3095+
# 500| getExpr(): [InOutExpr] &...
3096+
# 500| getSubExpr(): [DeclRefExpr] x
3097+
# 500| getArgument(1): [Argument] : 1
3098+
# 500| getExpr(): [IntegerLiteralExpr] 1
3099+
# 503| getElement(3): [IfStmt] if ... then { ... }
3100+
# 503| getCondition(): [StmtCondition] StmtCondition
3101+
# 503| getElement(0): [ConditionElement] #available
3102+
# 503| getAvailability(): [AvailabilityInfo] #available
3103+
# 503| getSpec(0): [PlatformVersionAvailabilitySpec] macOS 10.13
3104+
# 503| getSpec(1): [OtherAvailabilitySpec] *
3105+
# 503| getThen(): [BraceStmt] { ... }
3106+
# 504| getElement(0): [BinaryExpr] ... .+=(_:_:) ...
3107+
# 504| getFunction(): [MethodLookupExpr] .+=(_:_:)
3108+
# 504| getBase(): [TypeExpr] Int.Type
3109+
# 504| getTypeRepr(): [TypeRepr] Int
3110+
# 504| getMethodRef(): [DeclRefExpr] +=(_:_:)
3111+
# 504| getArgument(0): [Argument] : &...
3112+
# 504| getExpr(): [InOutExpr] &...
3113+
# 504| getSubExpr(): [DeclRefExpr] x
3114+
# 504| getArgument(1): [Argument] : 1
3115+
# 504| getExpr(): [IntegerLiteralExpr] 1
3116+
# 507| getElement(4): [IfStmt] if ... then { ... }
3117+
# 507| getCondition(): [StmtCondition] StmtCondition
3118+
# 507| getElement(0): [ConditionElement] #unavailable
3119+
# 507| getAvailability(): [AvailabilityInfo] #unavailable
3120+
# 507| getSpec(0): [PlatformVersionAvailabilitySpec] iOS 10
3121+
# 507| getSpec(1): [PlatformVersionAvailabilitySpec] watchOS 10
3122+
# 507| getSpec(2): [PlatformVersionAvailabilitySpec] macOS 10
3123+
# 507| getThen(): [BraceStmt] { ... }
3124+
# 508| getElement(0): [BinaryExpr] ... .+=(_:_:) ...
3125+
# 508| getFunction(): [MethodLookupExpr] .+=(_:_:)
3126+
# 508| getBase(): [TypeExpr] Int.Type
3127+
# 508| getTypeRepr(): [TypeRepr] Int
3128+
# 508| getMethodRef(): [DeclRefExpr] +=(_:_:)
3129+
# 508| getArgument(0): [Argument] : &...
3130+
# 508| getExpr(): [InOutExpr] &...
3131+
# 508| getSubExpr(): [DeclRefExpr] x
3132+
# 508| getArgument(1): [Argument] : 1
3133+
# 508| getExpr(): [IntegerLiteralExpr] 1
3134+
# 511| getElement(5): [GuardStmt] guard ... else { ... }
3135+
# 511| getCondition(): [StmtCondition] StmtCondition
3136+
# 511| getElement(0): [ConditionElement] #available
3137+
# 511| getAvailability(): [AvailabilityInfo] #available
3138+
# 511| getSpec(0): [PlatformVersionAvailabilitySpec] macOS 12
3139+
# 511| getSpec(1): [OtherAvailabilitySpec] *
3140+
# 511| getBody(): [BraceStmt] { ... }
3141+
# 512| getElement(0): [BinaryExpr] ... .+=(_:_:) ...
3142+
# 512| getFunction(): [MethodLookupExpr] .+=(_:_:)
3143+
# 512| getBase(): [TypeExpr] Int.Type
3144+
# 512| getTypeRepr(): [TypeRepr] Int
3145+
# 512| getMethodRef(): [DeclRefExpr] +=(_:_:)
3146+
# 512| getArgument(0): [Argument] : &...
3147+
# 512| getExpr(): [InOutExpr] &...
3148+
# 512| getSubExpr(): [DeclRefExpr] x
3149+
# 512| getArgument(1): [Argument] : 1
3150+
# 512| getExpr(): [IntegerLiteralExpr] 1
3151+
# 515| getElement(6): [IfStmt] if ... then { ... }
3152+
# 515| getCondition(): [StmtCondition] StmtCondition
3153+
# 515| getElement(0): [ConditionElement] #available
3154+
# 515| getAvailability(): [AvailabilityInfo] #available
3155+
# 515| getSpec(0): [PlatformVersionAvailabilitySpec] macOS 12
3156+
# 515| getSpec(1): [OtherAvailabilitySpec] *
3157+
# 515| getElement(1): [ConditionElement] #available
3158+
# 515| getAvailability(): [AvailabilityInfo] #available
3159+
# 515| getSpec(0): [PlatformVersionAvailabilitySpec] iOS 12
3160+
# 515| getSpec(1): [OtherAvailabilitySpec] *
3161+
# 515| getThen(): [BraceStmt] { ... }
3162+
# 516| getElement(0): [BinaryExpr] ... .+=(_:_:) ...
3163+
# 516| getFunction(): [MethodLookupExpr] .+=(_:_:)
3164+
# 516| getBase(): [TypeExpr] Int.Type
3165+
# 516| getTypeRepr(): [TypeRepr] Int
3166+
# 516| getMethodRef(): [DeclRefExpr] +=(_:_:)
3167+
# 516| getArgument(0): [Argument] : &...
3168+
# 516| getExpr(): [InOutExpr] &...
3169+
# 516| getSubExpr(): [DeclRefExpr] x
3170+
# 516| getArgument(1): [Argument] : 1
3171+
# 516| getExpr(): [IntegerLiteralExpr] 1
3172+
# 519| getElement(7): [ReturnStmt] return ...
3173+
# 519| getResult(): [DeclRefExpr] x
3174+
# 519| getResult().getFullyConverted(): [LoadExpr] (Int) ...
30743175
declarations.swift:
30753176
# 1| [StructDecl] Foo
30763177
# 2| getMember(0): [PatternBindingDecl] var ... = ...
@@ -5898,7 +5999,7 @@ statements.swift:
58985999
# 87| getBody(): [BraceStmt] { ... }
58996000
# 87| getElement(0): [IfStmt] if ... then { ... }
59006001
# 87| getCondition(): [StmtCondition] StmtCondition
5901-
# 87| getElement(0): (no string representation)
6002+
# 87| getElement(0): [ConditionElement] #available
59026003
# 87| getAvailability(): [AvailabilityInfo] #available
59036004
# 87| getSpec(0): [PlatformVersionAvailabilitySpec] macOS 155
59046005
# 87| getSpec(1): [OtherAvailabilitySpec] *
@@ -5918,7 +6019,7 @@ statements.swift:
59186019
# 91| getBody(): [BraceStmt] { ... }
59196020
# 91| getElement(0): [IfStmt] if ... then { ... }
59206021
# 91| getCondition(): [StmtCondition] StmtCondition
5921-
# 91| getElement(0): (no string representation)
6022+
# 91| getElement(0): [ConditionElement] #unavailable
59226023
# 91| getAvailability(): [AvailabilityInfo] #unavailable
59236024
# 91| getSpec(0): [PlatformVersionAvailabilitySpec] macOS 42
59246025
# 91| getThen(): [BraceStmt] { ... }

swift/ql/test/library-tests/ast/cfg.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,3 +492,29 @@ func testIfConfig() {
492492

493493
13
494494
}
495+
496+
func testAvailable() -> Int {
497+
var x = 0;
498+
499+
if #available(macOS 10, *) {
500+
x += 1
501+
}
502+
503+
if #available(macOS 10.13, *) {
504+
x += 1
505+
}
506+
507+
if #unavailable(iOS 10, watchOS 10, macOS 10) {
508+
x += 1
509+
}
510+
511+
guard #available(macOS 12, *) else {
512+
x += 1
513+
}
514+
515+
if #available(macOS 12, *), #available(iOS 12, *) {
516+
x += 1
517+
}
518+
519+
return x
520+
}

0 commit comments

Comments
 (0)