Skip to content

Commit a6e21da

Browse files
authored
Merge pull request github#13284 from github/redsun82/swift-remove-property-wrapper-inconsistencies
Swift: remove some AST and CFG inconsistencies
2 parents 6365739 + 192c0d5 commit a6e21da

File tree

37 files changed

+182
-333
lines changed

37 files changed

+182
-333
lines changed

swift/ql/.generated.list

Lines changed: 5 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swift/ql/.gitattributes

Lines changed: 2 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: fix
3+
---
4+
5+
* Fixed a number of inconsistencies in the abstract syntax tree (AST) and in the control-flow graph (CFG). This may lead to more results in queries that use these libraries, or libraries that depend on them (such as dataflow).

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

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -111,31 +111,21 @@ module Stmts {
111111

112112
override predicate propagatesAbnormal(ControlFlowElement node) { none() }
113113

114-
private predicate isBodyOfTapExpr() { any(TapExpr tap).getBody() = ast }
115-
116-
// Note: If the brace statement is the body of a `TapExpr`, the first element is the variable
117-
// declaration (see https://github.com/apple/swift/blob/main/include/swift/AST/Expr.h#L848)
118-
// that's initialized by the `TapExpr`. In `TapExprTree` we've already visited this declaration,
119-
// along with its initializer. So we skip the first element here.
120-
private AstNode getFirstElement() {
121-
if this.isBodyOfTapExpr() then result = ast.getElement(1) else result = ast.getFirstElement()
122-
}
123-
124114
override predicate first(ControlFlowElement first) {
125115
this.firstInner(first)
126116
or
127-
not exists(this.getFirstElement()) and first.asAstNode() = ast
117+
not exists(ast.getFirstElement()) and first.asAstNode() = ast
128118
}
129119

130120
override predicate last(ControlFlowElement last, Completion c) {
131121
this.lastInner(last, c)
132122
or
133-
not exists(this.getFirstElement()) and
123+
not exists(ast.getFirstElement()) and
134124
last.asAstNode() = ast and
135125
c instanceof SimpleCompletion
136126
}
137127

138-
predicate firstInner(ControlFlowElement first) { astFirst(this.getFirstElement(), first) }
128+
predicate firstInner(ControlFlowElement first) { astFirst(ast.getFirstElement(), first) }
139129

140130
/** Gets the body of the i'th `defer` statement. */
141131
private BraceStmt getDeferStmtBody(int i) {
@@ -969,6 +959,15 @@ module Decls {
969959
result.asAstNode() = ast.getPattern(j).getFullyUnresolved()
970960
)
971961
or
962+
// synthesized pattern bindings for property wrappers may be sharing the init with the backed
963+
// variable declaration, so we need to skip those
964+
not exists(VarDecl decl |
965+
ast =
966+
[
967+
decl.getPropertyWrapperBackingVarBinding(),
968+
decl.getPropertyWrapperProjectionVarBinding()
969+
]
970+
) and
972971
exists(int j |
973972
i = 2 * j + 1 and
974973
result.asAstNode() = ast.getInit(j).getFullyConverted()
@@ -1397,20 +1396,12 @@ module Exprs {
13971396
override TapExpr ast;
13981397

13991398
final override ControlFlowElement getChildElement(int i) {
1400-
// We first visit the local variable declaration.
1399+
// We first visit the expression that gives the local variable its initial value.
14011400
i = 0 and
1402-
result.asAstNode() = ast.getVar()
1403-
or
1404-
// Then we visit the expression that gives the local variable its initial value.
1405-
i = 1 and
14061401
result.asAstNode() = ast.getSubExpr().getFullyConverted()
14071402
or
1408-
// And finally, we visit the body that potentially mutates the local variable.
1409-
// Note that the CFG for the body will skip the first element in the
1410-
// body because it's guaranteed to be the variable declaration
1411-
// that we've already visited at i = 0. See the explanation
1412-
// in `BraceStmtTree` for why this is necessary.
1413-
i = 2 and
1403+
// And then we visit the body that potentially mutates the local variable.
1404+
i = 1 and
14141405
result.asAstNode() = ast.getBody()
14151406
}
14161407
}

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

Lines changed: 1 addition & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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

Lines changed: 15 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swift/ql/lib/codeql/swift/generated/expr/OpenExistentialExpr.qll

Lines changed: 19 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

swift/ql/lib/codeql/swift/printast/PrintAstNode.qll

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ private predicate shouldPrint(Locatable e) { any(PrintAstConfiguration config).s
2828
/**
2929
* An AST node that should be printed.
3030
*/
31-
private newtype TPrintAstNode = TLocatable(Locatable ast)
31+
private newtype TPrintAstNode = TPrintLocatable(Locatable ast)
3232

3333
/**
3434
* A node in the output tree.
@@ -60,6 +60,11 @@ class PrintAstNode extends TPrintAstNode {
6060
* the property is `key`.
6161
*/
6262
string getProperty(string key) { none() }
63+
64+
/**
65+
* Gets the underlying AST node, if any.
66+
*/
67+
abstract Locatable getAstNode();
6368
}
6469

6570
private string prettyPrint(Locatable e) {
@@ -73,10 +78,10 @@ private class Unresolved extends Locatable {
7378
/**
7479
* A graph node representing a real Locatable node.
7580
*/
76-
class PrintLocatable extends PrintAstNode, TLocatable {
81+
class PrintLocatable extends PrintAstNode, TPrintLocatable {
7782
Locatable ast;
7883

79-
PrintLocatable() { this = TLocatable(ast) }
84+
PrintLocatable() { this = TPrintLocatable(ast) }
8085

8186
override string toString() { result = prettyPrint(ast) }
8287

@@ -87,9 +92,9 @@ class PrintLocatable extends PrintAstNode, TLocatable {
8792
c = getChildAndAccessor(ast, i, accessor) and
8893
(
8994
// use even indexes for normal children, leaving odd slots for conversions if any
90-
child = TLocatable(c) and index = 2 * i and label = accessor
95+
child = TPrintLocatable(c) and index = 2 * i and label = accessor
9196
or
92-
child = TLocatable(c.getFullyUnresolved().(Unresolved)) and
97+
child = TPrintLocatable(c.getFullyUnresolved().(Unresolved)) and
9398
index = 2 * i + 1 and
9499
(
95100
if c instanceof Expr
@@ -100,6 +105,8 @@ class PrintLocatable extends PrintAstNode, TLocatable {
100105
)
101106
}
102107

108+
final override Locatable getAstNode() { result = ast }
109+
103110
final override Location getLocation() { result = ast.getLocation() }
104111
}
105112

@@ -112,17 +119,38 @@ class PrintUnresolved extends PrintLocatable {
112119

113120
override predicate hasChild(PrintAstNode child, int index, string label) {
114121
// only print immediate unresolved children from the "parallel" AST
115-
child = TLocatable(getImmediateChildAndAccessor(ast, index, label).(Unresolved))
122+
child = TPrintLocatable(getImmediateChildAndAccessor(ast, index, label).(Unresolved))
116123
}
117124
}
118125

126+
private predicate hasPropertyWrapperElement(VarDecl d, Locatable a) {
127+
a = [d.getPropertyWrapperBackingVar(), d.getPropertyWrapperProjectionVar()] or
128+
a = [d.getPropertyWrapperBackingVarBinding(), d.getPropertyWrapperProjectionVarBinding()]
129+
}
130+
119131
/**
120-
* A specialization of graph node for `VarDecl`, to add typing information.
132+
* A specialization of graph node for `VarDecl`, to add typing information and deal with ambiguity
133+
* over property wrapper children.
121134
*/
122135
class PrintVarDecl extends PrintLocatable {
123136
override VarDecl ast;
124137

125138
override string getProperty(string key) { key = "Type" and result = ast.getType().toString() }
139+
140+
override predicate hasChild(PrintAstNode child, int index, string label) {
141+
PrintLocatable.super.hasChild(child, index, label) and
142+
// exclude property wrapper related children when they are already listed in the enclosing
143+
// nominal type declaration or for a wrapped parameter for which this is a virtual local variable copy
144+
not exists(Locatable childAst |
145+
childAst = child.getAstNode() and
146+
hasPropertyWrapperElement(ast, childAst) and
147+
(
148+
childAst = ast.getDeclaringDecl().getAMember()
149+
or
150+
ast instanceof ConcreteVarDecl and hasPropertyWrapperElement(any(ParamDecl p), childAst)
151+
)
152+
)
153+
}
126154
}
127155

128156
/**
@@ -135,3 +163,23 @@ class PrintFunction extends PrintLocatable {
135163
key = "InterfaceType" and result = ast.getInterfaceType().toString()
136164
}
137165
}
166+
167+
/**
168+
* A specialization of graph node for `PatternBindingDecl`, to solve ambiguity on `getInit`.
169+
* When a property wrapper is involved, `getInit` may become shared between the explicit binding and
170+
* the implicit compiler synthesized one.
171+
*/
172+
class PrintPatternBindingDecl extends PrintLocatable {
173+
override PatternBindingDecl ast;
174+
175+
override predicate hasChild(PrintAstNode child, int index, string label) {
176+
PrintLocatable.super.hasChild(child, index, label) and
177+
// exclude `getInit` that are already the initializer of a variable that has this as a property wrapper backer
178+
not exists(Expr init, VarDecl var |
179+
init = child.getAstNode() and
180+
init = ast.getAnInit() and
181+
var.getPropertyWrapperBackingVarBinding() = ast and
182+
var.getParentInitializer() = init
183+
)
184+
}
185+
}

0 commit comments

Comments
 (0)