From 4a6040c9e58f8759c494fa9b70bef8afe4c041dd Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 10 Sep 2026 15:50:37 +0200 Subject: [PATCH 1/4] Cfg: Add shared inline CFG test. --- .../codeql/controlflow/ControlFlowGraph.qll | 26 +- .../codeql/controlflow/test/TestCfg.qll | 419 ++++++++++++++++++ 2 files changed, 442 insertions(+), 3 deletions(-) create mode 100644 shared/controlflow/codeql/controlflow/test/TestCfg.qll diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index 392a777d3ef4..12b142ebbffd 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -2208,13 +2208,21 @@ module Make0 Ast> { } } - module Cfg = BB::Make; + private module Cfg_ = BB::Make; - private module CfgAlias = Cfg; + module Cfg implements BB::CfgSig, TestCfg::CfgSig { + import Cfg_ - import CfgAlias + class AstNode = Ast::AstNode; + + class Callable = Ast::Callable; + } + + import Cfg_ } + private import test.TestCfg as TestCfg + private module Additional { /* * CFG printing @@ -2236,6 +2244,18 @@ module Make0 Ast> { import Pp::PrintGraph + /* + * CFG testing + */ + + private module TestInput implements TestCfg::InputSig { + AstNode getParent(AstNode node) { node = getChild(result, _) } + + predicate getEnclosingCallable = Ast::getEnclosingCallable/1; + } + + module TestCfgInline = TestCfg::Make; + /** Provides a set of consistency queries. */ module Consistency { /** Holds if the consistency query `query` has `results` results. */ diff --git a/shared/controlflow/codeql/controlflow/test/TestCfg.qll b/shared/controlflow/codeql/controlflow/test/TestCfg.qll new file mode 100644 index 000000000000..55a58e355b44 --- /dev/null +++ b/shared/controlflow/codeql/controlflow/test/TestCfg.qll @@ -0,0 +1,419 @@ +/** + * Provides query predicates for testing the CFG in an inline expectation qltest. + */ +overlay[local?] +module; + +private import codeql.controlflow.SuccessorType +private import codeql.util.Location + +signature module CfgSig { + /** An AST node. */ + class AstNode { + /** Gets a textual representation of this AST node. */ + string toString(); + + /** Gets the location of this AST node. */ + Location getLocation(); + } + + /** A callable, for example a function, method, constructor, or top-level script. */ + class Callable; + + /** A control flow node. */ + class ControlFlowNode { + /** Gets a textual representation of this control flow node. */ + string toString(); + + /** Gets the location of this control flow node. */ + Location getLocation(); + + /** Gets the basic block containing this control flow node. */ + BasicBlock getBasicBlock(); + + /** + * Holds if this is the unique control flow node that represents the + * given AST node. + */ + predicate injects(AstNode n); + + /** Gets the enclosing callable of this control flow node. */ + Callable getEnclosingCallable(); + } + + /** + * A basic block, that is, a maximal straight-line sequence of control flow nodes + * without branches or joins. + */ + class BasicBlock { + /** Gets a textual representation of this basic block. */ + string toString(); + + /** Gets the location of this basic block. */ + Location getLocation(); + + /** Gets the control flow node at a specific (zero-indexed) position in this basic block. */ + ControlFlowNode getNode(int pos); + + /** Gets an immediate successor of this basic block of a given type, if any. */ + BasicBlock getASuccessor(SuccessorType t); + + /** Gets the enclosing callable of this basic block. */ + Callable getEnclosingCallable(); + } +} + +signature class TypSig; + +signature module InputSig { + /** Gets the parent of `node`. */ + AstNode getParent(AstNode node); + + /** Gets the immediately enclosing callable that contains `node`. */ + Callable getEnclosingCallable(AstNode node); +} + +/** + * Constructs several query predicates for testing the CFG in an inline expectation qltest. + * + * The output is based on basic block slices, that is, block segments cut by line boundaries. + * Ordinary left-to-right intra-block control flow is elided, but everything else is represented. + * + * A nested module `BlockSlices` can be imported to dump all basic block slices. + */ +module Make Cfg, InputSig Input> +{ + private import Cfg + + /** Holds if `n` from `bb` starts at the given line. */ + private predicate node(int line, ControlFlowNode n, BasicBlock bb) { + n.injects(_) and + n.getLocation().getStartLine() = line and + n.getBasicBlock() = bb + } + + /** Gets the rank of `n` within the slice of `bb` at the given line. */ + private int sliceRank(int line, ControlFlowNode n, BasicBlock bb) { + n = + rank[result](ControlFlowNode n0, int i | + node(line, n0, bb) and n0 = bb.getNode(i) + | + n0 order by i + ) + } + + /** + * A direction in the location-induced AST restricted to a single line, that + * is, the tree arising from location-nesting. + * + * - `Up` and `Down` indicate location nesting. + * - `Right` indicates a non-overlapping location to the right. + * - `Id` indicates the same location. + * - `Other` most likely indicates a non-overlapping location to the left, + * but remains a catch-all for any other case. + */ + private newtype Dir = + Down() or + Up() or + Right() or + Id() or + Other() + + /** + * Holds if `n1` steps to `n2` within a basic block line slice and that the + * corresponding AST nodes are related with one being a transitive parent of + * the other. The direction in the AST is given by `dir`. + */ + private predicate astUpDownStep(ControlFlowNode n1, ControlFlowNode n2, Dir dir) { + exists(int line, BasicBlock bb, AstNode a1, AstNode a2 | + n1.injects(a1) and + n2.injects(a2) and + sliceRank(line, n1, bb) + 1 = sliceRank(line, n2, bb) + | + if Input::getParent+(a1) = a2 + then dir = Up() + else + if Input::getParent+(a2) = a1 + then dir = Down() + else none() + ) + } + + bindingset[l1, l2] + pragma[inline_late] + private predicate endsLessThan(Location l1, Location l2) { + l1.getEndLine() < l2.getEndLine() + or + l1.getEndLine() = l2.getEndLine() and + l1.getEndColumn() <= l2.getEndColumn() + } + + private predicate oneline(Location l) { l.getStartLine() = l.getEndLine() } + + /** + * Holds if `n1` steps to `n2` within a basic block line slice of `bb` at + * `line` and that the step in locations is given by `dir`. Some identical + * locations may be further resolved by peeking at the AST structure. + */ + private predicate singleLineBlockStep( + BasicBlock bb, int line, ControlFlowNode n1, ControlFlowNode n2, Dir dir + ) { + sliceRank(line, n1, bb) + 1 = sliceRank(line, n2, bb) and + exists(Location l1, Location l2 | n1.getLocation() = l1 and n2.getLocation() = l2 | + if oneline(l1) and l1.getEndColumn() < l2.getStartColumn() + then dir = Right() + else + if + l1.getStartColumn() <= l2.getStartColumn() and + endsLessThan(l2, l1) and + l1 != l2 + then dir = Down() + else + if + l2.getStartColumn() <= l1.getStartColumn() and + endsLessThan(l1, l2) and + l1 != l2 + then dir = Up() + else + if l1 != l2 + then dir = Other() + else ( + astUpDownStep(n1, n2, dir) + or + not astUpDownStep(n1, n2, _) and dir = Id() + ) + ) + } + + /** + * Holds if the `line` slice of the basic block `bb` is simple left-to-right + * evaluation order. + * + * Both pre-order and post-order traversal is allowed and allowed to be + * mixed. `Up` indicates the last part of a post-order traversal, and `Down` + * indicates the first part of a pre-order traversal, so an `Up` step + * followed by a `Down` step is inconsistent with simple left-to-right + * evaluation order. + */ + private predicate simpleLeftToRightBlock(int line, BasicBlock bb) { + node(line, _, bb) and + forall(ControlFlowNode n1, ControlFlowNode n2 | singleLineBlockStep(bb, line, n1, n2, _) | + exists(Dir dir | singleLineBlockStep(bb, line, n1, n2, dir) | + dir != Other() and + dir != Id() and + (dir = Up() implies not singleLineBlockStep(bb, line, n2, _, Down())) + ) + ) + } + + private string arrow(ControlFlowNode n) { + exists(Dir dir | singleLineBlockStep(_, _, n, _, dir) | + dir = Down() and result = " -V " + or + dir = Up() and result = " -^ " + or + dir = Right() and result = " -> " + or + dir = Id() and result = " -I " + or + dir = Other() and result = " -? " + ) + } + + module BlockSlices { + /** + * Holds if `blockSlice` is a string representation of the `line` slice of a + * basic block. `first` is the first node in the slice. + */ + query predicate blockSlice(int line, ControlFlowNode first, string blockSlice) { + exists(BasicBlock bb | + 1 = sliceRank(line, first, bb) and + blockSlice = + "'" + + strictconcat(ControlFlowNode n, int r, int i, string s | + r = sliceRank(line, n, bb) and + ( + i = 0 and s = n.toString() + or + i = 1 and s = arrow(n) + ) + | + s order by r, i + ) + "'" + ) + } + } + + final private class FinalControlFlowNode = ControlFlowNode; + + /** A `ControlFlowNode` with its location trimmed to a single line. */ + class ControlFlowNode1line extends FinalControlFlowNode { + predicate hasLocationInfo(string file, int sl, int sc, int el, int ec) { + exists(int el0, int ec0 | + super.getLocation().hasLocationInfo(file, sl, sc, el0, ec0) and + if el0 != sl then el = sl and ec = sc else (el = el0 and ec = ec0) + ) + } + } + + /** + * Holds if `blockSlice` is a string representation of a line slice of a + * basic block that does not follow simple left-to-right evaluation order. + * `first` is the first node in the slice. + */ + query predicate nonSimple(ControlFlowNode1line first, string blockSlice) { + exists(int line, BasicBlock bb | + BlockSlices::blockSlice(line, first, blockSlice) and + bb = first.getBasicBlock() and + not simpleLeftToRightBlock(line, bb) + ) + } + + /** + * Gets the rank of `n` within `bb` restricted to nodes that are canonical + * representatives of AST nodes. + */ + private int bbRank(ControlFlowNode n, BasicBlock bb) { + n = + rank[result](ControlFlowNode n0, int i | n0.injects(_) and bb.getNode(i) = n0 | n0 order by i) + } + + /** + * Holds if some AST node on `line` has a corresponding CFG node within the + * callable `c`. + */ + private predicate lineHasCfg(int line, Callable c) { + exists(ControlFlowNode n, Location loc | + n.getLocation() = loc and + loc.getStartLine() = line and + oneline(loc) and + n.injects(_) and + n.getEnclosingCallable() = c + ) + } + + /** + * Holds if `n1` and `n2` are consecutive nodes in a basic block (skipping + * over non-AST nodes), but are located on different lines, such that `n1` + * and `n2` link two line slices of a basic block. Additionally, the link is + * required to be non-trivial in the sense that it either goes backwards (the + * `lineDelta` is negative) or it skips over some lines with other CFG nodes. + */ + private predicate nonTrivialSliceLink( + int line, ControlFlowNode n1, ControlFlowNode n2, int lineDelta + ) { + line = n1.getLocation().getStartLine() and + exists(int last, BasicBlock bb | + last = sliceRank(line, n1, bb) and + not last + 1 = sliceRank(line, _, bb) and + bbRank(n1, bb) + 1 = bbRank(n2, bb) + ) and + lineDelta = n2.getLocation().getStartLine() - n1.getLocation().getStartLine() and + (lineDelta < 0 or lineHasCfg([line + 1 .. line + lineDelta - 1], n1.getEnclosingCallable())) + } + + bindingset[lineDelta] + private string ppDelta(int lineDelta) { + if lineDelta < 0 + then result = "(" + lineDelta.toString() + ")" + else result = "(+" + lineDelta.toString() + ")" + } + + /** + * Holds if the line slice ending at `n1` continues to another line slice via + * a non-trivial link. That is, it does not simply continue to the next line. + */ + query predicate bbContinues(ControlFlowNode1line n1, string link) { + exists(ControlFlowNode n2, int lineDelta | + nonTrivialSliceLink(_, n1, n2, lineDelta) and + link = "'" + n1.toString() + " goto " + n2.toString() + ppDelta(lineDelta) + "'" + ) + } + + /** + * Holds if `bb` only includes synthetic nodes, that is, no AST nodes are + * canonically represented in it. + */ + private predicate synthBlock(BasicBlock bb) { not exists(bbRank(_, bb)) } + + /** + * Holds if `bb1` transitively reaches `bb2` through a sequence of basic + * block steps where `bb2` is the only non-`synthBlock`. + */ + private predicate synthStep(BasicBlock bb1, BasicBlock bb2, string successorSuffix) { + bb1 = bb2 and successorSuffix = "" and not synthBlock(bb2) + or + exists(BasicBlock mid, SuccessorType t, string s | + synthBlock(bb1) and + bb1.getASuccessor(t) = mid and + synthStep(mid, bb2, s) and + if t instanceof DirectSuccessor + then successorSuffix = s + else successorSuffix = "," + t.toString() + s + ) + } + + /** + * Holds if there is a basic block step from `n1` to `n2` with successor + * type `t` originating at the given line. + */ + private predicate bbStep( + int line, ControlFlowNode n1, ControlFlowNode n2, SuccessorType t, string successorSuffix, + int lineDelta + ) { + exists(int last, BasicBlock bb1, BasicBlock mid, BasicBlock bb2 | + line = n1.getLocation().getStartLine() and + last = bbRank(n1, bb1) and + not last + 1 = bbRank(_, bb1) and + bb1.getASuccessor(t) = mid and + synthStep(mid, bb2, successorSuffix) and + 1 = bbRank(n2, bb2) and + lineDelta = n2.getLocation().getStartLine() - n1.getLocation().getStartLine() + ) + } + + /** + * Holds if there is a basic block step from `n1` described by `next`. + */ + query predicate bbStep(ControlFlowNode1line n1, string next) { + exists(ControlFlowNode n2, SuccessorType t, string s, int lineDelta | + bbStep(_, n1, n2, t, s, lineDelta) and + next = "'" + n1.toString() + " : " + t + s + " -> " + n2.toString() + ppDelta(lineDelta) + "'" + ) + } + + /** + * Holds if no CFG nodes exist in `c` on `line` and `a` is an AST node on + * that line. + */ + private predicate unreachable(int line, Callable c, AstNode a) { + oneline(a.getLocation()) and + a.getLocation().getStartLine() = line and + Input::getEnclosingCallable(a) = c and + not exists(ControlFlowNode n | + n.injects(_) and + oneline(n.getLocation()) and + n.getLocation().getStartLine() = line and + n.getEnclosingCallable() = c + ) + } + + /** + * Holds if no CFG nodes exist in `c` on `line` and `a` is the first AST + * node on that line. + */ + private predicate firstUnreachable(int line, Callable c, AstNode a) { + a = + min(AstNode a0, Location loc | + unreachable(line, c, a0) and loc = a0.getLocation() + | + a0 order by loc.getStartColumn(), loc.getEndColumn() + ) + } + + /** + * Holds if no CFG nodes exist in the callable of `a` on the same line as + * `a`, and `a` is the first AST node on that line. + */ + query predicate noCfg(AstNode a) { firstUnreachable(_, _, a) } +} From e09fa64689516f5e6dd7acc95f89850fada6cb2d Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Mon, 14 Sep 2026 10:35:36 +0200 Subject: [PATCH 2/4] Unified: Add Swift CFG test. --- .../controlflow/basicblock-slices.expected | 456 +++++++++++++ .../controlflow/basicblock-slices.ql | 2 + .../library-tests/controlflow/cfg.expected | 157 +++++ .../ql/test/library-tests/controlflow/cfg.ql | 2 + .../test/library-tests/controlflow/cfg.qlref | 2 + .../test/library-tests/controlflow/cfg.swift | 604 ++++++++++++++++++ 6 files changed, 1223 insertions(+) create mode 100644 unified/ql/test/library-tests/controlflow/basicblock-slices.expected create mode 100644 unified/ql/test/library-tests/controlflow/basicblock-slices.ql create mode 100644 unified/ql/test/library-tests/controlflow/cfg.expected create mode 100644 unified/ql/test/library-tests/controlflow/cfg.ql create mode 100644 unified/ql/test/library-tests/controlflow/cfg.qlref create mode 100644 unified/ql/test/library-tests/controlflow/cfg.swift diff --git a/unified/ql/test/library-tests/controlflow/basicblock-slices.expected b/unified/ql/test/library-tests/controlflow/basicblock-slices.expected new file mode 100644 index 000000000000..f9744225faf2 --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/basicblock-slices.expected @@ -0,0 +1,456 @@ +| 1 | cfg.swift:1:1:604:2 | Block | 'Block -V VariableDeclaration -V topLevelDecl -> Int -> 0' | +| 2 | cfg.swift:2:1:2:1 | 0 | '0' | +| 3 | cfg.swift:3:1:3:12 | topLevelDecl | 'topLevelDecl -> + -> 1 -^ BinaryExpr' | +| 5 | cfg.swift:5:1:5:37 | Block | 'Block -V 0 -^ ReturnExpr' | +| 5 | cfg.swift:5:1:5:37 | FunctionDeclaration | 'FunctionDeclaration' | +| 7 | cfg.swift:7:1:7:10 | returnZero | 'returnZero -^ CallExpr' | +| 8 | cfg.swift:8:1:8:6 | Double | 'Double -> Argument -V topLevelDecl -^ CallExpr' | +| 10 | cfg.swift:10:1:13:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyError -^ BaseType -V Error' | +| 11 | cfg.swift:11:10:11:16 | VariableDeclaration | 'VariableDeclaration -V error1 -> VariableDeclaration -V error2' | +| 12 | cfg.swift:12:10:12:31 | ClassLikeDeclaration | 'ClassLikeDeclaration -V error3 -^ ConstructorDeclaration' | +| 12 | cfg.swift:12:17:12:25 | withParam | 'withParam -^ Block' | +| 15 | cfg.swift:15:1:17:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 15 | cfg.swift:15:13:15:13 | x | 'x -^ Block' | +| 16 | cfg.swift:16:10:16:10 | x | 'x -> == -> 0 -^ BinaryExpr -^ ReturnExpr' | +| 19 | cfg.swift:19:1:26:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 19 | cfg.swift:19:17:19:17 | x | 'x -^ Block' | +| 20 | cfg.swift:20:3:22:3 | GuardIfStmt | 'GuardIfStmt -V x -> >= -> 0 -^ BinaryExpr -> Block' | +| 21 | cfg.swift:21:11:21:17 | MyError | 'MyError -^ MemberAccessExpr -^ ThrowExpr' | +| 28 | cfg.swift:28:1:45:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 28 | cfg.swift:28:15:28:15 | x | 'x -^ Block' | +| 29 | cfg.swift:29:3:43:3 | TryExpr | 'TryExpr -V Block' | +| 30 | cfg.swift:30:9:30:18 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try -^ UnaryExpr' | +| 31 | cfg.swift:31:5:31:9 | print | 'print -> Argument -V "Did not throw." -^ CallExpr' | +| 32 | cfg.swift:32:10:32:19 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try! -^ UnaryExpr' | +| 33 | cfg.swift:33:5:33:9 | print | 'print -> Argument -V "Still did not throw." -^ CallExpr' | +| 44 | cfg.swift:44:10:44:10 | 0 | '0 -^ ReturnExpr' | +| 47 | cfg.swift:47:1:51:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 47 | cfg.swift:47:21:47:21 | s | 's -^ Block' | +| 48 | cfg.swift:48:10:50:3 | Block | 'Block' | +| 48 | cfg.swift:48:10:50:3 | FunctionExpr | 'FunctionExpr -^ ReturnExpr' | +| 49 | cfg.swift:49:12:49:12 | s | 's -> + -> "" -^ BinaryExpr -^ ReturnExpr' | +| 53 | cfg.swift:53:1:58:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 53 | cfg.swift:53:21:53:21 | x | 'x -^ Block' | +| 54 | cfg.swift:54:3:56:3 | FunctionDeclaration | 'FunctionDeclaration' | +| 54 | cfg.swift:54:10:54:10 | y | 'y -^ Block' | +| 55 | cfg.swift:55:12:55:12 | x | 'x -> + -> y -^ BinaryExpr -^ ReturnExpr' | +| 57 | cfg.swift:57:10:57:10 | f | 'f -^ ReturnExpr' | +| 60 | cfg.swift:60:1:64:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 60 | cfg.swift:60:21:60:21 | x | 'x -^ Block' | +| 61 | cfg.swift:61:10:63:3 | Block | 'Block' | +| 61 | cfg.swift:61:10:63:3 | FunctionExpr | 'FunctionExpr -^ ReturnExpr' | +| 62 | cfg.swift:62:6:62:6 | y | 'y -> x -> + -> y -^ BinaryExpr' | +| 66 | cfg.swift:66:1:70:1 | Block | 'Block' | +| 66 | cfg.swift:66:1:70:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 67 | cfg.swift:67:3:67:34 | VariableDeclaration | 'VariableDeclaration -V x1 -> createClosure1 -> Argument -V "" -^ CallExpr -^ CallExpr' | +| 68 | cfg.swift:68:3:68:35 | VariableDeclaration | 'VariableDeclaration -V x2 -> createClosure2 -> Argument -V 0 -^ CallExpr -> Argument -V 10 -^ CallExpr' | +| 69 | cfg.swift:69:3:69:35 | VariableDeclaration | 'VariableDeclaration -V x3 -> createClosure3 -> Argument -V 0 -^ CallExpr -> Argument -V 10 -^ CallExpr' | +| 72 | cfg.swift:72:1:75:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 72 | cfg.swift:72:20:72:20 | s | 's -^ Block' | +| 73 | cfg.swift:73:3:73:23 | VariableDeclaration | 'VariableDeclaration -V n -> Optional -V Int -^ GenericTypeExpr -> Int -> Argument -V s -^ CallExpr' | +| 74 | cfg.swift:74:10:74:10 | n | 'n -^ ReturnExpr' | +| 77 | cfg.swift:77:1:81:1 | Block | 'Block' | +| 77 | cfg.swift:77:1:81:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 78 | cfg.swift:78:3:78:36 | VariableDeclaration | 'VariableDeclaration -V nBang -> maybeParseInt -> Argument -V "42" -^ CallExpr -^ ! -^ UnaryExpr' | +| 79 | cfg.swift:79:3:79:31 | VariableDeclaration | 'VariableDeclaration -V n -> maybeParseInt -> Argument -V "42" -^ CallExpr' | +| 80 | cfg.swift:80:10:80:14 | nBang | 'nBang -> + -> n -^ ! -^ UnaryExpr -^ BinaryExpr -^ ReturnExpr' | +| 83 | cfg.swift:83:1:98:1 | Block | 'Block' | +| 83 | cfg.swift:83:1:98:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 84 | cfg.swift:84:3:84:15 | VariableDeclaration | 'VariableDeclaration -V temp -> 10' | +| 86 | cfg.swift:86:3:88:3 | FunctionDeclaration | 'FunctionDeclaration' | +| 86 | cfg.swift:86:12:86:12 | a | 'a -^ Block' | +| 87 | cfg.swift:87:5:87:5 | a | 'a -> a -> + -> 1 -^ BinaryExpr -^ AssignExpr' | +| 90 | cfg.swift:90:3:92:3 | FunctionDeclaration | 'FunctionDeclaration' | +| 90 | cfg.swift:90:20:90:20 | a | 'a -^ Block' | +| 91 | cfg.swift:91:5:91:5 | a | 'a -> nil -^ AssignExpr' | +| 94 | cfg.swift:94:3:94:5 | add | 'add -> Argument -V -^ CallExpr' | +| 95 | cfg.swift:95:3:95:30 | VariableDeclaration | 'VariableDeclaration -V tempOptional -> Optional -V Int -^ GenericTypeExpr -> 10' | +| 96 | cfg.swift:96:3:96:13 | addOptional | 'addOptional -> Argument -V -^ CallExpr' | +| 97 | cfg.swift:97:10:97:13 | temp | 'temp -> + -> tempOptional -^ ! -^ UnaryExpr -^ BinaryExpr -^ ReturnExpr' | +| 100 | cfg.swift:100:1:109:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V C' | +| 101 | cfg.swift:101:3:101:16 | VariableDeclaration | 'VariableDeclaration -V myInt -> Int' | +| 102 | cfg.swift:102:3:104:3 | ConstructorDeclaration | 'ConstructorDeclaration' | +| 102 | cfg.swift:102:8:102:8 | n | 'n -^ Block' | +| 103 | cfg.swift:103:5:103:9 | myInt | 'myInt -> n -^ AssignExpr' | +| 106 | cfg.swift:106:3:108:3 | Block | 'Block' | +| 106 | cfg.swift:106:3:108:3 | FunctionDeclaration | 'FunctionDeclaration' | +| 107 | cfg.swift:107:12:107:16 | myInt | 'myInt -^ ReturnExpr' | +| 111 | cfg.swift:111:1:137:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 111 | cfg.swift:111:20:111:24 | param | 'param -> inoutParam -> opt -^ Block' | +| 112 | cfg.swift:112:3:112:18 | VariableDeclaration | 'VariableDeclaration -V c -> C -> Argument -V 42 -^ CallExpr' | +| 113 | cfg.swift:113:3:113:18 | VariableDeclaration | 'VariableDeclaration -V n1 -> c -^ MemberAccessExpr' | +| 114 | cfg.swift:114:3:114:23 | VariableDeclaration | 'VariableDeclaration -V n2 -> c -^ MemberAccessExpr -^ MemberAccessExpr' | +| 115 | cfg.swift:115:3:115:23 | VariableDeclaration | 'VariableDeclaration -V n3 -> c -^ MemberAccessExpr -^ CallExpr' | +| 116 | cfg.swift:116:3:116:28 | VariableDeclaration | 'VariableDeclaration -V n4 -> c -^ MemberAccessExpr -^ MemberAccessExpr -^ CallExpr' | +| 117 | cfg.swift:117:3:117:22 | VariableDeclaration | 'VariableDeclaration -V n5 -> param -^ MemberAccessExpr' | +| 118 | cfg.swift:118:3:118:27 | VariableDeclaration | 'VariableDeclaration -V n6 -> param -^ MemberAccessExpr -^ MemberAccessExpr' | +| 120 | cfg.swift:120:3:120:32 | VariableDeclaration | 'VariableDeclaration -V n8 -> param -^ MemberAccessExpr -^ MemberAccessExpr -^ CallExpr' | +| 122 | cfg.swift:122:3:122:27 | VariableDeclaration | 'VariableDeclaration -V n9 -> inoutParam -^ MemberAccessExpr' | +| 123 | cfg.swift:123:3:123:27 | VariableDeclaration | 'VariableDeclaration -V n7 -> param -^ MemberAccessExpr -^ CallExpr' | +| 124 | cfg.swift:124:3:124:33 | VariableDeclaration | 'VariableDeclaration -V n10 -> inoutParam -^ MemberAccessExpr -^ MemberAccessExpr' | +| 125 | cfg.swift:125:3:125:33 | VariableDeclaration | 'VariableDeclaration -V n11 -> inoutParam -^ MemberAccessExpr -^ CallExpr' | +| 126 | cfg.swift:126:3:126:38 | VariableDeclaration | 'VariableDeclaration -V n12 -> inoutParam -^ MemberAccessExpr -^ MemberAccessExpr -^ CallExpr' | +| 128 | cfg.swift:128:3:128:22 | VariableDeclaration | 'VariableDeclaration -V n13 -> opt -^ ! -^ UnaryExpr -^ MemberAccessExpr' | +| 129 | cfg.swift:129:3:129:27 | VariableDeclaration | 'VariableDeclaration -V n14 -> opt -^ ! -^ UnaryExpr -^ MemberAccessExpr -^ MemberAccessExpr' | +| 130 | cfg.swift:130:3:130:27 | VariableDeclaration | 'VariableDeclaration -V n15 -> opt -^ ! -^ UnaryExpr -^ MemberAccessExpr -^ CallExpr' | +| 131 | cfg.swift:131:3:131:32 | VariableDeclaration | 'VariableDeclaration -V n16 -> opt -^ ! -^ UnaryExpr -^ MemberAccessExpr -^ MemberAccessExpr -^ CallExpr' | +| 133 | cfg.swift:133:3:133:22 | VariableDeclaration | 'VariableDeclaration -V n17 -> opt -^ MemberAccessExpr' | +| 134 | cfg.swift:134:3:134:27 | VariableDeclaration | 'VariableDeclaration -V n18 -> opt -^ MemberAccessExpr -^ MemberAccessExpr' | +| 135 | cfg.swift:135:3:135:27 | VariableDeclaration | 'VariableDeclaration -V n19 -> opt -^ MemberAccessExpr -^ CallExpr' | +| 136 | cfg.swift:136:3:136:32 | VariableDeclaration | 'VariableDeclaration -V n20 -> opt -^ MemberAccessExpr -^ MemberAccessExpr -^ CallExpr' | +| 139 | cfg.swift:139:1:166:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 139 | cfg.swift:139:15:139:15 | x | 'x -^ Block' | +| 140 | cfg.swift:140:3:141:12 | ForEachStmt | 'ForEachStmt -V 0 -> ... -> 10 -^ BinaryExpr' | +| 140 | cfg.swift:140:7:140:7 | _ | '_' | +| 141 | cfg.swift:141:9:141:12 | Block | 'Block' | +| 143 | cfg.swift:143:3:153:3 | SwitchExpr | 'SwitchExpr -V x' | +| 144 | cfg.swift:144:5:146:17 | Block | 'Block' | +| 144 | cfg.swift:144:5:146:17 | SwitchCase | 'SwitchCase -V 0 -> 1 -^ OrPattern' | +| 145 | cfg.swift:145:14:145:17 | true | 'true -^ ReturnExpr' | +| 147 | cfg.swift:147:5:150:17 | Block | 'Block' | +| 147 | cfg.swift:147:5:150:17 | SwitchCase | 'SwitchCase' | +| 147 | cfg.swift:147:10:147:10 | x | 'x -^ ConditionalPattern' | +| 148 | cfg.swift:148:9:149:17 | BinaryExpr | 'BinaryExpr -V x -> >= -> 2 -^ BinaryExpr' | +| 149 | cfg.swift:149:13:149:13 | x | 'x -> < -> 5 -^ BinaryExpr' | +| 150 | cfg.swift:150:14:150:17 | true | 'true -^ ReturnExpr' | +| 151 | cfg.swift:151:5:152:18 | SwitchCase | 'SwitchCase -V Block' | +| 152 | cfg.swift:152:14:152:18 | false | 'false -^ ReturnExpr' | +| 168 | cfg.swift:168:1:184:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 168 | cfg.swift:168:16:168:16 | x | 'x -^ Block' | +| 170 | cfg.swift:170:3:172:3 | | '' | +| 174 | cfg.swift:174:3:176:3 | | '' | +| 178 | cfg.swift:178:3:183:3 | | '' | +| 186 | cfg.swift:186:1:198:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 186 | cfg.swift:186:9:186:9 | x | 'x -^ Block' | +| 187 | cfg.swift:187:3:197:3 | IfExpr | 'IfExpr -V x -> > -> 2 -^ BinaryExpr' | +| 187 | cfg.swift:187:12:189:3 | Block | 'Block' | +| 188 | cfg.swift:188:5:188:9 | print | 'print -> Argument -V "x is greater than 2" -^ CallExpr' | +| 190 | cfg.swift:190:8:197:3 | IfExpr | 'IfExpr -V BinaryExpr -V BinaryExpr -V x -> <= -> 2 -^ BinaryExpr' | +| 191 | cfg.swift:191:13:191:13 | x | 'x -> > -> 0 -^ BinaryExpr' | +| 192 | cfg.swift:192:13:192:21 | UnaryExpr | 'UnaryExpr -V x -> == -> 5 -^ BinaryExpr' | +| 192 | cfg.swift:192:23:194:3 | Block | 'Block' | +| 193 | cfg.swift:193:5:193:9 | print | 'print -> Argument -V "x is 1" -^ CallExpr' | +| 195 | cfg.swift:195:8:197:3 | Block | 'Block' | +| 196 | cfg.swift:196:5:196:9 | print | 'print -> Argument -V "I can't guess the number" -^ CallExpr' | +| 200 | cfg.swift:200:1:205:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 200 | cfg.swift:200:9:200:9 | b | 'b -^ Block' | +| 201 | cfg.swift:201:3:203:3 | IfExpr | 'IfExpr -V b' | +| 201 | cfg.swift:201:8:203:3 | Block | 'Block' | +| 202 | cfg.swift:202:12:202:12 | 0 | '0 -^ ReturnExpr' | +| 204 | cfg.swift:204:10:204:10 | 1 | '1 -^ ReturnExpr' | +| 207 | cfg.swift:207:1:215:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 207 | cfg.swift:207:9:207:9 | x | 'x -^ Block' | +| 208 | cfg.swift:208:3:213:3 | IfExpr | 'IfExpr -V x -> < -> 0 -^ BinaryExpr' | +| 208 | cfg.swift:208:12:213:3 | Block | 'Block' | +| 209 | cfg.swift:209:5:209:5 | x | 'x -> x -? - -^ UnaryExpr -^ AssignExpr' | +| 210 | cfg.swift:210:5:212:5 | IfExpr | 'IfExpr -V x -> > -> 10 -^ BinaryExpr' | +| 210 | cfg.swift:210:15:212:5 | Block | 'Block' | +| 211 | cfg.swift:211:7:211:7 | x | 'x -> x -> - -> 1 -^ BinaryExpr -^ AssignExpr' | +| 214 | cfg.swift:214:10:214:10 | x | 'x -^ ReturnExpr' | +| 217 | cfg.swift:217:1:223:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 217 | cfg.swift:217:10:217:11 | b1 | 'b1 -> b2 -> b3 -^ Block' | +| 218 | cfg.swift:218:3:222:20 | ReturnExpr | 'ReturnExpr' | +| 218 | cfg.swift:218:10:222:20 | IfExpr | 'IfExpr -V IfExpr -V b1' | +| 219 | cfg.swift:219:13:219:14 | b2 | 'b2' | +| 220 | cfg.swift:220:13:220:14 | b3 | 'b3' | +| 221 | cfg.swift:221:9:221:18 | "b2 \|\| b3" | '"b2 \|\| b3"' | +| 222 | cfg.swift:222:9:222:20 | "!b2 \|\| !b3" | '"!b2 \|\| !b3"' | +| 225 | cfg.swift:225:1:234:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 225 | cfg.swift:225:31:225:31 | b | 'b -^ Block' | +| 226 | cfg.swift:226:3:233:3 | IfExpr | 'IfExpr -V IfExpr -V b' | +| 227 | cfg.swift:227:8:227:11 | true | 'true' | +| 228 | cfg.swift:228:7:228:10 | Bool | 'Bool -> Argument -V false -^ CallExpr' | +| 228 | cfg.swift:228:19:230:3 | Block | 'Block' | +| 229 | cfg.swift:229:12:229:14 | "b" | '"b" -^ ReturnExpr' | +| 231 | cfg.swift:231:8:233:3 | Block | 'Block' | +| 232 | cfg.swift:232:12:232:15 | "!b" | '"!b" -^ ReturnExpr' | +| 236 | cfg.swift:236:1:240:1 | Block | 'Block' | +| 236 | cfg.swift:236:1:240:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 237 | cfg.swift:237:3:239:3 | IfExpr | 'IfExpr -V UnaryExpr -V true' | +| 242 | cfg.swift:242:1:248:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 242 | cfg.swift:242:17:242:17 | b | 'b -^ Block' | +| 243 | cfg.swift:243:3:246:9 | IfExpr | 'IfExpr -V b' | +| 243 | cfg.swift:243:8:245:3 | Block | 'Block' | +| 244 | cfg.swift:244:5:244:9 | print | 'print -> Argument -V "true" -^ CallExpr' | +| 246 | cfg.swift:246:8:246:9 | Block | 'Block' | +| 247 | cfg.swift:247:3:247:7 | print | 'print -> Argument -V "done" -^ CallExpr' | +| 250 | cfg.swift:250:1:254:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 250 | cfg.swift:250:16:250:17 | b1 | 'b1 -> b2 -^ Block' | +| 251 | cfg.swift:251:3:253:3 | IfExpr | 'IfExpr -V BinaryExpr -V b1' | +| 251 | cfg.swift:251:13:251:14 | b2 | 'b2' | +| 251 | cfg.swift:251:17:253:3 | Block | 'Block' | +| 252 | cfg.swift:252:5:252:9 | print | 'print -> Argument -V "b1 or b2" -^ CallExpr' | +| 256 | cfg.swift:256:1:273:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 256 | cfg.swift:256:18:256:18 | a | 'a -> b -^ Block' | +| 257 | cfg.swift:257:3:257:15 | VariableDeclaration | 'VariableDeclaration -V c -> a -> + -> b -^ BinaryExpr' | +| 258 | cfg.swift:258:3:258:15 | VariableDeclaration | 'VariableDeclaration -V d -> a -> - -> b -^ BinaryExpr' | +| 259 | cfg.swift:259:3:259:15 | VariableDeclaration | 'VariableDeclaration -V e -> a -> * -> b -^ BinaryExpr' | +| 260 | cfg.swift:260:3:260:15 | VariableDeclaration | 'VariableDeclaration -V f -> a -> / -> b -^ BinaryExpr' | +| 261 | cfg.swift:261:3:261:15 | VariableDeclaration | 'VariableDeclaration -V g -> a -> % -> b -^ BinaryExpr' | +| 262 | cfg.swift:262:3:262:15 | VariableDeclaration | 'VariableDeclaration -V h -> a -> & -> b -^ BinaryExpr' | +| 263 | cfg.swift:263:3:263:15 | VariableDeclaration | 'VariableDeclaration -V i -> a -> \| -> b -^ BinaryExpr' | +| 264 | cfg.swift:264:3:264:15 | VariableDeclaration | 'VariableDeclaration -V j -> a -> ^ -> b -^ BinaryExpr' | +| 265 | cfg.swift:265:3:265:16 | VariableDeclaration | 'VariableDeclaration -V k -> a -> << -> b -^ BinaryExpr' | +| 266 | cfg.swift:266:3:266:16 | VariableDeclaration | 'VariableDeclaration -V l -> a -> >> -> b -^ BinaryExpr' | +| 267 | cfg.swift:267:3:267:16 | VariableDeclaration | 'VariableDeclaration -V o -> a -> == -> b -^ BinaryExpr' | +| 268 | cfg.swift:268:3:268:16 | VariableDeclaration | 'VariableDeclaration -V p -> a -> != -> b -^ BinaryExpr' | +| 269 | cfg.swift:269:3:269:15 | VariableDeclaration | 'VariableDeclaration -V q -> a -> < -> b -^ BinaryExpr' | +| 270 | cfg.swift:270:3:270:16 | VariableDeclaration | 'VariableDeclaration -V r -> a -> <= -> b -^ BinaryExpr' | +| 271 | cfg.swift:271:3:271:15 | VariableDeclaration | 'VariableDeclaration -V s -> a -> > -> b -^ BinaryExpr' | +| 272 | cfg.swift:272:3:272:16 | VariableDeclaration | 'VariableDeclaration -V t -> a -> >= -> b -^ BinaryExpr' | +| 275 | cfg.swift:275:1:277:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 275 | cfg.swift:275:25:275:25 | x | 'x -> y -^ Block' | +| 276 | cfg.swift:276:11:276:10 | | ' -> interpolation -V Argument -V x -^ CallExpr -> + -> interpolation -V Argument -V y -^ CallExpr -> is equal to -> interpolation -V Argument -V x -> + -> y -^ BinaryExpr -^ CallExpr -> and here is a zero: -> interpolation -V Argument -V returnZero -^ CallExpr -^ CallExpr -> -^ StringInterpolationExpr -^ ReturnExpr' | +| 279 | cfg.swift:279:1:310:1 | Block | 'Block' | +| 279 | cfg.swift:279:1:310:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 280 | cfg.swift:280:3:280:44 | VariableDeclaration | 'VariableDeclaration -V a -> 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -^ ArrayLiteral' | +| 281 | cfg.swift:281:3:281:3 | a | 'a -> Argument -V 0 -^ CallExpr -> 0 -^ AssignExpr' | +| 282 | cfg.swift:282:3:282:3 | a | 'a -> Argument -V 1 -^ CallExpr -> += -> 1 -^ CompoundAssignExpr' | +| 283 | cfg.swift:283:3:283:3 | a | 'a -> Argument -V 2 -^ CallExpr -> -= -> 1 -^ CompoundAssignExpr' | +| 284 | cfg.swift:284:3:284:3 | a | 'a -> Argument -V 3 -^ CallExpr -> *= -> 1 -^ CompoundAssignExpr' | +| 285 | cfg.swift:285:3:285:3 | a | 'a -> Argument -V 4 -^ CallExpr -> /= -> 1 -^ CompoundAssignExpr' | +| 286 | cfg.swift:286:3:286:3 | a | 'a -> Argument -V 5 -^ CallExpr -> %= -> 1 -^ CompoundAssignExpr' | +| 287 | cfg.swift:287:3:287:3 | a | 'a -> Argument -V 6 -^ CallExpr -> &= -> 1 -^ CompoundAssignExpr' | +| 288 | cfg.swift:288:3:288:3 | a | 'a -> Argument -V 7 -^ CallExpr -> \|= -> 1 -^ CompoundAssignExpr' | +| 289 | cfg.swift:289:3:289:3 | a | 'a -> Argument -V 8 -^ CallExpr -> ^= -> 1 -^ CompoundAssignExpr' | +| 290 | cfg.swift:290:3:290:3 | a | 'a -> Argument -V 9 -^ CallExpr -> <<= -> 1 -^ CompoundAssignExpr' | +| 291 | cfg.swift:291:3:291:3 | a | 'a -> Argument -V 10 -^ CallExpr -> >>= -> 1 -^ CompoundAssignExpr' | +| 293 | cfg.swift:293:3:293:49 | VariableDeclaration | 'VariableDeclaration -V tupleWithA -> Argument -V a -> Argument -V 0 -^ CallExpr -> Argument -V a -> Argument -V 1 -^ CallExpr -> Argument -V a -> Argument -V 2 -^ CallExpr -> Argument -V a -> Argument -V 3 -^ CallExpr -> Argument -V a -> Argument -V 4 -^ CallExpr -^ TupleExpr' | +| 295 | cfg.swift:295:3:295:48 | VariableDeclaration | 'VariableDeclaration -V b -> 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> 10 -> 11 -^ ArrayLiteral' | +| 296 | cfg.swift:296:3:296:3 | b | 'b -> Argument -V 0 -^ CallExpr -> a -> Argument -V 10 -^ CallExpr -^ AssignExpr' | +| 297 | cfg.swift:297:3:297:3 | b | 'b -> Argument -V 1 -^ CallExpr -> b -> Argument -V 0 -^ CallExpr -> + -> 1 -^ BinaryExpr -^ AssignExpr' | +| 298 | cfg.swift:298:3:298:3 | b | 'b -> Argument -V 2 -^ CallExpr -> b -> Argument -V 1 -^ CallExpr -> - -> 1 -^ BinaryExpr -^ AssignExpr' | +| 299 | cfg.swift:299:3:299:3 | b | 'b -> Argument -V 3 -^ CallExpr -> b -> Argument -V 2 -^ CallExpr -> * -> 1 -^ BinaryExpr -^ AssignExpr' | +| 300 | cfg.swift:300:3:300:3 | b | 'b -> Argument -V 4 -^ CallExpr -> b -> Argument -V 3 -^ CallExpr -> / -> 1 -^ BinaryExpr -^ AssignExpr' | +| 301 | cfg.swift:301:3:301:3 | b | 'b -> Argument -V 5 -^ CallExpr -> b -> Argument -V 4 -^ CallExpr -> % -> 1 -^ BinaryExpr -^ AssignExpr' | +| 302 | cfg.swift:302:3:302:3 | b | 'b -> Argument -V 6 -^ CallExpr -> b -> Argument -V 5 -^ CallExpr -> & -> 1 -^ BinaryExpr -^ AssignExpr' | +| 303 | cfg.swift:303:3:303:3 | b | 'b -> Argument -V 7 -^ CallExpr -> b -> Argument -V 6 -^ CallExpr -> \| -> 1 -^ BinaryExpr -^ AssignExpr' | +| 304 | cfg.swift:304:3:304:3 | b | 'b -> Argument -V 8 -^ CallExpr -> b -> Argument -V 7 -^ CallExpr -> ^ -> 1 -^ BinaryExpr -^ AssignExpr' | +| 305 | cfg.swift:305:3:305:3 | b | 'b -> Argument -V 9 -^ CallExpr -> b -> Argument -V 8 -^ CallExpr -> << -> 1 -^ BinaryExpr -^ AssignExpr' | +| 306 | cfg.swift:306:3:306:3 | b | 'b -> Argument -V 10 -^ CallExpr -> b -> Argument -V 9 -^ CallExpr -> >> -> 1 -^ BinaryExpr -^ AssignExpr' | +| 308 | cfg.swift:308:3:308:39 | VariableDeclaration | 'VariableDeclaration -V Argument -V a1 -> Argument -V a2 -> Argument -V a3 -> Argument -V a4 -> Argument -V a5 -^ TupleExpr -> tupleWithA' | +| 309 | cfg.swift:309:11:309:20 | Argument | 'Argument -V a1 -> + -> b -> Argument -V 0 -^ CallExpr -^ BinaryExpr -> Argument -V a2 -> + -> b -> Argument -V 1 -^ CallExpr -^ BinaryExpr -> Argument -V a3 -> + -> b -> Argument -V 2 -^ CallExpr -^ BinaryExpr -> Argument -V a4 -> + -> b -> Argument -V 3 -^ CallExpr -^ BinaryExpr -> Argument -V a5 -> + -> b -> Argument -V 4 -^ CallExpr -^ BinaryExpr -^ TupleExpr -^ ReturnExpr' | +| 312 | cfg.swift:312:1:317:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 312 | cfg.swift:312:12:312:12 | x | 'x -^ Block' | +| 313 | cfg.swift:313:3:316:3 | WhileStmt | 'WhileStmt' | +| 313 | cfg.swift:313:9:313:9 | x | 'x -> >= -> 0 -^ BinaryExpr' | +| 313 | cfg.swift:313:16:316:3 | Block | 'Block' | +| 314 | cfg.swift:314:5:314:9 | print | 'print -> Argument -V x -^ CallExpr' | +| 315 | cfg.swift:315:5:315:5 | x | 'x -> -= -> 1 -^ CompoundAssignExpr' | +| 319 | cfg.swift:319:1:332:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 319 | cfg.swift:319:12:319:12 | x | 'x -^ Block' | +| 320 | cfg.swift:320:3:330:3 | WhileStmt | 'WhileStmt' | +| 320 | cfg.swift:320:9:320:9 | x | 'x -> >= -> 0 -^ BinaryExpr' | +| 320 | cfg.swift:320:16:330:3 | Block | 'Block' | +| 321 | cfg.swift:321:5:321:9 | print | 'print -> Argument -V x -^ CallExpr' | +| 322 | cfg.swift:322:5:322:5 | x | 'x -> -= -> 1 -^ CompoundAssignExpr' | +| 323 | cfg.swift:323:5:328:5 | IfExpr | 'IfExpr -V x -> > -> 100 -^ BinaryExpr' | +| 323 | cfg.swift:323:16:325:5 | Block | 'Block' | +| 324 | cfg.swift:324:7:324:11 | BreakExpr | 'BreakExpr' | +| 326 | cfg.swift:326:10:328:5 | IfExpr | 'IfExpr -V x -> > -> 50 -^ BinaryExpr' | +| 326 | cfg.swift:326:20:328:5 | Block | 'Block' | +| 327 | cfg.swift:327:7:327:14 | ContinueExpr | 'ContinueExpr' | +| 329 | cfg.swift:329:5:329:9 | print | 'print -> Argument -V "Iter" -^ CallExpr' | +| 331 | cfg.swift:331:3:331:7 | print | 'print -> Argument -V "Done" -^ CallExpr' | +| 334 | cfg.swift:334:1:349:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 334 | cfg.swift:334:18:334:18 | x | 'x -^ Block' | +| 335 | cfg.swift:335:3:348:3 | LabeledStmt | 'LabeledStmt -V WhileStmt' | +| 335 | cfg.swift:335:16:335:16 | x | 'x -> >= -> 0 -^ BinaryExpr' | +| 335 | cfg.swift:335:23:348:3 | Block | 'Block' | +| 336 | cfg.swift:336:5:346:5 | LabeledStmt | 'LabeledStmt -V WhileStmt' | +| 336 | cfg.swift:336:18:336:18 | x | 'x -> >= -> 0 -^ BinaryExpr' | +| 336 | cfg.swift:336:25:346:5 | Block | 'Block' | +| 337 | cfg.swift:337:7:337:11 | print | 'print -> Argument -V x -^ CallExpr' | +| 338 | cfg.swift:338:7:338:7 | x | 'x -> -= -> 1 -^ CompoundAssignExpr' | +| 339 | cfg.swift:339:7:344:7 | IfExpr | 'IfExpr -V x -> > -> 100 -^ BinaryExpr' | +| 339 | cfg.swift:339:18:341:7 | Block | 'Block' | +| 340 | cfg.swift:340:9:340:19 | BreakExpr | 'BreakExpr' | +| 342 | cfg.swift:342:12:344:7 | IfExpr | 'IfExpr -V x -> > -> 50 -^ BinaryExpr' | +| 342 | cfg.swift:342:22:344:7 | Block | 'Block' | +| 343 | cfg.swift:343:9:343:22 | ContinueExpr | 'ContinueExpr' | +| 345 | cfg.swift:345:7:345:11 | print | 'print -> Argument -V "Iter" -^ CallExpr' | +| 347 | cfg.swift:347:5:347:9 | print | 'print -> Argument -V "Done" -^ CallExpr' | +| 351 | cfg.swift:351:1:356:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 351 | cfg.swift:351:17:351:17 | x | 'x -^ Block' | +| 352 | cfg.swift:352:3:355:16 | DoWhileStmt | 'DoWhileStmt' | +| 352 | cfg.swift:352:10:355:3 | Block | 'Block' | +| 353 | cfg.swift:353:5:353:9 | print | 'print -> Argument -V x -^ CallExpr' | +| 354 | cfg.swift:354:5:354:5 | x | 'x -> -= -> 1 -^ CompoundAssignExpr' | +| 355 | cfg.swift:355:11:355:11 | x | 'x -> >= -> 0 -^ BinaryExpr' | +| 358 | cfg.swift:358:1:363:1 | Block | 'Block' | +| 358 | cfg.swift:358:1:363:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 359 | cfg.swift:359:3:359:11 | VariableDeclaration | 'VariableDeclaration -V x -> 0' | +| 360 | cfg.swift:360:3:362:3 | WhileStmt | 'WhileStmt' | +| 360 | cfg.swift:360:9:360:9 | x | 'x -> < -> 10 -^ BinaryExpr' | +| 360 | cfg.swift:360:17:362:3 | Block | 'Block' | +| 361 | cfg.swift:361:5:361:5 | x | 'x -> += -> 1 -^ CompoundAssignExpr' | +| 365 | cfg.swift:365:1:374:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V OptionalC' | +| 366 | cfg.swift:366:3:366:11 | VariableDeclaration | 'VariableDeclaration -V c -> Optional -V C -^ GenericTypeExpr' | +| 367 | cfg.swift:367:3:369:3 | ConstructorDeclaration | 'ConstructorDeclaration' | +| 367 | cfg.swift:367:8:367:10 | arg | 'arg -^ Block' | +| 368 | cfg.swift:368:5:368:5 | c | 'c -> arg -^ AssignExpr' | +| 371 | cfg.swift:371:3:373:3 | Block | 'Block' | +| 371 | cfg.swift:371:3:373:3 | FunctionDeclaration | 'FunctionDeclaration' | +| 372 | cfg.swift:372:12:372:12 | c | 'c -^ ReturnExpr' | +| 376 | cfg.swift:376:1:378:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 376 | cfg.swift:376:19:376:19 | c | 'c -^ Block' | +| 377 | cfg.swift:377:10:377:10 | c | 'c -^ MemberAccessExpr -^ CallExpr -^ MemberAccessExpr -^ CallExpr -^ ReturnExpr' | +| 380 | cfg.swift:380:1:384:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 380 | cfg.swift:380:18:380:18 | x | 'x -> y -^ Block' | +| 381 | cfg.swift:381:10:383:3 | Block | 'Block' | +| 381 | cfg.swift:381:10:383:3 | FunctionExpr | 'FunctionExpr -^ ReturnExpr' | +| 382 | cfg.swift:382:12:382:12 | z | 'z -^ ReturnExpr' | +| 386 | cfg.swift:386:1:388:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 386 | cfg.swift:386:23:386:23 | t | 't -^ Block' | +| 387 | cfg.swift:387:10:387:10 | t | 't -^ MemberAccessExpr -> + -> t -^ MemberAccessExpr -^ BinaryExpr -> + -> t -^ MemberAccessExpr -^ BinaryExpr -> + -> Argument -V 1 -> Argument -V 2 -> Argument -V 3 -^ TupleExpr -^ MemberAccessExpr -^ BinaryExpr -^ ReturnExpr' | +| 390 | cfg.swift:390:1:394:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V Derived -^ BaseType -V C' | +| 391 | cfg.swift:391:3:393:3 | Block | 'Block' | +| 391 | cfg.swift:391:3:393:3 | ConstructorDeclaration | 'ConstructorDeclaration' | +| 392 | cfg.swift:392:5:392:9 | | ' -^ MemberAccessExpr -> Argument -V 0 -^ CallExpr' | +| 396 | cfg.swift:396:1:404:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 396 | cfg.swift:396:21:396:21 | x | 'x -^ Block' | +| 397 | cfg.swift:397:3:402:3 | TryExpr | 'TryExpr -V Block' | +| 398 | cfg.swift:398:9:398:18 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try -^ UnaryExpr' | +| 399 | cfg.swift:399:5:399:9 | print | 'print -> Argument -V "Did not throw." -^ CallExpr' | +| 400 | cfg.swift:400:10:400:19 | mightThrow | 'mightThrow -> Argument -V 0 -^ CallExpr -^ try! -^ UnaryExpr' | +| 401 | cfg.swift:401:5:401:9 | print | 'print -> Argument -V "Still did not throw." -^ CallExpr' | +| 403 | cfg.swift:403:10:403:10 | 0 | '0 -^ ReturnExpr' | +| 406 | cfg.swift:406:1:415:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V Structors' | +| 407 | cfg.swift:407:3:407:16 | VariableDeclaration | 'VariableDeclaration -V field -> Int' | +| 408 | cfg.swift:408:3:410:3 | Block | 'Block' | +| 408 | cfg.swift:408:3:410:3 | ConstructorDeclaration | 'ConstructorDeclaration' | +| 409 | cfg.swift:409:5:409:9 | field | 'field -> 10 -^ AssignExpr' | +| 412 | cfg.swift:412:3:414:3 | Block | 'Block' | +| 412 | cfg.swift:412:3:414:3 | DestructorDeclaration | 'DestructorDeclaration' | +| 413 | cfg.swift:413:5:413:9 | field | 'field -> 0 -^ AssignExpr' | +| 417 | cfg.swift:417:1:419:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 417 | cfg.swift:417:24:417:24 | x | 'x -> y -^ Block' | +| 418 | cfg.swift:418:10:418:25 | MapLiteral | 'MapLiteral -^ ReturnExpr' | +| 421 | cfg.swift:421:1:444:1 | Block | 'Block' | +| 421 | cfg.swift:421:1:444:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 422 | cfg.swift:422:3:427:3 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyLocalClass' | +| 423 | cfg.swift:423:5:423:14 | VariableDeclaration | 'VariableDeclaration -V x -> Int' | +| 424 | cfg.swift:424:5:426:5 | Block | 'Block' | +| 424 | cfg.swift:424:5:426:5 | ConstructorDeclaration | 'ConstructorDeclaration' | +| 425 | cfg.swift:425:7:425:7 | x | 'x -> 10 -^ AssignExpr' | +| 429 | cfg.swift:429:3:434:3 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyLocalStruct' | +| 430 | cfg.swift:430:5:430:14 | VariableDeclaration | 'VariableDeclaration -V x -> Int' | +| 431 | cfg.swift:431:5:433:5 | Block | 'Block' | +| 431 | cfg.swift:431:5:433:5 | ConstructorDeclaration | 'ConstructorDeclaration' | +| 432 | cfg.swift:432:7:432:7 | x | 'x -> 10 -^ AssignExpr' | +| 436 | cfg.swift:436:3:439:3 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyLocalEnum' | +| 437 | cfg.swift:437:10:437:10 | VariableDeclaration | 'VariableDeclaration -V A' | +| 438 | cfg.swift:438:10:438:10 | VariableDeclaration | 'VariableDeclaration -V B' | +| 441 | cfg.swift:441:3:441:22 | VariableDeclaration | 'VariableDeclaration -V myLocalVar -> Int' | +| 443 | cfg.swift:443:10:443:10 | 0 | '0 -^ ReturnExpr' | +| 446 | cfg.swift:446:1:448:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V B' | +| 447 | cfg.swift:447:3:447:13 | VariableDeclaration | 'VariableDeclaration -V x -> Int' | +| 450 | cfg.swift:450:1:454:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V A' | +| 451 | cfg.swift:451:3:451:11 | VariableDeclaration | 'VariableDeclaration -V b -> B' | +| 452 | cfg.swift:452:3:452:14 | VariableDeclaration | 'VariableDeclaration -V bs -> Array -V B -^ GenericTypeExpr' | +| 453 | cfg.swift:453:3:453:15 | VariableDeclaration | 'VariableDeclaration -V mayB -> Optional -V B -^ GenericTypeExpr' | +| 456 | cfg.swift:456:1:466:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 456 | cfg.swift:456:11:456:11 | a | 'a -^ Block' | +| 457 | cfg.swift:457:3:457:24 | VariableDeclaration | 'VariableDeclaration -V kpGet_b_x -> ' | +| 458 | cfg.swift:458:3:458:31 | VariableDeclaration | 'VariableDeclaration -V kpGet_bs_0_x -> ' | +| 459 | cfg.swift:459:3:459:37 | VariableDeclaration | 'VariableDeclaration -V kpGet_mayB_force_x -> ' | +| 460 | cfg.swift:460:3:460:31 | VariableDeclaration | 'VariableDeclaration -V kpGet_mayB_x -> ' | +| 462 | cfg.swift:462:3:462:45 | VariableDeclaration | 'VariableDeclaration -V apply_kpGet_b_x -> a -> Argument -V kpGet_b_x -^ CallExpr' | +| 463 | cfg.swift:463:3:463:51 | VariableDeclaration | 'VariableDeclaration -V apply_kpGet_bs_0_x -> a -> Argument -V kpGet_bs_0_x -^ CallExpr' | +| 464 | cfg.swift:464:3:464:63 | VariableDeclaration | 'VariableDeclaration -V apply_kpGet_mayB_force_x -> a -> Argument -V kpGet_mayB_force_x -^ CallExpr' | +| 465 | cfg.swift:465:3:465:51 | VariableDeclaration | 'VariableDeclaration -V apply_kpGet_mayB_x -> a -> Argument -V kpGet_mayB_x -^ CallExpr' | +| 468 | cfg.swift:468:1:495:1 | Block | 'Block' | +| 468 | cfg.swift:468:1:495:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 469 | cfg.swift:469:1:475:6 | | '' | +| 477 | cfg.swift:477:3:477:3 | 5 | '5' | +| 479 | cfg.swift:479:1:482:6 | | '' | +| 484 | cfg.swift:484:3:484:3 | 8 | '8' | +| 486 | cfg.swift:486:1:492:6 | | '' | +| 494 | cfg.swift:494:3:494:4 | 13 | '13' | +| 497 | cfg.swift:497:1:522:1 | Block | 'Block' | +| 497 | cfg.swift:497:1:522:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 498 | cfg.swift:498:3:498:11 | VariableDeclaration | 'VariableDeclaration -V x -> 0' | +| 500 | cfg.swift:500:3:502:3 | IfExpr | 'IfExpr -V ' | +| 500 | cfg.swift:500:30:502:3 | Block | 'Block' | +| 501 | cfg.swift:501:5:501:5 | x | 'x -> += -> 1 -^ CompoundAssignExpr' | +| 504 | cfg.swift:504:3:506:3 | IfExpr | 'IfExpr -V ' | +| 504 | cfg.swift:504:33:506:3 | Block | 'Block' | +| 505 | cfg.swift:505:5:505:5 | x | 'x -> += -> 1 -^ CompoundAssignExpr' | +| 508 | cfg.swift:508:3:510:3 | IfExpr | 'IfExpr -V ' | +| 508 | cfg.swift:508:49:510:3 | Block | 'Block' | +| 509 | cfg.swift:509:5:509:5 | x | 'x -> += -> 1 -^ CompoundAssignExpr' | +| 512 | cfg.swift:512:3:514:3 | GuardIfStmt | 'GuardIfStmt -V -> Block' | +| 513 | cfg.swift:513:5:513:5 | x | 'x -> += -> 1 -^ CompoundAssignExpr' | +| 516 | cfg.swift:516:3:519:3 | IfExpr | 'IfExpr -V BinaryExpr -V ' | +| 517 | cfg.swift:517:7:517:27 | | '' | +| 517 | cfg.swift:517:29:519:3 | Block | 'Block' | +| 518 | cfg.swift:518:5:518:5 | x | 'x -> += -> 1 -^ CompoundAssignExpr' | +| 521 | cfg.swift:521:10:521:10 | x | 'x -^ ReturnExpr' | +| 524 | cfg.swift:524:1:538:1 | Block | 'Block' | +| 524 | cfg.swift:524:1:538:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 525 | cfg.swift:525:5:533:6 | VariableDeclaration | 'VariableDeclaration -V stream -> AsyncStream -> Argument -V Int -^ MemberAccessExpr -> Argument -V . -^ MemberAccessExpr -> Argument -V 5 -^ CallExpr -> Argument -V FunctionExpr -^ CallExpr' | +| 525 | cfg.swift:525:78:533:5 | Block | 'Block' | +| 526 | cfg.swift:526:9:526:20 | continuation | 'continuation' | +| 527 | cfg.swift:527:13:527:16 | Task | 'Task -^ MemberAccessExpr -^ Argument -V FunctionExpr -^ CallExpr' | +| 527 | cfg.swift:527:27:532:13 | Block | 'Block' | +| 528 | cfg.swift:528:17:530:17 | ForEachStmt | 'ForEachStmt -V 1 -> ... -> 100 -^ BinaryExpr' | +| 528 | cfg.swift:528:21:528:21 | i | 'i -> Block' | +| 529 | cfg.swift:529:21:529:32 | continuation | 'continuation -^ MemberAccessExpr -> Argument -V i -^ CallExpr' | +| 531 | cfg.swift:531:17:531:28 | continuation | 'continuation -^ MemberAccessExpr -^ CallExpr' | +| 535 | cfg.swift:535:5:537:5 | ForEachStmt | 'ForEachStmt -V stream' | +| 535 | cfg.swift:535:19:535:19 | i | 'i -> Block' | +| 536 | cfg.swift:536:9:536:13 | print | 'print -> Argument -V i -^ CallExpr' | +| 540 | cfg.swift:540:1:544:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 540 | cfg.swift:540:24:540:24 | x | 'x -^ Block' | +| 541 | cfg.swift:541:3:543:9 | ReturnExpr | 'ReturnExpr' | +| 542 | cfg.swift:542:5:543:9 | BinaryExpr | 'BinaryExpr -V x' | +| 543 | cfg.swift:543:9:543:9 | 0 | '0' | +| 546 | cfg.swift:546:1:553:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 546 | cfg.swift:546:25:546:25 | x | 'x -^ Block' | +| 547 | cfg.swift:547:3:552:3 | IfExpr | 'IfExpr -V BinaryExpr -V x' | +| 548 | cfg.swift:548:7:548:11 | false | 'false' | +| 548 | cfg.swift:548:13:550:3 | Block | 'Block' | +| 549 | cfg.swift:549:12:549:12 | 1 | '1 -^ ReturnExpr' | +| 550 | cfg.swift:550:10:552:3 | Block | 'Block' | +| 551 | cfg.swift:551:12:551:12 | 0 | '0 -^ ReturnExpr' | +| 555 | cfg.swift:555:1:557:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 555 | cfg.swift:555:24:555:27 | expr | 'expr -^ Block' | +| 556 | cfg.swift:556:10:556:13 | expr | 'expr -^ CallExpr -^ ReturnExpr' | +| 559 | cfg.swift:559:1:561:1 | Block | 'Block' | +| 559 | cfg.swift:559:1:561:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 560 | cfg.swift:560:3:560:17 | usesAutoclosure | 'usesAutoclosure -> Argument -V 1 -^ CallExpr' | +| 565 | cfg.swift:565:1:567:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyProtocol' | +| 566 | cfg.swift:566:2:566:21 | Block | 'Block' | +| 566 | cfg.swift:566:2:566:21 | FunctionDeclaration | 'FunctionDeclaration' | +| 569 | cfg.swift:569:1:571:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyProcotolImpl -^ BaseType -V MyProtocol' | +| 570 | cfg.swift:570:2:570:34 | Block | 'Block -V 0 -^ ReturnExpr' | +| 570 | cfg.swift:570:2:570:34 | FunctionDeclaration | 'FunctionDeclaration' | +| 573 | cfg.swift:573:1:573:62 | Block | 'Block -V MyProcotolImpl -^ CallExpr -^ ReturnExpr' | +| 573 | cfg.swift:573:1:573:62 | FunctionDeclaration | 'FunctionDeclaration' | +| 574 | cfg.swift:574:1:574:70 | Block | 'Block -V MyProcotolImpl -^ CallExpr -^ ReturnExpr' | +| 574 | cfg.swift:574:1:574:70 | FunctionDeclaration | 'FunctionDeclaration' | +| 576 | cfg.swift:576:1:576:23 | FunctionDeclaration | 'FunctionDeclaration' | +| 576 | cfg.swift:576:11:576:13 | arg | 'arg -^ Block' | +| 578 | cfg.swift:578:1:583:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 578 | cfg.swift:578:30:578:30 | x | 'x -> y -^ Block' | +| 579 | cfg.swift:579:2:579:5 | sink | 'sink -> Argument -V x -^ MemberAccessExpr -^ CallExpr -^ CallExpr' | +| 580 | cfg.swift:580:2:580:5 | sink | 'sink -> Argument -V y -^ MemberAccessExpr -^ CallExpr -^ CallExpr' | +| 581 | cfg.swift:581:2:581:5 | sink | 'sink -> Argument -V getMyProtocol -^ CallExpr -^ MemberAccessExpr -^ CallExpr -^ CallExpr' | +| 582 | cfg.swift:582:2:582:5 | sink | 'sink -> Argument -V getMyProtocolImpl -^ CallExpr -^ MemberAccessExpr -^ CallExpr -^ CallExpr' | +| 585 | cfg.swift:585:1:593:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 585 | cfg.swift:585:23:585:23 | x | 'x -^ Block' | +| 586 | cfg.swift:586:3:589:3 | VariableDeclaration | 'VariableDeclaration -V a -> SwitchExpr -V x' | +| 587 | cfg.swift:587:5:587:17 | Block | 'Block -V 1' | +| 587 | cfg.swift:587:5:587:17 | SwitchCase | 'SwitchCase -V 0 -> ..< -> 5 -^ BinaryExpr' | +| 588 | cfg.swift:588:5:588:14 | SwitchCase | 'SwitchCase -V Block -V 2' | +| 590 | cfg.swift:590:3:592:18 | VariableDeclaration | 'VariableDeclaration -V b' | +| 591 | cfg.swift:591:9:592:18 | IfExpr | 'IfExpr -V x -> < -> 42 -^ BinaryExpr' | +| 591 | cfg.swift:591:21:591:25 | Block | 'Block -V 1' | +| 592 | cfg.swift:592:14:592:18 | Block | 'Block -V 2' | +| 596 | cfg.swift:596:1:598:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V ValueGenericsStruct -> TypeParameter -V N -> Int' | +| 597 | cfg.swift:597:5:597:13 | VariableDeclaration | 'VariableDeclaration -V x -> N' | +| 600 | cfg.swift:600:1:604:1 | FunctionDeclaration | 'FunctionDeclaration' | +| 600 | cfg.swift:600:36:600:40 | value | 'value -^ Block' | +| 601 | cfg.swift:601:5:601:13 | VariableDeclaration | 'VariableDeclaration -V x -> N' | +| 602 | cfg.swift:602:5:602:9 | print | 'print -> Argument -V x -^ CallExpr' | +| 603 | cfg.swift:603:5:603:5 | _ | '_ -> value -^ AssignExpr' | diff --git a/unified/ql/test/library-tests/controlflow/basicblock-slices.ql b/unified/ql/test/library-tests/controlflow/basicblock-slices.ql new file mode 100644 index 000000000000..1b57b4b5d5e2 --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/basicblock-slices.ql @@ -0,0 +1,2 @@ +import unified +import ControlFlow::TestCfgInline::BlockSlices diff --git a/unified/ql/test/library-tests/controlflow/cfg.expected b/unified/ql/test/library-tests/controlflow/cfg.expected new file mode 100644 index 000000000000..fedda2b0ed23 --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/cfg.expected @@ -0,0 +1,157 @@ +bbContinues +| cfg.swift:147:5:147:5 | Block | 'Block goto true(+3)' | +| cfg.swift:525:78:525:78 | Block | 'Block goto Task(+2)' | +| cfg.swift:526:9:526:20 | continuation | 'continuation goto Block(-1)' | +bbStep +| cfg.swift:140:12:140:17 | BinaryExpr | 'BinaryExpr : empty -> SwitchExpr(+3)' | +| cfg.swift:140:12:140:17 | BinaryExpr | 'BinaryExpr : non-empty -> _(+0)' | +| cfg.swift:141:9:141:12 | Block | 'Block : successor -> SwitchExpr(+2)' | +| cfg.swift:141:9:141:12 | Block | 'Block : successor -> _(-1)' | +| cfg.swift:144:5:144:5 | OrPattern | 'OrPattern : match -> Block(+0)' | +| cfg.swift:144:5:144:5 | OrPattern | 'OrPattern : no-match -> SwitchCase(+3)' | +| cfg.swift:147:10:147:10 | ConditionalPattern | 'ConditionalPattern : match -> Block(+0)' | +| cfg.swift:147:10:147:10 | ConditionalPattern | 'ConditionalPattern : no-match -> SwitchCase(+4)' | +| cfg.swift:148:10:148:15 | BinaryExpr | 'BinaryExpr : false -> x(-1)' | +| cfg.swift:148:10:148:15 | BinaryExpr | 'BinaryExpr : true -> x(+1)' | +| cfg.swift:149:13:149:17 | BinaryExpr | 'BinaryExpr : successor -> x(-2)' | +| cfg.swift:187:6:187:10 | BinaryExpr | 'BinaryExpr : false -> IfExpr(+3)' | +| cfg.swift:187:6:187:10 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:190:11:190:16 | BinaryExpr | 'BinaryExpr : false,false,false -> Block(+5)' | +| cfg.swift:190:11:190:16 | BinaryExpr | 'BinaryExpr : true -> x(+1)' | +| cfg.swift:191:13:191:17 | BinaryExpr | 'BinaryExpr : false,false,false -> Block(+4)' | +| cfg.swift:191:13:191:17 | BinaryExpr | 'BinaryExpr : true -> UnaryExpr(+1)' | +| cfg.swift:192:15:192:20 | BinaryExpr | 'BinaryExpr : false -> Block(+0)' | +| cfg.swift:192:15:192:20 | BinaryExpr | 'BinaryExpr : true,false -> Block(+3)' | +| cfg.swift:201:6:201:6 | b | 'b : false -> 1(+3)' | +| cfg.swift:201:6:201:6 | b | 'b : true -> Block(+0)' | +| cfg.swift:208:6:208:10 | BinaryExpr | 'BinaryExpr : false -> x(+6)' | +| cfg.swift:208:6:208:10 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:210:8:210:13 | BinaryExpr | 'BinaryExpr : false -> x(+4)' | +| cfg.swift:210:8:210:13 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:211:7:211:15 | AssignExpr | 'AssignExpr : successor -> x(+3)' | +| cfg.swift:218:11:218:12 | b1 | 'b1 : false -> b3(+2)' | +| cfg.swift:218:11:218:12 | b1 | 'b1 : true -> b2(+1)' | +| cfg.swift:219:13:219:14 | b2 | 'b2 : false,false -> "!b2 \|\| !b3"(+3)' | +| cfg.swift:219:13:219:14 | b2 | 'b2 : true,true -> "b2 \|\| b3"(+2)' | +| cfg.swift:220:13:220:14 | b3 | 'b3 : false,false -> "!b2 \|\| !b3"(+2)' | +| cfg.swift:220:13:220:14 | b3 | 'b3 : true,true -> "b2 \|\| b3"(+1)' | +| cfg.swift:221:9:221:18 | "b2 \|\| b3" | '"b2 \|\| b3" : successor -> ReturnExpr(-3)' | +| cfg.swift:222:9:222:20 | "!b2 \|\| !b3" | '"!b2 \|\| !b3" : successor -> ReturnExpr(-4)' | +| cfg.swift:226:6:226:6 | b | 'b : false -> Bool(+2)' | +| cfg.swift:226:6:226:6 | b | 'b : true -> true(+1)' | +| cfg.swift:227:8:227:11 | true | 'true : true -> Block(+1)' | +| cfg.swift:228:7:228:17 | CallExpr | 'CallExpr : false -> Block(+3)' | +| cfg.swift:228:7:228:17 | CallExpr | 'CallExpr : true,true -> Block(+0)' | +| cfg.swift:243:6:243:6 | b | 'b : false -> Block(+3)' | +| cfg.swift:243:6:243:6 | b | 'b : true -> Block(+0)' | +| cfg.swift:244:5:244:17 | CallExpr | 'CallExpr : successor -> print(+3)' | +| cfg.swift:246:8:246:9 | Block | 'Block : successor -> print(+1)' | +| cfg.swift:251:7:251:8 | b1 | 'b1 : false -> b2(+0)' | +| cfg.swift:251:7:251:8 | b1 | 'b1 : true,true -> Block(+0)' | +| cfg.swift:251:13:251:14 | b2 | 'b2 : true,true -> Block(+0)' | +| cfg.swift:313:3:313:3 | WhileStmt | 'WhileStmt : successor -> x(+0)' | +| cfg.swift:313:9:313:14 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:315:5:315:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> x(-2)' | +| cfg.swift:320:3:320:3 | WhileStmt | 'WhileStmt : successor -> x(+0)' | +| cfg.swift:320:9:320:14 | BinaryExpr | 'BinaryExpr : false -> print(+11)' | +| cfg.swift:320:9:320:14 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:323:8:323:14 | BinaryExpr | 'BinaryExpr : false -> IfExpr(+3)' | +| cfg.swift:323:8:323:14 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:324:7:324:11 | BreakExpr | 'BreakExpr : break -> print(+7)' | +| cfg.swift:326:13:326:18 | BinaryExpr | 'BinaryExpr : false -> print(+3)' | +| cfg.swift:326:13:326:18 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:327:7:327:14 | ContinueExpr | 'ContinueExpr : continue -> x(-7)' | +| cfg.swift:329:5:329:17 | CallExpr | 'CallExpr : successor -> x(-9)' | +| cfg.swift:335:10:335:10 | WhileStmt | 'WhileStmt : successor -> x(+0)' | +| cfg.swift:335:16:335:21 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:336:12:336:12 | WhileStmt | 'WhileStmt : successor -> x(+0)' | +| cfg.swift:336:18:336:23 | BinaryExpr | 'BinaryExpr : false -> print(+11)' | +| cfg.swift:336:18:336:23 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:339:10:339:16 | BinaryExpr | 'BinaryExpr : false -> IfExpr(+3)' | +| cfg.swift:339:10:339:16 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:342:15:342:20 | BinaryExpr | 'BinaryExpr : false -> print(+3)' | +| cfg.swift:342:15:342:20 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:343:9:343:22 | ContinueExpr | 'ContinueExpr : continue -> x(-7)' | +| cfg.swift:345:7:345:19 | CallExpr | 'CallExpr : successor -> x(-9)' | +| cfg.swift:347:5:347:17 | CallExpr | 'CallExpr : successor -> x(-12)' | +| cfg.swift:352:3:352:3 | DoWhileStmt | 'DoWhileStmt : successor -> Block(+0)' | +| cfg.swift:355:11:355:16 | BinaryExpr | 'BinaryExpr : true -> Block(-3)' | +| cfg.swift:360:3:360:3 | WhileStmt | 'WhileStmt : successor -> x(+0)' | +| cfg.swift:360:9:360:14 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +| cfg.swift:361:5:361:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> x(-1)' | +| cfg.swift:500:6:500:28 | | ' : false -> IfExpr(+4)' | +| cfg.swift:500:6:500:28 | | ' : true -> Block(+0)' | +| cfg.swift:501:5:501:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> IfExpr(+3)' | +| cfg.swift:504:6:504:31 | | ' : false -> IfExpr(+4)' | +| cfg.swift:504:6:504:31 | | ' : true -> Block(+0)' | +| cfg.swift:505:5:505:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> IfExpr(+3)' | +| cfg.swift:508:6:508:47 | | ' : false -> GuardIfStmt(+4)' | +| cfg.swift:508:6:508:47 | | ' : true -> Block(+0)' | +| cfg.swift:509:5:509:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> GuardIfStmt(+3)' | +| cfg.swift:516:6:516:28 | | ' : false,false -> x(+5)' | +| cfg.swift:516:6:516:28 | | ' : true -> (+1)' | +| cfg.swift:517:7:517:27 | | ' : false,false -> x(+4)' | +| cfg.swift:517:7:517:27 | | ' : true -> Block(+0)' | +| cfg.swift:518:5:518:10 | CompoundAssignExpr | 'CompoundAssignExpr : successor -> x(+3)' | +| cfg.swift:528:26:528:32 | BinaryExpr | 'BinaryExpr : empty -> continuation(+3)' | +| cfg.swift:528:26:528:32 | BinaryExpr | 'BinaryExpr : non-empty -> i(+0)' | +| cfg.swift:529:21:529:41 | CallExpr | 'CallExpr : successor -> continuation(+2)' | +| cfg.swift:529:21:529:41 | CallExpr | 'CallExpr : successor -> i(-1)' | +| cfg.swift:535:24:535:29 | stream | 'stream : non-empty -> i(+0)' | +| cfg.swift:536:9:536:16 | CallExpr | 'CallExpr : successor -> i(-1)' | +| cfg.swift:542:5:542:5 | x | 'x : non-null -> ReturnExpr(-1)' | +| cfg.swift:542:5:542:5 | x | 'x : null -> 0(+1)' | +| cfg.swift:543:9:543:9 | 0 | '0 : successor -> ReturnExpr(-2)' | +| cfg.swift:547:6:547:6 | x | 'x : non-null,false -> Block(+3)' | +| cfg.swift:547:6:547:6 | x | 'x : non-null,true -> Block(+1)' | +| cfg.swift:547:6:547:6 | x | 'x : null -> false(+1)' | +| cfg.swift:548:7:548:11 | false | 'false : false -> Block(+2)' | +| cfg.swift:587:10:587:14 | BinaryExpr | 'BinaryExpr : match -> Block(+0)' | +| cfg.swift:587:10:587:14 | BinaryExpr | 'BinaryExpr : no-match -> SwitchCase(+1)' | +| cfg.swift:587:17:587:17 | 1 | '1 : successor -> VariableDeclaration(+3)' | +| cfg.swift:588:14:588:14 | 2 | '2 : successor -> VariableDeclaration(+2)' | +| cfg.swift:591:13:591:18 | BinaryExpr | 'BinaryExpr : false -> Block(+1)' | +| cfg.swift:591:13:591:18 | BinaryExpr | 'BinaryExpr : true -> Block(+0)' | +noCfg +| cfg.swift:23:9:23:9 | x | +| cfg.swift:24:5:24:42 | ThrowExpr | +| cfg.swift:35:11:35:17 | MyError | +| cfg.swift:36:5:36:12 | ReturnExpr | +| cfg.swift:37:11:37:17 | MyError | +| cfg.swift:38:5:38:20 | ReturnExpr | +| cfg.swift:39:11:39:20 | | +| cfg.swift:40:5:40:9 | print | +| cfg.swift:42:5:42:9 | print | +| cfg.swift:47:42:47:47 | String | +| cfg.swift:53:34:53:34 | _ | +| cfg.swift:60:34:60:34 | _ | +| cfg.swift:66:6:66:17 | callClosures | +| cfg.swift:77:6:77:27 | forceAndBackToOptional | +| cfg.swift:83:6:83:14 | testInOut | +| cfg.swift:106:8:106:15 | getMyInt | +| cfg.swift:146:7:146:17 | ReturnExpr | +| cfg.swift:155:3:155:5 | var | +| cfg.swift:156:6:156:8 | obj | +| cfg.swift:157:5:157:15 | ReturnExpr | +| cfg.swift:160:3:160:5 | let | +| cfg.swift:161:6:161:34 | PatternGuardExpr | +| cfg.swift:162:5:162:17 | ReturnExpr | +| cfg.swift:164:5:164:16 | ReturnExpr | +| cfg.swift:236:6:236:23 | constant_condition | +| cfg.swift:238:5:238:9 | print | +| cfg.swift:279:6:279:22 | testSubscriptExpr | +| cfg.swift:358:6:358:28 | loop_with_identity_expr | +| cfg.swift:371:8:371:18 | getOptional | +| cfg.swift:380:45:380:47 | Int | +| cfg.swift:381:13:381:13 | z | +| cfg.swift:421:6:421:22 | localDeclarations | +| cfg.swift:468:6:468:17 | testIfConfig | +| cfg.swift:497:6:497:18 | testAvailable | +| cfg.swift:524:6:524:17 | testAsyncFor | +| cfg.swift:559:6:559:20 | autoclosureTest | +nonSimple +| cfg.swift:10:1:10:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyError -^ BaseType -V Error' | +| cfg.swift:209:5:209:5 | x | 'x -> x -? - -^ UnaryExpr -^ AssignExpr' | +| cfg.swift:390:1:390:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V Derived -^ BaseType -V C' | +| cfg.swift:527:13:527:16 | Task | 'Task -^ MemberAccessExpr -^ Argument -V FunctionExpr -^ CallExpr' | +| cfg.swift:569:1:569:1 | ClassLikeDeclaration | 'ClassLikeDeclaration -V MyProcotolImpl -^ BaseType -V MyProtocol' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.ql b/unified/ql/test/library-tests/controlflow/cfg.ql new file mode 100644 index 000000000000..e86a681954e8 --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/cfg.ql @@ -0,0 +1,2 @@ +import unified +import ControlFlow::TestCfgInline diff --git a/unified/ql/test/library-tests/controlflow/cfg.qlref b/unified/ql/test/library-tests/controlflow/cfg.qlref new file mode 100644 index 000000000000..d1b031838715 --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/cfg.qlref @@ -0,0 +1,2 @@ +query: cfg.ql +postprocess: utils/test/InlineExpectationsTestQuery.ql diff --git a/unified/ql/test/library-tests/controlflow/cfg.swift b/unified/ql/test/library-tests/controlflow/cfg.swift new file mode 100644 index 000000000000..db2d1cb9394d --- /dev/null +++ b/unified/ql/test/library-tests/controlflow/cfg.swift @@ -0,0 +1,604 @@ +var topLevelDecl : Int = 0 +0 +topLevelDecl + 1 + +func returnZero() -> Int { return 0 } + +returnZero() +Double(topLevelDecl) + +enum MyError: Error { // $ nonSimple='ClassLikeDeclaration -V MyError -^ BaseType -V Error' + case error1, error2 + case error3(withParam: Int) +} + +func isZero(x : Int) -> Bool { + return x == 0 +} + +func mightThrow(x : Int) throws -> Void { + guard x >= 0 else { + throw MyError.error1 + } + guard x <= 0 else { // $ noCfg + throw MyError.error3(withParam: x + 1) // $ noCfg + } +} + +func tryCatch(x : Int) -> Int { + do { + try mightThrow(x: 0) + print("Did not throw.") + try! mightThrow(x: 0) + print("Still did not throw.") + + } catch MyError.error1 , MyError.error2 where isZero(x: x) { // $ noCfg + return 0 // $ noCfg + } catch MyError.error3(let withParam) { // $ noCfg + return withParam // $ noCfg + } catch is MyError { // $ noCfg + print("MyError") // $ noCfg + } catch { + print("Unknown error \(error)") // $ noCfg + } + return 0 +} + +func createClosure1(s : String) -> () -> String { // $ noCfg + return { + return s + "" + } +} + +func createClosure2(x : Int) -> (_ : Int) -> Int { // $ noCfg + func f(y : Int) -> Int { + return x + y + } + return f +} + +func createClosure3(x : Int) -> (_ : Int) -> Int { // $ noCfg + return { + (y) -> Int in x + y + } +} + +func callClosures() { // $ noCfg + var x1 = createClosure1(s: "")() + var x2 = createClosure2(x: 0)(10) + var x3 = createClosure3(x: 0)(10) +} + +func maybeParseInt(s : String) -> Int? { + var n : Int? = Int(s) + return n +} + +func forceAndBackToOptional() -> Int? { // $ noCfg + var nBang = maybeParseInt(s:"42")! + var n = maybeParseInt(s:"42") + return nBang + n! +} + +func testInOut() -> Int { // $ noCfg + var temp = 10 + + func add(a: inout Int) { + a = a + 1 + } + + func addOptional(a: inout Int?) { + a = nil + } + + add(a:&temp) + var tempOptional : Int? = 10 + addOptional(a:&tempOptional) + return temp + tempOptional! +} + +class C { + let myInt: Int + init(n: Int) { + myInt = n + } + + func getMyInt() -> Int { // $ noCfg + return myInt + } +} + +func testMemberRef(param : C, inoutParam : inout C, opt : C?) { + let c = C(n: 42) + let n1 = c.myInt + let n2 = c.self.myInt + let n3 = c.getMyInt() + let n4 = c.self.getMyInt() + let n5 = param.myInt + let n6 = param.self.myInt + + let n8 = param.self.getMyInt() + + let n9 = inoutParam.myInt + let n7 = param.getMyInt() + let n10 = inoutParam.self.myInt + let n11 = inoutParam.getMyInt() + let n12 = inoutParam.self.getMyInt() + + let n13 = opt!.myInt + let n14 = opt!.self.myInt + let n15 = opt!.getMyInt() + let n16 = opt!.self.getMyInt() + + let n17 = opt?.myInt + let n18 = opt?.self.myInt + let n19 = opt?.getMyInt() + let n20 = opt?.self.getMyInt() +} + +func patterns(x : Int) -> Bool { + for _ in 0...10 // $ bbStep='BinaryExpr : empty -> SwitchExpr(+3)' bbStep='BinaryExpr : non-empty -> _(+0)' + { } // $ bbStep='Block : successor -> _(-1)' bbStep='Block : successor -> SwitchExpr(+2)' + + switch x { + case 0, 1: // $ bbStep='OrPattern : match -> Block(+0)' bbStep='OrPattern : no-match -> SwitchCase(+3)' + return true + return true // $ noCfg + case x where // $ bbContinues='Block goto true(+3)' bbStep='ConditionalPattern : match -> Block(+0)' bbStep='ConditionalPattern : no-match -> SwitchCase(+4)' + (x >= 2) && // $ bbStep='BinaryExpr : false -> x(-1)' bbStep='BinaryExpr : true -> x(+1)' + x < 5: // $ bbStep='BinaryExpr : successor -> x(-2)' + return true + default: + return false + } + + var obj : AnyObject = C(n: x) // $ noCfg + if obj is C { // $ noCfg + return true // $ noCfg + } + + let xOptional: Int? = x // $ noCfg + if case .some(let x) = xOptional { // $ noCfg + return x == 0 // $ noCfg + } else { + return false // $ noCfg + } +} + +func testDefer(x : inout Int) { + // Will print 1, 2, 3, 4 + defer { + print("4") + } + + defer { + print("3") + } + + defer { + print("1") + defer { + print("2") + } + } +} + +func m1(x : Int) { + if x > 2 { // $ bbStep='BinaryExpr : false -> IfExpr(+3)' bbStep='BinaryExpr : true -> Block(+0)' + print("x is greater than 2") + } + else if x <= 2 && // $ bbStep='BinaryExpr : true -> x(+1)' bbStep='BinaryExpr : false,false,false -> Block(+5)' + x > 0 && // $ bbStep='BinaryExpr : true -> UnaryExpr(+1)' bbStep='BinaryExpr : false,false,false -> Block(+4)' + !(x == 5) { // $ bbStep='BinaryExpr : false -> Block(+0)' bbStep='BinaryExpr : true,false -> Block(+3)' + print("x is 1") + } + else { + print("I can't guess the number") + } +} + +func m2(b : Bool) -> Int { + if b { // $ bbStep='b : false -> 1(+3)' bbStep='b : true -> Block(+0)' + return 0 + } + return 1 +} + +func m3(x : inout Int) -> Int { + if x < 0 { // $ bbStep='BinaryExpr : true -> Block(+0)' bbStep='BinaryExpr : false -> x(+6)' + x = -x // $ nonSimple='x -> x -? - -^ UnaryExpr -^ AssignExpr' + if x > 10 { // $ bbStep='BinaryExpr : true -> Block(+0)' bbStep='BinaryExpr : false -> x(+4)' + x = x - 1 // $ bbStep='AssignExpr : successor -> x(+3)' + } + } + return x +} + +func m4 (b1 : Bool, b2 : Bool, b3 : Bool) -> String { + return (b1 ? // $ bbStep='b1 : false -> b3(+2)' bbStep='b1 : true -> b2(+1)' + b2 : // $ bbStep='b2 : false,false -> "!b2 \|\| !b3"(+3)' bbStep='b2 : true,true -> "b2 \|\| b3"(+2)' + b3) ? // $ bbStep='b3 : false,false -> "!b2 \|\| !b3"(+2)' bbStep='b3 : true,true -> "b2 \|\| b3"(+1)' + "b2 || b3" : // $ bbStep='"b2 \|\| b3" : successor -> ReturnExpr(-3)' + "!b2 || !b3" // $ bbStep='"!b2 \|\| !b3" : successor -> ReturnExpr(-4)' +} + +func conversionsInSplitEntry (b : Bool) -> String { + if b ? // $ bbStep='b : false -> Bool(+2)' bbStep='b : true -> true(+1)' + (true) : // $ bbStep='true : true -> Block(+1)' + Bool(false) { // $ bbStep='CallExpr : true,true -> Block(+0)' bbStep='CallExpr : false -> Block(+3)' + return "b" + } + else { + return "!b" + } +} + +func constant_condition() { // $ noCfg + if !true { + print("Impossible") // $ noCfg + } +} + +func empty_else(b : Bool) { + if b { // $ bbStep='b : false -> Block(+3)' bbStep='b : true -> Block(+0)' + print("true") // $ bbStep='CallExpr : successor -> print(+3)' + } + else {} // $ bbStep='Block : successor -> print(+1)' + print("done") +} + +func disjunct (b1 : Bool, b2 : Bool) { + if (b1 || b2) { // $ bbStep='b1 : false -> b2(+0)' bbStep='b1 : true,true -> Block(+0)' bbStep='b2 : true,true -> Block(+0)' + print("b1 or b2") + } +} + +func binaryExprs(a : Int, b : Int) { + let c = a + b + let d = a - b + let e = a * b + let f = a / b + let g = a % b + let h = a & b + let i = a | b + let j = a ^ b + let k = a << b + let l = a >> b + let o = a == b + let p = a != b + let q = a < b + let r = a <= b + let s = a > b + let t = a >= b +} + +func interpolatedString(x : Int, y : Int) -> String { + return "\(x) + \(y) is equal to \(x + y) and here is a zero: \(returnZero())" +} + +func testSubscriptExpr() -> (Int, Int, Int, Int, Int) { // $ noCfg + var a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + a[0] = 0 + a[1] += 1 + a[2] -= 1 + a[3] *= 1 + a[4] /= 1 + a[5] %= 1 + a[6] &= 1 + a[7] |= 1 + a[8] ^= 1 + a[9] <<= 1 + a[10] >>= 1 + + var tupleWithA = (a[0], a[1], a[2], a[3], a[4]) + + var b = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + b[0] = a[10] + b[1] = b[0] + 1 + b[2] = b[1] - 1 + b[3] = b[2] * 1 + b[4] = b[3] / 1 + b[5] = b[4] % 1 + b[6] = b[5] & 1 + b[7] = b[6] | 1 + b[8] = b[7] ^ 1 + b[9] = b[8] << 1 + b[10] = b[9] >> 1 + + let (a1, a2, a3, a4, a5) = tupleWithA + return (a1 + b[0], a2 + b[1], a3 + b[2], a4 + b[3], a5 + b[4]) +} + +func loop1(x : inout Int) { + while x >= 0 { // $ bbStep='WhileStmt : successor -> x(+0)' bbStep='BinaryExpr : true -> Block(+0)' + print(x) + x -= 1 // $ bbStep='CompoundAssignExpr : successor -> x(-2)' + } +} + +func loop2(x : inout Int) { + while x >= 0 { // $ bbStep='WhileStmt : successor -> x(+0)' bbStep='BinaryExpr : false -> print(+11)' bbStep='BinaryExpr : true -> Block(+0)' + print(x) + x -= 1 + if x > 100 { // $ bbStep='BinaryExpr : false -> IfExpr(+3)' bbStep='BinaryExpr : true -> Block(+0)' + break // $ bbStep='BreakExpr : break -> print(+7)' + } + else if x > 50 { // $ bbStep='BinaryExpr : false -> print(+3)' bbStep='BinaryExpr : true -> Block(+0)' + continue // $ bbStep='ContinueExpr : continue -> x(-7)' + } + print("Iter") // $ bbStep='CallExpr : successor -> x(-9)' + } + print("Done") +} + +func labeledLoop(x : inout Int) { + outer: while x >= 0 { // $ bbStep='WhileStmt : successor -> x(+0)' bbStep='BinaryExpr : true -> Block(+0)' + inner: while x >= 0 { // $ bbStep='BinaryExpr : false -> print(+11)' bbStep='WhileStmt : successor -> x(+0)' bbStep='BinaryExpr : true -> Block(+0)' + print(x) + x -= 1 + if x > 100 { // $ bbStep='BinaryExpr : false -> IfExpr(+3)' bbStep='BinaryExpr : true -> Block(+0)' + break outer + } + else if x > 50 { // $ bbStep='BinaryExpr : false -> print(+3)' bbStep='BinaryExpr : true -> Block(+0)' + continue inner // $ bbStep='ContinueExpr : continue -> x(-7)' + } + print("Iter") // $ bbStep='CallExpr : successor -> x(-9)' + } + print("Done") // $ bbStep='CallExpr : successor -> x(-12)' + } +} + +func testRepeat(x : inout Int) { + repeat { // $ bbStep='DoWhileStmt : successor -> Block(+0)' + print(x) + x -= 1 + } while x >= 0 // $ bbStep='BinaryExpr : true -> Block(-3)' +} + +func loop_with_identity_expr() { // $ noCfg + var x = 0 + while(x < 10) { // $ bbStep='WhileStmt : successor -> x(+0)' bbStep='BinaryExpr : true -> Block(+0)' + x += 1 // $ bbStep='CompoundAssignExpr : successor -> x(-1)' + } +} + +class OptionalC { + let c: C? + init(arg: C?) { + c = arg + } + + func getOptional() -> C? { // $ noCfg + return c + } +} + +func testOptional(c : OptionalC?) -> Int? { + return c?.getOptional()?.getMyInt() +} + +func testCapture(x : Int, y : Int) -> () -> Int { // $ noCfg + return { [z = x + y, t = "literal"] in // $ noCfg + return z + } +} + +func testTupleElement(t : (a: Int, Int, c: Int)) -> Int { + return t.a + t.1 + t.c + (1, 2, 3).0 +} + +class Derived : C { // $ nonSimple='ClassLikeDeclaration -V Derived -^ BaseType -V C' + init() { + super.init(n: 0) + } +} + +func doWithoutCatch(x : Int) throws -> Int { + do { + try mightThrow(x: 0) + print("Did not throw.") + try! mightThrow(x: 0) + print("Still did not throw.") + } + return 0 +} + +class Structors { + var field: Int + init() { + field = 10 + } + + deinit { + field = 0 + } +} + +func dictionaryLiteral(x: Int, y: Int) -> [String: Int] { + return ["x": x, "y": y] +} + +func localDeclarations() -> Int { // $ noCfg + class MyLocalClass { + var x: Int + init() { + x = 10 + } + } + + struct MyLocalStruct { + var x: Int + init() { + x = 10 + } + } + + enum MyLocalEnum { + case A + case B + } + + var myLocalVar : Int; + + return 0 +} + +struct B { + var x : Int +} + +struct A { + var b : B + var bs : [B] + var mayB : B? +} + +func test(a : A) { + var kpGet_b_x = \A.b.x + var kpGet_bs_0_x = \A.bs[0].x + var kpGet_mayB_force_x = \A.mayB!.x + var kpGet_mayB_x = \A.mayB?.x + + var apply_kpGet_b_x = a[keyPath: kpGet_b_x] + var apply_kpGet_bs_0_x = a[keyPath: kpGet_bs_0_x] + var apply_kpGet_mayB_force_x = a[keyPath: kpGet_mayB_force_x] + var apply_kpGet_mayB_x = a[keyPath: kpGet_mayB_x] +} + +func testIfConfig() { // $ noCfg +#if FOO + 1 + 2 +#else + 3 + 4 +#endif + + 5 + +#if BAR + 6 + 7 +#endif + + 8 + +#if FOO + 9 + 10 +#elseif true + 11 + 12 +#endif + + 13 +} + +func testAvailable() -> Int { // $ noCfg + var x = 0; + + if #available(macOS 10, *) { // $ bbStep=' : false -> IfExpr(+4)' bbStep=' : true -> Block(+0)' + x += 1 // $ bbStep='CompoundAssignExpr : successor -> IfExpr(+3)' + } + + if #available(macOS 10.13, *) { // $ bbStep=' : false -> IfExpr(+4)' bbStep=' : true -> Block(+0)' + x += 1 // $ bbStep='CompoundAssignExpr : successor -> IfExpr(+3)' + } + + if #unavailable(iOS 10, watchOS 10, macOS 10) { // $ bbStep=' : false -> GuardIfStmt(+4)' bbStep=' : true -> Block(+0)' + x += 1 // $ bbStep='CompoundAssignExpr : successor -> GuardIfStmt(+3)' + } + + guard #available(macOS 12, *) else { + x += 1 + } + + if #available(macOS 12, *), // $ bbStep=' : true -> (+1)' bbStep=' : false,false -> x(+5)' + #available(iOS 12, *) { // $ bbStep=' : false,false -> x(+4)' bbStep=' : true -> Block(+0)' + x += 1 // $ bbStep='CompoundAssignExpr : successor -> x(+3)' + } + + return x +} + +func testAsyncFor () async { // $ noCfg + var stream = AsyncStream(Int.self, bufferingPolicy: .bufferingNewest(5), { // $ bbContinues='Block goto Task(+2)' + continuation in // $ bbContinues='continuation goto Block(-1)' + Task.detached { // $ nonSimple='Task -^ MemberAccessExpr -^ Argument -V FunctionExpr -^ CallExpr' + for i in 1...100 { // $ bbStep='BinaryExpr : empty -> continuation(+3)' bbStep='BinaryExpr : non-empty -> i(+0)' + continuation.yield(i) // $ bbStep='CallExpr : successor -> continuation(+2)' bbStep='CallExpr : successor -> i(-1)' + } + continuation.finish() + } + }) + + for try await i in stream { // $ bbStep='stream : non-empty -> i(+0)' + print(i) // $ bbStep='CallExpr : successor -> i(-1)' + } +} + +func testNilCoalescing(x: Int?) -> Int { + return + x ?? // $ bbStep='x : non-null -> ReturnExpr(-1)' bbStep='x : null -> 0(+1)' + 0 // $ bbStep='0 : successor -> ReturnExpr(-2)' +} + +func testNilCoalescing2(x: Bool?) -> Int { + if x ?? // $ bbStep='x : non-null,false -> Block(+3)' bbStep='x : non-null,true -> Block(+1)' bbStep='x : null -> false(+1)' + false { // $ bbStep='false : false -> Block(+2)' + return 1 + } else { + return 0 + } +} + +func usesAutoclosure(_ expr: @autoclosure () -> Int) -> Int { + return expr() +} + +func autoclosureTest() { // $ noCfg + usesAutoclosure(1) +} + +// --- + +protocol MyProtocol { + func source() -> Int +} + +class MyProcotolImpl : MyProtocol { // $ nonSimple='ClassLikeDeclaration -V MyProcotolImpl -^ BaseType -V MyProtocol' + func source() -> Int { return 0 } +} + +func getMyProtocol() -> MyProtocol { return MyProcotolImpl() } +func getMyProtocolImpl() -> MyProcotolImpl { return MyProcotolImpl() } + +func sink(arg: Int) { } + +func testOpenExistentialExpr(x: MyProtocol, y: MyProcotolImpl) { + sink(arg: x.source()) + sink(arg: y.source()) + sink(arg: getMyProtocol().source()) + sink(arg: getMyProtocolImpl().source()) +} + +func singleStmtExpr(_ x: Int) { + let a = switch x { + case 0..<5: 1 // $ bbStep='BinaryExpr : match -> Block(+0)' bbStep='BinaryExpr : no-match -> SwitchCase(+1)' bbStep='1 : successor -> VariableDeclaration(+3)' + default: 2 // $ bbStep='2 : successor -> VariableDeclaration(+2)' + } + let b = + if (x < 42) { 1 } // $ bbStep='BinaryExpr : false -> Block(+1)' bbStep='BinaryExpr : true -> Block(+0)' + else { 2 } +} +// --- + +struct ValueGenericsStruct { + var x = N; +} + +func valueGenericsFn(_ value: ValueGenericsStruct) { + var x = N; + print(x); + _ = value; +} From c1c7f92efd30c4ea9708930a20b64b7c997517e8 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Tue, 15 Sep 2026 09:19:15 +0200 Subject: [PATCH 3/4] Apply batched suggestions from code review Co-authored-by: Anders Schack-Mulligen --- shared/controlflow/codeql/controlflow/test/TestCfg.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/test/TestCfg.qll b/shared/controlflow/codeql/controlflow/test/TestCfg.qll index 55a58e355b44..d59fcc14fabb 100644 --- a/shared/controlflow/codeql/controlflow/test/TestCfg.qll +++ b/shared/controlflow/codeql/controlflow/test/TestCfg.qll @@ -206,7 +206,7 @@ module Make Cfg, InputSig Cfg, InputSig Date: Tue, 15 Sep 2026 12:39:45 +0200 Subject: [PATCH 4/4] Cfg: Rework test. --- .../codeql/controlflow/test/TestCfg.qll | 133 +++++++++++------- .../controlflow/basicblock-slices.expected | 3 +- .../library-tests/controlflow/cfg.expected | 1 + .../test/library-tests/controlflow/cfg.swift | 2 +- 4 files changed, 83 insertions(+), 56 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/test/TestCfg.qll b/shared/controlflow/codeql/controlflow/test/TestCfg.qll index d59fcc14fabb..9767688433fb 100644 --- a/shared/controlflow/codeql/controlflow/test/TestCfg.qll +++ b/shared/controlflow/codeql/controlflow/test/TestCfg.qll @@ -85,21 +85,59 @@ module Make Cfg, InputSig Cfg, InputSig Cfg, InputSig Cfg, InputSig Cfg, InputSig Cfg, InputSig Cfg, InputSig Cfg, InputSig x -> + -> y -^ BinaryExpr' | +| 62 | cfg.swift:62:6:62:6 | y | 'y' | +| 62 | cfg.swift:62:19:62:19 | x | 'x -> + -> y -^ BinaryExpr' | | 66 | cfg.swift:66:1:70:1 | Block | 'Block' | | 66 | cfg.swift:66:1:70:1 | FunctionDeclaration | 'FunctionDeclaration' | | 67 | cfg.swift:67:3:67:34 | VariableDeclaration | 'VariableDeclaration -V x1 -> createClosure1 -> Argument -V "" -^ CallExpr -^ CallExpr' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.expected b/unified/ql/test/library-tests/controlflow/cfg.expected index fedda2b0ed23..675e2bbe0312 100644 --- a/unified/ql/test/library-tests/controlflow/cfg.expected +++ b/unified/ql/test/library-tests/controlflow/cfg.expected @@ -1,4 +1,5 @@ bbContinues +| cfg.swift:62:6:62:6 | y | 'y goto Block(-1)' | | cfg.swift:147:5:147:5 | Block | 'Block goto true(+3)' | | cfg.swift:525:78:525:78 | Block | 'Block goto Task(+2)' | | cfg.swift:526:9:526:20 | continuation | 'continuation goto Block(-1)' | diff --git a/unified/ql/test/library-tests/controlflow/cfg.swift b/unified/ql/test/library-tests/controlflow/cfg.swift index db2d1cb9394d..984983fc1b52 100644 --- a/unified/ql/test/library-tests/controlflow/cfg.swift +++ b/unified/ql/test/library-tests/controlflow/cfg.swift @@ -59,7 +59,7 @@ func createClosure2(x : Int) -> (_ : Int) -> Int { // $ noCfg func createClosure3(x : Int) -> (_ : Int) -> Int { // $ noCfg return { - (y) -> Int in x + y + (y) -> Int in x + y // $ bbContinues='y goto Block(-1)' } }