Skip to content

Commit 53e80e0

Browse files
authored
Merge pull request #14398 from rdmarsh2/rdmarsh2/swift/autoclosure-cfg
Swift: add CFG for normal autoclosures
2 parents bbf9bcd + efb04f6 commit 53e80e0

File tree

14 files changed

+147
-11
lines changed

14 files changed

+147
-11
lines changed

swift/ql/.generated.list

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swift/ql/.gitattributes

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
5+
* The contents of autoclosure function parameters are now included in the control flow graph and data flow libraries.

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

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ cached
44
newtype TControlFlowElement =
55
TAstElement(AstNode n) or
66
TFuncDeclElement(Function func) { func.hasBody() } or
7-
TClosureElement(ExplicitClosureExpr clos) or
7+
TClosureElement(ClosureExpr clos) { isNormalAutoClosureOrExplicitClosure(clos) } or
88
TPropertyGetterElement(Decl accessor, Expr ref) { isPropertyGetterElement(accessor, ref) } or
99
TPropertySetterElement(Accessor accessor, AssignExpr assign) {
1010
isPropertySetterElement(accessor, assign)
@@ -41,6 +41,15 @@ predicate isPropertyGetterElement(PropertyGetterElement pge, Accessor accessor,
4141
pge = TPropertyGetterElement(accessor, ref)
4242
}
4343

44+
predicate isNormalAutoClosureOrExplicitClosure(ClosureExpr clos) {
45+
// short-circuiting operators have a `BinaryExpr` as the parent of the `AutoClosureExpr`,
46+
// so we exclude them by checking for a `CallExpr`.
47+
clos instanceof AutoClosureExpr and
48+
exists(CallExpr call | call.getAnArgument().getExpr() = clos)
49+
or
50+
clos instanceof ExplicitClosureExpr
51+
}
52+
4453
private predicate hasDirectToImplementationSemantics(Expr e) {
4554
e.(MemberRefExpr).hasDirectToImplementationSemantics()
4655
or
@@ -194,14 +203,18 @@ class KeyPathElement extends ControlFlowElement, TKeyPathElement {
194203
override string toString() { result = expr.toString() }
195204
}
196205

206+
/**
207+
* A control flow element representing a closure in its role as a control flow
208+
* scope.
209+
*/
197210
class ClosureElement extends ControlFlowElement, TClosureElement {
198-
ExplicitClosureExpr expr;
211+
ClosureExpr expr;
199212

200213
ClosureElement() { this = TClosureElement(expr) }
201214

202215
override Location getLocation() { result = expr.getLocation() }
203216

204-
ExplicitClosureExpr getAst() { result = expr }
217+
ClosureExpr getAst() { result = expr }
205218

206219
override string toString() { result = expr.toString() }
207220
}

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,13 @@ module CfgScope {
6767
final override predicate exit(ControlFlowElement last, Completion c) { last(tree, last, c) }
6868
}
6969

70-
private class ClosureExprScope extends Range_ instanceof ExplicitClosureExpr {
70+
private class ClosureExprScope extends Range_ instanceof ClosureExpr {
7171
Exprs::ClosureExprTree tree;
7272

73-
ClosureExprScope() { tree.getAst() = this }
73+
ClosureExprScope() {
74+
isNormalAutoClosureOrExplicitClosure(this) and
75+
tree.getAst() = this
76+
}
7477

7578
final override predicate entry(ControlFlowElement first) { first(tree, first) }
7679

@@ -1145,12 +1148,16 @@ module Exprs {
11451148
}
11461149
}
11471150

1151+
/**
1152+
* The control flow for an explicit closure or a normal autoclosure in its
1153+
* role as a control flow scope.
1154+
*/
11481155
class ClosureExprTree extends StandardPreOrderTree, TClosureElement {
1149-
ExplicitClosureExpr expr;
1156+
ClosureExpr expr;
11501157

11511158
ClosureExprTree() { this = TClosureElement(expr) }
11521159

1153-
ExplicitClosureExpr getAst() { result = expr }
1160+
ClosureExpr getAst() { result = expr }
11541161

11551162
final override ControlFlowElement getChildElement(int i) {
11561163
result.asAstNode() = expr.getParam(i)

swift/ql/lib/codeql/swift/elements/expr/AutoClosureExpr.qll

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ private import codeql.swift.elements.expr.Expr
44

55
/**
66
* A Swift autoclosure expression, that is, a closure automatically generated
7-
* around an argument when the parameter has the `@autoclosure` attribute. For
7+
* around an argument when the parameter has the `@autoclosure` attribute or
8+
* for the right-hand operand of short-circuiting logical operations. For
89
* example, there is an `AutoClosureExpr` around the value `0` in:
910
* ```
1011
* func myFunction(_ expr: @autoclosure () -> Int) {
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.expr.ExplicitClosureExpr
32

3+
/**
4+
* A Swift explicit closure expr, that is, a closure written using
5+
* `{ ... -> ... in ... }` syntax rather than automatically generated by the
6+
* compiler.
7+
*/
48
class ExplicitClosureExpr extends Generated::ExplicitClosureExpr { }

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3366,6 +3366,24 @@ cfg.swift:
33663366
# 545| getElse(): [BraceStmt] { ... }
33673367
# 546| getElement(0): [ReturnStmt] return ...
33683368
# 546| getResult(): [IntegerLiteralExpr] 0
3369+
# 550| [NamedFunction] usesAutoclosure(_:)
3370+
# 550| InterfaceType = (@autoclosure () -> Int) -> Int
3371+
# 550| getParam(0): [ParamDecl] expr
3372+
# 550| Type = () -> Int
3373+
# 550| getBody(): [BraceStmt] { ... }
3374+
# 551| getElement(0): [ReturnStmt] return ...
3375+
# 551| getResult(): [CallExpr] call to ...
3376+
# 551| getFunction(): [DeclRefExpr] expr
3377+
# 554| [NamedFunction] autoclosureTest()
3378+
# 554| InterfaceType = () -> ()
3379+
# 554| getBody(): [BraceStmt] { ... }
3380+
# 555| getElement(0): [CallExpr] call to usesAutoclosure(_:)
3381+
# 555| getFunction(): [DeclRefExpr] usesAutoclosure(_:)
3382+
# 555| getArgument(0): [Argument] : { ... }
3383+
# 555| getExpr(): [AutoClosureExpr] { ... }
3384+
# 555| getBody(): [BraceStmt] { ... }
3385+
# 555| getElement(0): [ReturnStmt] return ...
3386+
# 555| getResult(): [IntegerLiteralExpr] 1
33693387
declarations.swift:
33703388
# 1| [StructDecl] Foo
33713389
# 2| getMember(0): [PatternBindingDecl] var ... = ...

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -546,3 +546,11 @@ func testNilCoalescing2(x: Bool?) -> Int {
546546
return 0
547547
}
548548
}
549+
550+
func usesAutoclosure(_ expr: @autoclosure () -> Int) -> Int {
551+
return expr()
552+
}
553+
554+
func autoclosureTest() {
555+
usesAutoclosure(1)
556+
}

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

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6317,3 +6317,63 @@ cfg.swift:
63176317

63186318
# 546| 0
63196319
#-----| -> return ...
6320+
6321+
# 550| enter usesAutoclosure(_:)
6322+
#-----| -> usesAutoclosure(_:)
6323+
6324+
# 550| exit usesAutoclosure(_:)
6325+
6326+
# 550| exit usesAutoclosure(_:) (normal)
6327+
#-----| -> exit usesAutoclosure(_:)
6328+
6329+
# 550| usesAutoclosure(_:)
6330+
#-----| -> expr
6331+
6332+
# 550| expr
6333+
#-----| -> expr
6334+
6335+
# 551| return ...
6336+
#-----| return -> exit usesAutoclosure(_:) (normal)
6337+
6338+
# 551| expr
6339+
#-----| -> call to ...
6340+
6341+
# 551| call to ...
6342+
#-----| -> return ...
6343+
6344+
# 554| autoclosureTest()
6345+
#-----| -> usesAutoclosure(_:)
6346+
6347+
# 554| enter autoclosureTest()
6348+
#-----| -> autoclosureTest()
6349+
6350+
# 554| exit autoclosureTest()
6351+
6352+
# 554| exit autoclosureTest() (normal)
6353+
#-----| -> exit autoclosureTest()
6354+
6355+
# 555| usesAutoclosure(_:)
6356+
#-----| -> { ... }
6357+
6358+
# 555| call to usesAutoclosure(_:)
6359+
#-----| -> exit autoclosureTest() (normal)
6360+
6361+
# 555| 1
6362+
#-----| -> return ...
6363+
6364+
# 555| enter { ... }
6365+
#-----| -> { ... }
6366+
6367+
# 555| exit { ... }
6368+
6369+
# 555| exit { ... } (normal)
6370+
#-----| -> exit { ... }
6371+
6372+
# 555| return ...
6373+
#-----| -> exit { ... } (normal)
6374+
6375+
# 555| { ... }
6376+
#-----| -> 1
6377+
6378+
# 555| { ... }
6379+
#-----| -> call to usesAutoclosure(_:)

0 commit comments

Comments
 (0)