Swift: CFG for nil coalescing operator

This commit is contained in:
Robert Marsh
2023-09-14 16:16:30 +00:00
parent dd01da4938
commit 9a5fa42dbe
3 changed files with 49 additions and 5 deletions

View File

@@ -1179,7 +1179,7 @@ module Exprs {
}
/**
* An autoclosure expression that is generated as part of a logical operation.
* An autoclosure expression that is generated as part of a logical operation or nil coalescing expression.
*
* This is needed because the Swift AST for `b1 && b2` is really syntactic sugar a function call:
* ```swift
@@ -1188,10 +1188,13 @@ module Exprs {
* So the `true` edge from `b1` cannot just go to `b2` since this is an implicit autoclosure.
* To handle this dig into the autoclosure when it's an operand of a logical operator.
*/
private class LogicalAutoClosureTree extends AstPreOrderTree {
private class ShortCircuitingAutoClosureTree extends AstPreOrderTree {
override AutoClosureExpr ast;
LogicalAutoClosureTree() { ast = any(LogicalOperation op).getAnOperand() }
ShortCircuitingAutoClosureTree() {
ast = any(LogicalOperation op).getAnOperand() or
ast = any(NilCoalescingExpr expr).getAnOperand()
}
override predicate last(ControlFlowElement last, Completion c) {
exists(Completion completion | astLast(ast.getReturn(), last, completion) |
@@ -1217,7 +1220,7 @@ module Exprs {
private class AutoClosureTree extends AstLeafTree {
override AutoClosureExpr ast;
AutoClosureTree() { not this instanceof LogicalAutoClosureTree }
AutoClosureTree() { not this instanceof ShortCircuitingAutoClosureTree }
}
}
@@ -1557,7 +1560,9 @@ module Exprs {
// This one is handled in `LogicalNotTree`.
not ast instanceof UnaryLogicalOperation and
// These are handled in `LogicalOrTree` and `LogicalAndTree`.
not ast instanceof BinaryLogicalOperation
not ast instanceof BinaryLogicalOperation and
// This one is handled in `NilCoalescingTree`
not ast instanceof NilCoalescingExpr
}
final override ControlFlowElement getChildElement(int i) {
@@ -1581,6 +1586,36 @@ module Exprs {
}
}
private class NilCoalescingTree extends AstControlFlowTree {
override NilCoalescingExpr ast;
final override predicate propagatesAbnormal(ControlFlowElement child) {
child.asAstNode() = ast.getAnOperand().getFullyConverted()
}
final override predicate first(ControlFlowElement first) {
astFirst(ast.getLeftOperand().getFullyConverted(), first)
}
final override predicate last(ControlFlowElement last, Completion c) {
last.asAstNode() = ast and
exists(EmptinessCompletion ec | ec = c | not ec.isEmpty())
or
astLast(ast.getRightOperand().getFullyConverted(), last, c) and
c instanceof NormalCompletion
}
final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
astLast(ast.getLeftOperand().getFullyConverted(), pred, c) and
c instanceof NormalCompletion and
succ.asAstNode() = ast
or
pred.asAstNode() = ast and
c.(EmptinessCompletion).isEmpty() and
astFirst(ast.getRightOperand().getFullyConverted(), succ)
}
}
private class LogicalAndTree extends AstPostOrderTree {
override LogicalAndExpr ast;

View File

@@ -140,6 +140,7 @@ import codeql.swift.elements.expr.MakeTemporarilyEscapableExpr
import codeql.swift.elements.expr.MemberRefExpr
import codeql.swift.elements.expr.MetatypeConversionExpr
import codeql.swift.elements.expr.MethodLookupExpr
import codeql.swift.elements.expr.NilCoalescingExpr
import codeql.swift.elements.expr.NilLiteralExpr
import codeql.swift.elements.expr.NumberLiteralExpr
import codeql.swift.elements.expr.ObjCSelectorExpr

View File

@@ -0,0 +1,8 @@
private import codeql.swift.elements.expr.Expr
private import codeql.swift.elements.expr.BinaryExpr
class NilCoalescingExpr extends BinaryExpr {
NilCoalescingExpr() {
this.getStaticTarget().getName() = "??(_:_:)"
}
}