From ab268514a174c7fe8925b2639e0ef5786b06b3fc Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 24 May 2022 13:45:53 +0100 Subject: [PATCH 1/4] Swift: Create a custom IPA type for 'ControlFlowElement's and fixup various type annotations. --- .../codeql/swift/controlflow/BasicBlocks.qll | 13 +- .../lib/codeql/swift/controlflow/CfgNodes.qll | 7 +- .../swift/controlflow/ControlFlowGraph.qll | 7 +- .../swift/controlflow/internal/Completion.qll | 64 +++++-- .../internal/ControlFlowElements.qll | 163 ++++++++++++++++++ .../internal/ControlFlowGraphImplSpecific.qll | 26 +-- .../swift/controlflow/internal/Scope.qll | 10 +- .../swift/controlflow/internal/Splitting.qll | 33 ++-- .../swift/elements/expr/SubscriptExpr.qll | 1 - 9 files changed, 258 insertions(+), 66 deletions(-) create mode 100644 swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll diff --git a/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll b/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll index dd021a41ddf..462af68202f 100644 --- a/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll +++ b/swift/ql/lib/codeql/swift/controlflow/BasicBlocks.qll @@ -3,6 +3,7 @@ private import swift private import ControlFlowGraph private import internal.ControlFlowGraphImpl +private import internal.ControlFlowElements private import CfgNodes private import SuccessorTypes @@ -198,8 +199,18 @@ private module JoinBlockPredecessors { private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y) + private AstNode projctToAst(ControlFlowElement n) { + result = n.asAstNode() + or + isPropertyGetterElement(n, _, result) + or + isPropertySetterElement(n, _, result) + or + isPropertyObserverElement(n, _, result) + } + int getId(JoinBlockPredecessor jbp) { - idOf(jbp.getFirstNode().(AstCfgNode).getNode(), result) + idOf(projctToAst(jbp.getFirstNode().(AstCfgNode).getNode()), result) or idOf(jbp.(EntryBasicBlock).getScope(), result) } diff --git a/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll b/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll index 1833089896c..6c9f3206e04 100644 --- a/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll +++ b/swift/ql/lib/codeql/swift/controlflow/CfgNodes.qll @@ -4,6 +4,7 @@ private import swift private import BasicBlocks private import ControlFlowGraph private import internal.ControlFlowGraphImpl +private import internal.ControlFlowElements private import internal.Splitting /** An entry node for a given scope. */ @@ -66,11 +67,11 @@ class ExitNode extends ControlFlowNode, TExitNode { */ class AstCfgNode extends ControlFlowNode, TElementNode { private Splits splits; - private AstNode n; + private ControlFlowElement n; AstCfgNode() { this = TElementNode(_, n, splits) } - final override AstNode getNode() { result = n } + final override ControlFlowElement getNode() { result = n } override Location getLocation() { result = n.getLocation() } @@ -96,7 +97,7 @@ class AstCfgNode extends ControlFlowNode, TElementNode { class ExprCfgNode extends AstCfgNode { Expr e; - ExprCfgNode() { e = this.getNode() } + ExprCfgNode() { e = this.getNode().asAstNode() } /** Gets the underlying expression. */ Expr getExpr() { result = e } diff --git a/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll b/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll index a54043499d6..d6eec89d939 100644 --- a/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll +++ b/swift/ql/lib/codeql/swift/controlflow/ControlFlowGraph.qll @@ -6,13 +6,14 @@ private import SuccessorTypes private import internal.ControlFlowGraphImpl private import internal.Completion private import internal.Scope +private import internal.ControlFlowElements /** An AST node with an associated control-flow graph. */ class CfgScope extends Scope instanceof CfgScope::Range_ { /** Gets the CFG scope that this scope is nested under, if any. */ final CfgScope getOuterCfgScope() { - exists(AstNode parent | - parent = getParent(this) and + exists(ControlFlowElement parent | + parent.asAstNode() = getParentOfAst(this) and result = getCfgScope(parent) ) } @@ -31,7 +32,7 @@ class ControlFlowNode extends TCfgNode { string toString() { none() } /** Gets the AST node that this node corresponds to, if any. */ - AstNode getNode() { none() } + ControlFlowElement getNode() { none() } /** Gets the location of this control flow node. */ Location getLocation() { none() } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/Completion.qll b/swift/ql/lib/codeql/swift/controlflow/internal/Completion.qll index 8180e75f2f9..dd3d3219453 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/Completion.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/Completion.qll @@ -6,6 +6,7 @@ private import swift private import codeql.swift.controlflow.ControlFlowGraph +private import ControlFlowElements private import ControlFlowGraphImpl private import SuccessorTypes @@ -41,8 +42,8 @@ private predicate completionIsValidForStmt(Stmt stmt, Completion c) { /** A completion of a statement or an expression. */ abstract class Completion extends TCompletion { - private predicate isValidForSpecific(AstNode n) { - completionIsValidForStmt(n, this) + private predicate isValidForSpecific(ControlFlowElement n) { + completionIsValidForStmt(n.asAstNode(), this) or mustHaveBooleanCompletion(n) and ( @@ -52,22 +53,24 @@ abstract class Completion extends TCompletion { this = TBooleanCompletion(_) ) or - mustHaveMatchingCompletion(n) and + mustHaveMatchingCompletion(n.asAstNode()) and ( - exists(boolean value | isMatchingConstant(n, value) | this = TMatchingCompletion(value)) + exists(boolean value | isMatchingConstant(n.asAstNode(), value) | + this = TMatchingCompletion(value) + ) or - not isMatchingConstant(n, _) and + not isMatchingConstant(n.asAstNode(), _) and this = TMatchingCompletion(_) ) or - mustHaveThrowCompletion(n, this) + mustHaveThrowCompletion(n.asAstNode(), this) } /** Holds if this completion is valid for node `n`. */ - predicate isValidFor(AstNode n) { + predicate isValidFor(ControlFlowElement n) { this.isValidForSpecific(n) or - mayHaveThrowCompletion(n, this) + mayHaveThrowCompletion(n.asAstNode(), this) or not any(Completion c).isValidForSpecific(n) and this = TSimpleCompletion() @@ -87,25 +90,35 @@ abstract class Completion extends TCompletion { } /** Holds if node `n` has the Boolean constant value `value`. */ -private predicate isBooleanConstant(AstNode n, boolean value) { +private predicate isBooleanConstant(ControlFlowElement n, boolean value) { mustHaveBooleanCompletion(n) and - value = n.(BooleanLiteralExpr).getValue() + value = n.asAstNode().(BooleanLiteralExpr).getValue() or // Boolean consants hidden inside conversions are also // constants that resolve to the same value. - isBooleanConstant(n.getResolveStep(), value) + exists(ControlFlowElement parent | + parent.asAstNode() = n.asAstNode().getResolveStep() and + isBooleanConstant(parent, value) + ) } /** * Holds if a normal completion of `n` must be a Boolean completion. */ -private predicate mustHaveBooleanCompletion(AstNode n) { inBooleanContext(n) } +private predicate mustHaveBooleanCompletion(ControlFlowElement n) { inBooleanContext(n) } /** * Holds if `n` is used in a Boolean context. That is, the value * that `n` evaluates to determines a true/false branch successor. */ -private predicate inBooleanContext(AstNode n) { +private predicate inBooleanContext(ControlFlowElement n) { + astInBooleanContext(n.asAstNode()) or + astInBooleanContext(n.(PropertyGetterElement).getRef()) or + astInBooleanContext(n.(PropertySetterElement).getAssignExpr()) or + astInBooleanContext(n.(PropertyObserverElement).getAssignExpr()) +} + +private predicate astInBooleanContext(AstNode n) { n = any(ConditionElement condElem).getFullyUnresolved() or n = any(StmtCondition stmtCond).getFullyUnresolved() @@ -115,30 +128,30 @@ private predicate inBooleanContext(AstNode n) { exists(LogicalAndExpr parent | n = parent.getLeftOperand().getFullyConverted() or - inBooleanContext(parent) and + astInBooleanContext(parent) and n = parent.getRightOperand().getFullyConverted() ) or exists(LogicalOrExpr parent | n = parent.getLeftOperand().getFullyConverted() or - inBooleanContext(parent) and + astInBooleanContext(parent) and n = parent.getRightOperand().getFullyConverted() ) or - n = any(NotExpr parent | inBooleanContext(parent)).getOperand().getFullyConverted() + n = any(NotExpr parent | astInBooleanContext(parent)).getOperand().getFullyConverted() or exists(IfExpr ifExpr | ifExpr.getCondition().getFullyConverted() = n or - inBooleanContext(ifExpr) and + astInBooleanContext(ifExpr) and n = ifExpr.getBranch(_).getFullyConverted() ) or exists(ForEachStmt foreach | n = foreach.getWhere().getFullyConverted()) or exists(Exprs::Conversions::ConversionOrIdentityTree parent | - inBooleanContext(parent) and + astInBooleanContext(parent.getAst()) and parent.convertsFrom(n) ) } @@ -477,3 +490,18 @@ class ThrowCompletion extends TThrowCompletion, Completion { override string toString() { result = "throw" } } + +/** + * Hold if `c` represents normal evaluation of a statement or an + * expression. + */ +predicate completionIsNormal(Completion c) { c instanceof NormalCompletion } + +/** + * Hold if `c` represents simple (normal) evaluation of a statement or an + * expression. + */ +predicate completionIsSimple(Completion c) { c instanceof SimpleCompletion } + +/** Holds if `c` is a valid completion for `e`. */ +predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll new file mode 100644 index 00000000000..2a12d688ac8 --- /dev/null +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowElements.qll @@ -0,0 +1,163 @@ +private import swift + +cached +newtype TControlFlowElement = + TAstElement(AstNode n) or + TPropertyGetterElement(Decl accessor, Expr ref) { isPropertyGetterElement(accessor, ref) } or + TPropertySetterElement(AccessorDecl accessor, AssignExpr assign) { + isPropertySetterElement(accessor, assign) + } or + TPropertyObserverElement(AccessorDecl observer, AssignExpr assign) { + isPropertyObserverElement(observer, assign) + } + +predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e } + +predicate isRValue(Expr e) { not isLValue(e) } + +predicate ignoreAstElement(AstNode n) { + isPropertyGetterElement(_, n) + or + isPropertySetterElement(_, n) +} + +private AccessorDecl getAnAccessorDecl(Decl d) { + result = d.(VarDecl).getAnAccessorDecl() or + result = d.(SubscriptDecl).getAnAccessorDecl() +} + +predicate isPropertyGetterElement(AccessorDecl accessor, Expr ref) { + hasDirectToImplementationOrOrdinarySemantics(ref) and + isRValue(ref) and + accessor.isGetter() and + accessor = getAnAccessorDecl([ref.(LookupExpr).getMember(), ref.(DeclRefExpr).getDecl()]) +} + +predicate isPropertyGetterElement(PropertyGetterElement pge, AccessorDecl accessor, Expr ref) { + pge = TPropertyGetterElement(accessor, ref) +} + +private predicate hasDirectToImplementationSemantics(Expr e) { + e.(MemberRefExpr).hasDirectToImplementationSemantics() + or + e.(SubscriptExpr).hasDirectToImplementationSemantics() + or + e.(DeclRefExpr).hasDirectToImplementationSemantics() +} + +private predicate hasOrdinarySemantics(Expr e) { + e.(MemberRefExpr).hasOrdinarySemantics() + or + e.(SubscriptExpr).hasOrdinarySemantics() + or + e.(DeclRefExpr).hasOrdinarySemantics() +} + +private predicate hasDirectToImplementationOrOrdinarySemantics(Expr e) { + hasDirectToImplementationSemantics(e) or hasOrdinarySemantics(e) +} + +predicate isPropertySetterElement(AccessorDecl accessor, AssignExpr assign) { + exists(Expr lhs | lhs = assign.getDest() | + hasDirectToImplementationOrOrdinarySemantics(lhs) and + accessor.isSetter() and + isLValue(lhs) and + accessor = getAnAccessorDecl([lhs.(LookupExpr).getMember(), lhs.(DeclRefExpr).getDecl()]) + ) +} + +predicate isPropertySetterElement( + PropertySetterElement pse, AccessorDecl accessor, AssignExpr assign +) { + pse = TPropertySetterElement(accessor, assign) +} + +predicate isPropertyObserverElement(AccessorDecl observer, AssignExpr assign) { + exists(Expr lhs | lhs = assign.getDest() | + hasDirectToImplementationOrOrdinarySemantics(lhs) and + observer.isPropertyObserver() and + isLValue(lhs) and + observer = getAnAccessorDecl([lhs.(LookupExpr).getMember(), lhs.(DeclRefExpr).getDecl()]) + ) +} + +predicate isPropertyObserverElement( + PropertyObserverElement poe, AccessorDecl accessor, AssignExpr assign +) { + poe = TPropertyObserverElement(accessor, assign) +} + +class ControlFlowElement extends TControlFlowElement { + string toString() { none() } // overriden in subclasses + + AstNode asAstNode() { none() } + + Location getLocation() { none() } // overriden in subclasses +} + +class AstElement extends ControlFlowElement, TAstElement { + AstNode n; + + AstElement() { this = TAstElement(n) } + + override string toString() { result = n.toString() } + + override AstNode asAstNode() { result = n } + + override Location getLocation() { result = n.getLocation() } +} + +class PropertyGetterElement extends ControlFlowElement, TPropertyGetterElement { + AccessorDecl accessor; + Expr ref; + + PropertyGetterElement() { this = TPropertyGetterElement(accessor, ref) } + + override string toString() { result = "getter for " + ref.toString() } + + override Location getLocation() { result = ref.getLocation() } + + Expr getRef() { result = ref } + + AccessorDecl getAccessorDecl() { result = accessor } +} + +class PropertySetterElement extends ControlFlowElement, TPropertySetterElement { + AccessorDecl accessor; + AssignExpr assign; + + PropertySetterElement() { this = TPropertySetterElement(accessor, assign) } + + override string toString() { result = "setter for " + assign } + + override Location getLocation() { result = assign.getLocation() } + + AccessorDecl getAccessorDecl() { result = accessor } + + AssignExpr getAssignExpr() { result = assign } +} + +class PropertyObserverElement extends ControlFlowElement, TPropertyObserverElement { + AccessorDecl observer; + AssignExpr assign; + + PropertyObserverElement() { this = TPropertyObserverElement(observer, assign) } + + override string toString() { + this.isWillSet() and + result = "willSet observer for " + assign.toString() + or + this.isDidSet() and + result = "didSet observer for " + assign.toString() + } + + override Location getLocation() { result = assign.getLocation() } + + AccessorDecl getObserver() { result = observer } + + predicate isWillSet() { observer.isWillSet() } + + predicate isDidSet() { observer.isDidSet() } + + AssignExpr getAssignExpr() { result = assign } +} diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll index ff72929e41d..7c11c2c2f84 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplSpecific.qll @@ -1,30 +1,14 @@ private import swift as S private import ControlFlowGraphImpl as Impl -private import Completion as Comp +import Completion private import codeql.swift.controlflow.ControlFlowGraph as CFG private import Splitting as Splitting +private import Scope +import ControlFlowElements +import AstControlFlowTrees /** The base class for `ControlFlowTree`. */ -class ControlFlowTreeBase extends S::AstNode { } - -class ControlFlowElement = S::AstNode; - -class Completion = Comp::Completion; - -/** - * Hold if `c` represents normal evaluation of a statement or an - * expression. - */ -predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion } - -/** - * Hold if `c` represents simple (normal) evaluation of a statement or an - * expression. - */ -predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion } - -/** Holds if `c` is a valid completion for `e`. */ -predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) } +class ControlFlowTreeBase = ControlFlowElement; class CfgScope = CFG::CfgScope; diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/Scope.qll b/swift/ql/lib/codeql/swift/controlflow/internal/Scope.qll index 99dc955d1e6..c06c67d1665 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/Scope.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/Scope.qll @@ -16,7 +16,7 @@ private module Scope { class TypeRange = Callable::TypeRange; class Range extends AstNode, TypeRange { - Range getOuterScope() { result = scopeOf(this) } + Range getOuterScope() { result = scopeOfAst(this) } } } @@ -235,14 +235,14 @@ private module Cached { } cached - AstNode getParent(AstNode ast) { getChild(result, _) = ast } + AstNode getParentOfAst(AstNode ast) { getChild(result, _) = ast } } /** Gets the enclosing scope of a node */ cached -AstNode scopeOf(AstNode n) { - exists(AstNode p | p = getParent(n) | - if p instanceof Scope then p = result else result = scopeOf(p) +AstNode scopeOfAst(AstNode n) { + exists(AstNode p | p = getParentOfAst(n) | + if p instanceof Scope then p = result else result = scopeOfAst(p) ) } diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/Splitting.qll b/swift/ql/lib/codeql/swift/controlflow/internal/Splitting.qll index d09e9b2fd2b..e0b2961283b 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/Splitting.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/Splitting.qll @@ -6,6 +6,8 @@ private import swift private import Completion private import ControlFlowGraphImpl private import codeql.swift.controlflow.ControlFlowGraph +private import AstControlFlowTrees +private import ControlFlowElements cached private module Cached { @@ -37,7 +39,7 @@ private module ConditionalCompletionSplitting { private class ConditionalCompletionSplitKind extends SplitKind, TConditionalCompletionSplitKind { override int getListOrder() { result = 0 } - override predicate isEnabled(AstNode n) { this.appliesTo(n) } + override predicate isEnabled(ControlFlowElement n) { this.appliesTo(n) } override string toString() { result = "ConditionalCompletion" } } @@ -45,47 +47,50 @@ private module ConditionalCompletionSplitting { private class ConditionalCompletionSplitImpl extends SplitImpl, ConditionalCompletionSplit { override ConditionalCompletionSplitKind getKind() { any() } - override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { + override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { succ(pred, succ, c) and last(succ, _, completion) and ( - last(succ.(NotExpr).getOperand().getFullyConverted(), pred, c) and + astLast(succ.asAstNode().(NotExpr).getOperand().getFullyConverted(), pred, c) and completion.(BooleanCompletion).getDual() = c or - last(succ.(LogicalAndExpr).getAnOperand().getFullyConverted(), pred, c) and + astLast(succ.asAstNode().(LogicalAndExpr).getAnOperand().getFullyConverted(), pred, c) and completion = c or - last(succ.(LogicalOrExpr).getAnOperand().getFullyConverted(), pred, c) and + astLast(succ.asAstNode().(LogicalOrExpr).getAnOperand().getFullyConverted(), pred, c) and completion = c or - succ = + succ.asAstNode() = any(IfExpr ce | - last(ce.getBranch(_).getFullyConverted(), pred, c) and + astLast(ce.getBranch(_).getFullyConverted(), pred, c) and completion = c ) or - exists(Expr e | - succ.(Exprs::Conversions::ConversionOrIdentityTree).convertsFrom(e) and - last(e, pred, c) and + exists(Expr e, Exprs::Conversions::ConversionOrIdentityTree conv | + succ.asAstNode() = conv.getAst() and + conv.convertsFrom(e) and + astLast(e, pred, c) and completion = c ) ) } - override predicate hasEntryScope(CfgScope scope, AstNode succ) { none() } + override predicate hasEntryScope(CfgScope scope, ControlFlowElement succ) { none() } - override predicate hasExit(AstNode pred, AstNode succ, Completion c) { + override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { this.appliesTo(pred) and succ(pred, succ, c) and if c instanceof ConditionalCompletion then completion = c else any() } - override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { + override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { this.appliesTo(last) and succExit(scope, last, c) and if c instanceof ConditionalCompletion then completion = c else any() } - override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { none() } + override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + none() + } } } diff --git a/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll index 8ae51baae26..31fd87a55ad 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll @@ -1,4 +1,3 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.expr.SubscriptExpr class SubscriptExpr extends SubscriptExprBase { } From 1f4924f978bc5e3fff7a32c13cc9e307085a611a Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Tue, 24 May 2022 13:47:36 +0100 Subject: [PATCH 2/4] Swift: Create a custom "AST" version of the public CFG classes. This is necessary because the CFG library doesn't support the following two requirements simultaneously: 1. Traverse AST classes by virtual dispatch 2. Construct ControlFlowElements from non-AST classes Because the CFG trees derive from the a base type that must be a subtype of `ControlFlowElement`. So if we make `ControlFlowElement` an IPA type, we cannot write: ``` class AssignTree extends PostOrderTree instanceof AssignExpr { ... } ``` because `AssignExpr` is not a subtype of PostOrderTree (since PostOrderTree is now a subtype of the new IPA type). To fix this, Tom suggested the following (which is implemented in this PR): 1. Create a copy of the CFG tree classes (i.e., Pre/PostOrderTree, LeafTree, etc.) and call them AstPreOrderTree/AstPostOrderTree, AstLeafTree, etc. 2. For each tree AstTree from step 1, create a instance of the internal CFG library's appropriate class. 3. In `ControlFlowGraphImpl`, proceed as normal with virtual dispatch using `instanceof`, but extend the AstTree classes from step 1 instead of the CFG's own tree classes. This works because each AstTree implements one of the CFG library's tree classes (as per step 2). This commit performs step 1 and 2. Step 3 will be the next commit. --- .../internal/AstControlFlowTrees.qll | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 swift/ql/lib/codeql/swift/controlflow/internal/AstControlFlowTrees.qll diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/AstControlFlowTrees.qll b/swift/ql/lib/codeql/swift/controlflow/internal/AstControlFlowTrees.qll new file mode 100644 index 00000000000..0fdf4d1735a --- /dev/null +++ b/swift/ql/lib/codeql/swift/controlflow/internal/AstControlFlowTrees.qll @@ -0,0 +1,40 @@ +private import swift +private import Completion +private import ControlFlowGraphImplShared +private import ControlFlowElements + +abstract class AstControlFlowTree extends ControlFlowTree { + AstNode ast; + + AstControlFlowTree() { ast = this.asAstNode() } + + AstNode getAst() { result = ast } +} + +/** + * Holds if `first` is the first element executed within control flow + * element `cft`. + */ +predicate astFirst(AstNode cft, ControlFlowElement first) { + first(any(ControlFlowTree tree | cft = tree.asAstNode()), first) +} + +/** + * Holds if `last` with completion `c` is a potential last element executed + * within control flow element `cft`. + */ +predicate astLast(AstNode cft, ControlFlowElement last, Completion c) { + last(any(ControlFlowTree tree | cft = tree.asAstNode()), last, c) +} + +abstract class AstPreOrderTree extends AstControlFlowTree, PreOrderTree { } + +abstract class AstPostOrderTree extends AstControlFlowTree, PostOrderTree { } + +abstract class AstStandardTree extends AstControlFlowTree, StandardTree { } + +abstract class AstStandardPreOrderTree extends AstStandardTree, StandardPreOrderTree { } + +abstract class AstStandardPostOrderTree extends AstStandardTree, StandardPostOrderTree { } + +abstract class AstLeafTree extends AstPreOrderTree, AstPostOrderTree, LeafTree { } From 67cc1b503b41230413b27bf65bf7722ea55098a7 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 25 May 2022 13:44:59 +0100 Subject: [PATCH 3/4] Swift: Implement step 3 from the previous commit message. --- .../internal/ControlFlowGraphImpl.qll | 1032 ++++++++++------- .../swift/elements/decl/AccessorDecl.qll | 15 +- .../swift/elements/expr/SubscriptExpr.qll | 16 +- .../controlflow/graph/Cfg.expected | 8 +- 4 files changed, 656 insertions(+), 415 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index 446c1637101..640af10b6b6 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -36,59 +36,73 @@ private import codeql.swift.controlflow.ControlFlowGraph private import Completion private import Scope import ControlFlowGraphImplShared +private import ControlFlowElements +private import AstControlFlowTrees module CfgScope { abstract class Range_ extends AstNode { - abstract predicate entry(AstNode first); + abstract predicate entry(ControlFlowElement first); - abstract predicate exit(AstNode last, Completion c); + abstract predicate exit(ControlFlowElement last, Completion c); } private class BodyStmtCallableScope extends Range_ instanceof AbstractFunctionDecl { - final override predicate entry(AstNode first) { - super.getBody().(Stmts::BraceStmtTree).firstInner(first) + final override predicate entry(ControlFlowElement first) { + exists(Stmts::BraceStmtTree tree | + tree.getAst() = super.getBody() and + tree.firstInner(first) + ) } - final override predicate exit(AstNode last, Completion c) { - super.getBody().(Stmts::BraceStmtTree).last(last, c) + final override predicate exit(ControlFlowElement last, Completion c) { + exists(Stmts::BraceStmtTree tree | + tree.getAst() = super.getBody() and + tree.last(last, c) + ) } } } /** Holds if `first` is first executed when entering `scope`. */ pragma[nomagic] -predicate succEntry(CfgScope::Range_ scope, AstNode first) { scope.entry(first) } +predicate succEntry(CfgScope::Range_ scope, ControlFlowElement first) { scope.entry(first) } /** Holds if `last` with completion `c` can exit `scope`. */ pragma[nomagic] -predicate succExit(CfgScope::Range_ scope, AstNode last, Completion c) { scope.exit(last, c) } +predicate succExit(CfgScope::Range_ scope, ControlFlowElement last, Completion c) { + scope.exit(last, c) +} /** * Control-flow for statements. */ module Stmts { - class BraceStmtTree extends ControlFlowTree instanceof BraceStmt { - override predicate propagatesAbnormal(AstNode node) { none() } + class BraceStmtTree extends AstControlFlowTree { + override BraceStmt ast; - override predicate first(AstNode first) { + override predicate propagatesAbnormal(ControlFlowElement node) { none() } + + override predicate first(ControlFlowElement first) { this.firstInner(first) or - not exists(super.getFirstElement()) and first = this + not exists(ast.getFirstElement()) and first.asAstNode() = ast } - override predicate last(AstNode last, Completion c) { + override predicate last(ControlFlowElement last, Completion c) { this.lastInner(last, c) or - not exists(super.getFirstElement()) and last = this and c instanceof SimpleCompletion + not exists(ast.getFirstElement()) and + last.asAstNode() = ast and + c instanceof SimpleCompletion } - predicate firstInner(AstNode first) { first(super.getFirstElement(), first) } + predicate firstInner(ControlFlowElement first) { astFirst(ast.getFirstElement(), first) } /** Gets the body of the i'th `defer` statement. */ private BraceStmt getDeferStmtBody(int i) { result = rank[i](DeferStmt defer, int index | - defer = super.getElement(index) + defer = ast.getElement(index) | defer.getBody() order by index ) @@ -117,7 +131,7 @@ module Stmts { int getDeferIndex(int i) { exists(DeferStmt defer | this.getDeferStmtBody(i) = defer.getBody() and - super.getElement(result) = defer + ast.getElement(result) = defer ) } @@ -136,143 +150,153 @@ module Stmts { ) } - predicate lastInner(AstNode last, Completion c) { + predicate lastInner(ControlFlowElement last, Completion c) { // Normal exit and no defer statements - last(super.getLastElement(), last, c) and + astLast(ast.getLastElement(), last, c) and not exists(this.getFirstDeferStmtBody()) and c instanceof NormalCompletion or // Normal exit from the last defer statement to be executed - last(this.getLastDeferStmtBody(), last, c) and + astLast(this.getLastDeferStmtBody(), last, c) and c instanceof NormalCompletion or // Abnormal exit without any defer statements not c instanceof NormalCompletion and - last(super.getAnElement(), last, c) and + astLast(ast.getAnElement(), last, c) and not exists(this.getFirstDeferStmtBody()) } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // left-to-right evaluation of statements exists(int i | - last(super.getElement(i), pred, c) and - first(super.getElement(i + 1), succ) and + astLast(ast.getElement(i), pred, c) and + astFirst(ast.getElement(i + 1), succ) and c instanceof NormalCompletion ) or // Flow from last elements to the first defer statement to be executed c instanceof NormalCompletion and - last(super.getLastElement(), pred, c) and - first(this.getFirstDeferStmtBody(), succ) + astLast(ast.getLastElement(), pred, c) and + astFirst(this.getFirstDeferStmtBody(), succ) or // Flow from a defer statement to the next defer to be executed c instanceof NormalCompletion and exists(int i | - last(this.getDeferStmtBody(i), pred, c) and - first(this.getDeferStmtBody(i - 1), succ) + astLast(this.getDeferStmtBody(i), pred, c) and + astFirst(this.getDeferStmtBody(i - 1), succ) ) or // Abnormal exit from an element to the first defer statement to be executed. not c instanceof NormalCompletion and exists(int i | - last(super.getElement(i), pred, c) and - first(this.getDeferStmtBodyAfterStmt(i), succ) + astLast(ast.getElement(i), pred, c) and + astFirst(this.getDeferStmtBodyAfterStmt(i), succ) ) } } - private class ReturnStmtTree extends StandardPostOrderTree, ReturnStmt { - final override ControlFlowTree getChildElement(int i) { - result = this.getResult().getFullyConverted() and i = 0 + private class ReturnStmtTree extends AstStandardPostOrderTree { + override ReturnStmt ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getResult().getFullyConverted() and i = 0 } } - private class FailTree extends LeafTree instanceof FailStmt { } + private class FailTree extends AstLeafTree { + override FailStmt ast; + } - private class StmtConditionTree extends PostOrderTree instanceof StmtCondition { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getAnElement().getUnderlyingCondition() + private class StmtConditionTree extends AstPostOrderTree { + override StmtCondition ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getAnElement().getUnderlyingCondition() } - final override predicate first(AstNode first) { - first(super.getFirstElement().getUnderlyingCondition().getFullyUnresolved(), first) + final override predicate first(ControlFlowElement first) { + astFirst(ast.getFirstElement().getUnderlyingCondition().getFullyUnresolved(), first) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Left-to-right evaluation of elements exists(int i | - last(super.getElement(i).getUnderlyingCondition().getFullyUnresolved(), pred, c) and + astLast(ast.getElement(i).getUnderlyingCondition().getFullyUnresolved(), pred, c) and c instanceof NormalCompletion and - first(super.getElement(i + 1).getUnderlyingCondition().getFullyUnresolved(), succ) + astFirst(ast.getElement(i + 1).getUnderlyingCondition().getFullyUnresolved(), succ) ) or // Post-order: flow from thrown expression to the throw statement. - last(super.getLastElement().getUnderlyingCondition().getFullyUnresolved(), pred, c) and + astLast(ast.getLastElement().getUnderlyingCondition().getFullyUnresolved(), pred, c) and c instanceof NormalCompletion and - succ = this + succ.asAstNode() = ast } } - private class IfStmtTree extends PreOrderTree instanceof IfStmt { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getCondition().getFullyUnresolved() or - child = super.getThen() or - child = super.getElse() + private class IfStmtTree extends AstPreOrderTree { + override IfStmt ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getCondition().getFullyUnresolved() or + child.asAstNode() = ast.getThen() or + child.asAstNode() = ast.getElse() } - final override predicate last(AstNode last, Completion c) { + final override predicate last(ControlFlowElement last, Completion c) { // Condition exits with a false completion and there is no `else` branch - last(super.getCondition().getFullyUnresolved(), last, c) and + astLast(ast.getCondition().getFullyUnresolved(), last, c) and c instanceof FalseCompletion and - not exists(super.getElse()) + not exists(ast.getElse()) or // Then/Else branch exits with any completion - last(super.getBranch(_), last, c) + astLast(ast.getBranch(_), last, c) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: flow from statement itself to first element of condition - pred = this and - first(super.getCondition().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getCondition().getFullyUnresolved(), succ) and c instanceof SimpleCompletion or - last(super.getCondition().getFullyUnresolved(), pred, c) and + astLast(ast.getCondition().getFullyUnresolved(), pred, c) and ( // Flow from last element of condition to first element of then branch - c instanceof TrueCompletion and first(super.getThen(), succ) + c instanceof TrueCompletion and astFirst(ast.getThen(), succ) or // Flow from last element of condition to first element of else branch - c instanceof FalseCompletion and first(super.getElse(), succ) + c instanceof FalseCompletion and astFirst(ast.getElse(), succ) ) } } - private class GuardTree extends PreOrderTree instanceof GuardStmt { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getCondition().getFullyUnresolved() or - child = super.getBody() + private class GuardTree extends AstPreOrderTree { + override GuardStmt ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getCondition().getFullyUnresolved() or + child.asAstNode() = ast.getBody() } - final override predicate last(AstNode last, Completion c) { + final override predicate last(ControlFlowElement last, Completion c) { // Normal exit after evaluating the body - last(super.getBody(), last, c) and + astLast(ast.getBody(), last, c) and c instanceof NormalCompletion or // Exit when a condition is true - last(super.getCondition().getFullyUnresolved(), last, c) and + astLast(ast.getCondition().getFullyUnresolved(), last, c) and c instanceof TrueCompletion } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: flow from statement itself to first element of condition - pred = this and - first(super.getCondition().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getCondition().getFullyUnresolved(), succ) and c instanceof SimpleCompletion or // Flow to the body when the condition is false - last(super.getCondition().getFullyUnresolved(), pred, c) and + astLast(ast.getCondition().getFullyUnresolved(), pred, c) and c instanceof FalseCompletion and - first(super.getBody(), succ) + astFirst(ast.getBody(), succ) } } @@ -284,14 +308,21 @@ module Stmts { class LoopStmt = @for_each_stmt or ConditionalLoop; - abstract class LoopTree extends PreOrderTree instanceof ConditionalLoop { - abstract AstNode getCondition(); + abstract class LoopTree extends AstPreOrderTree { + LoopTree() { + ast instanceof WhileStmt or + ast instanceof RepeatWhileStmt + } - abstract AstNode getBody(); + abstract ControlFlowElement getCondition(); - final override predicate propagatesAbnormal(AstNode child) { child = this.getCondition() } + abstract ControlFlowElement getBody(); - final override predicate last(AstNode last, Completion c) { + final override predicate propagatesAbnormal(ControlFlowElement child) { + child = this.getCondition() + } + + final override predicate last(ControlFlowElement last, Completion c) { // Condition exits with a false completion last(this.getCondition(), last, c) and c instanceof FalseCompletion @@ -300,190 +331,204 @@ module Stmts { exists(BreakCompletion break | last(this.getBody(), last, break) and // Propagate the break upwards if we need to break out of multiple loops. - if break.getTarget() = this then c instanceof SimpleCompletion else c = break + if break.getTarget() = ast then c instanceof SimpleCompletion else c = break ) or // Body exits with a completion that does not continue the loop last(this.getBody(), last, c) and not c instanceof BreakCompletion and - not c.continuesLoop(this) + not c.continuesLoop(ast) } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { last(this.getCondition(), pred, c) and c instanceof TrueCompletion and first(this.getBody(), succ) or last(this.getBody(), pred, c) and first(this.getCondition(), succ) and - c.continuesLoop(this) + c.continuesLoop(ast) } } - private class WhileTree extends LoopTree instanceof WhileStmt { - final override AstNode getCondition() { - result = WhileStmt.super.getCondition().getFullyUnresolved() + private class WhileTree extends LoopTree { + override WhileStmt ast; + + final override ControlFlowElement getCondition() { + result.asAstNode() = ast.getCondition().getFullyUnresolved() } - final override AstNode getBody() { result = WhileStmt.super.getBody() } + final override ControlFlowElement getBody() { result.asAstNode() = ast.getBody() } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { LoopTree.super.succ(pred, succ, c) or - pred = this and + pred.asAstNode() = ast and first(this.getCondition(), succ) and c instanceof SimpleCompletion } } - private class RepeatWhileTree extends LoopTree instanceof RepeatWhileStmt { - final override AstNode getCondition() { - result = RepeatWhileStmt.super.getCondition().getFullyConverted() + private class RepeatWhileTree extends LoopTree { + override RepeatWhileStmt ast; + + final override ControlFlowElement getCondition() { + result.asAstNode() = ast.getCondition().getFullyConverted() } - final override AstNode getBody() { result = RepeatWhileStmt.super.getBody() } + final override ControlFlowElement getBody() { result.asAstNode() = ast.getBody() } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { LoopTree.super.succ(pred, succ, c) or // Pre-order: Flow from the element to the body. - pred = this and + pred.asAstNode() = ast and first(this.getBody(), succ) and c instanceof SimpleCompletion } } - private class ForEachTree extends ControlFlowTree instanceof ForEachStmt { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getSequence().getFullyConverted() + private class ForEachTree extends AstControlFlowTree { + override ForEachStmt ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getSequence().getFullyConverted() or - child = super.getPattern().getFullyUnresolved() + child.asAstNode() = ast.getPattern().getFullyUnresolved() } - final override predicate first(AstNode first) { + final override predicate first(ControlFlowElement first) { // Unlike most other statements, `foreach` statements are not modelled in // pre-order, because we use the `foreach` node itself to represent the // emptiness test that determines whether to execute the loop body - first(super.getSequence().getFullyConverted(), first) + astFirst(ast.getSequence().getFullyConverted(), first) } - final override predicate last(AstNode last, Completion c) { + final override predicate last(ControlFlowElement last, Completion c) { // Emptiness test exits with no more elements - last = this and + last.asAstNode() = ast and c.(EmptinessCompletion).isEmpty() or // Body exits with a break completion exists(BreakCompletion break | - last(super.getBody(), last, break) and + astLast(ast.getBody(), last, break) and // Propagate the break upwards if we need to break out of multiple loops. - if break.getTarget() = this then c instanceof SimpleCompletion else c = break + if break.getTarget() = ast then c instanceof SimpleCompletion else c = break ) or // Body exits abnormally - last(super.getBody(), last, c) and + astLast(ast.getBody(), last, c) and not c instanceof NormalCompletion and not c instanceof ContinueCompletion and not c instanceof BreakCompletion } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Flow from last element of iterator expression to emptiness test - last(super.getSequence().getFullyConverted(), pred, c) and + astLast(ast.getSequence().getFullyConverted(), pred, c) and c instanceof NormalCompletion and - succ = this + succ.asAstNode() = ast or // Flow from emptiness test to first element of variable declaration/loop body - pred = this and + pred.asAstNode() = ast and c = any(EmptinessCompletion ec | not ec.isEmpty()) and - first(super.getPattern().getFullyUnresolved(), succ) + astFirst(ast.getPattern().getFullyUnresolved(), succ) or // Flow from last element of variable declaration ... - last(super.getPattern().getFullyUnresolved(), pred, c) and + astLast(ast.getPattern().getFullyUnresolved(), pred, c) and c instanceof SimpleCompletion and ( // ... to first element of loop body if no 'where' clause exists, - first(super.getBody(), succ) and - not exists(super.getWhere()) + astFirst(ast.getBody(), succ) and + not exists(ast.getWhere()) or // ... or to the 'where' clause. - first(super.getWhere().getFullyConverted(), succ) + astFirst(ast.getWhere().getFullyConverted(), succ) ) or // Flow from the where 'clause' ... - last(super.getWhere().getFullyConverted(), pred, c) and + astLast(ast.getWhere().getFullyConverted(), pred, c) and ( // to the loop body if the condition is true, c instanceof TrueCompletion and - first(super.getBody(), succ) + astFirst(ast.getBody(), succ) or // or to the emptiness test if the condition is false. c instanceof FalseCompletion and - succ = this + succ.asAstNode() = ast ) or // Flow from last element of loop body back to emptiness test. - last(super.getBody(), pred, c) and - c.continuesLoop(this) and - succ = this + astLast(ast.getBody(), pred, c) and + c.continuesLoop(ast) and + succ.asAstNode() = ast } } } - private class BreakTree extends LeafTree instanceof BreakStmt { } + private class BreakTree extends AstLeafTree { + override BreakStmt ast; + } - private class ContinueTree extends LeafTree instanceof ContinueStmt { } + private class ContinueTree extends AstLeafTree { + override ContinueStmt ast; + } - private class SwitchTree extends PreOrderTree instanceof SwitchStmt { - override predicate propagatesAbnormal(AstNode child) { - child = super.getExpr().getFullyConverted() + private class SwitchTree extends AstPreOrderTree { + override SwitchStmt ast; + + override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getExpr().getFullyConverted() } - final override predicate last(AstNode last, Completion c) { + final override predicate last(ControlFlowElement last, Completion c) { // Switch expression exits normally and there are no cases - not exists(super.getACase()) and - last(super.getExpr().getFullyConverted(), last, c) and + not exists(ast.getACase()) and + astLast(ast.getExpr().getFullyConverted(), last, c) and c instanceof NormalCompletion or // A statement exits (TODO: with a `break` completion?) - last(super.getACase().getBody(), last, c) and + astLast(ast.getACase().getBody(), last, c) and c instanceof NormalCompletion // Note: There's no need for an exit with a non-match as // Swift's switch statements are always exhaustive. } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Flow from last element of switch expression to first element of first case c instanceof NormalCompletion and - last(super.getExpr().getFullyConverted(), pred, c) and - first(super.getFirstCase(), succ) + astLast(ast.getExpr().getFullyConverted(), pred, c) and + astFirst(ast.getFirstCase(), succ) or // Flow from last element of case pattern to next case - exists(CaseStmt case, int i | case = super.getCase(i) | - last(case.getLastLabel(), pred, c) and + exists(CaseStmt case, int i | case = ast.getCase(i) | + astLast(case.getLastLabel(), pred, c) and c.(MatchingCompletion).isNonMatch() and - first(super.getCase(i + 1), succ) + astFirst(ast.getCase(i + 1), succ) ) or // Flow from a case statement that ends in a fallthrough // statement to the next statement - last(super.getACase().getBody(), pred, c) and - first(c.(FallthroughCompletion).getDestination().getBody(), succ) + astLast(ast.getACase().getBody(), pred, c) and + astFirst(c.(FallthroughCompletion).getDestination().getBody(), succ) or // Pre-order: flow from statement itself to first switch expression - pred = this and - first(super.getExpr().getFullyConverted(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getExpr().getFullyConverted(), succ) and c instanceof SimpleCompletion } } - private class CaseLabelItemTree extends PreOrderTree instanceof CaseLabelItem { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getGuard().getFullyConverted() + private class CaseLabelItemTree extends AstPreOrderTree { + override CaseLabelItem ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getGuard().getFullyConverted() } - final override predicate last(AstNode last, Completion c) { + final override predicate last(ControlFlowElement last, Completion c) { // If the last node is the pattern ... - last(super.getPattern().getFullyUnresolved(), last, c) and + astLast(ast.getPattern().getFullyUnresolved(), last, c) and ( // then it's because we failed to match it c.(MatchingCompletion).isNonMatch() @@ -491,333 +536,383 @@ module Stmts { // Or because, there is no guard (in which case we can also finish the evaluation // here on a succesful match). c.(MatchingCompletion).isMatch() and - not super.hasGuard() + not ast.hasGuard() ) or // Or it can be the guard if a guard exists exists(BooleanCompletion booleanComp | - last(super.getGuard().getFullyUnresolved(), last, booleanComp) + astLast(ast.getGuard().getFullyUnresolved(), last, booleanComp) | booleanComp.getValue() = c.(MatchingCompletion).getValue() ) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: Flow from this to the pattern - pred = this and - first(super.getPattern().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getPattern().getFullyUnresolved(), succ) and c instanceof SimpleCompletion or // Flow from a matching pattern to the guard, if any - last(super.getPattern().getFullyUnresolved(), pred, c) and + astLast(ast.getPattern().getFullyUnresolved(), pred, c) and c instanceof MatchingCompletion and - succ = super.getGuard().getFullyConverted() + succ.asAstNode() = ast.getGuard().getFullyConverted() } } - private class CaseTree extends PreOrderTree instanceof CaseStmt { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getALabel() + private class CaseTree extends AstPreOrderTree { + override CaseStmt ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getALabel() or - child = super.getBody() + child.asAstNode() = ast.getBody() } - final override predicate last(AstNode last, Completion c) { + final override predicate last(ControlFlowElement last, Completion c) { // Case pattern exits with a non-match - last(super.getLastLabel().getFullyUnresolved(), last, c) and + astLast(ast.getLastLabel().getFullyUnresolved(), last, c) and not c.(MatchingCompletion).isMatch() or // Case body exits with any completion - last(super.getBody(), last, c) + astLast(ast.getBody(), last, c) } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: Flow from the case statement itself to the first label - pred = this and - first(super.getFirstLabel().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getFirstLabel().getFullyUnresolved(), succ) and c instanceof SimpleCompletion or // Left-to-right evaluation of labels until we find a match exists(int i | - last(super.getLabel(i).getFullyUnresolved(), pred, c) and - first(super.getLabel(i + 1).getFullyUnresolved(), succ) and + astLast(ast.getLabel(i).getFullyUnresolved(), pred, c) and + astFirst(ast.getLabel(i + 1).getFullyUnresolved(), succ) and c.(MatchingCompletion).isNonMatch() ) or // Flow from last element of pattern to first element of body - last(super.getALabel().getFullyUnresolved(), pred, c) and - first(super.getBody(), succ) and + astLast(ast.getALabel().getFullyUnresolved(), pred, c) and + astFirst(ast.getBody(), succ) and c.(MatchingCompletion).isMatch() } } - private class FallThroughTree extends LeafTree instanceof FallthroughStmt { } + private class FallThroughTree extends AstLeafTree { + override FallthroughStmt ast; + } - private class DeferTree extends LeafTree instanceof DeferStmt { } + private class DeferTree extends AstLeafTree { + override DeferStmt ast; + } - private class ThrowExprTree extends PostOrderTree instanceof ThrowStmt { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getSubExpr().getFullyConverted() + private class ThrowExprTree extends AstPostOrderTree { + override ThrowStmt ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getSubExpr().getFullyConverted() } - final override predicate first(AstNode first) { - first(super.getSubExpr().getFullyConverted(), first) + final override predicate first(ControlFlowElement first) { + astFirst(ast.getSubExpr().getFullyConverted(), first) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Post-order: flow from thrown expression to the throw statement. - last(super.getSubExpr().getFullyConverted(), pred, c) and + astLast(ast.getSubExpr().getFullyConverted(), pred, c) and c instanceof NormalCompletion and - succ = this + succ.asAstNode() = ast } } - class DoOrDoCatchStmt = @do_stmt or @do_catch_stmt; + abstract class DoOrDoCatchTree extends AstPreOrderTree { + DoOrDoCatchTree() { + ast instanceof DoStmt + or + ast instanceof DoCatchStmt + } - abstract class DoOrDoCatchTree extends PreOrderTree instanceof DoOrDoCatchStmt { - abstract AstNode getBody(); + abstract ControlFlowElement getBody(); - override predicate last(AstNode last, Completion c) { + override predicate last(ControlFlowElement last, Completion c) { // The last element of the body is always a candidate for the last element of the statement. // Note that this is refined in `DoCatchTree` to only be the last statement if the body // doesn't end with a throw completion. last(this.getBody(), last, c) } - override predicate propagatesAbnormal(AstNode child) { none() } + override predicate propagatesAbnormal(ControlFlowElement child) { none() } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: flow from statement itself to first element of body - pred = this and + pred.asAstNode() = ast and first(this.getBody(), succ) and c instanceof SimpleCompletion } } - class DoCatchTree extends DoOrDoCatchTree instanceof DoCatchStmt { - final override AstNode getBody() { result = DoCatchStmt.super.getBody() } + class DoCatchTree extends DoOrDoCatchTree { + override DoCatchStmt ast; - override predicate last(AstNode last, Completion c) { + final override ControlFlowTree getBody() { result.asAstNode() = ast.getBody() } + + override predicate last(ControlFlowElement last, Completion c) { DoOrDoCatchTree.super.last(last, c) and not c instanceof ThrowCompletion or // Any catch can be the last element to be evaluated. - last(super.getACatch(), last, c) + astLast(ast.getACatch().getBody(), last, c) or // We failed to match on any of the clauses. // TODO: This can actually only happen if the enclosing function is marked with 'throws'. // So we could make this more precise. - last(super.getLastCatch(), last, c) and + astLast(ast.getLastCatch(), last, c) and c = any(MatchingCompletion mc | not mc.isMatch()) } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { DoOrDoCatchTree.super.succ(pred, succ, c) or // Flow from the body to the first catch when an exception occurs last(this.getBody(), pred, c) and c instanceof ThrowCompletion and - first(super.getFirstCatch(), succ) + astFirst(ast.getFirstCatch(), succ) or exists(int i | // Flow from one `catch` clause to the next - last(super.getCatch(i), pred, c) and - first(super.getCatch(i + 1), succ) and + astLast(ast.getCatch(i), pred, c) and + astFirst(ast.getCatch(i + 1), succ) and c = any(MatchingCompletion mc | not mc.isMatch()) ) } } - class DoTree extends DoOrDoCatchTree instanceof DoStmt { - final override AstNode getBody() { result = DoStmt.super.getBody() } + class DoTree extends DoOrDoCatchTree { + override DoStmt ast; - final override predicate propagatesAbnormal(AstNode child) { child = this.getBody() } + final override ControlFlowElement getBody() { result.asAstNode() = ast.getBody() } + + final override predicate propagatesAbnormal(ControlFlowElement child) { child = this.getBody() } } } /** A class that exists to allow control-flow through incorrectly extracted AST nodes. */ -private class UnknownAstTree extends LeafTree instanceof UnknownElement { } +private class UnknownAstTree extends AstLeafTree { + UnknownAstTree() { ast.isUnknown() } +} /** * Control-flow for patterns (which appear in `switch` statements * and `catch` statements). */ module Patterns { - private class TypedTree extends StandardPostOrderTree instanceof TypedPattern { - final override ControlFlowTree getChildElement(int i) { + private class TypedTree extends AstStandardPostOrderTree { + override TypedPattern ast; + + final override ControlFlowElement getChildElement(int i) { i = 0 and - result = super.getSubPattern().getFullyUnresolved() + result.asAstNode() = ast.getSubPattern().getFullyUnresolved() } } - private class TupleTree extends PreOrderTree instanceof TuplePattern { - final override predicate propagatesAbnormal(AstNode n) { - n = super.getAnElement().getFullyUnresolved() + private class TupleTree extends AstPreOrderTree { + override TuplePattern ast; + + final override predicate propagatesAbnormal(ControlFlowElement n) { + n.asAstNode() = ast.getAnElement().getFullyUnresolved() } - final override predicate last(AstNode n, Completion c) { + final override predicate last(ControlFlowElement n, Completion c) { // A subpattern failed to match - last(super.getAnElement().getFullyUnresolved(), n, c) and + astLast(ast.getAnElement().getFullyUnresolved(), n, c) and not c.(MatchingCompletion).isMatch() or // We finished matching on all subpatterns - last(super.getLastElement().getFullyUnresolved(), n, c) and + astLast(ast.getLastElement().getFullyUnresolved(), n, c) and c instanceof NormalCompletion } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: Flow from this to the first subpattern - pred = this and - first(super.getFirstElement().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getFirstElement().getFullyUnresolved(), succ) and c instanceof SimpleCompletion or // Flow from an element that matched to the next element exists(int i | - last(super.getElement(i).getFullyUnresolved(), pred, c) and + astLast(ast.getElement(i).getFullyUnresolved(), pred, c) and c.(MatchingCompletion).isMatch() and - first(super.getElement(i + 1).getFullyUnresolved(), succ) + astFirst(ast.getElement(i + 1).getFullyUnresolved(), succ) ) } } - private class ParenTree extends StandardPreOrderTree instanceof ParenPattern { - final override ControlFlowTree getChildElement(int i) { + private class ParenTree extends AstStandardPreOrderTree { + override ParenPattern ast; + + final override ControlFlowElement getChildElement(int i) { i = 0 and - result = ParenPattern.super.getResolveStep() + result.asAstNode() = ast.getResolveStep() } } - private class NamedTree extends LeafTree instanceof NamedPattern { } + private class NamedTree extends AstLeafTree { + override NamedPattern ast; + } - private class BoolTree extends LeafTree instanceof BoolPattern { } + private class BoolTree extends AstLeafTree { + override BoolPattern ast; + } - private class AnyTree extends LeafTree instanceof AnyPattern { } + private class AnyTree extends AstLeafTree { + override AnyPattern ast; + } - private class IsTree extends StandardPostOrderTree instanceof IsPattern { - final override ControlFlowTree getChildElement(int i) { + private class IsTree extends AstStandardPostOrderTree { + override IsPattern ast; + + final override ControlFlowElement getChildElement(int i) { // Note: `getSubPattern` only has a result if the `is` pattern is of the form `pattern as type`. i = 0 and - result = super.getSubPattern().getFullyUnresolved() + result.asAstNode() = ast.getSubPattern().getFullyUnresolved() or i = 1 and - result = super.getCastTypeRepr() + result.asAstNode() = ast.getCastTypeRepr() } } - private class EnumElementTree extends PreOrderTree instanceof EnumElementPattern { - final override predicate propagatesAbnormal(AstNode n) { - n = super.getSubPattern().getFullyUnresolved() + private class EnumElementTree extends AstPreOrderTree { + override EnumElementPattern ast; + + final override predicate propagatesAbnormal(ControlFlowElement n) { + n.asAstNode() = ast.getSubPattern().getFullyUnresolved() } - final override predicate last(AstNode n, Completion c) { + final override predicate last(ControlFlowElement n, Completion c) { // We finished the subpattern check - last(super.getSubPattern().getFullyUnresolved(), n, c) and + astLast(ast.getSubPattern().getFullyUnresolved(), n, c) and c instanceof NormalCompletion or // Or we got to the enum element check which failed - n = this and + n.asAstNode() = ast and c.(MatchingCompletion).isNonMatch() or // No sub pattern: We can either succeed or fail this match depending on the enum element check. - n = this and - not exists(super.getSubPattern()) and + n.asAstNode() = ast and + not exists(ast.getSubPattern()) and c instanceof MatchingCompletion } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: Flow from the enumeration check to the subpattern - pred = this and - first(super.getSubPattern().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getSubPattern().getFullyUnresolved(), succ) and c.(MatchingCompletion).isMatch() } } - private class BindingTree extends PostOrderTree instanceof BindingPattern { - final override predicate propagatesAbnormal(AstNode n) { - n = super.getSubPattern().getFullyUnresolved() + private class BindingTree extends AstPostOrderTree { + override BindingPattern ast; + + final override predicate propagatesAbnormal(ControlFlowElement n) { + n.asAstNode() = ast.getSubPattern().getFullyUnresolved() } - final override predicate first(AstNode n) { - first(super.getSubPattern().getFullyUnresolved(), n) + final override predicate first(ControlFlowElement n) { + astFirst(ast.getSubPattern().getFullyUnresolved(), n) } - override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(super.getSubPattern().getFullyUnresolved(), pred, c) and + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getSubPattern().getFullyUnresolved(), pred, c) and c.(MatchingCompletion).isMatch() and - succ = this + succ.asAstNode() = ast } } - private class ExprTree extends StandardPostOrderTree instanceof ExprPattern { - final override ControlFlowTree getChildElement(int i) { + private class ExprTree extends AstStandardPostOrderTree { + override ExprPattern ast; + + final override ControlFlowElement getChildElement(int i) { i = 0 and - result = super.getSubExpr().getFullyConverted() + result.asAstNode() = ast.getSubExpr().getFullyConverted() } } - private class OptionalSomeTree extends PreOrderTree instanceof OptionalSomePattern { - final override predicate propagatesAbnormal(AstNode n) { - n = super.getSubPattern().getFullyUnresolved() + private class OptionalSomeTree extends AstPreOrderTree { + override OptionalSomePattern ast; + + final override predicate propagatesAbnormal(ControlFlowElement n) { + n.asAstNode() = ast.getSubPattern().getFullyUnresolved() } - final override predicate last(AstNode n, Completion c) { + final override predicate last(ControlFlowElement n, Completion c) { // The subpattern check either failed or succeeded - last(super.getSubPattern().getFullyUnresolved(), n, c) and + astLast(ast.getSubPattern().getFullyUnresolved(), n, c) and c instanceof NormalCompletion or // Or we got to the some/none check and it failed - n = this and + n.asAstNode() = ast and not c.(MatchingCompletion).isMatch() } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre-order: Flow from a matching some/none check to the subpattern - pred = this and - first(super.getSubPattern().getFullyUnresolved(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getSubPattern().getFullyUnresolved(), succ) and c.(MatchingCompletion).isMatch() } } } module Decls { - private class VarDeclTree extends LeafTree instanceof VarDecl { } + private class VarDeclTree extends AstLeafTree { + override VarDecl ast; + } - private class PatternBindingDeclTree extends StandardPostOrderTree instanceof PatternBindingDecl { - final override ControlFlowTree getChildElement(int i) { + private class PatternBindingDeclTree extends AstStandardPostOrderTree { + override PatternBindingDecl ast; + + final override ControlFlowElement getChildElement(int i) { exists(int j | i = 2 * j and - result = super.getPattern(j).getFullyUnresolved() + result.asAstNode() = ast.getPattern(j).getFullyUnresolved() ) or exists(int j | i = 2 * j + 1 and - result = super.getInit(j).getFullyConverted() + result.asAstNode() = ast.getInit(j).getFullyConverted() ) } } } module Exprs { - private class AssignExprTree extends StandardPostOrderTree instanceof AssignExpr { + private class AssignExprTree extends AstStandardPostOrderTree { + override AssignExpr ast; + final override ControlFlowTree getChildElement(int i) { - result = super.getDest().getFullyConverted() and i = 0 + result.asAstNode() = ast.getDest().getFullyConverted() and i = 0 or - result = super.getSource().getFullyConverted() and i = 1 + result.asAstNode() = ast.getSource().getFullyConverted() and i = 1 } } - private class BindOptionalTree extends StandardPostOrderTree instanceof BindOptionalExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + private class BindOptionalTree extends AstStandardPostOrderTree { + override BindOptionalExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 } } - private class CaptureListTree extends StandardPostOrderTree instanceof CaptureListExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getBindingDecl(i).getFullyUnresolved() + private class CaptureListTree extends AstStandardPostOrderTree { + override CaptureListExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getBindingDecl(i).getFullyUnresolved() or - i = super.getNumberOfBindingDecls() and - result = super.getClosureBody().getFullyConverted() + i = ast.getNumberOfBindingDecls() and + result.asAstNode() = ast.getClosureBody().getFullyConverted() } } @@ -825,7 +920,9 @@ module Exprs { class Closure = @auto_closure_expr or @closure_expr; // TODO: Traverse the expressions in the capture list once we extract it. - private class ClosureTree extends LeafTree instanceof ClosureExpr { } + private class ClosureTree extends AstLeafTree { + override ClosureExpr ast; + } /** * An autoclosure expression that is generated as part of a logical operation. @@ -837,298 +934,412 @@ 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 PreOrderTree instanceof AutoClosureExpr { - LogicalAutoClosureTree() { this = any(LogicalOperation op).getAnOperand() } + private class LogicalAutoClosureTree extends AstPreOrderTree { + override AutoClosureExpr ast; - override predicate last(AstNode last, Completion c) { - exists(Completion completion | last(super.getReturn(), last, completion) | + LogicalAutoClosureTree() { ast = any(LogicalOperation op).getAnOperand() } + + override predicate last(ControlFlowElement last, Completion c) { + exists(Completion completion | astLast(ast.getReturn(), last, completion) | if completion instanceof ReturnCompletion then c instanceof SimpleCompletion else c = completion ) } - override predicate propagatesAbnormal(AstNode child) { child = super.getReturn() } + override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getReturn() + } - override predicate succ(AstNode pred, AstNode succ, Completion c) { + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { // Pre order: Flow from this to the uniquely wrapped expr - pred = this and - first(super.getReturn(), succ) and + pred.asAstNode() = ast and + astFirst(ast.getReturn(), succ) and c instanceof SimpleCompletion } } /** An autoclosure expression that is _not_ part of a logical operation. */ - private class AutoClosureTree extends LeafTree instanceof AutoClosureExpr { + private class AutoClosureTree extends AstLeafTree { + override AutoClosureExpr ast; + AutoClosureTree() { not this instanceof LogicalAutoClosureTree } } } - private class InOutTree extends StandardPostOrderTree instanceof InOutExpr { - final override ControlFlowTree getChildElement(int i) { - i = 0 and result = super.getSubExpr().getFullyConverted() + private class InOutTree extends AstStandardPostOrderTree { + override InOutExpr ast; + + final override ControlFlowElement getChildElement(int i) { + i = 0 and result.asAstNode() = ast.getSubExpr().getFullyConverted() } } - private class SubScriptTree extends StandardPostOrderTree instanceof SubscriptExpr { - final override ControlFlowTree getChildElement(int i) { - i = 0 and result = super.getBaseExpr().getFullyConverted() + private class SubscriptTree extends AstControlFlowTree { + override SubscriptExpr ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getBaseExpr().getFullyConverted() or - i = 1 and result = super.getAnArgument().getExpr().getFullyConverted() + child.asAstNode() = ast.getAnArgument().getExpr().getFullyConverted() } - } - private class TupleTree extends StandardPostOrderTree instanceof TupleExpr { - TupleTree() { not this.getAnElement() = any(LogicalOperation op).getAnOperand() } - - final override ControlFlowTree getChildElement(int i) { - result = super.getElement(i).getFullyConverted() + final override predicate first(ControlFlowElement first) { + astFirst(ast.getBaseExpr().getFullyConverted(), first) } - } - private class TupleElementTree extends StandardPostOrderTree instanceof TupleElementExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + final override predicate last(ControlFlowElement last, Completion c) { + ( + // If we need to do a call to a getter/setter, in which case + // the index expression is the last expression we compute before we call the property access. + if ignoreAstElement(ast) + then isPropertyGetterElement(last, _, ast) + else + // If the subscript has direct-to-storage semantics we transfer flow to th subscript + // expression itself. + last.asAstNode() = ast + ) and + completionIsValidFor(c, last) } - } - private class RebindSelfInConstructorTree extends StandardPostOrderTree instanceof RebindSelfInConstructorExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 - } - } - - private class SuperRefTree extends LeafTree instanceof SuperRefExpr { } - - private class DotSyntaxBaseIgnoredTree extends StandardPostOrderTree instanceof DotSyntaxBaseIgnoredExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getQualifier().getFullyConverted() and i = 0 + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getBaseExpr().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + astFirst(ast.getFirstArgument().getExpr().getFullyConverted(), succ) or - result = super.getSubExpr().getFullyConverted() and i = 1 + exists(int i | + astLast(ast.getArgument(i).getExpr().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + astFirst(ast.getArgument(i + 1).getExpr().getFullyConverted(), succ) + ) + or + astLast(ast.getLastArgument().getExpr().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + ( + if ignoreAstElement(ast) + then isPropertyGetterElement(succ, _, ast) + else succ.asAstNode() = ast + ) } } - private class TypeTree extends LeafTree instanceof TypeExpr { } + private class TupleTree extends AstStandardPostOrderTree { + override TupleExpr ast; - private class DynamicTypeTree extends StandardPostOrderTree instanceof DynamicTypeExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getBaseExpr().getFullyConverted() and i = 0 + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getElement(i).getFullyConverted() } } - private class LazyInitializerTree extends StandardPostOrderTree instanceof LazyInitializerExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + private class TupleElementTree extends AstStandardPostOrderTree { + override TupleElementExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 } } - private class ObjCSelectorTree extends StandardPostOrderTree instanceof ObjCSelectorExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + private class RebindSelfInConstructorTree extends AstStandardPostOrderTree { + override RebindSelfInConstructorExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 } } - private class OneWayTree extends StandardPostOrderTree instanceof OneWayExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + private class SuperRefTree extends AstLeafTree { + override SuperRefExpr ast; + } + + private class DotSyntaxBaseIgnoredTree extends AstStandardPostOrderTree { + override DotSyntaxBaseIgnoredExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getQualifier().getFullyConverted() and i = 0 + or + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 1 } } - private class OptionalEvaluationTree extends StandardPostOrderTree instanceof OptionalEvaluationExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + private class TypeTree extends AstLeafTree { + override TypeExpr ast; + } + + private class DynamicTypeTree extends AstStandardPostOrderTree { + override DynamicTypeExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getBaseExpr().getFullyConverted() and i = 0 } } - private class NonInterpolatedLiteralExprTree extends LeafTree instanceof LiteralExpr { + private class LazyInitializerTree extends AstStandardPostOrderTree { + override LazyInitializerExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 + } + } + + private class ObjCSelectorTree extends AstStandardPostOrderTree { + override ObjCSelectorExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 + } + } + + private class OneWayTree extends AstStandardPostOrderTree { + override OneWayExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 + } + } + + private class OptionalEvaluationTree extends AstStandardPostOrderTree { + override OptionalEvaluationExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 + } + } + + private class NonInterpolatedLiteralExprTree extends AstLeafTree { + override LiteralExpr ast; + NonInterpolatedLiteralExprTree() { // This one needs special handling - not this instanceof InterpolatedStringLiteralExpr + not ast instanceof InterpolatedStringLiteralExpr } } - private class InterpolatedStringLiteralExprTree extends StandardPostOrderTree instanceof InterpolatedStringLiteralExpr { - final override ControlFlowTree getChildElement(int i) { + private class InterpolatedStringLiteralExprTree extends AstStandardPostOrderTree { + override InterpolatedStringLiteralExpr ast; + + final override ControlFlowElement getChildElement(int i) { none() // TODO } } - private class DeclRefExprTree extends LeafTree instanceof DeclRefExpr { } + private class DeclRefExprTree extends AstLeafTree { + override DeclRefExpr ast; + } + + private class MemberRefTree extends AstStandardPostOrderTree { + override MemberRefExpr ast; - private class MemberRefTree extends StandardPostOrderTree instanceof MemberRefExpr { final override AstNode getChildElement(int i) { - result = super.getBaseExpr().getFullyConverted() and i = 0 + result = ast.getBaseExpr().getFullyConverted() and i = 0 } } - private class ApplyExprTree extends StandardPostOrderTree instanceof ApplyExpr { + private class ApplyExprTree extends AstStandardPostOrderTree { + override ApplyExpr ast; + ApplyExprTree() { // This one is handled in `LogicalNotTree`. - not this instanceof UnaryLogicalOperation and + not ast instanceof UnaryLogicalOperation and // These are handled in `LogicalOrTree` and `LogicalAndTree`. - not this instanceof BinaryLogicalOperation + not ast instanceof BinaryLogicalOperation } - final override ControlFlowTree getChildElement(int i) { + final override ControlFlowElement getChildElement(int i) { i = -1 and - result = super.getFunction().getFullyConverted() + result.asAstNode() = ast.getFunction().getFullyConverted() or - result = super.getArgument(i).getExpr().getFullyConverted() + result.asAstNode() = ast.getArgument(i).getExpr().getFullyConverted() } } - private class DefaultArgumentTree extends LeafTree instanceof DefaultArgumentExpr { } + private class DefaultArgumentTree extends AstLeafTree { + override DefaultArgumentExpr ast; + } - private class ForceValueTree extends StandardPostOrderTree instanceof ForceValueExpr { - override AstNode getChildElement(int i) { + private class ForceValueTree extends AstStandardPostOrderTree { + override ForceValueExpr ast; + + override ControlFlowElement getChildElement(int i) { i = 0 and - result = super.getSubExpr().getFullyConverted() + result.asAstNode() = ast.getSubExpr().getFullyConverted() } } - private class LogicalAndTree extends PostOrderTree, LogicalAndExpr { - final override predicate propagatesAbnormal(AstNode child) { - child = this.getAnOperand().getFullyConverted() + private class LogicalAndTree extends AstPostOrderTree { + override LogicalAndExpr ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getAnOperand().getFullyConverted() } - final override predicate first(AstNode first) { - first(this.getLeftOperand().getFullyConverted(), first) + final override predicate first(ControlFlowElement first) { + astFirst(ast.getLeftOperand().getFullyConverted(), first) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getLeftOperand().getFullyConverted(), pred, c) and + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getLeftOperand().getFullyConverted(), pred, c) and c instanceof TrueCompletion and - first(this.getRightOperand().getFullyConverted(), succ) + astFirst(ast.getRightOperand().getFullyConverted(), succ) or - last(this.getLeftOperand().getFullyConverted(), pred, c) and + astLast(ast.getLeftOperand().getFullyConverted(), pred, c) and c instanceof FalseCompletion and - succ = this + succ.asAstNode() = ast or - last(this.getRightOperand().getFullyConverted(), pred, c) and + astLast(ast.getRightOperand().getFullyConverted(), pred, c) and c instanceof NormalCompletion and - succ = this + succ.asAstNode() = ast } } - private class LogicalNotTree extends PostOrderTree, NotExpr { - final override predicate propagatesAbnormal(AstNode child) { - child = this.getOperand().getFullyConverted() + private class LogicalNotTree extends AstPostOrderTree { + override NotExpr ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getOperand().getFullyConverted() } - final override predicate first(AstNode first) { - first(this.getOperand().getFullyConverted(), first) + final override predicate first(ControlFlowElement first) { + astFirst(ast.getOperand().getFullyConverted(), first) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - succ = this and - last(this.getOperand().getFullyConverted(), pred, c) and + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + succ.asAstNode() = ast and + astLast(ast.getOperand().getFullyConverted(), pred, c) and c instanceof NormalCompletion } } - private class LogicalOrTree extends PostOrderTree, LogicalOrExpr { - final override predicate propagatesAbnormal(AstNode child) { - child = this.getAnOperand().getFullyConverted() + private class LogicalOrTree extends AstPostOrderTree { + override LogicalOrExpr ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getAnOperand().getFullyConverted() } - final override predicate first(AstNode first) { - first(this.getLeftOperand().getFullyConverted(), first) + final override predicate first(ControlFlowElement first) { + astFirst(ast.getLeftOperand().getFullyConverted(), first) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(this.getLeftOperand().getFullyConverted(), pred, c) and + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getLeftOperand().getFullyConverted(), pred, c) and c instanceof FalseCompletion and - first(this.getRightOperand().getFullyConverted(), succ) + astFirst(ast.getRightOperand().getFullyConverted(), succ) or - last(this.getLeftOperand().getFullyConverted(), pred, c) and + astLast(ast.getLeftOperand().getFullyConverted(), pred, c) and c instanceof TrueCompletion and - succ = this + succ.asAstNode() = ast or - last(this.getRightOperand().getFullyConverted(), pred, c) and + astLast(ast.getRightOperand().getFullyConverted(), pred, c) and c instanceof NormalCompletion and - succ = this + succ.asAstNode() = ast } } - private class VarargExpansionTree extends StandardPostOrderTree instanceof VarargExpansionExpr { - final override ControlFlowTree getChildElement(int i) { + private class VarargExpansionTree extends AstStandardPostOrderTree { + override VarargExpansionExpr ast; + + final override ControlFlowElement getChildElement(int i) { i = 0 and - result = super.getSubExpr().getFullyConverted() + result.asAstNode() = ast.getSubExpr().getFullyConverted() } } - private class ArrayTree extends StandardPostOrderTree instanceof ArrayExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getElement(i).getFullyConverted() + private class ArrayTree extends AstStandardPostOrderTree { + override ArrayExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getElement(i).getFullyConverted() } } - private class EnumIsCaseTree extends StandardPostOrderTree instanceof EnumIsCaseExpr { - final override ControlFlowTree getChildElement(int i) { - result = super.getSubExpr().getFullyConverted() and i = 0 + private class EnumIsCaseTree extends AstStandardPostOrderTree { + override EnumIsCaseExpr ast; + + final override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getSubExpr().getFullyConverted() and i = 0 or - result = super.getTypeRepr().getFullyUnresolved() and i = 1 + result.asAstNode() = ast.getTypeRepr().getFullyUnresolved() and i = 1 } } - private class IfTree extends PostOrderTree instanceof IfExpr { - final override predicate propagatesAbnormal(AstNode child) { - child = super.getCondition().getFullyConverted() + private class IfTree extends AstPostOrderTree { + override IfExpr ast; + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getCondition().getFullyConverted() or - child = super.getBranch(_).getFullyConverted() + child.asAstNode() = ast.getBranch(_).getFullyConverted() } - final override predicate first(AstNode first) { - first(super.getCondition().getFullyConverted(), first) + final override predicate first(ControlFlowElement first) { + astFirst(ast.getCondition().getFullyConverted(), first) } - final override predicate succ(AstNode pred, AstNode succ, Completion c) { - last(super.getCondition().getFullyConverted(), pred, c) and + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getCondition().getFullyConverted(), pred, c) and exists(boolean b | b = c.(BooleanCompletion).getValue() and - first(super.getBranch(b).getFullyConverted(), succ) + astFirst(ast.getBranch(b).getFullyConverted(), succ) ) or - last(super.getBranch(_).getFullyConverted(), pred, c) and - succ = this and + astLast(ast.getBranch(_).getFullyConverted(), pred, c) and + succ.asAstNode() = ast and c instanceof NormalCompletion } } - private class TryTree extends StandardPostOrderTree instanceof TryExpr { - override ControlFlowTree getChildElement(int i) { + private class TryTree extends AstStandardPostOrderTree { + override TryExpr ast; + + override ControlFlowElement getChildElement(int i) { i = 0 and - result = super.getSubExpr().getFullyConverted() + result.asAstNode() = ast.getSubExpr().getFullyConverted() } } - private class DictionaryLiteralTree extends StandardPostOrderTree instanceof DictionaryExpr { - override ControlFlowTree getChildElement(int i) { result = super.getElement(i) } + private class DictionaryLiteralTree extends AstStandardPostOrderTree { + override DictionaryExpr ast; + + override ControlFlowElement getChildElement(int i) { + result.asAstNode() = ast.getElement(i).getFullyConverted() + } } module Conversions { class ConversionOrIdentity = @identity_expr or @explicit_cast_expr or @implicit_conversion_expr; - abstract class ConversionOrIdentityTree extends StandardPostOrderTree instanceof ConversionOrIdentity { + abstract class ConversionOrIdentityTree extends AstStandardPostOrderTree { + ConversionOrIdentityTree() { + ast instanceof IdentityExpr or + ast instanceof ExplicitCastExpr or + ast instanceof ImplicitConversionExpr + } + abstract predicate convertsFrom(Expr e); - override ControlFlowTree getChildElement(int i) { + override ControlFlowElement getChildElement(int i) { i = 0 and - this.convertsFrom(result) + this.convertsFrom(result.asAstNode()) } } // This isn't actually a conversion, but it behaves like one. - private class IdentityTree extends ConversionOrIdentityTree instanceof IdentityExpr { - override predicate convertsFrom(Expr e) { IdentityExpr.super.convertsFrom(e) } + private class IdentityTree extends ConversionOrIdentityTree { + override IdentityExpr ast; + + override predicate convertsFrom(Expr e) { ast.convertsFrom(e) } } - private class ExplicitCastTree extends ConversionOrIdentityTree instanceof ExplicitCastExpr { - override predicate convertsFrom(Expr e) { ExplicitCastExpr.super.convertsFrom(e) } + private class ExplicitCastTree extends ConversionOrIdentityTree { + override ExplicitCastExpr ast; + + override predicate convertsFrom(Expr e) { ast.convertsFrom(e) } } - private class ImplicitConversionTree extends ConversionOrIdentityTree instanceof ImplicitConversionExpr { - override predicate convertsFrom(Expr e) { ImplicitConversionExpr.super.convertsFrom(e) } + private class ImplicitConversionTree extends ConversionOrIdentityTree { + override ImplicitConversionExpr ast; + + override predicate convertsFrom(Expr e) { ast.convertsFrom(e) } } } } @@ -1140,8 +1351,8 @@ private Scope parent(Scope n) { /** Gets the CFG scope of node `n`. */ pragma[inline] -CfgScope getCfgScope(AstNode n) { - exists(AstNode n0 | +CfgScope getCfgScope(ControlFlowElement n) { + exists(ControlFlowElement n0 | pragma[only_bind_into](n0) = n and pragma[only_bind_into](result) = getCfgScopeImpl(n0) ) @@ -1151,11 +1362,18 @@ cached private module Cached { /** Gets the CFG scope of node `n`. */ cached - CfgScope getCfgScopeImpl(AstNode n) { + CfgScope getCfgScopeImpl(ControlFlowElement n) { forceCachingInSameStage() and result = parent*(scopeOf(n)) } + private CfgScope scopeOf(ControlFlowElement n) { + result = scopeOfAst(n.asAstNode()) or + result = scopeOfAst(n.(PropertyGetterElement).getRef()) or + result = scopeOfAst(n.(PropertySetterElement).getAssignExpr()) or + result = scopeOfAst(n.(PropertyObserverElement).getAssignExpr()) + } + cached newtype TSuccessorType = TSuccessorSuccessor() or diff --git a/swift/ql/lib/codeql/swift/elements/decl/AccessorDecl.qll b/swift/ql/lib/codeql/swift/elements/decl/AccessorDecl.qll index 1d885e0c4c3..8a94235310c 100644 --- a/swift/ql/lib/codeql/swift/elements/decl/AccessorDecl.qll +++ b/swift/ql/lib/codeql/swift/elements/decl/AccessorDecl.qll @@ -1,4 +1,15 @@ -// generated by codegen/codegen.py, remove this comment if you wish to edit this file private import codeql.swift.generated.decl.AccessorDecl -class AccessorDecl extends AccessorDeclBase { } +class AccessorDecl extends AccessorDeclBase { + predicate isPropertyObserver() { + this instanceof WillSetObserver or this instanceof DidSetObserver + } +} + +class WillSetObserver extends AccessorDecl { + WillSetObserver() { this.isWillSet() } +} + +class DidSetObserver extends AccessorDecl { + DidSetObserver() { this.isDidSet() } +} diff --git a/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll b/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll index 31fd87a55ad..1c88e19e5d0 100644 --- a/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll +++ b/swift/ql/lib/codeql/swift/elements/expr/SubscriptExpr.qll @@ -1,3 +1,17 @@ private import codeql.swift.generated.expr.SubscriptExpr -class SubscriptExpr extends SubscriptExprBase { } +class SubscriptExpr extends SubscriptExprBase { + Argument getFirstArgument() { + exists(int i | + result = this.getArgument(i) and + not exists(this.getArgument(i - 1)) + ) + } + + Argument getLastArgument() { + exists(int i | + result = this.getArgument(i) and + not exists(this.getArgument(i + 1)) + ) + } +} diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index 08ecb747577..99ba27e5c0b 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -214,15 +214,14 @@ cfg.swift: #-----| match -> IntegerLiteralExpr # 33| EnumElementPattern -#-----| match -> IsPattern +#-----| no-match -> IsPattern # 33| IsPattern #-----| match -> IntegerLiteralExpr #-----| no-match -> CaseStmt -#-----| no-match -> IntegerLiteralExpr # 33| EnumElementPattern -#-----| match -> IsPattern +#-----| no-match -> IsPattern # 33| CaseLabelItem #-----| -> EnumElementPattern @@ -242,7 +241,6 @@ cfg.swift: # 35| IsPattern #-----| match -> DeclRefExpr #-----| no-match -> CaseStmt -#-----| no-match -> IntegerLiteralExpr # 35| EnumElementPattern #-----| no-match -> IsPattern @@ -252,7 +250,7 @@ cfg.swift: #-----| -> NamedPattern # 35| BindingPattern -#-----| match -> IsPattern +#-----| no-match -> IsPattern # 35| NamedPattern #-----| match -> BindingPattern From 80fad348bb72286bc4405d18aaebf6093e032629 Mon Sep 17 00:00:00 2001 From: Mathias Vorreiter Pedersen Date: Wed, 25 May 2022 13:46:14 +0100 Subject: [PATCH 4/4] Swift: Implement CFG for property reads, writes, and observers. --- .../internal/ControlFlowGraphImpl.qll | 231 +++++++++++++++++- .../controlflow/graph/Cfg.expected | 229 +++++++++-------- 2 files changed, 333 insertions(+), 127 deletions(-) diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll index 640af10b6b6..cc89ab2f836 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll +++ b/swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImpl.qll @@ -887,13 +887,96 @@ module Decls { } module Exprs { - private class AssignExprTree extends AstStandardPostOrderTree { - override AssignExpr ast; + module AssignExprs { + /** + * The control-flow of an assignment operation. + * + * There are two implementation of this base class: + * - One where the left-hand side has direct-to-storage-access semantics + * - One where the left-hand side has direct-to-implementation-access semantics + */ + abstract private class AssignExprTree extends AstControlFlowTree { + override AssignExpr ast; - final override ControlFlowTree getChildElement(int i) { - result.asAstNode() = ast.getDest().getFullyConverted() and i = 0 - or - result.asAstNode() = ast.getSource().getFullyConverted() and i = 1 + final override predicate first(ControlFlowElement first) { + astFirst(ast.getDest().getFullyConverted(), first) + } + + abstract predicate isSet(ControlFlowElement n); + + abstract predicate isLast(ControlFlowElement n, Completion c); + + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getDest().getFullyConverted() or + child.asAstNode() = ast.getSource().getFullyConverted() + } + + predicate hasWillSetObserver() { isPropertyObserverElement(any(WillSetObserver obs), ast) } + + predicate hasDidSetObserver() { isPropertyObserverElement(any(WillSetObserver obs), ast) } + + final override predicate last(ControlFlowElement last, Completion c) { + isPropertyObserverElement(last, any(DidSetObserver obs), ast) and + completionIsValidFor(c, last) + or + not this.hasDidSetObserver() and + this.isLast(last, c) + } + + final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + // Flow from the destination to the source + astLast(ast.getDest().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + astFirst(ast.getSource().getFullyConverted(), succ) + or + // Flow from the source to the `willSet` observer, if any. Otherwise, flow to the set operation + astLast(ast.getSource().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + ( + if this.hasWillSetObserver() + then isPropertyObserverElement(succ, any(WillSetObserver obs), ast) + else this.isSet(succ) + ) + or + // Flow from the set operation to the `didSet` observer, if any + this.isSet(pred) and + completionIsValidFor(c, pred) and + isPropertyObserverElement(succ, any(DidSetObserver obs), ast) + } + } + + /** + * The control-flow for assignments where the left-hand side has + * direct-to-implmentation-access semantics. + */ + class PropertyAssignExpr extends AssignExprTree { + AccessorDecl accessorDecl; + + PropertyAssignExpr() { isPropertySetterElement(accessorDecl, ast) } + + final override predicate isLast(ControlFlowElement last, Completion c) { + isPropertySetterElement(last, accessorDecl, ast) and + completionIsValidFor(c, last) + } + + final override predicate isSet(ControlFlowElement node) { + isPropertySetterElement(node, _, ast) + } + } + + /** + * The control-flow for assignments where the left-hand side has + * direct-to-storage-access semantics. + */ + class DirectAssignExpr extends AssignExprTree { + DirectAssignExpr() { not this instanceof PropertyAssignExpr } + + final override predicate isLast(ControlFlowElement last, Completion c) { + last.asAstNode() = ast and + completionIsValidFor(c, last) + } + + final override predicate isSet(ControlFlowElement node) { node.asAstNode() = ast } } } @@ -1122,15 +1205,139 @@ module Exprs { } } - private class DeclRefExprTree extends AstLeafTree { - override DeclRefExpr ast; + module DeclRefExprs { + class DeclRefExprLValueTree extends AstLeafTree { + override DeclRefExpr ast; + + DeclRefExprLValueTree() { isLValue(ast) } + } + + abstract class DeclRefExprRValueTree extends AstControlFlowTree { + override DeclRefExpr ast; + + DeclRefExprRValueTree() { isRValue(ast) } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + none() + } + + override predicate propagatesAbnormal(ControlFlowElement child) { none() } + } + + private class PropertyDeclRefRValueTree extends DeclRefExprRValueTree { + AccessorDecl accessor; + + PropertyDeclRefRValueTree() { isPropertyGetterElement(accessor, ast) } + + final override predicate first(ControlFlowElement first) { + isPropertyGetterElement(first, accessor, ast) + } + + final override predicate last(ControlFlowElement last, Completion c) { + isPropertyGetterElement(last, accessor, ast) and + completionIsValidFor(c, last) + } + } + + private class DirectDeclRefRValueTree extends DeclRefExprRValueTree { + DirectDeclRefRValueTree() { not this instanceof PropertyDeclRefRValueTree } + + final override predicate first(ControlFlowElement first) { first.asAstNode() = ast } + + final override predicate last(ControlFlowElement last, Completion c) { + last.asAstNode() = ast and + completionIsValidFor(c, last) + } + } } - private class MemberRefTree extends AstStandardPostOrderTree { - override MemberRefExpr ast; + module MemberRefs { + /** + * The control-flow of a member reference expression. + * + * There are two implementation of this base class: + * - One for lvalues + * - One for rvalues + */ + abstract private class MemberRefTreeBase extends AstControlFlowTree { + override MemberRefExpr ast; - final override AstNode getChildElement(int i) { - result = ast.getBaseExpr().getFullyConverted() and i = 0 + final override predicate propagatesAbnormal(ControlFlowElement child) { + child.asAstNode() = ast.getBaseExpr().getFullyConverted() + } + + final override predicate first(ControlFlowElement first) { + astFirst(ast.getBaseExpr().getFullyConverted(), first) + } + } + + /** + * The lvalue implementation of `MemberRefTreeBase` + */ + private class MemberRefLValueTree extends MemberRefTreeBase { + MemberRefLValueTree() { isLValue(ast) } + + final override predicate last(ControlFlowElement last, Completion c) { + last.asAstNode() = ast and + completionIsValidFor(c, last) + } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getBaseExpr().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + succ.asAstNode() = ast + } + } + + /** + * The rvalue base implementation of `MemberRefTreeBase`. + * + * There are two implementations of this class: + * - One for direct-storage semantics + * - One for calls to getters + */ + abstract private class MemberRefRValueTree extends MemberRefTreeBase { + MemberRefRValueTree() { isRValue(ast) } + } + + /** + * Control-flow for rvalue member accesses with direct-to-storage semantics + * or ordinary semantics without a getter. + */ + private class DirectMemberRefRValue extends MemberRefRValueTree { + DirectMemberRefRValue() { not this instanceof PropertyMemberRefRValue } + + final override predicate last(ControlFlowElement last, Completion c) { + last.asAstNode() = ast and + completionIsValidFor(c, last) + } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getBaseExpr().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + succ.asAstNode() = ast + } + } + + /** + * Control-flow for rvalue member accesses with direct-to-implementation semantics + * or ordinary semantics that includes a getter. + */ + private class PropertyMemberRefRValue extends MemberRefRValueTree { + AccessorDecl accessor; + + PropertyMemberRefRValue() { isPropertyGetterElement(accessor, ast) } + + final override predicate last(ControlFlowElement last, Completion c) { + isPropertyGetterElement(last, accessor, ast) and + completionIsValidFor(c, last) + } + + override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + astLast(ast.getBaseExpr().getFullyConverted(), pred, c) and + c instanceof NormalCompletion and + isPropertyGetterElement(succ, accessor, ast) + } } } diff --git a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected index 99ba27e5c0b..181ae94bf32 100644 --- a/swift/ql/test/library-tests/controlflow/graph/Cfg.expected +++ b/swift/ql/test/library-tests/controlflow/graph/Cfg.expected @@ -270,7 +270,6 @@ cfg.swift: # 37| IsPattern #-----| match -> DeclRefExpr #-----| no-match -> CaseStmt -#-----| no-match -> IntegerLiteralExpr # 37| TBD (SimpleIdentTypeRepr) #-----| -> IsPattern @@ -718,9 +717,9 @@ cfg.swift: #-----| return -> exit ConcreteFuncDecl (normal) # 105| DeclRefExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 105| MemberRefExpr +# 105| getter for MemberRefExpr #-----| -> ReturnStmt # 109| enter ConcreteFuncDecl @@ -765,9 +764,9 @@ cfg.swift: #-----| -> DeclRefExpr # 111| DeclRefExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 111| MemberRefExpr +# 111| getter for MemberRefExpr #-----| -> PatternBindingDecl # 112| PatternBindingDecl @@ -780,9 +779,9 @@ cfg.swift: #-----| -> TBD (DotSelfExpr) # 112| TBD (DotSelfExpr) -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 112| MemberRefExpr +# 112| getter for MemberRefExpr #-----| -> PatternBindingDecl # 113| PatternBindingDecl @@ -839,9 +838,9 @@ cfg.swift: #-----| -> DeclRefExpr # 116| DeclRefExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 116| MemberRefExpr +# 116| getter for MemberRefExpr #-----| -> PatternBindingDecl # 117| PatternBindingDecl @@ -854,9 +853,9 @@ cfg.swift: #-----| -> TBD (DotSelfExpr) # 117| TBD (DotSelfExpr) -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 117| MemberRefExpr +# 117| getter for MemberRefExpr #-----| -> PatternBindingDecl # 118| PatternBindingDecl @@ -916,9 +915,9 @@ cfg.swift: #-----| -> LoadExpr # 121| LoadExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 121| MemberRefExpr +# 121| getter for MemberRefExpr #-----| -> PatternBindingDecl # 122| PatternBindingDecl @@ -931,12 +930,12 @@ cfg.swift: #-----| -> TBD (DotSelfExpr) # 122| LoadExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr # 122| TBD (DotSelfExpr) #-----| -> LoadExpr -# 122| MemberRefExpr +# 122| getter for MemberRefExpr #-----| -> PatternBindingDecl # 123| PatternBindingDecl @@ -1002,9 +1001,9 @@ cfg.swift: #-----| -> ForceValueExpr # 126| ForceValueExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 126| MemberRefExpr +# 126| getter for MemberRefExpr #-----| -> PatternBindingDecl # 127| PatternBindingDecl @@ -1017,9 +1016,9 @@ cfg.swift: #-----| -> TBD (DotSelfExpr) # 127| TBD (DotSelfExpr) -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 127| MemberRefExpr +# 127| getter for MemberRefExpr #-----| -> PatternBindingDecl # 128| PatternBindingDecl @@ -1082,17 +1081,17 @@ cfg.swift: #-----| -> BindOptionalExpr # 131| BindOptionalExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr # 131| InjectIntoOptionalExpr #-----| -> OptionalEvaluationExpr -# 131| MemberRefExpr -#-----| -> InjectIntoOptionalExpr - # 131| OptionalEvaluationExpr #-----| -> PatternBindingDecl +# 131| getter for MemberRefExpr +#-----| -> InjectIntoOptionalExpr + # 132| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -1103,17 +1102,17 @@ cfg.swift: #-----| -> TBD (DotSelfExpr) # 132| TBD (DotSelfExpr) -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr # 132| InjectIntoOptionalExpr #-----| -> OptionalEvaluationExpr -# 132| MemberRefExpr -#-----| -> InjectIntoOptionalExpr - # 132| OptionalEvaluationExpr #-----| -> PatternBindingDecl +# 132| getter for MemberRefExpr +#-----| -> InjectIntoOptionalExpr + # 133| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -2544,14 +2543,14 @@ cfg.swift: # 268| SubscriptExpr #-----| -> IntegerLiteralExpr -# 268| AssignExpr +# 268| setter for AssignExpr #-----| -> DeclRefExpr # 268| IntegerLiteralExpr #-----| -> SubscriptExpr # 268| IntegerLiteralExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 269| DeclRefExpr #-----| -> InOutExpr @@ -2562,14 +2561,14 @@ cfg.swift: # 269| InOutExpr #-----| -> IntegerLiteralExpr -# 269| SubscriptExpr +# 269| getter for SubscriptExpr #-----| -> InOutExpr # 269| BinaryExpr #-----| -> DeclRefExpr # 269| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 269| DeclRefExpr #-----| -> TypeExpr @@ -2593,14 +2592,14 @@ cfg.swift: # 270| InOutExpr #-----| -> IntegerLiteralExpr -# 270| SubscriptExpr +# 270| getter for SubscriptExpr #-----| -> InOutExpr # 270| BinaryExpr #-----| -> DeclRefExpr # 270| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 270| DeclRefExpr #-----| -> TypeExpr @@ -2624,14 +2623,14 @@ cfg.swift: # 271| InOutExpr #-----| -> IntegerLiteralExpr -# 271| SubscriptExpr +# 271| getter for SubscriptExpr #-----| -> InOutExpr # 271| BinaryExpr #-----| -> DeclRefExpr # 271| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 271| DeclRefExpr #-----| -> TypeExpr @@ -2655,14 +2654,14 @@ cfg.swift: # 272| InOutExpr #-----| -> IntegerLiteralExpr -# 272| SubscriptExpr +# 272| getter for SubscriptExpr #-----| -> InOutExpr # 272| BinaryExpr #-----| -> DeclRefExpr # 272| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 272| DeclRefExpr #-----| -> TypeExpr @@ -2686,14 +2685,14 @@ cfg.swift: # 273| InOutExpr #-----| -> IntegerLiteralExpr -# 273| SubscriptExpr +# 273| getter for SubscriptExpr #-----| -> InOutExpr # 273| BinaryExpr #-----| -> DeclRefExpr # 273| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 273| DeclRefExpr #-----| -> TypeExpr @@ -2717,14 +2716,14 @@ cfg.swift: # 274| InOutExpr #-----| -> IntegerLiteralExpr -# 274| SubscriptExpr +# 274| getter for SubscriptExpr #-----| -> InOutExpr # 274| BinaryExpr #-----| -> DeclRefExpr # 274| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 274| DeclRefExpr #-----| -> TypeExpr @@ -2748,14 +2747,14 @@ cfg.swift: # 275| InOutExpr #-----| -> IntegerLiteralExpr -# 275| SubscriptExpr +# 275| getter for SubscriptExpr #-----| -> InOutExpr # 275| BinaryExpr #-----| -> DeclRefExpr # 275| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 275| DeclRefExpr #-----| -> TypeExpr @@ -2779,14 +2778,14 @@ cfg.swift: # 276| InOutExpr #-----| -> IntegerLiteralExpr -# 276| SubscriptExpr +# 276| getter for SubscriptExpr #-----| -> InOutExpr # 276| BinaryExpr #-----| -> DeclRefExpr # 276| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 276| DeclRefExpr #-----| -> TypeExpr @@ -2810,14 +2809,14 @@ cfg.swift: # 277| InOutExpr #-----| -> IntegerLiteralExpr -# 277| SubscriptExpr +# 277| getter for SubscriptExpr #-----| -> InOutExpr # 277| BinaryExpr #-----| -> DeclRefExpr # 277| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 277| DeclRefExpr #-----| -> TypeExpr @@ -2841,14 +2840,14 @@ cfg.swift: # 278| InOutExpr #-----| -> IntegerLiteralExpr -# 278| SubscriptExpr +# 278| getter for SubscriptExpr #-----| -> InOutExpr # 278| BinaryExpr #-----| -> NamedPattern # 278| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 278| DeclRefExpr #-----| -> TypeExpr @@ -2884,11 +2883,11 @@ cfg.swift: # 280| LoadExpr #-----| -> DeclRefExpr -# 280| SubscriptExpr +# 280| getter for SubscriptExpr #-----| -> LoadExpr # 280| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 280| DeclRefExpr #-----| -> InOutExpr @@ -2899,11 +2898,11 @@ cfg.swift: # 280| LoadExpr #-----| -> DeclRefExpr -# 280| SubscriptExpr +# 280| getter for SubscriptExpr #-----| -> LoadExpr # 280| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 280| DeclRefExpr #-----| -> InOutExpr @@ -2914,11 +2913,11 @@ cfg.swift: # 280| LoadExpr #-----| -> DeclRefExpr -# 280| SubscriptExpr +# 280| getter for SubscriptExpr #-----| -> LoadExpr # 280| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 280| DeclRefExpr #-----| -> InOutExpr @@ -2929,11 +2928,11 @@ cfg.swift: # 280| LoadExpr #-----| -> DeclRefExpr -# 280| SubscriptExpr +# 280| getter for SubscriptExpr #-----| -> LoadExpr # 280| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 280| DeclRefExpr #-----| -> InOutExpr @@ -2944,11 +2943,11 @@ cfg.swift: # 280| LoadExpr #-----| -> TupleExpr -# 280| SubscriptExpr +# 280| getter for SubscriptExpr #-----| -> LoadExpr # 280| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 282| PatternBindingDecl #-----| -> ConcreteVarDecl @@ -3007,7 +3006,7 @@ cfg.swift: # 283| SubscriptExpr #-----| -> DeclRefExpr -# 283| AssignExpr +# 283| setter for AssignExpr #-----| -> DeclRefExpr # 283| IntegerLiteralExpr @@ -3020,13 +3019,13 @@ cfg.swift: #-----| -> IntegerLiteralExpr # 283| LoadExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr -# 283| SubscriptExpr +# 283| getter for SubscriptExpr #-----| -> LoadExpr # 283| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 284| DeclRefExpr #-----| -> InOutExpr @@ -3037,7 +3036,7 @@ cfg.swift: # 284| SubscriptExpr #-----| -> DeclRefExpr -# 284| AssignExpr +# 284| setter for AssignExpr #-----| -> DeclRefExpr # 284| IntegerLiteralExpr @@ -3052,14 +3051,14 @@ cfg.swift: # 284| LoadExpr #-----| -> IntegerLiteralExpr -# 284| SubscriptExpr +# 284| getter for SubscriptExpr #-----| -> LoadExpr # 284| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 284| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 284| DeclRefExpr #-----| -> TypeExpr @@ -3083,7 +3082,7 @@ cfg.swift: # 285| SubscriptExpr #-----| -> DeclRefExpr -# 285| AssignExpr +# 285| setter for AssignExpr #-----| -> DeclRefExpr # 285| IntegerLiteralExpr @@ -3098,14 +3097,14 @@ cfg.swift: # 285| LoadExpr #-----| -> IntegerLiteralExpr -# 285| SubscriptExpr +# 285| getter for SubscriptExpr #-----| -> LoadExpr # 285| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 285| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 285| DeclRefExpr #-----| -> TypeExpr @@ -3129,7 +3128,7 @@ cfg.swift: # 286| SubscriptExpr #-----| -> DeclRefExpr -# 286| AssignExpr +# 286| setter for AssignExpr #-----| -> DeclRefExpr # 286| IntegerLiteralExpr @@ -3144,14 +3143,14 @@ cfg.swift: # 286| LoadExpr #-----| -> IntegerLiteralExpr -# 286| SubscriptExpr +# 286| getter for SubscriptExpr #-----| -> LoadExpr # 286| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 286| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 286| DeclRefExpr #-----| -> TypeExpr @@ -3175,7 +3174,7 @@ cfg.swift: # 287| SubscriptExpr #-----| -> DeclRefExpr -# 287| AssignExpr +# 287| setter for AssignExpr #-----| -> DeclRefExpr # 287| IntegerLiteralExpr @@ -3190,14 +3189,14 @@ cfg.swift: # 287| LoadExpr #-----| -> IntegerLiteralExpr -# 287| SubscriptExpr +# 287| getter for SubscriptExpr #-----| -> LoadExpr # 287| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 287| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 287| DeclRefExpr #-----| -> TypeExpr @@ -3221,7 +3220,7 @@ cfg.swift: # 288| SubscriptExpr #-----| -> DeclRefExpr -# 288| AssignExpr +# 288| setter for AssignExpr #-----| -> DeclRefExpr # 288| IntegerLiteralExpr @@ -3236,14 +3235,14 @@ cfg.swift: # 288| LoadExpr #-----| -> IntegerLiteralExpr -# 288| SubscriptExpr +# 288| getter for SubscriptExpr #-----| -> LoadExpr # 288| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 288| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 288| DeclRefExpr #-----| -> TypeExpr @@ -3267,7 +3266,7 @@ cfg.swift: # 289| SubscriptExpr #-----| -> DeclRefExpr -# 289| AssignExpr +# 289| setter for AssignExpr #-----| -> DeclRefExpr # 289| IntegerLiteralExpr @@ -3282,14 +3281,14 @@ cfg.swift: # 289| LoadExpr #-----| -> IntegerLiteralExpr -# 289| SubscriptExpr +# 289| getter for SubscriptExpr #-----| -> LoadExpr # 289| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 289| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 289| DeclRefExpr #-----| -> TypeExpr @@ -3313,7 +3312,7 @@ cfg.swift: # 290| SubscriptExpr #-----| -> DeclRefExpr -# 290| AssignExpr +# 290| setter for AssignExpr #-----| -> DeclRefExpr # 290| IntegerLiteralExpr @@ -3328,14 +3327,14 @@ cfg.swift: # 290| LoadExpr #-----| -> IntegerLiteralExpr -# 290| SubscriptExpr +# 290| getter for SubscriptExpr #-----| -> LoadExpr # 290| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 290| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 290| DeclRefExpr #-----| -> TypeExpr @@ -3359,7 +3358,7 @@ cfg.swift: # 291| SubscriptExpr #-----| -> DeclRefExpr -# 291| AssignExpr +# 291| setter for AssignExpr #-----| -> DeclRefExpr # 291| IntegerLiteralExpr @@ -3374,14 +3373,14 @@ cfg.swift: # 291| LoadExpr #-----| -> IntegerLiteralExpr -# 291| SubscriptExpr +# 291| getter for SubscriptExpr #-----| -> LoadExpr # 291| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 291| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 291| DeclRefExpr #-----| -> TypeExpr @@ -3405,7 +3404,7 @@ cfg.swift: # 292| SubscriptExpr #-----| -> DeclRefExpr -# 292| AssignExpr +# 292| setter for AssignExpr #-----| -> DeclRefExpr # 292| IntegerLiteralExpr @@ -3420,14 +3419,14 @@ cfg.swift: # 292| LoadExpr #-----| -> IntegerLiteralExpr -# 292| SubscriptExpr +# 292| getter for SubscriptExpr #-----| -> LoadExpr # 292| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 292| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 292| DeclRefExpr #-----| -> TypeExpr @@ -3451,7 +3450,7 @@ cfg.swift: # 293| SubscriptExpr #-----| -> DeclRefExpr -# 293| AssignExpr +# 293| setter for AssignExpr #-----| -> TuplePattern # 293| IntegerLiteralExpr @@ -3466,14 +3465,14 @@ cfg.swift: # 293| LoadExpr #-----| -> IntegerLiteralExpr -# 293| SubscriptExpr +# 293| getter for SubscriptExpr #-----| -> LoadExpr # 293| BinaryExpr -#-----| -> AssignExpr +#-----| -> setter for AssignExpr # 293| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 293| DeclRefExpr #-----| -> TypeExpr @@ -3549,11 +3548,11 @@ cfg.swift: # 296| LoadExpr #-----| -> BinaryExpr -# 296| SubscriptExpr +# 296| getter for SubscriptExpr #-----| -> LoadExpr # 296| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 296| DeclRefExpr #-----| -> DeclRefExpr @@ -3580,11 +3579,11 @@ cfg.swift: # 296| LoadExpr #-----| -> BinaryExpr -# 296| SubscriptExpr +# 296| getter for SubscriptExpr #-----| -> LoadExpr # 296| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 296| DeclRefExpr #-----| -> DeclRefExpr @@ -3611,11 +3610,11 @@ cfg.swift: # 296| LoadExpr #-----| -> BinaryExpr -# 296| SubscriptExpr +# 296| getter for SubscriptExpr #-----| -> LoadExpr # 296| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 296| DeclRefExpr #-----| -> DeclRefExpr @@ -3642,11 +3641,11 @@ cfg.swift: # 296| LoadExpr #-----| -> BinaryExpr -# 296| SubscriptExpr +# 296| getter for SubscriptExpr #-----| -> LoadExpr # 296| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 296| DeclRefExpr #-----| -> DeclRefExpr @@ -3673,11 +3672,11 @@ cfg.swift: # 296| LoadExpr #-----| -> BinaryExpr -# 296| SubscriptExpr +# 296| getter for SubscriptExpr #-----| -> LoadExpr # 296| IntegerLiteralExpr -#-----| -> SubscriptExpr +#-----| -> getter for SubscriptExpr # 299| enter ConcreteFuncDecl #-----| -> WhileStmt @@ -4396,9 +4395,9 @@ cfg.swift: #-----| return -> exit ConcreteFuncDecl (normal) # 359| DeclRefExpr -#-----| -> MemberRefExpr +#-----| -> getter for MemberRefExpr -# 359| MemberRefExpr +# 359| getter for MemberRefExpr #-----| -> ReturnStmt # 363| enter ConcreteFuncDecl