Skip to content

Commit 1f861fd

Browse files
authored
Merge pull request github#12736 from d10c/swift/capture-flow
Swift: Closure Capture Helper APIs
2 parents 76f8d46 + 7f675d8 commit 1f861fd

File tree

9 files changed

+1473
-63
lines changed

9 files changed

+1473
-63
lines changed

swift/ql/.generated.list

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ ql/lib/codeql/swift/elements/decl/SubscriptDeclConstructor.qll 3a88617b41f96827c
6060
ql/lib/codeql/swift/elements/decl/TopLevelCodeDeclConstructor.qll 6920a4e7aec45ae2a561cef95b9082b861f81c16c259698541f317481645e194 4bd65820b93a5ec7332dd1bbf59326fc19b77e94c122ad65d41393c84e6ac581
6161
ql/lib/codeql/swift/elements/decl/TypeAliasDecl.qll 984c5802c35e595388f7652cef1a50fb963b32342ab4f9d813b7200a0e6a37ca 630dc9cbf20603855c599a9f86037ba0d889ad3d2c2b6f9ac17508d398bff9e3
6262
ql/lib/codeql/swift/elements/decl/TypeAliasDeclConstructor.qll ba70bb69b3a14283def254cc1859c29963838f624b3f1062a200a8df38f1edd5 96ac51d1b3156d4139e583f7f803e9eb95fe25cc61c12986e1b2972a781f9c8b
63-
ql/lib/codeql/swift/elements/decl/ValueDecl.qll 1b7d8eeb17be4bdbabc156cb0689641ed4f9e697e334d2d14f423ed3d1a419f6 e3056cf6a883da2737cb220a89499a9e3977eb1c56b9e1d2f41a56b71a0c29f9
6463
ql/lib/codeql/swift/elements/expr/AbiSafeConversionExpr.qll 39b856c89b8aff769b75051fd9e319f2d064c602733eaa6fed90d8f626516306 a87738539276438cef63145461adf25309d1938cfac367f53f53d33db9b12844
6564
ql/lib/codeql/swift/elements/expr/AbiSafeConversionExprConstructor.qll 7d70e7c47a9919efcb1ebcbf70e69cab1be30dd006297b75f6d72b25ae75502a e7a741c42401963f0c1da414b3ae779adeba091e9b8f56c9abf2a686e3a04d52
6665
ql/lib/codeql/swift/elements/expr/AbstractClosureExpr.qll 4027b51a171387332f96cb7b78ca87a6906aec76419938157ac24a60cff16519 400790fe643585ad39f40c433eff8934bbe542d140b81341bca3b6dfc5b22861

swift/ql/lib/codeql/swift/elements/AstNode.qll

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
private import codeql.swift.generated.AstNode
22
private import codeql.swift.elements.decl.AbstractFunctionDecl
33
private import codeql.swift.elements.decl.Decl
4+
private import codeql.swift.elements.expr.AbstractClosureExpr
5+
private import codeql.swift.elements.Callable
46
private import codeql.swift.generated.ParentChild
57

68
private module Cached {
@@ -21,10 +23,59 @@ private module Cached {
2123
AbstractFunctionDecl getEnclosingFunction(AstNode ast) {
2224
result = getEnclosingFunctionStep*(getEnclosingDecl(ast))
2325
}
26+
27+
private Element getEnclosingClosureStep(Element e) {
28+
not e instanceof Callable and
29+
result = getImmediateParent(e)
30+
}
31+
32+
cached
33+
AbstractClosureExpr getEnclosingClosure(AstNode ast) {
34+
result = getEnclosingClosureStep*(getImmediateParent(ast))
35+
}
2436
}
2537

38+
/**
39+
* A node in the abstract syntax tree.
40+
*/
2641
class AstNode extends Generated::AstNode {
42+
/**
43+
* Gets the nearest function definition that contains this AST node, if any.
44+
* This includes functions, methods, (de)initializers, and accessors, but not closures.
45+
*
46+
* For example, in the following code, the AST node for `n + 1` has `foo` as its
47+
* enclosing function (via `getEnclosingFunction`), whereas its enclosing callable is
48+
* the closure `{(n : Int) in n + 1 }` (via `getEnclosingCallable`):
49+
*
50+
* ```swift
51+
* func foo() {
52+
* var f = { (n : Int) in n + 1 }
53+
* }
54+
* ```
55+
*/
2756
final AbstractFunctionDecl getEnclosingFunction() { result = Cached::getEnclosingFunction(this) }
2857

58+
/**
59+
* Gets the nearest declaration that contains this AST node, if any.
60+
*/
2961
final Decl getEnclosingDecl() { result = Cached::getEnclosingDecl(this) }
62+
63+
/**
64+
* Gets the nearest `Callable` that contains this AST node, if any.
65+
* This includes (auto)closures, functions, methods, (de)initializers, and accessors.
66+
*
67+
* For example, in the following code, the AST node for `n + 1` has the closure
68+
* `{(n : Int) in n + 1 }` as its enclosing callable.
69+
*
70+
* ```swift
71+
* func foo() {
72+
* var f = { (n : Int) in n + 1 }
73+
* }
74+
* ```
75+
*/
76+
final Callable getEnclosingCallable() {
77+
if exists(Cached::getEnclosingClosure(this))
78+
then result = Cached::getEnclosingClosure(this)
79+
else result = Cached::getEnclosingFunction(this)
80+
}
3081
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,23 @@
11
private import codeql.swift.generated.decl.CapturedDecl
2+
private import codeql.swift.elements.Callable
3+
private import codeql.swift.elements.expr.DeclRefExpr
24

5+
/**
6+
* A captured variable or function parameter in the scope of a closure.
7+
*/
38
class CapturedDecl extends Generated::CapturedDecl {
49
override string toString() { result = this.getDecl().toString() }
10+
11+
/**
12+
* Gets the closure or function that captures this variable.
13+
*/
14+
Callable getScope() { result.getACapture() = this }
15+
16+
/**
17+
* Get an access to this capture within the scope of its closure.
18+
*/
19+
DeclRefExpr getAnAccess() {
20+
result.getEnclosingCallable() = this.getScope() and
21+
result.getDecl() = this.getDecl()
22+
}
523
}
Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,23 @@
1-
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
21
private import codeql.swift.generated.decl.ValueDecl
2+
private import codeql.swift.elements.decl.CapturedDecl
3+
private import codeql.swift.elements.expr.DeclRefExpr
34

4-
class ValueDecl extends Generated::ValueDecl { }
5+
/**
6+
* A declaration that introduces a value with a type.
7+
*/
8+
class ValueDecl extends Generated::ValueDecl {
9+
/**
10+
* Gets a capture of this declaration in the scope of a closure.
11+
*/
12+
CapturedDecl asCapturedDecl() { result.getDecl() = this }
13+
14+
/**
15+
* Holds if this declaration is captured by a closure.
16+
*/
17+
predicate isCaptured() { exists(this.asCapturedDecl()) }
18+
19+
/**
20+
* Gets an expression that references this declaration.
21+
*/
22+
DeclRefExpr getAnAccess() { result.getDecl() = this }
23+
}
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
private import codeql.swift.generated.decl.VarDecl
2-
private import codeql.swift.elements.expr.DeclRefExpr
32
private import codeql.swift.elements.decl.Decl
43

4+
/**
5+
* A variable declaration.
6+
*/
57
class VarDecl extends Generated::VarDecl {
68
override string toString() { result = this.getName() }
7-
8-
DeclRefExpr getAnAccess() { result.getDecl() = this }
99
}
1010

11+
/**
12+
* A field declaration.
13+
*/
1114
class FieldDecl extends VarDecl {
1215
FieldDecl() { this = any(Decl ctx).getAMember() }
1316
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
11
private import codeql.swift.generated.expr.DeclRefExpr
2+
private import codeql.swift.elements.decl.CapturedDecl
23

4+
/**
5+
* An expression that references or accesses a declaration.
6+
*/
37
class DeclRefExpr extends Generated::DeclRefExpr {
48
override string toString() {
59
if exists(this.getDecl().toString())
610
then result = this.getDecl().toString()
711
else result = "(unknown declaration)"
812
}
13+
14+
/**
15+
* Gets the closure capture referenced by this expression, if any.
16+
*/
17+
CapturedDecl getCapturedDecl() { result.getAnAccess() = this }
18+
19+
/**
20+
* Holds if this expression references a closure capture.
21+
*/
22+
predicate hasCapturedDecl() { exists(this.getCapturedDecl()) }
923
}
Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
11
| closures.swift:8:12:8:12 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:7:6:7:6 | x | isDirect: | yes | isEscaping: | no |
22
| closures.swift:9:12:9:12 | y | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:6:7:6:7 | y | isDirect: | yes | isEscaping: | no |
3+
| closures.swift:16:3:16:3 | escape | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:12:5:12:5 | escape | isDirect: | yes | isEscaping: | yes |
34
| closures.swift:17:5:17:5 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:15:7:15:7 | x | isDirect: | yes | isEscaping: | yes |
4-
| closures.swift:20:3:20:3 | escape | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:12:5:12:5 | escape | isDirect: | yes | isEscaping: | yes |
5-
| closures.swift:25:3:25:3 | escape | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:12:5:12:5 | escape | isDirect: | yes | isEscaping: | yes |
5+
| closures.swift:24:3:24:3 | escape | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:12:5:12:5 | escape | isDirect: | yes | isEscaping: | yes |
6+
| closures.swift:31:11:31:11 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:29:7:29:7 | x | isDirect: | no | isEscaping: | no |
7+
| closures.swift:32:14:32:14 | f | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:28:7:28:7 | f | isDirect: | no | isEscaping: | no |
8+
| closures.swift:32:14:32:14 | f | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:28:7:28:7 | f | isDirect: | yes | isEscaping: | no |
9+
| closures.swift:32:17:32:17 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:29:7:29:7 | x | isDirect: | yes | isEscaping: | no |
10+
| closures.swift:39:20:39:20 | callback | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:36:21:36:58 | callback | isDirect: | yes | isEscaping: | yes |
11+
| closures.swift:42:35:42:35 | wrapper(_:) | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:38:5:40:5 | wrapper(_:) | isDirect: | no | isEscaping: | yes |
12+
| closures.swift:52:18:52:18 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:51:7:51:7 | x | isDirect: | yes | isEscaping: | yes |
13+
| closures.swift:54:13:54:13 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:51:7:51:7 | x | isDirect: | yes | isEscaping: | yes |
14+
| closures.swift:61:18:61:18 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:60:7:60:7 | x | isDirect: | yes | isEscaping: | yes |
15+
| closures.swift:63:13:63:13 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:60:7:60:7 | x | isDirect: | yes | isEscaping: | yes |
16+
| closures.swift:71:3:71:3 | g | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:68:5:68:5 | g | isDirect: | yes | isEscaping: | yes |
17+
| closures.swift:71:14:71:14 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:70:7:70:7 | x | isDirect: | yes | isEscaping: | yes |
18+
| closures.swift:73:13:73:13 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:70:7:70:7 | x | isDirect: | yes | isEscaping: | yes |
19+
| closures.swift:85:7:85:7 | y | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:79:7:79:7 | y | isDirect: | no | isEscaping: | yes |
20+
| closures.swift:85:7:85:7 | y | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:79:7:79:7 | y | isDirect: | yes | isEscaping: | yes |
21+
| closures.swift:85:18:85:18 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:82:9:82:9 | x | isDirect: | yes | isEscaping: | yes |
22+
| closures.swift:88:9:88:9 | b() | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:92:5:98:5 | b() | isDirect: | no | isEscaping: | yes |
23+
| closures.swift:93:7:93:7 | y | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:79:7:79:7 | y | isDirect: | yes | isEscaping: | yes |
24+
| closures.swift:93:20:93:20 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:82:9:82:9 | x | isDirect: | yes | isEscaping: | yes |
25+
| closures.swift:96:9:96:9 | a() | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:84:5:90:5 | a() | isDirect: | no | isEscaping: | yes |
26+
| closures.swift:111:15:111:15 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:110:9:110:9 | x | isDirect: | yes | isEscaping: | yes |
27+
| closures.swift:111:27:111:27 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:110:9:110:9 | x | isDirect: | yes | isEscaping: | yes |
28+
| closures.swift:115:5:115:5 | incrX | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:109:8:109:8 | incrX | isDirect: | yes | isEscaping: | yes |
29+
| closures.swift:130:25:130:25 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:128:7:128:7 | x | isDirect: | yes | isEscaping: | yes |
30+
| closures.swift:133:20:133:20 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:128:7:128:7 | x | isDirect: | no | isEscaping: | yes |
31+
| closures.swift:133:20:133:20 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:128:7:128:7 | x | isDirect: | yes | isEscaping: | yes |
32+
| closures.swift:133:24:133:24 | y | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:132:22:132:22 | y | isDirect: | yes | isEscaping: | yes |
33+
| closures.swift:151:21:151:21 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:149:10:149:15 | x | isDirect: | yes | isEscaping: | yes |
34+
| closures.swift:154:16:154:16 | g(_:) | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:158:3:165:3 | g(_:) | isDirect: | no | isEscaping: | yes |
35+
| closures.swift:155:21:155:21 | next | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:154:9:154:9 | next | isDirect: | yes | isEscaping: | yes |
36+
| closures.swift:155:34:155:34 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:149:10:149:15 | x | isDirect: | yes | isEscaping: | yes |
37+
| closures.swift:160:21:160:21 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:158:10:158:15 | x | isDirect: | yes | isEscaping: | yes |
38+
| closures.swift:163:16:163:16 | f(_:) | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:149:3:156:3 | f(_:) | isDirect: | no | isEscaping: | yes |
39+
| closures.swift:164:21:164:21 | next | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:163:9:163:9 | next | isDirect: | yes | isEscaping: | yes |
40+
| closures.swift:164:36:164:36 | x | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:158:10:158:15 | x | isDirect: | yes | isEscaping: | yes |
41+
| closures.swift:195:3:195:3 | g | getModule: | file://:0:0:0:0 | closures | getNumberOfMembers: | 0 | getDecl: | closures.swift:68:5:68:5 | g | isDirect: | yes | isEscaping: | yes |

0 commit comments

Comments
 (0)