Skip to content

Commit 9a5fa42

Browse files
committed
Swift: CFG for nil coalescing operator
1 parent dd01da4 commit 9a5fa42

File tree

3 files changed

+49
-5
lines changed

3 files changed

+49
-5
lines changed

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

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,7 +1179,7 @@ module Exprs {
11791179
}
11801180

11811181
/**
1182-
* An autoclosure expression that is generated as part of a logical operation.
1182+
* An autoclosure expression that is generated as part of a logical operation or nil coalescing expression.
11831183
*
11841184
* This is needed because the Swift AST for `b1 && b2` is really syntactic sugar a function call:
11851185
* ```swift
@@ -1188,10 +1188,13 @@ module Exprs {
11881188
* So the `true` edge from `b1` cannot just go to `b2` since this is an implicit autoclosure.
11891189
* To handle this dig into the autoclosure when it's an operand of a logical operator.
11901190
*/
1191-
private class LogicalAutoClosureTree extends AstPreOrderTree {
1191+
private class ShortCircuitingAutoClosureTree extends AstPreOrderTree {
11921192
override AutoClosureExpr ast;
11931193

1194-
LogicalAutoClosureTree() { ast = any(LogicalOperation op).getAnOperand() }
1194+
ShortCircuitingAutoClosureTree() {
1195+
ast = any(LogicalOperation op).getAnOperand() or
1196+
ast = any(NilCoalescingExpr expr).getAnOperand()
1197+
}
11951198

11961199
override predicate last(ControlFlowElement last, Completion c) {
11971200
exists(Completion completion | astLast(ast.getReturn(), last, completion) |
@@ -1217,7 +1220,7 @@ module Exprs {
12171220
private class AutoClosureTree extends AstLeafTree {
12181221
override AutoClosureExpr ast;
12191222

1220-
AutoClosureTree() { not this instanceof LogicalAutoClosureTree }
1223+
AutoClosureTree() { not this instanceof ShortCircuitingAutoClosureTree }
12211224
}
12221225
}
12231226

@@ -1557,7 +1560,9 @@ module Exprs {
15571560
// This one is handled in `LogicalNotTree`.
15581561
not ast instanceof UnaryLogicalOperation and
15591562
// These are handled in `LogicalOrTree` and `LogicalAndTree`.
1560-
not ast instanceof BinaryLogicalOperation
1563+
not ast instanceof BinaryLogicalOperation and
1564+
// This one is handled in `NilCoalescingTree`
1565+
not ast instanceof NilCoalescingExpr
15611566
}
15621567

15631568
final override ControlFlowElement getChildElement(int i) {
@@ -1581,6 +1586,36 @@ module Exprs {
15811586
}
15821587
}
15831588

1589+
private class NilCoalescingTree extends AstControlFlowTree {
1590+
override NilCoalescingExpr ast;
1591+
1592+
final override predicate propagatesAbnormal(ControlFlowElement child) {
1593+
child.asAstNode() = ast.getAnOperand().getFullyConverted()
1594+
}
1595+
1596+
final override predicate first(ControlFlowElement first) {
1597+
astFirst(ast.getLeftOperand().getFullyConverted(), first)
1598+
}
1599+
1600+
final override predicate last(ControlFlowElement last, Completion c) {
1601+
last.asAstNode() = ast and
1602+
exists(EmptinessCompletion ec | ec = c | not ec.isEmpty())
1603+
or
1604+
astLast(ast.getRightOperand().getFullyConverted(), last, c) and
1605+
c instanceof NormalCompletion
1606+
}
1607+
1608+
final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
1609+
astLast(ast.getLeftOperand().getFullyConverted(), pred, c) and
1610+
c instanceof NormalCompletion and
1611+
succ.asAstNode() = ast
1612+
or
1613+
pred.asAstNode() = ast and
1614+
c.(EmptinessCompletion).isEmpty() and
1615+
astFirst(ast.getRightOperand().getFullyConverted(), succ)
1616+
}
1617+
}
1618+
15841619
private class LogicalAndTree extends AstPostOrderTree {
15851620
override LogicalAndExpr ast;
15861621

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

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
private import codeql.swift.elements.expr.Expr
2+
private import codeql.swift.elements.expr.BinaryExpr
3+
4+
class NilCoalescingExpr extends BinaryExpr {
5+
NilCoalescingExpr() {
6+
this.getStaticTarget().getName() = "??(_:_:)"
7+
}
8+
}

0 commit comments

Comments
 (0)