From dd8dee0a40dfa6d23196cd7604866610b354d957 Mon Sep 17 00:00:00 2001 From: Tom Hvitved Date: Tue, 27 Jun 2023 16:59:46 +0200 Subject: [PATCH] C#: Adopt shared CFG construction library from shared `controlflow` pack --- config/identical-files.json | 4 - .../ql/consistency-queries/CfgConsistency.ql | 5 +- .../DataFlowConsistency.ql | 4 +- csharp/ql/lib/qlpack.yml | 1 + .../semmle/code/csharp/commons/Assertions.qll | 3 - .../code/csharp/controlflow/BasicBlocks.qll | 8 +- .../csharp/controlflow/ControlFlowElement.qll | 12 +- .../csharp/controlflow/ControlFlowGraph.qll | 105 +- .../semmle/code/csharp/controlflow/Guards.qll | 2 +- .../internal/ControlFlowGraphImpl.qll | 952 ++++++++------- .../internal/ControlFlowGraphImplShared.qll | 1027 ----------------- .../internal/ControlFlowGraphImplSpecific.qll | 69 -- .../controlflow/internal/PreBasicBlocks.qll | 6 +- .../csharp/controlflow/internal/Splitting.qll | 229 ++-- .../lib/semmle/code/csharp/dataflow/SSA.qll | 2 +- .../internal/ControlFlowReachability.qll | 4 +- .../dataflow/internal/DataFlowDispatch.qll | 4 +- .../dataflow/internal/DataFlowPrivate.qll | 16 +- .../dataflow/internal/DataFlowPublic.qll | 2 +- .../code/csharp/dataflow/internal/SsaImpl.qll | 4 +- .../rangeanalysis/ControlFlowReachability.qll | 4 +- .../rangeanalysis/SsaReadPositionSpecific.qll | 4 +- .../ql/src/Concurrency/FutileSyncOnField.ql | 2 +- .../Concurrency/UnsynchronizedStaticAccess.ql | 2 +- .../Likely Bugs/NestedLoopsSameVariable.ql | 6 +- .../src/Likely Bugs/Statements/UseBraces.ql | 4 +- .../src/Likely Bugs/UncheckedCastInEquals.ql | 6 +- .../CWE-384/AbandonSession.ql | 6 +- .../library-tests/cil/dataflow/ControlFlow.ql | 2 +- .../controlflow/graph/NodeGraph.ql | 2 +- .../library-tests/controlflow/graph/Nodes.ql | 6 +- .../controlflow/splits/SplittingStressTest.ql | 2 +- .../dataflow/ssa/PreSsaConsistency.ql | 10 +- csharp/ql/test/library-tests/goto/Goto1.ql | 2 +- .../standalone/controlflow/cfg.ql | 2 +- shared/controlflow/codeql/controlflow/Cfg.qll | 34 +- 36 files changed, 691 insertions(+), 1862 deletions(-) delete mode 100644 csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll delete mode 100644 csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplSpecific.qll diff --git a/config/identical-files.json b/config/identical-files.json index 19103323a23e2..7955ab322c068 100644 --- a/config/identical-files.json +++ b/config/identical-files.json @@ -484,10 +484,6 @@ "ruby/ql/lib/codeql/ruby/security/internal/SensitiveDataHeuristics.qll", "swift/ql/lib/codeql/swift/security/internal/SensitiveDataHeuristics.qll" ], - "CFG": [ - "csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll", - "swift/ql/lib/codeql/swift/controlflow/internal/ControlFlowGraphImplShared.qll" - ], "TypeTracker": [ "python/ql/lib/semmle/python/dataflow/new/internal/TypeTracker.qll", "ruby/ql/lib/codeql/ruby/typetracking/TypeTracker.qll" diff --git a/csharp/ql/consistency-queries/CfgConsistency.ql b/csharp/ql/consistency-queries/CfgConsistency.ql index c50d7aaa10152..9802ddff264b4 100644 --- a/csharp/ql/consistency-queries/CfgConsistency.ql +++ b/csharp/ql/consistency-queries/CfgConsistency.ql @@ -2,15 +2,14 @@ import csharp import semmle.code.csharp.controlflow.internal.Completion import semmle.code.csharp.controlflow.internal.PreBasicBlocks import ControlFlow -import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl +import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::Consistency import semmle.code.csharp.controlflow.internal.Splitting -import Consistency private predicate splitBB(ControlFlow::BasicBlock bb) { exists(ControlFlow::Node first | first = bb.getFirstNode() and first.isJoin() and - strictcount(first.getAPredecessor().getElement()) = 1 + strictcount(first.getAPredecessor().getAstNode()) = 1 ) } diff --git a/csharp/ql/consistency-queries/DataFlowConsistency.ql b/csharp/ql/consistency-queries/DataFlowConsistency.ql index 8f099fe6daf5f..4ac709fd36684 100644 --- a/csharp/ql/consistency-queries/DataFlowConsistency.ql +++ b/csharp/ql/consistency-queries/DataFlowConsistency.ql @@ -10,7 +10,7 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { // TODO: Remove once static initializers are folded into the // static constructors exists(ControlFlow::Node cfn | - cfn.getElement() = any(FieldOrProperty f | f.isStatic()).getAChild+() and + cfn.getAstNode() = any(FieldOrProperty f | f.isStatic()).getAChild+() and cfn = n.getControlFlowNode() ) } @@ -19,7 +19,7 @@ private class MyConsistencyConfiguration extends ConsistencyConfiguration { // TODO: Remove once static initializers are folded into the // static constructors exists(ControlFlow::Node cfn | - cfn.getElement() = any(FieldOrProperty f | f.isStatic()).getAChild+() and + cfn.getAstNode() = any(FieldOrProperty f | f.isStatic()).getAChild+() and cfn = call.getControlFlowNode() ) } diff --git a/csharp/ql/lib/qlpack.yml b/csharp/ql/lib/qlpack.yml index c50ace576acba..16fd577be1276 100644 --- a/csharp/ql/lib/qlpack.yml +++ b/csharp/ql/lib/qlpack.yml @@ -6,6 +6,7 @@ extractor: csharp library: true upgrades: upgrades dependencies: + codeql/controlflow: ${workspace} codeql/dataflow: ${workspace} codeql/mad: ${workspace} codeql/ssa: ${workspace} diff --git a/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll b/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll index 2c81702e7e97c..ccd1e42582aa9 100644 --- a/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll +++ b/csharp/ql/lib/semmle/code/csharp/commons/Assertions.qll @@ -1,12 +1,9 @@ /** Provides classes for assertions. */ -private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl private import semmle.code.csharp.frameworks.system.Diagnostics private import semmle.code.csharp.frameworks.system.diagnostics.Contracts private import semmle.code.csharp.frameworks.test.VisualStudio private import semmle.code.csharp.frameworks.System -private import ControlFlow -private import ControlFlow::BasicBlocks private newtype TAssertionFailure = TExceptionAssertionFailure(Class c) or diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll index 44ff56706a72d..4392a42f9acd8 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/BasicBlocks.qll @@ -400,13 +400,13 @@ class ExitBasicBlock extends BasicBlock { private module JoinBlockPredecessors { private import ControlFlow::Nodes - private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl + private import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as Impl int getId(JoinBlockPredecessor jbp) { - exists(ControlFlowTree::Range_ t | ControlFlowTree::idOf(t, result) | - t = jbp.getFirstNode().getElement() + exists(Impl::AstNode n | result = n.getId() | + n = jbp.getFirstNode().getAstNode() or - t = jbp.(EntryBasicBlock).getCallable() + n = jbp.(EntryBasicBlock).getCallable() ) } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll index 6c6a66c4ac2dc..9fb450cd56d3f 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowElement.qll @@ -7,7 +7,7 @@ private import ControlFlow private import ControlFlow::BasicBlocks private import SuccessorTypes private import semmle.code.csharp.Caching -private import internal.ControlFlowGraphImpl +private import internal.ControlFlowGraphImpl as Impl /** * A program element that can possess control flow. That is, either a statement or @@ -39,20 +39,20 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { * several `ControlFlow::Node`s, for example to represent the continuation * flow in a `try/catch/finally` construction. */ - Nodes::ElementNode getAControlFlowNode() { result.getElement() = this } + Nodes::ElementNode getAControlFlowNode() { result.getAstNode() = this } /** * Gets a first control flow node executed within this element. */ Nodes::ElementNode getAControlFlowEntryNode() { - result = getAControlFlowEntryNode(this).getAControlFlowNode() + result = Impl::getAControlFlowEntryNode(this).(ControlFlowElement).getAControlFlowNode() } /** * Gets a potential last control flow node executed within this element. */ Nodes::ElementNode getAControlFlowExitNode() { - result = getAControlFlowExitNode(this).getAControlFlowNode() + result = Impl::getAControlFlowExitNode(this).(ControlFlowElement).getAControlFlowNode() } /** @@ -88,7 +88,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { ) { // Only calculate dominance by explicit recursion for split nodes; // all other nodes can use regular CFG dominance - this instanceof SplitControlFlowElement and + this instanceof Impl::SplitAstNode and cb.getLastNode() = this.getAControlFlowNode() and succ = cb.getASuccessorByType(s) } @@ -111,7 +111,7 @@ class ControlFlowElement extends ExprOrStmtParent, @control_flow_element { succ.dominates(pred) or // `pred` might be another split of this element - pred.getLastNode().getElement() = this and + pred.getLastNode().getAstNode() = this and t = s ) } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll index 462755a121041..0489044d9228b 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/ControlFlowGraph.qll @@ -7,7 +7,7 @@ module ControlFlow { private import semmle.code.csharp.controlflow.BasicBlocks as BBs import semmle.code.csharp.controlflow.internal.SuccessorType private import SuccessorTypes - private import internal.ControlFlowGraphImpl + private import internal.ControlFlowGraphImpl as Impl private import internal.Splitting as Splitting /** @@ -25,18 +25,16 @@ module ControlFlow { * Only nodes that can be reached from the callable entry point are included in * the CFG. */ - class Node extends TCfgNode { - /** Gets a textual representation of this control flow node. */ - string toString() { none() } - + class Node extends Impl::Node { /** Gets the control flow element that this node corresponds to, if any. */ - ControlFlowElement getElement() { none() } - - /** Gets the location of this control flow node. */ - Location getLocation() { result = this.getElement().getLocation() } + final ControlFlowElement getAstNode() { result = super.getAstNode() } - /** Holds if this control flow node has conditional successors. */ - predicate isCondition() { exists(this.getASuccessorByType(any(ConditionalSuccessor e))) } + /** + * DEPRECATED: Use `getAstNode` instead. + * + * Gets the control flow element that this node corresponds to, if any. + */ + deprecated ControlFlowElement getElement() { result = this.getAstNode() } /** Gets the basic block that this control flow node belongs to. */ BasicBlock getBasicBlock() { result.getANode() = this } @@ -183,7 +181,7 @@ module ControlFlow { } /** Gets a successor node of a given type, if any. */ - Node getASuccessorByType(SuccessorType t) { result = getASuccessor(this, t) } + Node getASuccessorByType(SuccessorType t) { result = this.getASuccessor(t) } /** Gets an immediate successor, if any. */ Node getASuccessor() { result = this.getASuccessorByType(_) } @@ -234,80 +232,39 @@ module ControlFlow { result = this.getASuccessorByType(any(BooleanSuccessor t | t.getValue() = false)) } - /** Holds if this node has more than one predecessor. */ - predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } - - /** Holds if this node has more than one successor. */ - predicate isBranch() { strictcount(this.getASuccessor()) > 1 } - /** Gets the enclosing callable of this control flow node. */ - final Callable getEnclosingCallable() { result = getNodeCfgScope(this) } + final Callable getEnclosingCallable() { result = Impl::getNodeCfgScope(this) } } /** Provides different types of control flow nodes. */ module Nodes { /** A node for a callable entry point. */ - class EntryNode extends Node, TEntryNode { + class EntryNode extends Node instanceof Impl::EntryNode { /** Gets the callable that this entry applies to. */ - Callable getCallable() { this = TEntryNode(result) } + Callable getCallable() { result = this.getScope() } override BasicBlocks::EntryBlock getBasicBlock() { result = Node.super.getBasicBlock() } - - private Assignable getAssignable() { this = TEntryNode(result) } - - override Location getLocation() { - result in [this.getCallable().getLocation(), this.getAssignable().getLocation()] - } - - override string toString() { - result = "enter " + [this.getCallable().toString(), this.getAssignable().toString()] - } } /** A node for a callable exit point, annotated with the type of exit. */ - class AnnotatedExitNode extends Node, TAnnotatedExitNode { - private CfgScope scope; - private boolean normal; - - AnnotatedExitNode() { this = TAnnotatedExitNode(scope, normal) } + class AnnotatedExitNode extends Node instanceof Impl::AnnotatedExitNode { + /** Holds if this node represent a normal exit. */ + final predicate isNormal() { super.isNormal() } /** Gets the callable that this exit applies to. */ - CfgScope getCallable() { result = scope } - - /** Holds if this node represents a normal exit. */ - predicate isNormal() { normal = true } + Callable getCallable() { result = this.getScope() } override BasicBlocks::AnnotatedExitBlock getBasicBlock() { result = Node.super.getBasicBlock() } - - override Location getLocation() { result = scope.getLocation() } - - override string toString() { - exists(string s | - normal = true and s = "normal" - or - normal = false and s = "abnormal" - | - result = "exit " + scope + " (" + s + ")" - ) - } } /** A node for a callable exit point. */ - class ExitNode extends Node, TExitNode { - private CfgScope scope; - - ExitNode() { this = TExitNode(scope) } - + class ExitNode extends Node instanceof Impl::ExitNode { /** Gets the callable that this exit applies to. */ - Callable getCallable() { result = scope } + Callable getCallable() { result = this.getScope() } override BasicBlocks::ExitBlock getBasicBlock() { result = Node.super.getBasicBlock() } - - override Location getLocation() { result = scope.getLocation() } - - override string toString() { result = "exit " + scope } } /** @@ -317,35 +274,19 @@ module ControlFlow { * the element is in unreachable (dead) code, and multiple when there are * different splits for the element. */ - class ElementNode extends Node, TElementNode { - private Splits splits; - private ControlFlowElement cfe; - - ElementNode() { this = TElementNode(_, cfe, splits) } - - override ControlFlowElement getElement() { result = cfe } - - override string toString() { - result = "[" + this.getSplitsString() + "] " + cfe.toString() - or - not exists(this.getSplitsString()) and result = cfe.toString() - } - + class ElementNode extends Node instanceof Impl::AstCfgNode { /** Gets a comma-separated list of strings for each split in this node, if any. */ - string getSplitsString() { - result = splits.toString() and - result != "" - } + final string getSplitsString() { result = super.getSplitsString() } /** Gets a split for this control flow node, if any. */ - Split getASplit() { result = splits.getASplit() } + final Split getASplit() { result = super.getASplit() } } /** A control-flow node for an expression. */ class ExprNode extends ElementNode { Expr e; - ExprNode() { e = unique(Expr e_ | e_ = this.getElement() | e_) } + ExprNode() { e = unique(Expr e_ | e_ = this.getAstNode() | e_) } /** Gets the expression that this control-flow node belongs to. */ Expr getExpr() { result = e } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll index 5b210a4004d43..d4ef0e503d766 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/Guards.qll @@ -1946,7 +1946,7 @@ module Internal { | def = guarded - .getElement() + .getAstNode() .(AccessOrCallExpr) .getAnSsaQualifier(guarded.getBasicBlock().getANode()) and if v.isReferentialProperty() diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll index 37fc97903e6f8..eb2e776da55df 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImpl.qll @@ -1,53 +1,14 @@ /** - * Provides auxiliary classes and predicates used to construct the basic successor - * relation on control flow elements. - * - * The implementation is centered around the concept of a _completion_, which - * models how the execution of a statement or expression terminates. - * Completions are represented as an algebraic data type `Completion` defined in - * `Completion.qll`. - * - * The CFG is built by structural recursion over the AST. To achieve this the - * CFG edges related to a given AST node, `n`, are divided into three categories: - * - * 1. The in-going edge that points to the first CFG node to execute when - * `n` is going to be executed. - * 2. The out-going edges for control flow leaving `n` that are going to some - * other node in the surrounding context of `n`. - * 3. The edges that have both of their end-points entirely within the AST - * node and its children. - * - * The edges in (1) and (2) are inherently non-local and are therefore - * initially calculated as half-edges, that is, the single node, `k`, of the - * edge contained within `n`, by the predicates `k = first(n)` and `k = last(n, _)`, - * respectively. The edges in (3) can then be enumerated directly by the predicate - * `succ` by calling `first` and `last` recursively on the children of `n` and - * connecting the end-points. This yields the entire CFG, since all edges are in - * (3) for _some_ AST node. - * - * The second parameter of `last` is the completion, which is necessary to distinguish - * the out-going edges from `n`. Note that the completion changes as the calculation of - * `last` proceeds outward through the AST; for example, a `BreakCompletion` is - * caught up by its surrounding loop and turned into a `NormalCompletion`, and a - * `NormalCompletion` proceeds outward through the end of a `finally` block and is - * turned into whatever completion was caught by the `finally`. - * - * An important goal of the CFG is to get the order of side-effects correct. - * Most expressions can have side-effects and must therefore be modeled in the - * CFG in AST post-order. For example, a `MethodCall` evaluates its arguments - * before the call. Most statements do not have side-effects, but merely affect - * the control flow and some could therefore be excluded from the CFG. However, - * as a design choice, all statements are included in the CFG and generally - * serve as their own entry-points, thus executing in some version of AST - * pre-order. + * Provides an implementation for constructing control-flow graphs (CFGs) from + * abstract syntax trees (ASTs), using the shared library from `codeql.controlflow.Cfg`. */ import csharp +private import codeql.controlflow.Cfg as CfgShared private import Completion private import Splitting private import semmle.code.csharp.ExprOrStmtParent private import semmle.code.csharp.commons.Compilation -import ControlFlowGraphImplShared /** An element that defines a new CFG scope. */ class CfgScope extends Element, @top_level_exprorstmt_parent { @@ -60,6 +21,78 @@ class CfgScope extends Element, @top_level_exprorstmt_parent { } } +private class TAstNode = @callable or @control_flow_element; + +private Element getAChild(Element p) { + result = p.getAChild() or + result = p.(AssignOperation).getExpandedAssignment() +} + +/** An AST node. */ +class AstNode extends Element, TAstNode { + AstNode() { this = getAChild*(any(@top_level_exprorstmt_parent p | not p instanceof Attribute)) } + + int getId() { idOf(this, result) } +} + +private predicate id(AstNode x, AstNode y) { x = y } + +private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y) + +private module CfgInput implements CfgShared::InputSig { + private import ControlFlowGraphImpl as Impl + private import Completion as Comp + private import Splitting as Splitting + private import SuccessorType as ST + private import semmle.code.csharp.Caching + + class AstNode = Impl::AstNode; + + class Completion = Comp::Completion; + + predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion } + + predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion } + + predicate completionIsValidFor(Completion c, AstNode e) { c.isValidFor(e) } + + class CfgScope = Impl::CfgScope; + + CfgScope getCfgScope(AstNode n) { + Stages::ControlFlowStage::forceCachingInSameStage() and + result = n.(ControlFlowElement).getEnclosingCallable() + } + + predicate scopeFirst(CfgScope scope, AstNode first) { Impl::scopeFirst(scope, first) } + + predicate scopeLast(CfgScope scope, AstNode last, Completion c) { + Impl::scopeLast(scope, last, c) + } + + class SplitKindBase = Splitting::TSplitKind; + + class Split = Splitting::Split; + + class SuccessorType = ST::SuccessorType; + + SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } + + predicate successorTypeIsSimple(SuccessorType t) { + t instanceof ST::SuccessorTypes::NormalSuccessor + } + + predicate successorTypeIsCondition(SuccessorType t) { + t instanceof ST::SuccessorTypes::ConditionalSuccessor + } + + predicate isAbnormalExitType(SuccessorType t) { + t instanceof ST::SuccessorTypes::ExceptionSuccessor or + t instanceof ST::SuccessorTypes::ExitSuccessor + } +} + +import CfgShared::Make + /** * A compilation. * @@ -80,23 +113,6 @@ CompilationExt getCompilation(File f) { result = TBuildless() } -module ControlFlowTree { - class Range_ = @callable or @control_flow_element; - - class Range extends Element, Range_ { - Range() { this = getAChild*(any(@top_level_exprorstmt_parent p | not p instanceof Attribute)) } - } - - Element getAChild(Element p) { - result = p.getAChild() or - result = p.(AssignOperation).getExpandedAssignment() - } - - private predicate id(Range_ x, Range_ y) { x = y } - - predicate idOf(Range_ x, int y) = equivalenceRelation(id/2)(x, y) -} - /** * The `expr_parent_top_level_adjusted()` relation restricted to exclude relations * between properties and their getters' expression bodies in properties such as @@ -116,7 +132,7 @@ private predicate expr_parent_top_level_adjusted2( } /** Holds if `first` is first executed when entering `scope`. */ -predicate scopeFirst(CfgScope scope, ControlFlowElement first) { +predicate scopeFirst(CfgScope scope, AstNode first) { scope = any(Callable c | if exists(c.(Constructor).getInitializer()) @@ -132,7 +148,7 @@ predicate scopeFirst(CfgScope scope, ControlFlowElement first) { } /** Holds if `scope` is exited when `last` finishes with completion `c`. */ -predicate scopeLast(CfgScope scope, ControlFlowElement last, Completion c) { +predicate scopeLast(CfgScope scope, AstNode last, Completion c) { scope = any(Callable callable | last(callable.getBody(), last, c) and @@ -146,21 +162,21 @@ predicate scopeLast(CfgScope scope, ControlFlowElement last, Completion c) { not scope instanceof Callable } -private class ConstructorTree extends ControlFlowTree, Constructor { - final override predicate propagatesAbnormal(ControlFlowElement child) { none() } +private class ConstructorTree extends ControlFlowTree instanceof Constructor { + final override predicate propagatesAbnormal(AstNode child) { none() } - final override predicate first(ControlFlowElement first) { none() } + final override predicate first(AstNode first) { none() } - final override predicate last(ControlFlowElement last, Completion c) { none() } + final override predicate last(AstNode last, Completion c) { none() } /** Gets the body of this constructor belonging to compilation `comp`. */ pragma[noinline] - ControlFlowElement getBody(CompilationExt comp) { - result = this.getBody() and + AstNode getBody(CompilationExt comp) { + result = super.getBody() and comp = getCompilation(result.getFile()) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { exists(CompilationExt comp, int i, AssignExpr ae | ae = InitializerSplitting::constructorInitializeOrder(this, comp, i) and last(ae, pred, c) and @@ -176,53 +192,53 @@ private class ConstructorTree extends ControlFlowTree, Constructor { } } -abstract private class SwitchTree extends ControlFlowTree, Switch { - override predicate propagatesAbnormal(ControlFlowElement child) { child = this.getExpr() } +abstract private class SwitchTree extends ControlFlowTree instanceof Switch { + override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() } - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of switch expression to first element of first case - last(this.getExpr(), pred, c) and + last(super.getExpr(), pred, c) and c instanceof NormalCompletion and - first(this.getCase(0), succ) + first(super.getCase(0), succ) or // Flow from last element of case pattern to next case - exists(Case case, int i | case = this.getCase(i) | + exists(Case case, int i | case = super.getCase(i) | last(case.getPattern(), pred, c) and c.(MatchingCompletion).isNonMatch() and - first(this.getCase(i + 1), succ) + first(super.getCase(i + 1), succ) ) or // Flow from last element of condition to next case - exists(Case case, int i | case = this.getCase(i) | + exists(Case case, int i | case = super.getCase(i) | last(case.getCondition(), pred, c) and c instanceof FalseCompletion and - first(this.getCase(i + 1), succ) + first(super.getCase(i + 1), succ) ) } } -abstract private class CaseTree extends ControlFlowTree, Case { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child in [this.getPattern(), this.getCondition().(ControlFlowElement), this.getBody()] +abstract private class CaseTree extends ControlFlowTree instanceof Case { + final override predicate propagatesAbnormal(AstNode child) { + child in [super.getPattern().(ControlFlowElement), super.getCondition(), super.getBody()] } - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - last(this.getPattern(), pred, c) and + override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(super.getPattern(), pred, c) and c.(MatchingCompletion).isMatch() and ( - if exists(this.getCondition()) + if exists(super.getCondition()) then // Flow from the last element of pattern to the condition - first(this.getCondition(), succ) + first(super.getCondition(), succ) else // Flow from last element of pattern to first element of body - first(this.getBody(), succ) + first(super.getBody(), succ) ) or // Flow from last element of condition to first element of body - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and c instanceof TrueCompletion and - first(this.getBody(), succ) + first(super.getBody(), succ) } } @@ -252,7 +268,7 @@ module Expressions { } } - private ControlFlowElement getExprChild0(Expr e, int i) { + private AstNode getExprChild0(Expr e, int i) { not e instanceof NameOfExpr and not e instanceof QualifiableExpr and not e instanceof Assignment and @@ -276,9 +292,9 @@ module Expressions { ) } - private ControlFlowElement getExprChild(Expr e, int i) { + private AstNode getExprChild(Expr e, int i) { result = - rank[i + 1](ControlFlowElement cfe, int j | + rank[i + 1](AstNode cfe, int j | cfe = getExprChild0(e, j) and not cfe instanceof NoNodeExpr | @@ -286,14 +302,14 @@ module Expressions { ) } - private ControlFlowElement getLastExprChild(Expr e) { + private AstNode getLastExprChild(Expr e) { exists(int last | result = getExprChild(e, last) and not exists(getExprChild(e, last + 1)) ) } - private class StandardExpr extends StandardPostOrderTree, Expr { + private class StandardExpr extends StandardPostOrderTree instanceof Expr { StandardExpr() { // The following expressions need special treatment not this instanceof LogicalNotExpr and @@ -320,14 +336,15 @@ module Expressions { not this instanceof PropertyPatternExpr } - final override ControlFlowElement getChildElement(int i) { result = getExprChild(this, i) } + final override AstNode getChildNode(int i) { result = getExprChild(this, i) } } /** * A qualified write access. In a qualified write access, the access itself is * not evaluated, only the qualifier and the indexer arguments (if any). */ - private class QualifiedWriteAccess extends WriteAccess, QualifiableExpr, ControlFlowTree { + private class QualifiedWriteAccess extends ControlFlowTree instanceof WriteAccess, QualifiableExpr + { QualifiedWriteAccess() { this.hasQualifier() or @@ -340,18 +357,16 @@ module Expressions { this = any(MemberInitializer mi).getLValue() } - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = getExprChild(this, _) - } + final override predicate propagatesAbnormal(AstNode child) { child = getExprChild(this, _) } - final override predicate first(ControlFlowElement first) { first(getExprChild(this, 0), first) } + final override predicate first(AstNode first) { first(getExprChild(this, 0), first) } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Skip the access in a qualified write access last(getLastExprChild(this), last, c) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { exists(int i | last(getExprChild(this, i), pred, c) and c instanceof NormalCompletion and @@ -378,7 +393,7 @@ module Expressions { * x -> 0 -> set_Prop -> x.Prop = 0 * ``` */ - class AccessorWrite extends Expr, PostOrderTree { + class AccessorWrite extends PostOrderTree instanceof Expr { AssignableDefinition def; AccessorWrite() { @@ -404,15 +419,15 @@ module Expressions { not def instanceof AssignableDefinitions::TupleAssignmentDefinition } - final override predicate propagatesAbnormal(ControlFlowElement child) { + final override predicate propagatesAbnormal(AstNode child) { child = getExprChild(this, _) or child = this.getCall(_) } - final override predicate first(ControlFlowElement first) { first(getExprChild(this, 0), first) } + final override predicate first(AstNode first) { first(getExprChild(this, 0), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Standard left-to-right evaluation exists(int i | last(getExprChild(this, i), pred, c) and @@ -442,118 +457,118 @@ module Expressions { } } - private class LogicalNotExprTree extends PostOrderTree, LogicalNotExpr { + private class LogicalNotExprTree extends PostOrderTree instanceof LogicalNotExpr { private Expr operand; LogicalNotExprTree() { operand = this.getOperand() } - final override predicate propagatesAbnormal(ControlFlowElement child) { child = operand } + final override predicate propagatesAbnormal(AstNode child) { child = operand } - final override predicate first(ControlFlowElement first) { first(operand, first) } + final override predicate first(AstNode first) { first(operand, first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { succ = this and last(operand, pred, c) and c instanceof NormalCompletion } } - private class LogicalAndExprTree extends PostOrderTree, LogicalAndExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child in [this.getLeftOperand(), this.getRightOperand()] + private class LogicalAndExprTree extends PostOrderTree instanceof LogicalAndExpr { + final override predicate propagatesAbnormal(AstNode child) { + child in [super.getLeftOperand(), super.getRightOperand()] } - final override predicate first(ControlFlowElement first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of left operand to first element of right operand - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof TrueCompletion and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or // Post-order: flow from last element of left operand to element itself - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof FalseCompletion and succ = this or // Post-order: flow from last element of right operand to element itself - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class LogicalOrExprTree extends PostOrderTree, LogicalOrExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child in [this.getLeftOperand(), this.getRightOperand()] + private class LogicalOrExprTree extends PostOrderTree instanceof LogicalOrExpr { + final override predicate propagatesAbnormal(AstNode child) { + child in [super.getLeftOperand(), super.getRightOperand()] } - final override predicate first(ControlFlowElement first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of left operand to first element of right operand - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof FalseCompletion and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or // Post-order: flow from last element of left operand to element itself - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c instanceof TrueCompletion and succ = this or // Post-order: flow from last element of right operand to element itself - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class NullCoalescingExprTree extends PostOrderTree, NullCoalescingExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child in [this.getLeftOperand(), this.getRightOperand()] + private class NullCoalescingExprTree extends PostOrderTree instanceof NullCoalescingExpr { + final override predicate propagatesAbnormal(AstNode child) { + child in [super.getLeftOperand(), super.getRightOperand()] } - final override predicate first(ControlFlowElement first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of left operand to first element of right operand - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c.(NullnessCompletion).isNull() and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or // Post-order: flow from last element of left operand to element itself - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and succ = this and c instanceof NormalCompletion and not c.(NullnessCompletion).isNull() or // Post-order: flow from last element of right operand to element itself - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class ConditionalExprTree extends PostOrderTree, ConditionalExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child in [this.getCondition(), this.getThen(), this.getElse()] + private class ConditionalExprTree extends PostOrderTree instanceof ConditionalExpr { + final override predicate propagatesAbnormal(AstNode child) { + child in [super.getCondition(), super.getThen(), super.getElse()] } - final override predicate first(ControlFlowElement first) { first(this.getCondition(), first) } + final override predicate first(AstNode first) { first(super.getCondition(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of condition to first element of then branch - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and c instanceof TrueCompletion and - first(this.getThen(), succ) + first(super.getThen(), succ) or // Flow from last element of condition to first element of else branch - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and c instanceof FalseCompletion and - first(this.getElse(), succ) + first(super.getElse(), succ) or // Post-order: flow from last element of a branch to element itself - last([this.getThen(), this.getElse()], pred, c) and + last([super.getThen(), super.getElse()], pred, c) and c instanceof NormalCompletion and succ = this } @@ -564,38 +579,35 @@ module Expressions { * version in the control flow graph in order to get better data flow / taint * tracking. */ - private class AssignOperationWithExpandedAssignment extends AssignOperation, ControlFlowTree { + private class AssignOperationWithExpandedAssignment extends ControlFlowTree instanceof AssignOperation + { private Expr expanded; AssignOperationWithExpandedAssignment() { expanded = this.getExpandedAssignment() } - final override predicate first(ControlFlowElement first) { first(expanded, first) } + final override predicate first(AstNode first) { first(expanded, first) } - final override predicate last(ControlFlowElement last, Completion c) { last(expanded, last, c) } + final override predicate last(AstNode last, Completion c) { last(expanded, last, c) } - final override predicate propagatesAbnormal(ControlFlowElement child) { none() } + final override predicate propagatesAbnormal(AstNode child) { none() } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - none() - } + final override predicate succ(AstNode pred, AstNode succ, Completion c) { none() } } /** A conditionally qualified expression. */ - private class ConditionallyQualifiedExpr extends PostOrderTree, QualifiableExpr { + private class ConditionallyQualifiedExpr extends PostOrderTree instanceof QualifiableExpr { private Expr qualifier; ConditionallyQualifiedExpr() { this.isConditional() and qualifier = getExprChild(this, 0) } - final override predicate propagatesAbnormal(ControlFlowElement child) { child = qualifier } + final override predicate propagatesAbnormal(AstNode child) { child = qualifier } - final override predicate first(ControlFlowElement first) { first(qualifier, first) } + final override predicate first(AstNode first) { first(qualifier, first) } pragma[nomagic] - private predicate lastQualifier(ControlFlowElement last, Completion c) { - last(qualifier, last, c) - } + private predicate lastQualifier(AstNode last, Completion c) { last(qualifier, last, c) } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { PostOrderTree.super.last(last, c) or // Qualifier exits with a `null` completion @@ -603,7 +615,7 @@ module Expressions { c.(NullnessCompletion).isNull() } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { exists(int i | last(getExprChild(this, i), pred, c) and c instanceof NormalCompletion and @@ -619,48 +631,48 @@ module Expressions { } } - private class ThrowExprTree extends PostOrderTree, ThrowExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { child = this.getExpr() } + private class ThrowExprTree extends PostOrderTree instanceof ThrowExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getExpr() } - final override predicate first(ControlFlowElement first) { first(this.getExpr(), first) } + final override predicate first(AstNode first) { first(super.getExpr(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - last(this.getExpr(), pred, c) and + final override predicate succ(AstNode pred, AstNode succ, Completion c) { + last(super.getExpr(), pred, c) and c instanceof NormalCompletion and succ = this } } - private class ObjectCreationTree extends ControlFlowTree, ObjectCreation { + private class ObjectCreationTree extends ControlFlowTree instanceof ObjectCreation { private Expr getObjectCreationArgument(int i) { i >= 0 and - if this.hasInitializer() + if super.hasInitializer() then result = getExprChild(this, i + 1) else result = getExprChild(this, i) } - final override predicate propagatesAbnormal(ControlFlowElement child) { + final override predicate propagatesAbnormal(AstNode child) { child = this.getObjectCreationArgument(_) } - final override predicate first(ControlFlowElement first) { + final override predicate first(AstNode first) { first(this.getObjectCreationArgument(0), first) or not exists(this.getObjectCreationArgument(0)) and first = this } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Post-order: element itself (when no initializer) last = this and - not this.hasInitializer() and + not super.hasInitializer() and c.isValidFor(this) or // Last element of initializer - last(this.getInitializer(), last, c) + last(super.getInitializer(), last, c) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of argument `i` to first element of argument `i+1` exists(int i | last(this.getObjectCreationArgument(i), pred, c) | first(this.getObjectCreationArgument(i + 1), succ) and @@ -676,89 +688,89 @@ module Expressions { or // Flow from self to first element of initializer pred = this and - first(this.getInitializer(), succ) and + first(super.getInitializer(), succ) and c instanceof SimpleCompletion } } - private class ArrayCreationTree extends ControlFlowTree, ArrayCreation { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getALengthArgument() + private class ArrayCreationTree extends ControlFlowTree instanceof ArrayCreation { + final override predicate propagatesAbnormal(AstNode child) { + child = super.getALengthArgument() } - final override predicate first(ControlFlowElement first) { + final override predicate first(AstNode first) { // First element of first length argument - first(this.getLengthArgument(0), first) + first(super.getLengthArgument(0), first) or // No length argument: element itself - not exists(this.getLengthArgument(0)) and + not exists(super.getLengthArgument(0)) and first = this } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Post-order: element itself (when no initializer) last = this and - not this.hasInitializer() and + not super.hasInitializer() and c.isValidFor(this) or // Last element of initializer - last(this.getInitializer(), last, c) + last(super.getInitializer(), last, c) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from self to first element of initializer pred = this and - first(this.getInitializer(), succ) and + first(super.getInitializer(), succ) and c instanceof SimpleCompletion or exists(int i | - last(this.getLengthArgument(i), pred, c) and + last(super.getLengthArgument(i), pred, c) and c instanceof SimpleCompletion | // Flow from last length argument to self - i = max(int j | exists(this.getLengthArgument(j))) and + i = max(int j | exists(super.getLengthArgument(j))) and succ = this or // Flow from one length argument to the next - first(this.getLengthArgument(i + 1), succ) + first(super.getLengthArgument(i + 1), succ) ) } } - private class SwitchExprTree extends PostOrderTree, SwitchTree, SwitchExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { + private class SwitchExprTree extends PostOrderTree, SwitchTree instanceof SwitchExpr { + final override predicate propagatesAbnormal(AstNode child) { SwitchTree.super.propagatesAbnormal(child) or - child = this.getACase() + child = super.getACase() } - final override predicate first(ControlFlowElement first) { first(this.getExpr(), first) } + final override predicate first(AstNode first) { first(super.getExpr(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { SwitchTree.super.succ(pred, succ, c) or - last(this.getACase(), pred, c) and + last(super.getACase(), pred, c) and succ = this and c instanceof NormalCompletion } } - private class SwitchCaseExprTree extends PostOrderTree, CaseTree, SwitchCaseExpr { - final override predicate first(ControlFlowElement first) { first(this.getPattern(), first) } + private class SwitchCaseExprTree extends PostOrderTree, CaseTree instanceof SwitchCaseExpr { + final override predicate first(AstNode first) { first(super.getPattern(), first) } pragma[noinline] - private predicate lastNoMatch(ControlFlowElement last, ConditionalCompletion cc) { - last([this.getPattern(), this.getCondition()], last, cc) and + private predicate lastNoMatch(AstNode last, ConditionalCompletion cc) { + last([super.getPattern(), super.getCondition()], last, cc) and (cc.(MatchingCompletion).isNonMatch() or cc instanceof FalseCompletion) } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { PostOrderTree.super.last(last, c) or // Last case exists with a non-match exists(SwitchExpr se, int i, ConditionalCompletion cc | this = se.getCase(i) and - not this.matchesAll() and + not super.matchesAll() and not exists(se.getCase(i + 1)) and this.lastNoMatch(last, cc) and c = @@ -773,48 +785,46 @@ module Expressions { ) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { CaseTree.super.succ(pred, succ, c) or - last(this.getBody(), pred, c) and + last(super.getBody(), pred, c) and succ = this and c instanceof NormalCompletion } } - private class ConstructorInitializerTree extends PostOrderTree, ConstructorInitializer { - private ControlFlowTree getChildElement(int i) { result = getExprChild(this, i) } + private class ConstructorInitializerTree extends PostOrderTree instanceof ConstructorInitializer { + private ControlFlowTree getChildNode(int i) { result = getExprChild(this, i) } - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getChildElement(_) - } + final override predicate propagatesAbnormal(AstNode child) { child = this.getChildNode(_) } - final override predicate first(ControlFlowElement first) { - first(this.getChildElement(0), first) + final override predicate first(AstNode first) { + first(this.getChildNode(0), first) or - not exists(this.getChildElement(0)) and + not exists(this.getChildNode(0)) and first = this } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Post-order: flow from last element of last child to element itself exists(int lst | - lst = max(int i | exists(this.getChildElement(i))) and - last(this.getChildElement(lst), pred, c) and + lst = max(int i | exists(this.getChildNode(i))) and + last(this.getChildNode(lst), pred, c) and succ = this and c instanceof NormalCompletion ) or // Standard left-to-right evaluation exists(int i | - last(this.getChildElement(i), pred, c) and + last(this.getChildNode(i), pred, c) and c instanceof NormalCompletion and - first(this.getChildElement(i + 1), succ) + first(this.getChildNode(i + 1), succ) ) or exists(ConstructorTree con, CompilationExt comp | last(this, pred, c) and - con = this.getConstructor() and + con = super.getConstructor() and comp = getCompilation(this.getFile()) and c instanceof NormalCompletion | @@ -828,97 +838,89 @@ module Expressions { } } - private class NotPatternExprTree extends PostOrderTree, NotPatternExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getPattern() - } + private class NotPatternExprTree extends PostOrderTree instanceof NotPatternExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern() } - final override predicate first(ControlFlowElement first) { first(this.getPattern(), first) } + final override predicate first(AstNode first) { first(super.getPattern(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { succ = this and - last(this.getPattern(), pred, c) and + last(super.getPattern(), pred, c) and c instanceof NormalCompletion } } - private class AndPatternExprTree extends PostOrderTree, AndPatternExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getAnOperand() - } + private class AndPatternExprTree extends PostOrderTree instanceof AndPatternExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() } - final override predicate first(ControlFlowElement first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of left operand to first element of right operand - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c.(MatchingCompletion).getValue() = true and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or // Post-order: flow from last element of left operand to element itself - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c.(MatchingCompletion).getValue() = false and succ = this or // Post-order: flow from last element of right operand to element itself - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof MatchingCompletion and succ = this } } - private class OrPatternExprTree extends PostOrderTree, OrPatternExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getAnOperand() - } + private class OrPatternExprTree extends PostOrderTree instanceof OrPatternExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getAnOperand() } - final override predicate first(ControlFlowElement first) { first(this.getLeftOperand(), first) } + final override predicate first(AstNode first) { first(super.getLeftOperand(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of left operand to first element of right operand - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c.(MatchingCompletion).getValue() = false and - first(this.getRightOperand(), succ) + first(super.getRightOperand(), succ) or // Post-order: flow from last element of left operand to element itself - last(this.getLeftOperand(), pred, c) and + last(super.getLeftOperand(), pred, c) and c.(MatchingCompletion).getValue() = true and succ = this or // Post-order: flow from last element of right operand to element itself - last(this.getRightOperand(), pred, c) and + last(super.getRightOperand(), pred, c) and c instanceof MatchingCompletion and succ = this } } } -private class RecursivePatternExprTree extends PostOrderTree, RecursivePatternExpr { +private class RecursivePatternExprTree extends PostOrderTree instanceof RecursivePatternExpr { private Expr getTypeExpr() { - result = this.getVariableDeclExpr() + result = super.getVariableDeclExpr() or - not exists(this.getVariableDeclExpr()) and - result = this.getTypeAccess() + not exists(super.getVariableDeclExpr()) and + result = super.getTypeAccess() } private PatternExpr getChildPattern() { - result = this.getPositionalPatterns() + result = super.getPositionalPatterns() or - result = this.getPropertyPatterns() + result = super.getPropertyPatterns() } - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getChildPattern() - } + final override predicate propagatesAbnormal(AstNode child) { child = this.getChildPattern() } - final override predicate first(ControlFlowElement first) { + final override predicate first(AstNode first) { first(this.getTypeExpr(), first) or not exists(this.getTypeExpr()) and first(this.getChildPattern(), first) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from type test to child pattern last(this.getTypeExpr(), pred, c) and first(this.getChildPattern(), succ) and @@ -936,68 +938,64 @@ private class RecursivePatternExprTree extends PostOrderTree, RecursivePatternEx } } -private class PositionalPatternExprTree extends PreOrderTree, PositionalPatternExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getPattern(_) - } +private class PositionalPatternExprTree extends PreOrderTree instanceof PositionalPatternExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern(_) } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { last = this and c.(MatchingCompletion).getValue() = false or - last(this.getPattern(_), last, c) and + last(super.getPattern(_), last, c) and c.(MatchingCompletion).getValue() = false or exists(int lst | - last(this.getPattern(lst), last, c) and - not exists(this.getPattern(lst + 1)) + last(super.getPattern(lst), last, c) and + not exists(super.getPattern(lst + 1)) ) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from self to first pattern pred = this and c.(MatchingCompletion).getValue() = true and - first(this.getPattern(0), succ) + first(super.getPattern(0), succ) or // Flow from one pattern to the next exists(int i | - last(this.getPattern(i), pred, c) and + last(super.getPattern(i), pred, c) and c.(MatchingCompletion).getValue() = true and - first(this.getPattern(i + 1), succ) + first(super.getPattern(i + 1), succ) ) } } -private class PropertyPatternExprExprTree extends PostOrderTree, PropertyPatternExpr { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getPattern(_) - } +private class PropertyPatternExprExprTree extends PostOrderTree instanceof PropertyPatternExpr { + final override predicate propagatesAbnormal(AstNode child) { child = super.getPattern(_) } - final override predicate first(ControlFlowElement first) { - first(this.getPattern(0), first) + final override predicate first(AstNode first) { + first(super.getPattern(0), first) or - not exists(this.getPattern(0)) and + not exists(super.getPattern(0)) and first = this } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from one pattern to the next exists(int i | - last(this.getPattern(i), pred, c) and + last(super.getPattern(i), pred, c) and c.(MatchingCompletion).getValue() = true and - first(this.getPattern(i + 1), succ) + first(super.getPattern(i + 1), succ) ) or // Post-order: flow from last element of failing pattern to element itself - last(this.getPattern(_), pred, c) and + last(super.getPattern(_), pred, c) and c.(MatchingCompletion).getValue() = false and succ = this or // Post-order: flow from last element of last pattern to element itself exists(int last | - last(this.getPattern(last), pred, c) and - not exists(this.getPattern(last + 1)) and + last(super.getPattern(last), pred, c) and + not exists(super.getPattern(last + 1)) and c instanceof MatchingCompletion and succ = this ) @@ -1005,7 +1003,7 @@ private class PropertyPatternExprExprTree extends PostOrderTree, PropertyPattern } module Statements { - private class StandardStmt extends StandardPreOrderTree, Stmt { + private class StandardStmt extends StandardPreOrderTree instanceof Stmt { StandardStmt() { // The following statements need special treatment not this instanceof IfStmt and @@ -1018,7 +1016,7 @@ module Statements { not this instanceof LabeledStmt } - private ControlFlowTree getChildElement0(int i) { + private ControlFlowTree getChildNode0(int i) { not this instanceof GeneralCatchClause and not this instanceof FixedStmt and not this instanceof UsingBlockStmt and @@ -1046,69 +1044,66 @@ module Statements { ) } - final override ControlFlowElement getChildElement(int i) { - result = - rank[i + 1](ControlFlowElement cfe, int j | cfe = this.getChildElement0(j) | cfe order by j) + final override AstNode getChildNode(int i) { + result = rank[i + 1](AstNode cfe, int j | cfe = this.getChildNode0(j) | cfe order by j) } } - private class IfStmtTree extends PreOrderTree, IfStmt { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getCondition() - } + private class IfStmtTree extends PreOrderTree instanceof IfStmt { + final override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Condition exits with a false completion and there is no `else` branch - last(this.getCondition(), last, c) and + last(super.getCondition(), last, c) and c instanceof FalseCompletion and - not exists(this.getElse()) + not exists(super.getElse()) or // Then branch exits with any completion - last(this.getThen(), last, c) + last(super.getThen(), last, c) or // Else branch exits with any completion - last(this.getElse(), last, c) + last(super.getElse(), last, c) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Pre-order: flow from statement itself to first element of condition pred = this and - first(this.getCondition(), succ) and + first(super.getCondition(), succ) and c instanceof SimpleCompletion or - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and ( // Flow from last element of condition to first element of then branch - c instanceof TrueCompletion and first(this.getThen(), succ) + c instanceof TrueCompletion and first(super.getThen(), succ) or // Flow from last element of condition to first element of else branch - c instanceof FalseCompletion and first(this.getElse(), succ) + c instanceof FalseCompletion and first(super.getElse(), succ) ) } } - private class SwitchStmtTree extends PreOrderTree, SwitchTree, SwitchStmt { - final override predicate last(ControlFlowElement last, Completion c) { + private class SwitchStmtTree extends PreOrderTree, SwitchTree instanceof SwitchStmt { + final override predicate last(AstNode last, Completion c) { // Switch expression exits normally and there are no cases - not exists(this.getACase()) and - last(this.getExpr(), last, c) and + not exists(super.getACase()) and + last(super.getExpr(), last, c) and c instanceof NormalCompletion or // A statement exits with a `break` completion - last(this.getStmt(_), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + last(super.getStmt(_), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) or // A statement exits abnormally - last(this.getStmt(_), last, c) and + last(super.getStmt(_), last, c) and not c instanceof BreakCompletion and not c instanceof NormalCompletion and not any(LabeledStmtTree t | - t.hasLabelInCallable(c.(GotoCompletion).getLabel(), this.getEnclosingCallable()) + t.hasLabelInCallable(c.(GotoCompletion).getLabel(), super.getEnclosingCallable()) ) instanceof CaseStmt or // Last case exits with a non-match exists(CaseStmt cs, int last_ | - last_ = max(int i | exists(this.getCase(i))) and - cs = this.getCase(last_) + last_ = max(int i | exists(super.getCase(i))) and + cs = super.getCase(last_) | last(cs.getPattern(), last, c) and not c.(MatchingCompletion).isMatch() @@ -1118,200 +1113,196 @@ module Statements { ) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { SwitchTree.super.succ(pred, succ, c) or // Pre-order: flow from statement itself to first switch expression pred = this and - first(this.getExpr(), succ) and + first(super.getExpr(), succ) and c instanceof SimpleCompletion or // Flow from last element of non-`case` statement `i` to first element of statement `i+1` - exists(int i | last(this.getStmt(i), pred, c) | - not this.getStmt(i) instanceof CaseStmt and + exists(int i | last(super.getStmt(i), pred, c) | + not super.getStmt(i) instanceof CaseStmt and c instanceof NormalCompletion and - first(this.getStmt(i + 1), succ) + first(super.getStmt(i + 1), succ) ) or // Flow from last element of `case` statement `i` to first element of statement `i+1` - exists(int i | last(this.getStmt(i).(CaseStmt).getBody(), pred, c) | + exists(int i | last(super.getStmt(i).(CaseStmt).getBody(), pred, c) | c instanceof NormalCompletion and - first(this.getStmt(i + 1), succ) + first(super.getStmt(i + 1), succ) ) } } - private class CaseStmtTree extends PreOrderTree, CaseTree, CaseStmt { - final override predicate last(ControlFlowElement last, Completion c) { + private class CaseStmtTree extends PreOrderTree, CaseTree instanceof CaseStmt { + final override predicate last(AstNode last, Completion c) { // Condition exists with a `false` completion - last(this.getCondition(), last, c) and + last(super.getCondition(), last, c) and c instanceof FalseCompletion or // Case pattern exits with a non-match - last(this.getPattern(), last, c) and + last(super.getPattern(), last, c) and not c.(MatchingCompletion).isMatch() or // Case body exits with any completion - last(this.getBody(), last, c) + last(super.getBody(), last, c) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { CaseTree.super.succ(pred, succ, c) or pred = this and - first(this.getPattern(), succ) and + first(super.getPattern(), succ) and c instanceof SimpleCompletion } } - abstract private class LoopStmtTree extends PreOrderTree, LoopStmt { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getCondition() - } + abstract private class LoopStmtTree extends PreOrderTree instanceof LoopStmt { + final override predicate propagatesAbnormal(AstNode child) { child = super.getCondition() } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Condition exits with a false completion - last(this.getCondition(), last, c) and + last(super.getCondition(), last, c) and c instanceof FalseCompletion or // Body exits with a break completion - last(this.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + last(super.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) or // Body exits with a completion that does not continue the loop - last(this.getBody(), last, c) and + last(super.getBody(), last, c) and not c instanceof BreakCompletion and not c.continuesLoop() } - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of condition to first element of loop body - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and c instanceof TrueCompletion and - first(this.getBody(), succ) + first(super.getBody(), succ) or // Flow from last element of loop body back to first element of condition not this instanceof ForStmt and - last(this.getBody(), pred, c) and + last(super.getBody(), pred, c) and c.continuesLoop() and - first(this.getCondition(), succ) + first(super.getCondition(), succ) } } - private class WhileStmtTree extends LoopStmtTree, WhileStmt { - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + private class WhileStmtTree extends LoopStmtTree instanceof WhileStmt { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { LoopStmtTree.super.succ(pred, succ, c) or pred = this and - first(this.getCondition(), succ) and + first(super.getCondition(), succ) and c instanceof SimpleCompletion } } - private class DoStmtTree extends LoopStmtTree, DoStmt { - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + private class DoStmtTree extends LoopStmtTree instanceof DoStmt { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { LoopStmtTree.super.succ(pred, succ, c) or pred = this and - first(this.getBody(), succ) and + first(super.getBody(), succ) and c instanceof SimpleCompletion } } - private class ForStmtTree extends LoopStmtTree, ForStmt { + private class ForStmtTree extends LoopStmtTree instanceof ForStmt { /** Gets the condition if it exists, otherwise the body. */ - private ControlFlowElement getConditionOrBody() { - result = this.getCondition() + private AstNode getConditionOrBody() { + result = super.getCondition() or - not exists(this.getCondition()) and - result = this.getBody() + not exists(super.getCondition()) and + result = super.getBody() } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { LoopStmtTree.super.succ(pred, succ, c) or // Pre-order: flow from statement itself to first element of first initializer/ // condition/loop body - exists(ControlFlowElement next | + exists(AstNode next | pred = this and first(next, succ) and c instanceof SimpleCompletion | - next = this.getInitializer(0) + next = super.getInitializer(0) or - not exists(this.getInitializer(0)) and + not exists(super.getInitializer(0)) and next = this.getConditionOrBody() ) or // Flow from last element of initializer `i` to first element of initializer `i+1` - exists(int i | last(this.getInitializer(i), pred, c) | + exists(int i | last(super.getInitializer(i), pred, c) | c instanceof NormalCompletion and - first(this.getInitializer(i + 1), succ) + first(super.getInitializer(i + 1), succ) ) or // Flow from last element of last initializer to first element of condition/loop body - exists(int last | last = max(int i | exists(this.getInitializer(i))) | - last(this.getInitializer(last), pred, c) and + exists(int last | last = max(int i | exists(super.getInitializer(i))) | + last(super.getInitializer(last), pred, c) and c instanceof NormalCompletion and first(this.getConditionOrBody(), succ) ) or // Flow from last element of condition into first element of loop body - last(this.getCondition(), pred, c) and + last(super.getCondition(), pred, c) and c instanceof TrueCompletion and - first(this.getBody(), succ) + first(super.getBody(), succ) or // Flow from last element of loop body to first element of update/condition/self - exists(ControlFlowElement next | - last(this.getBody(), pred, c) and + exists(AstNode next | + last(super.getBody(), pred, c) and c.continuesLoop() and first(next, succ) and - if exists(this.getUpdate(0)) - then next = this.getUpdate(0) + if exists(super.getUpdate(0)) + then next = super.getUpdate(0) else next = this.getConditionOrBody() ) or // Flow from last element of update to first element of next update/condition/loop body - exists(ControlFlowElement next, int i | - last(this.getUpdate(i), pred, c) and + exists(AstNode next, int i | + last(super.getUpdate(i), pred, c) and c instanceof NormalCompletion and first(next, succ) and - if exists(this.getUpdate(i + 1)) - then next = this.getUpdate(i + 1) + if exists(super.getUpdate(i + 1)) + then next = super.getUpdate(i + 1) else next = this.getConditionOrBody() ) } } - private class ForeachStmtTree extends ControlFlowTree, ForeachStmt { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getIterableExpr() - } + private class ForeachStmtTree extends ControlFlowTree instanceof ForeachStmt { + final override predicate propagatesAbnormal(AstNode child) { child = super.getIterableExpr() } - final override predicate first(ControlFlowElement first) { + final override predicate first(AstNode first) { // Unlike most other statements, `foreach` statements are not modeled in // pre-order, because we use the `foreach` node itself to represent the // emptiness test that determines whether to execute the loop body - first(this.getIterableExpr(), first) + first(super.getIterableExpr(), first) } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Emptiness test exits with no more elements last = this and c.(EmptinessCompletion).isEmpty() or // Body exits with a break completion - last(this.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) + last(super.getBody(), last, c.(NestedBreakCompletion).getAnInnerCompatibleCompletion()) or // Body exits abnormally - last(this.getBody(), last, c) and + last(super.getBody(), last, c) and not c instanceof NormalCompletion and not c instanceof ContinueCompletion and not c instanceof BreakCompletion } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from last element of iterator expression to emptiness test - last(this.getIterableExpr(), pred, c) and + last(super.getIterableExpr(), pred, c) and c instanceof NormalCompletion and succ = this or @@ -1319,63 +1310,61 @@ module Statements { pred = this and c = any(EmptinessCompletion ec | not ec.isEmpty()) and ( - first(this.getVariableDeclExpr(), succ) + first(super.getVariableDeclExpr(), succ) or - first(this.getVariableDeclTuple(), succ) + first(super.getVariableDeclTuple(), succ) or - not exists(this.getVariableDeclExpr()) and - not exists(this.getVariableDeclTuple()) and - first(this.getBody(), succ) + not exists(super.getVariableDeclExpr()) and + not exists(super.getVariableDeclTuple()) and + first(super.getBody(), succ) ) or // Flow from last element of variable declaration to first element of loop body ( - last(this.getVariableDeclExpr(), pred, c) or - last(this.getVariableDeclTuple(), pred, c) + last(super.getVariableDeclExpr(), pred, c) or + last(super.getVariableDeclTuple(), pred, c) ) and c instanceof SimpleCompletion and - first(this.getBody(), succ) + first(super.getBody(), succ) or // Flow from last element of loop body back to emptiness test - last(this.getBody(), pred, c) and + last(super.getBody(), pred, c) and c.continuesLoop() and succ = this } } pragma[nomagic] - private ControlFlowElement lastLastCatchClause(CatchClause cc, Completion c) { + private AstNode lastLastCatchClause(CatchClause cc, Completion c) { cc.isLast() and last(cc, result, c) } pragma[nomagic] - private ControlFlowElement lastCatchClauseBlock(CatchClause cc, Completion c) { + private AstNode lastCatchClauseBlock(CatchClause cc, Completion c) { last(cc.getBlock(), result, c) } /** Gets a child of `cfe` that is in CFG scope `scope`. */ pragma[noinline] - private ControlFlowElement getAChildInScope(ControlFlowElement cfe, Callable scope) { - result = ControlFlowTree::getAChild(cfe) and + private ControlFlowElement getAChildInScope(AstNode cfe, Callable scope) { + result = getAChild(cfe) and scope = result.getEnclosingCallable() } - class TryStmtTree extends PreOrderTree, TryStmt { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getFinally() - } + class TryStmtTree extends PreOrderTree instanceof TryStmt { + final override predicate propagatesAbnormal(AstNode child) { child = super.getFinally() } /** - * Gets a descendant that belongs to the `finally` block of this try statement. + * Gets a descendant that belongs to the finally block of this try statement. */ - ControlFlowElement getAFinallyDescendant() { - result = this.getFinally() + AstNode getAFinallyDescendant() { + result = super.getFinally() or exists(ControlFlowElement mid | mid = this.getAFinallyDescendant() and result = getAChildInScope(mid, mid.getEnclosingCallable()) and - not exists(TryStmtTree nestedTry | + not exists(TryStmt nestedTry | result = nestedTry.getFinally() and nestedTry != this ) @@ -1383,63 +1372,61 @@ module Statements { } /** - * Holds if `innerTry` has a `finally` block and is immediately nested inside the - * `finally` block of this `try` statement. + * Holds if `innerTry` has a finally block and is immediately nested inside the + * finally block of this try statement. */ - private predicate nestedFinally(TryStmtTree innerTry) { - exists(ControlFlowElement innerFinally | - innerFinally = getAChildInScope(this.getAFinallyDescendant(), this.getEnclosingCallable()) and + private predicate nestedFinally(TryStmt innerTry) { + exists(AstNode innerFinally | + innerFinally = getAChildInScope(this.getAFinallyDescendant(), super.getEnclosingCallable()) and innerFinally = innerTry.getFinally() ) } /** - * Gets the `finally`-nesting level of this `try` statement. That is, the number of - * `finally` blocks that this `try` statement is nested under. + * Gets the finally-nesting level of this try statement. That is, the number of + * finally blocks that this try statement is nested under. */ int nestLevel() { result = count(TryStmtTree outer | outer.nestedFinally+(this)) } - /** Holds if `last` is a last element of the block of this `try` statement. */ + /** Holds if `last` is a last element of the block of this try statement. */ pragma[nomagic] - predicate lastBlock(ControlFlowElement last, Completion c) { last(this.getBlock(), last, c) } + predicate lastBlock(AstNode last, Completion c) { last(super.getBlock(), last, c) } /** - * Gets a last element from a `try` or `catch` block of this `try` statement + * Gets a last element from a `try` or `catch` block of this try statement * that may finish with completion `c`, such that control may be transferred - * to the `finally` block (if it exists), but only if `finalizable = true`. + * to the finally block (if it exists), but only if `finalizable = true`. */ pragma[nomagic] - ControlFlowElement getAFinallyPredecessor(Completion c, boolean finalizable) { - // Exit completions skip the `finally` block + AstNode getAFinallyPredecessor(Completion c, boolean finalizable) { + // Exit completions skip the finally block (if c instanceof ExitCompletion then finalizable = false else finalizable = true) and ( this.lastBlock(result, c) and ( // Any non-throw completion from the `try` block will always continue directly - // to the `finally` block + // to the finally block not c instanceof ThrowCompletion or - // Any completion from the `try` block will continue to the `finally` block + // Any completion from the `try` block will continue to the finally block // when there are no catch clauses - not exists(this.getACatchClause()) + not exists(super.getACatchClause()) ) or - // Last element from any of the `catch` clause blocks continues to the `finally` block - result = lastCatchClauseBlock(this.getACatchClause(), c) + // Last element from any of the `catch` clause blocks continues to the finally block + result = lastCatchClauseBlock(super.getACatchClause(), c) or - // Last element of last `catch` clause continues to the `finally` block - result = lastLastCatchClause(this.getACatchClause(), c) + // Last element of last `catch` clause continues to the finally block + result = lastLastCatchClause(super.getACatchClause(), c) ) } pragma[nomagic] - private predicate lastFinally0(ControlFlowElement last, Completion c) { - last(this.getFinally(), last, c) - } + private predicate lastFinally0(AstNode last, Completion c) { last(super.getFinally(), last, c) } pragma[nomagic] private predicate lastFinally( - ControlFlowElement last, NormalCompletion finally, Completion outer, int nestLevel + AstNode last, NormalCompletion finally, Completion outer, int nestLevel ) { this.lastFinally0(last, finally) and exists( @@ -1448,19 +1435,19 @@ module Statements { nestLevel = this.nestLevel() } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { exists(boolean finalizable | last = this.getAFinallyPredecessor(c, finalizable) | - // If there is no `finally` block, last elements are from the body, from + // If there is no finally block, last elements are from the body, from // the blocks of one of the `catch` clauses, or from the last `catch` clause - not this.hasFinally() + not super.hasFinally() or finalizable = false ) or this.lastFinally(last, c, any(NormalCompletion nc), _) or - // If the `finally` block completes normally, it inherits any non-normal - // completion that was current before the `finally` block was entered + // If the finally block completes normally, it inherits any non-normal + // completion that was current before the finally block was entered exists(int nestLevel | c = any(NestedCompletion nc | @@ -1477,77 +1464,75 @@ module Statements { * Gets an exception type that is thrown by `cfe` in the block of this `try` * statement. Throw completion `c` matches the exception type. */ - ExceptionClass getAThrownException(ControlFlowElement cfe, ThrowCompletion c) { + ExceptionClass getAThrownException(AstNode cfe, ThrowCompletion c) { this.lastBlock(cfe, c) and result = c.getExceptionClass() } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Pre-order: flow from statement itself to first element of body pred = this and - first(this.getBlock(), succ) and + first(super.getBlock(), succ) and c instanceof SimpleCompletion or // Flow from last element of body to first `catch` clause exists(this.getAThrownException(pred, c)) and - first(this.getCatchClause(0), succ) + first(super.getCatchClause(0), succ) or - exists(CatchClause cc, int i | cc = this.getCatchClause(i) | + exists(CatchClause cc, int i | cc = super.getCatchClause(i) | // Flow from one `catch` clause to the next pred = cc and - last(this.getCatchClause(i), cc, c) and - first(this.getCatchClause(i + 1), succ) and + last(super.getCatchClause(i), cc, c) and + first(super.getCatchClause(i + 1), succ) and c = any(MatchingCompletion mc | not mc.isMatch()) or // Flow from last element of `catch` clause filter to next `catch` clause - last(this.getCatchClause(i), pred, c) and + last(super.getCatchClause(i), pred, c) and last(cc.getFilterClause(), pred, _) and - first(this.getCatchClause(i + 1), succ) and + first(super.getCatchClause(i + 1), succ) and c instanceof FalseCompletion ) or - // Flow into `finally` block + // Flow into finally block pred = this.getAFinallyPredecessor(c, true) and - first(this.getFinally(), succ) + first(super.getFinally(), succ) } } - private class SpecificCatchClauseTree extends PreOrderTree, SpecificCatchClause { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getFilterClause() - } + private class SpecificCatchClauseTree extends PreOrderTree instanceof SpecificCatchClause { + final override predicate propagatesAbnormal(AstNode child) { child = super.getFilterClause() } pragma[nomagic] - private predicate lastFilterClause(ControlFlowElement last, Completion c) { - last(this.getFilterClause(), last, c) + private predicate lastFilterClause(AstNode last, Completion c) { + last(super.getFilterClause(), last, c) } /** * Holds if the `try` block that this catch clause belongs to may throw an * exception of type `c`, where no `catch` clause is guaranteed to catch it. - * This catch clause is the last catch clause in the `try` statement that + * This catch clause is the last catch clause in the try statement that * it belongs to. */ pragma[nomagic] private predicate throwMayBeUncaught(ThrowCompletion c) { - exists(TryStmtTree ts | - ts = this.getTryStmt() and - ts.lastBlock(_, c) and + exists(TryStmt ts | + ts = super.getTryStmt() and + ts.(TryStmtTree).lastBlock(_, c) and not ts.getACatchClause() instanceof GeneralCatchClause and forall(SpecificCatchClause scc | scc = ts.getACatchClause() | scc.hasFilterClause() or not c.getExceptionClass().getABaseType*() = scc.getCaughtExceptionType() ) and - this.isLast() + super.isLast() ) } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { // Last element of `catch` block - last(this.getBlock(), last, c) + last(super.getBlock(), last, c) or - not this.isLast() and + not super.isLast() and ( // Incompatible exception type: clause itself last = this and @@ -1560,7 +1545,7 @@ module Statements { or // Last `catch` clause inherits throw completions from the `try` block, // when the clause does not match - this.isLast() and + super.isLast() and c = any(NestedCompletion nc | nc.getNestLevel() = 0 and @@ -1580,47 +1565,45 @@ module Statements { ) } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { // Flow from catch clause to variable declaration/filter clause/block pred = this and c.(MatchingCompletion).isMatch() and - exists(ControlFlowElement next | first(next, succ) | - if exists(this.getVariableDeclExpr()) - then next = this.getVariableDeclExpr() + exists(AstNode next | first(next, succ) | + if exists(super.getVariableDeclExpr()) + then next = super.getVariableDeclExpr() else - if exists(this.getFilterClause()) - then next = this.getFilterClause() - else next = this.getBlock() + if exists(super.getFilterClause()) + then next = super.getFilterClause() + else next = super.getBlock() ) or // Flow from variable declaration to filter clause/block - last(this.getVariableDeclExpr(), pred, c) and + last(super.getVariableDeclExpr(), pred, c) and c instanceof SimpleCompletion and - exists(ControlFlowElement next | first(next, succ) | - if exists(this.getFilterClause()) - then next = this.getFilterClause() - else next = this.getBlock() + exists(AstNode next | first(next, succ) | + if exists(super.getFilterClause()) + then next = super.getFilterClause() + else next = super.getBlock() ) or // Flow from filter to block - last(this.getFilterClause(), pred, c) and + last(super.getFilterClause(), pred, c) and c instanceof TrueCompletion and - first(this.getBlock(), succ) + first(super.getBlock(), succ) } } - private class JumpStmtTree extends PostOrderTree, JumpStmt { - final override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getChild(0) - } + private class JumpStmtTree extends PostOrderTree instanceof JumpStmt { + final override predicate propagatesAbnormal(AstNode child) { child = this.getChild(0) } - final override predicate first(ControlFlowElement first) { + final override predicate first(AstNode first) { first(this.getChild(0), first) or not exists(this.getChild(0)) and first = this } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { last(this.getChild(0), pred, c) and succ = this and c instanceof NormalCompletion @@ -1630,20 +1613,21 @@ module Statements { pragma[nomagic] private predicate goto(ControlFlowElement cfe, GotoCompletion gc, string label, Callable enclosing) { last(_, cfe, gc) and - // Special case: when a `goto` happens inside a `try` statement with a - // `finally` block, flow does not go directly to the target, but instead - // to the `finally` block (and from there possibly to the target) - not cfe = any(Statements::TryStmtTree t | t.hasFinally()).getAFinallyPredecessor(_, true) and + // Special case: when a `goto` happens inside a try statement with a + // finally block, flow does not go directly to the target, but instead + // to the finally block (and from there possibly to the target) + not cfe = + any(Statements::TryStmtTree t | t.(TryStmt).hasFinally()).getAFinallyPredecessor(_, true) and label = gc.getLabel() and enclosing = cfe.getEnclosingCallable() } - private class LabeledStmtTree extends PreOrderTree, LabeledStmt { - final override predicate propagatesAbnormal(ControlFlowElement child) { none() } + private class LabeledStmtTree extends PreOrderTree instanceof LabeledStmt { + final override predicate propagatesAbnormal(AstNode child) { none() } - final override predicate last(ControlFlowElement last, Completion c) { + final override predicate last(AstNode last, Completion c) { if this instanceof DefaultCase - then last(this.getStmt(), last, c) + then last(super.getStmt(), last, c) else ( not this instanceof CaseStmt and last = this and @@ -1653,14 +1637,14 @@ module Statements { pragma[noinline] predicate hasLabelInCallable(string label, Callable c) { - this.getEnclosingCallable() = c and - label = this.getLabel() + super.getEnclosingCallable() = c and + label = super.getLabel() } - final override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + final override predicate succ(AstNode pred, AstNode succ, Completion c) { this instanceof DefaultCase and pred = this and - first(this.getStmt(), succ) and + first(super.getStmt(), succ) and c instanceof SimpleCompletion or // Flow from element with matching `goto` completion to this statement @@ -1674,6 +1658,6 @@ module Statements { } /** A control flow element that is split into multiple control flow nodes. */ -class SplitControlFlowElement extends ControlFlowElement { - SplitControlFlowElement() { strictcount(this.getAControlFlowNode()) > 1 } +class SplitAstNode extends AstNode, ControlFlowElement { + SplitAstNode() { strictcount(this.getAControlFlowNode()) > 1 } } diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll deleted file mode 100644 index 5039d09ff22a0..0000000000000 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplShared.qll +++ /dev/null @@ -1,1027 +0,0 @@ -/** Provides language-independent definitions for AST-to-CFG construction. */ - -private import ControlFlowGraphImplSpecific - -/** An element with associated control flow. */ -abstract class ControlFlowTree extends ControlFlowTreeBase { - /** Holds if `first` is the first element executed within this element. */ - pragma[nomagic] - abstract predicate first(ControlFlowElement first); - - /** - * Holds if `last` with completion `c` is a potential last element executed - * within this element. - */ - pragma[nomagic] - abstract predicate last(ControlFlowElement last, Completion c); - - /** Holds if abnormal execution of `child` should propagate upwards. */ - abstract predicate propagatesAbnormal(ControlFlowElement child); - - /** - * Holds if `succ` is a control flow successor for `pred`, given that `pred` - * finishes with completion `c`. - */ - pragma[nomagic] - abstract predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c); -} - -/** - * Holds if `first` is the first element executed within control flow - * element `cft`. - */ -predicate first(ControlFlowTree cft, ControlFlowElement first) { cft.first(first) } - -/** - * Holds if `last` with completion `c` is a potential last element executed - * within control flow element `cft`. - */ -predicate last(ControlFlowTree cft, ControlFlowElement last, Completion c) { - cft.last(last, c) - or - exists(ControlFlowElement cfe | - cft.propagatesAbnormal(cfe) and - last(cfe, last, c) and - not completionIsNormal(c) - ) -} - -/** - * Holds if `succ` is a control flow successor for `pred`, given that `pred` - * finishes with completion `c`. - */ -pragma[nomagic] -predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - any(ControlFlowTree cft).succ(pred, succ, c) -} - -/** An element that is executed in pre-order. */ -abstract class PreOrderTree extends ControlFlowTree { - final override predicate first(ControlFlowElement first) { first = this } -} - -/** An element that is executed in post-order. */ -abstract class PostOrderTree extends ControlFlowTree { - override predicate last(ControlFlowElement last, Completion c) { - last = this and - completionIsValidFor(c, last) - } -} - -/** - * An element where the children are evaluated following a standard left-to-right - * evaluation. The actual evaluation order is determined by the predicate - * `getChildElement()`. - */ -abstract class StandardTree extends ControlFlowTree { - /** Gets the `i`th child element, in order of evaluation. */ - abstract ControlFlowElement getChildElement(int i); - - private ControlFlowElement getChildElementRanked(int i) { - result = - rank[i + 1](ControlFlowElement child, int j | - child = this.getChildElement(j) - | - child order by j - ) - } - - /** Gets the first child node of this element. */ - final ControlFlowElement getFirstChildElement() { result = this.getChildElementRanked(0) } - - /** Gets the last child node of this node. */ - final ControlFlowElement getLastChildElement() { - exists(int last | - result = this.getChildElementRanked(last) and - not exists(this.getChildElementRanked(last + 1)) - ) - } - - /** Holds if this element has no children. */ - predicate isLeafElement() { not exists(this.getFirstChildElement()) } - - override predicate propagatesAbnormal(ControlFlowElement child) { - child = this.getChildElement(_) - } - - pragma[nomagic] - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - exists(int i | - last(this.getChildElementRanked(i), pred, c) and - completionIsNormal(c) and - first(this.getChildElementRanked(i + 1), succ) - ) - } -} - -/** A standard element that is executed in pre-order. */ -abstract class StandardPreOrderTree extends StandardTree, PreOrderTree { - override predicate last(ControlFlowElement last, Completion c) { - last(this.getLastChildElement(), last, c) - or - this.isLeafElement() and - completionIsValidFor(c, this) and - last = this - } - - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - StandardTree.super.succ(pred, succ, c) - or - pred = this and - first(this.getFirstChildElement(), succ) and - completionIsSimple(c) - } -} - -/** A standard element that is executed in post-order. */ -abstract class StandardPostOrderTree extends StandardTree, PostOrderTree { - override predicate first(ControlFlowElement first) { - first(this.getFirstChildElement(), first) - or - not exists(this.getFirstChildElement()) and - first = this - } - - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - StandardTree.super.succ(pred, succ, c) - or - last(this.getLastChildElement(), pred, c) and - succ = this and - completionIsNormal(c) - } -} - -/** An element that is a leaf in the control flow graph. */ -abstract class LeafTree extends PreOrderTree, PostOrderTree { - override predicate propagatesAbnormal(ControlFlowElement child) { none() } - - override predicate succ(ControlFlowElement pred, ControlFlowElement succ, Completion c) { none() } -} - -/** - * Holds if split kinds `sk1` and `sk2` may overlap. That is, they may apply - * to at least one common AST node inside `scope`. - */ -private predicate overlapping(CfgScope scope, SplitKind sk1, SplitKind sk2) { - exists(ControlFlowElement e | - sk1.appliesTo(e) and - sk2.appliesTo(e) and - scope = getCfgScope(e) - ) -} - -/** - * A split kind. Each control flow node can have at most one split of a - * given kind. - */ -abstract class SplitKind extends SplitKindBase { - /** Gets a split of this kind. */ - SplitImpl getASplit() { result.getKind() = this } - - /** Holds if some split of this kind applies to AST node `n`. */ - predicate appliesTo(ControlFlowElement n) { this.getASplit().appliesTo(n) } - - /** - * Gets a unique integer representing this split kind. The integer is used - * to represent sets of splits as ordered lists. - */ - abstract int getListOrder(); - - /** Gets the rank of this split kind among all overlapping kinds for `c`. */ - private int getRank(CfgScope scope) { - this = rank[result](SplitKind sk | overlapping(scope, this, sk) | sk order by sk.getListOrder()) - } - - /** - * Holds if this split kind is enabled for AST node `n`. For performance reasons, - * the number of splits is restricted by the `maxSplits()` predicate. - */ - predicate isEnabled(ControlFlowElement n) { - this.appliesTo(n) and - this.getRank(getCfgScope(n)) <= maxSplits() - } - - /** - * Gets the rank of this split kind among all the split kinds that apply to - * AST node `n`. The rank is based on the order defined by `getListOrder()`. - */ - int getListRank(ControlFlowElement n) { - this.isEnabled(n) and - this = rank[result](SplitKind sk | sk.appliesTo(n) | sk order by sk.getListOrder()) - } - - /** Gets a textual representation of this split kind. */ - abstract string toString(); -} - -/** An interface for implementing an entity to split on. */ -abstract class SplitImpl extends Split { - /** Gets the kind of this split. */ - abstract SplitKind getKind(); - - /** - * Holds if this split is entered when control passes from `pred` to `succ` with - * completion `c`. - * - * Invariant: `hasEntry(pred, succ, c) implies succ(pred, succ, c)`. - */ - abstract predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c); - - /** - * Holds if this split is entered when control passes from `scope` to the entry point - * `first`. - * - * Invariant: `hasEntryScope(scope, first) implies scopeFirst(scope, first)`. - */ - abstract predicate hasEntryScope(CfgScope scope, ControlFlowElement first); - - /** - * Holds if this split is left when control passes from `pred` to `succ` with - * completion `c`. - * - * Invariant: `hasExit(pred, succ, c) implies succ(pred, succ, c)`. - */ - abstract predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c); - - /** - * Holds if this split is left when control passes from `last` out of the enclosing - * scope `scope` with completion `c`. - * - * Invariant: `hasExitScope(scope, last, c) implies scopeLast(scope, last, c)` - */ - abstract predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c); - - /** - * Holds if this split is maintained when control passes from `pred` to `succ` with - * completion `c`. - * - * Invariant: `hasSuccessor(pred, succ, c) implies succ(pred, succ, c)` - */ - abstract predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c); - - /** Holds if this split applies to control flow element `cfe`. */ - final predicate appliesTo(ControlFlowElement cfe) { - this.hasEntry(_, cfe, _) - or - this.hasEntryScope(_, cfe) - or - exists(ControlFlowElement pred | this.appliesTo(pred) | this.hasSuccessor(pred, cfe, _)) - } - - /** The `succ` relation restricted to predecessors `pred` that this split applies to. */ - pragma[noinline] - final predicate appliesSucc(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - this.appliesTo(pred) and - succ(pred, succ, c) - } -} - -/** - * A set of control flow node splits. The set is represented by a list of splits, - * ordered by ascending rank. - */ -class Splits extends TSplits { - /** Gets a textual representation of this set of splits. */ - string toString() { result = splitsToString(this) } - - /** Gets a split belonging to this set of splits. */ - SplitImpl getASplit() { - exists(SplitImpl head, Splits tail | this = TSplitsCons(head, tail) | - result = head - or - result = tail.getASplit() - ) - } -} - -private predicate succEntrySplitsFromRank( - CfgScope pred, ControlFlowElement succ, Splits splits, int rnk -) { - splits = TSplitsNil() and - scopeFirst(pred, succ) and - rnk = 0 - or - exists(SplitImpl head, Splits tail | succEntrySplitsCons(pred, succ, head, tail, rnk) | - splits = TSplitsCons(head, tail) - ) -} - -private predicate succEntrySplitsCons( - CfgScope pred, ControlFlowElement succ, SplitImpl head, Splits tail, int rnk -) { - succEntrySplitsFromRank(pred, succ, tail, rnk - 1) and - head.hasEntryScope(pred, succ) and - rnk = head.getKind().getListRank(succ) -} - -/** - * Holds if `succ` with splits `succSplits` is the first element that is executed - * when entering callable `pred`. - */ -pragma[noinline] -private predicate succEntrySplits( - CfgScope pred, ControlFlowElement succ, Splits succSplits, SuccessorType t -) { - exists(int rnk | - scopeFirst(pred, succ) and - successorTypeIsSimple(t) and - succEntrySplitsFromRank(pred, succ, succSplits, rnk) - | - rnk = 0 and - not any(SplitImpl split).hasEntryScope(pred, succ) - or - rnk = max(SplitImpl split | split.hasEntryScope(pred, succ) | split.getKind().getListRank(succ)) - ) -} - -/** - * Holds if `pred` with splits `predSplits` can exit the enclosing callable - * `succ` with type `t`. - */ -private predicate succExitSplits( - ControlFlowElement pred, Splits predSplits, CfgScope succ, SuccessorType t -) { - exists(Reachability::SameSplitsBlock b, Completion c | pred = b.getAnElement() | - b.isReachable(succ, predSplits) and - t = getAMatchingSuccessorType(c) and - scopeLast(succ, pred, c) and - forall(SplitImpl predSplit | predSplit = predSplits.getASplit() | - predSplit.hasExitScope(succ, pred, c) - ) - ) -} - -/** - * Provides a predicate for the successor relation with split information, - * as well as logic used to construct the type `TSplits` representing sets - * of splits. Only sets of splits that can be reached are constructed, hence - * the predicates are mutually recursive. - * - * For the successor relation - * - * ```ql - * succSplits(ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, Completion c) - * ``` - * - * the following invariants are maintained: - * - * 1. `pred` is reachable with split set `predSplits`. - * 2. For all `split` in `predSplits`: - * - If `split.hasSuccessor(pred, succ, c)` then `split` in `succSplits`. - * 3. For all `split` in `predSplits`: - * - If `split.hasExit(pred, succ, c)` and not `split.hasEntry(pred, succ, c)` then - * `split` not in `succSplits`. - * 4. For all `split` with kind not in `predSplits`: - * - If `split.hasEntry(pred, succ, c)` then `split` in `succSplits`. - * 5. For all `split` in `succSplits`: - * - `split.hasSuccessor(pred, succ, c)` and `split` in `predSplits`, or - * - `split.hasEntry(pred, succ, c)`. - * - * The algorithm divides into four cases: - * - * 1. The set of splits for the successor is the same as the set of splits - * for the predecessor: - * a) The successor is in the same `SameSplitsBlock` as the predecessor. - * b) The successor is *not* in the same `SameSplitsBlock` as the predecessor. - * 2. The set of splits for the successor is different from the set of splits - * for the predecessor: - * a) The set of splits for the successor is *maybe* non-empty. - * b) The set of splits for the successor is *always* empty. - * - * Only case 2a may introduce new sets of splits, so only predicates from - * this case are used in the definition of `TSplits`. - * - * The predicates in this module are named after the cases above. - */ -private module SuccSplits { - private predicate succInvariant1( - Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, - ControlFlowElement succ, Completion c - ) { - pred = b.getAnElement() and - b.isReachable(_, predSplits) and - succ(pred, succ, c) - } - - private predicate case1b0( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - exists(Reachability::SameSplitsBlock b | - // Invariant 1 - succInvariant1(b, pred, predSplits, succ, c) - | - (succ = b.getAnElement() implies succ = b) and - // Invariant 4 - not exists(SplitImpl split | split.hasEntry(pred, succ, c)) - ) - } - - /** - * Case 1b. - * - * Invariants 1 and 4 hold in the base case, and invariants 2, 3, and 5 are - * maintained for all splits in `predSplits` (= `succSplits`), except - * possibly for the splits in `except`. - * - * The predicate is written using explicit recursion, as opposed to a `forall`, - * to avoid negative recursion. - */ - private predicate case1bForall( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except - ) { - case1b0(pred, predSplits, succ, c) and - except = predSplits - or - exists(SplitImpl split | - case1bForallCons(pred, predSplits, succ, c, split, except) and - split.hasSuccessor(pred, succ, c) - ) - } - - pragma[noinline] - private predicate case1bForallCons( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, - SplitImpl exceptHead, Splits exceptTail - ) { - case1bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) - } - - private predicate case1( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - // Case 1a - exists(Reachability::SameSplitsBlock b | succInvariant1(b, pred, predSplits, succ, c) | - succ = b.getAnElement() and - not succ = b - ) - or - // Case 1b - case1bForall(pred, predSplits, succ, c, TSplitsNil()) - } - - pragma[noinline] - private SplitImpl succInvariant1GetASplit( - Reachability::SameSplitsBlock b, ControlFlowElement pred, Splits predSplits, - ControlFlowElement succ, Completion c - ) { - succInvariant1(b, pred, predSplits, succ, c) and - result = predSplits.getASplit() - } - - private predicate case2aux( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - exists(Reachability::SameSplitsBlock b | - succInvariant1(b, pred, predSplits, succ, c) and - (succ = b.getAnElement() implies succ = b) - | - succInvariant1GetASplit(b, pred, predSplits, succ, c).hasExit(pred, succ, c) - or - any(SplitImpl split).hasEntry(pred, succ, c) - ) - } - - /** - * Holds if `succSplits` should not inherit a split of kind `sk` from - * `predSplits`, except possibly because of a split in `except`. - * - * The predicate is written using explicit recursion, as opposed to a `forall`, - * to avoid negative recursion. - */ - private predicate case2aNoneInheritedOfKindForall( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk, - Splits except - ) { - case2aux(pred, predSplits, succ, c) and - sk.appliesTo(succ) and - except = predSplits - or - exists(Splits mid, SplitImpl split | - case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, mid) and - mid = TSplitsCons(split, except) - | - split.getKind() = any(SplitKind sk0 | sk0 != sk and sk0.appliesTo(succ)) - or - split.hasExit(pred, succ, c) - ) - } - - pragma[nomagic] - private predicate entryOfKind( - ControlFlowElement pred, ControlFlowElement succ, Completion c, SplitImpl split, SplitKind sk - ) { - split.hasEntry(pred, succ, c) and - sk = split.getKind() - } - - /** Holds if `succSplits` should not have a split of kind `sk`. */ - pragma[nomagic] - private predicate case2aNoneOfKind( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk - ) { - // None inherited from predecessor - case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk, TSplitsNil()) and - // None newly entered into - not entryOfKind(pred, succ, c, _, sk) - } - - /** Holds if `succSplits` should not have a split of kind `sk` at rank `rnk`. */ - pragma[nomagic] - private predicate case2aNoneAtRank( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk - ) { - exists(SplitKind sk | case2aNoneOfKind(pred, predSplits, succ, c, sk) | - rnk = sk.getListRank(succ) - ) - } - - pragma[nomagic] - private SplitImpl case2auxGetAPredecessorSplit( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c - ) { - case2aux(pred, predSplits, succ, c) and - result = predSplits.getASplit() - } - - /** Gets a split that should be in `succSplits`. */ - pragma[nomagic] - private SplitImpl case2aSome( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, SplitKind sk - ) { - ( - // Inherited from predecessor - result = case2auxGetAPredecessorSplit(pred, predSplits, succ, c) and - result.hasSuccessor(pred, succ, c) - or - // Newly entered into - exists(SplitKind sk0 | - case2aNoneInheritedOfKindForall(pred, predSplits, succ, c, sk0, TSplitsNil()) - | - entryOfKind(pred, succ, c, result, sk0) - ) - ) and - sk = result.getKind() - } - - /** Gets a split that should be in `succSplits` at rank `rnk`. */ - pragma[nomagic] - SplitImpl case2aSomeAtRank( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk - ) { - exists(SplitKind sk | result = case2aSome(pred, predSplits, succ, c, sk) | - rnk = sk.getListRank(succ) - ) - } - - /** - * Case 2a. - * - * As opposed to the other cases, in this case we need to construct a new set - * of splits `succSplits`. Since this involves constructing the very IPA type, - * we cannot recurse directly over the structure of `succSplits`. Instead, we - * recurse over the ranks of all splits that *might* be in `succSplits`. - * - * - Invariant 1 holds in the base case, - * - invariant 2 holds for all splits with rank at least `rnk`, - * - invariant 3 holds for all splits in `predSplits`, - * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`, - * and - * - invariant 4 holds for all splits in `succSplits` with rank at least `rnk`. - */ - predicate case2aFromRank( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c, int rnk - ) { - case2aux(pred, predSplits, succ, c) and - succSplits = TSplitsNil() and - rnk = max(any(SplitKind sk).getListRank(succ)) + 1 - or - case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and - case2aNoneAtRank(pred, predSplits, succ, c, rnk) - or - exists(Splits mid, SplitImpl split | split = case2aCons(pred, predSplits, succ, mid, c, rnk) | - succSplits = TSplitsCons(split, mid) - ) - } - - pragma[noinline] - private SplitImpl case2aCons( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c, int rnk - ) { - case2aFromRank(pred, predSplits, succ, succSplits, c, rnk + 1) and - result = case2aSomeAtRank(pred, predSplits, succ, c, rnk) - } - - /** - * Case 2b. - * - * Invariants 1, 4, and 5 hold in the base case, and invariants 2 and 3 are - * maintained for all splits in `predSplits`, except possibly for the splits - * in `except`. - * - * The predicate is written using explicit recursion, as opposed to a `forall`, - * to avoid negative recursion. - */ - private predicate case2bForall( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, Splits except - ) { - // Invariant 1 - case2aux(pred, predSplits, succ, c) and - // Invariants 4 and 5 - not any(SplitKind sk).appliesTo(succ) and - except = predSplits - or - exists(SplitImpl split | case2bForallCons(pred, predSplits, succ, c, split, except) | - // Invariants 2 and 3 - split.hasExit(pred, succ, c) - ) - } - - pragma[noinline] - private predicate case2bForallCons( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, - SplitImpl exceptHead, Splits exceptTail - ) { - case2bForall(pred, predSplits, succ, c, TSplitsCons(exceptHead, exceptTail)) - } - - private predicate case2( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c - ) { - case2aFromRank(pred, predSplits, succ, succSplits, c, 1) - or - case2bForall(pred, predSplits, succ, c, TSplitsNil()) and - succSplits = TSplitsNil() - } - - /** - * Holds if `succ` with splits `succSplits` is a successor of type `t` for `pred` - * with splits `predSplits`. - */ - predicate succSplits( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - Completion c - ) { - case1(pred, predSplits, succ, c) and - succSplits = predSplits - or - case2(pred, predSplits, succ, succSplits, c) - } -} - -import SuccSplits - -/** Provides logic for calculating reachable control flow nodes. */ -private module Reachability { - /** - * Holds if `cfe` is a control flow element where the set of possible splits may - * be different from the set of possible splits for one of `cfe`'s predecessors. - * That is, `cfe` starts a new block of elements with the same set of splits. - */ - private predicate startsSplits(ControlFlowElement cfe) { - scopeFirst(_, cfe) - or - exists(SplitImpl s | - s.hasEntry(_, cfe, _) - or - s.hasExit(_, cfe, _) - ) - or - exists(ControlFlowElement pred, SplitImpl split, Completion c | succ(pred, cfe, c) | - split.appliesTo(pred) and - not split.hasSuccessor(pred, cfe, c) - ) - } - - private predicate intraSplitsSucc(ControlFlowElement pred, ControlFlowElement succ) { - succ(pred, succ, _) and - not startsSplits(succ) - } - - private predicate splitsBlockContains(ControlFlowElement start, ControlFlowElement cfe) = - fastTC(intraSplitsSucc/2)(start, cfe) - - /** - * A block of control flow elements where the set of splits is guaranteed - * to remain unchanged, represented by the first element in the block. - */ - class SameSplitsBlock extends ControlFlowElement { - SameSplitsBlock() { startsSplits(this) } - - /** Gets a control flow element in this block. */ - ControlFlowElement getAnElement() { - splitsBlockContains(this, result) - or - result = this - } - - pragma[noinline] - private SameSplitsBlock getASuccessor(Splits predSplits, Splits succSplits) { - exists(ControlFlowElement pred | pred = this.getAnElement() | - succSplits(pred, predSplits, result, succSplits, _) - ) - } - - /** - * Holds if the elements of this block are reachable from a callable entry - * point, with the splits `splits`. - */ - predicate isReachable(CfgScope scope, Splits splits) { - // Base case - succEntrySplits(scope, this, splits, _) - or - // Recursive case - exists(SameSplitsBlock pred, Splits predSplits | pred.isReachable(scope, predSplits) | - this = pred.getASuccessor(predSplits, splits) - ) - } - } -} - -cached -private module Cached { - /** - * If needed, call this predicate from `ControlFlowGraphImplSpecific.qll` in order to - * force a stage-dependency on the `ControlFlowGraphImplShared.qll` stage and thereby - * collapsing the two stages. - */ - cached - predicate forceCachingInSameStage() { any() } - - cached - newtype TSplits = - TSplitsNil() or - TSplitsCons(SplitImpl head, Splits tail) { - exists( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Completion c, int rnk - | - case2aFromRank(pred, predSplits, succ, tail, c, rnk + 1) and - head = case2aSomeAtRank(pred, predSplits, succ, c, rnk) - ) - or - succEntrySplitsCons(_, _, head, tail, _) - } - - cached - string splitsToString(Splits splits) { - splits = TSplitsNil() and - result = "" - or - exists(SplitImpl head, Splits tail, string headString, string tailString | - splits = TSplitsCons(head, tail) - | - headString = head.toString() and - tailString = tail.toString() and - if tailString = "" - then result = headString - else - if headString = "" - then result = tailString - else result = headString + ", " + tailString - ) - } - - /** - * Internal representation of control flow nodes in the control flow graph. - * The control flow graph is pruned for unreachable nodes. - */ - cached - newtype TCfgNode = - TEntryNode(CfgScope scope) { succEntrySplits(scope, _, _, _) } or - TAnnotatedExitNode(CfgScope scope, boolean normal) { - exists(Reachability::SameSplitsBlock b, SuccessorType t | b.isReachable(scope, _) | - succExitSplits(b.getAnElement(), _, scope, t) and - if isAbnormalExitType(t) then normal = false else normal = true - ) - } or - TExitNode(CfgScope scope) { - exists(Reachability::SameSplitsBlock b | b.isReachable(scope, _) | - succExitSplits(b.getAnElement(), _, scope, _) - ) - } or - TElementNode(CfgScope scope, ControlFlowElement cfe, Splits splits) { - exists(Reachability::SameSplitsBlock b | b.isReachable(scope, splits) | - cfe = b.getAnElement() - ) - } - - /** Gets a successor node of a given flow type, if any. */ - cached - TCfgNode getASuccessor(TCfgNode pred, SuccessorType t) { - // Callable entry node -> callable body - exists(ControlFlowElement succElement, Splits succSplits, CfgScope scope | - result = TElementNode(scope, succElement, succSplits) and - pred = TEntryNode(scope) and - succEntrySplits(scope, succElement, succSplits, t) - ) - or - exists(CfgScope scope, ControlFlowElement predElement, Splits predSplits | - pred = TElementNode(pragma[only_bind_into](scope), predElement, predSplits) - | - // Element node -> callable exit (annotated) - exists(boolean normal | - result = TAnnotatedExitNode(pragma[only_bind_into](scope), normal) and - succExitSplits(predElement, predSplits, scope, t) and - if isAbnormalExitType(t) then normal = false else normal = true - ) - or - // Element node -> element node - exists(ControlFlowElement succElement, Splits succSplits, Completion c | - result = TElementNode(pragma[only_bind_into](scope), succElement, succSplits) - | - succSplits(predElement, predSplits, succElement, succSplits, c) and - t = getAMatchingSuccessorType(c) - ) - ) - or - // Callable exit (annotated) -> callable exit - exists(CfgScope scope | - pred = TAnnotatedExitNode(scope, _) and - result = TExitNode(scope) and - successorTypeIsSimple(t) - ) - } - - /** - * Gets a first control flow element executed within `cfe`. - */ - cached - ControlFlowElement getAControlFlowEntryNode(ControlFlowElement cfe) { first(cfe, result) } - - /** - * Gets a potential last control flow element executed within `cfe`. - */ - cached - ControlFlowElement getAControlFlowExitNode(ControlFlowElement cfe) { last(cfe, result, _) } - - /** - * Gets the CFG scope of node `n`. Unlike `getCfgScope`, this predicate - * is calculated based on reachability from an entry node, and it may - * yield different results for AST elements that are split into multiple - * scopes. - */ - cached - CfgScope getNodeCfgScope(TCfgNode n) { - n = TEntryNode(result) - or - n = TAnnotatedExitNode(result, _) - or - n = TExitNode(result) - or - n = TElementNode(result, _, _) - } -} - -import Cached - -/** - * Import this module into a `.ql` file of `@kind graph` to render a CFG. The - * graph is restricted to nodes from `RelevantNode`. - */ -module TestOutput { - abstract class RelevantNode extends Node { - /** - * Gets a string used to resolve ties in node and edge ordering. - */ - string getOrderDisambiguation() { result = "" } - } - - query predicate nodes(RelevantNode n, string attr, string val) { - attr = "semmle.order" and - val = - any(int i | - n = - rank[i](RelevantNode p, Location l | - l = p.getLocation() - | - p - order by - l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), p.toString(), - p.getOrderDisambiguation() - ) - ).toString() - } - - query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) { - attr = "semmle.label" and - val = - strictconcat(SuccessorType t, string s | - succ = getASuccessor(pred, t) and - if successorTypeIsSimple(t) then s = "" else s = t.toString() - | - s, ", " order by s - ) - or - attr = "semmle.order" and - val = - any(int i | - succ = - rank[i](RelevantNode s, SuccessorType t, Location l | - s = getASuccessor(pred, t) and - l = s.getLocation() - | - s - order by - l.getFile().getBaseName(), l.getFile().getAbsolutePath(), l.getStartLine(), - l.getStartColumn(), l.getEndLine(), l.getEndColumn(), t.toString(), s.toString(), - s.getOrderDisambiguation() - ) - ).toString() - } -} - -/** Provides a set of splitting-related consistency queries. */ -module Consistency { - query predicate nonUniqueSetRepresentation(Splits s1, Splits s2) { - forex(Split s | s = s1.getASplit() | s = s2.getASplit()) and - forex(Split s | s = s2.getASplit() | s = s1.getASplit()) and - s1 != s2 - } - - query predicate breakInvariant2( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split = predSplits.getASplit() and - split.hasSuccessor(pred, succ, c) and - not split = succSplits.getASplit() - } - - query predicate breakInvariant3( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split = predSplits.getASplit() and - split.hasExit(pred, succ, c) and - not split.hasEntry(pred, succ, c) and - split = succSplits.getASplit() - } - - query predicate breakInvariant4( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split.hasEntry(pred, succ, c) and - not split.getKind() = predSplits.getASplit().getKind() and - not split = succSplits.getASplit() and - split.getKind().isEnabled(succ) - } - - query predicate breakInvariant5( - ControlFlowElement pred, Splits predSplits, ControlFlowElement succ, Splits succSplits, - SplitImpl split, Completion c - ) { - succSplits(pred, predSplits, succ, succSplits, c) and - split = succSplits.getASplit() and - not (split.hasSuccessor(pred, succ, c) and split = predSplits.getASplit()) and - not split.hasEntry(pred, succ, c) - } - - private class SimpleSuccessorType extends SuccessorType { - SimpleSuccessorType() { - this = getAMatchingSuccessorType(any(Completion c | completionIsSimple(c))) - } - } - - private class NormalSuccessorType extends SuccessorType { - NormalSuccessorType() { - this = getAMatchingSuccessorType(any(Completion c | completionIsNormal(c))) - } - } - - query predicate multipleSuccessors(Node node, SuccessorType t, Node successor) { - strictcount(getASuccessor(node, t)) > 1 and - successor = getASuccessor(node, t) and - // allow for functions with multiple bodies - not (t instanceof SimpleSuccessorType and node instanceof TEntryNode) - } - - query predicate simpleAndNormalSuccessors( - Node node, NormalSuccessorType t1, SimpleSuccessorType t2, Node succ1, Node succ2 - ) { - t1 != t2 and - succ1 = getASuccessor(node, t1) and - succ2 = getASuccessor(node, t2) - } - - query predicate deadEnd(Node node) { - not node instanceof TExitNode and - not exists(getASuccessor(node, _)) - } - - query predicate nonUniqueSplitKind(SplitImpl split, SplitKind sk) { - sk = split.getKind() and - strictcount(split.getKind()) > 1 - } - - query predicate nonUniqueListOrder(SplitKind sk, int ord) { - ord = sk.getListOrder() and - strictcount(sk.getListOrder()) > 1 - } -} diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplSpecific.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplSpecific.qll deleted file mode 100644 index 9c3f5276dd9f7..0000000000000 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/ControlFlowGraphImplSpecific.qll +++ /dev/null @@ -1,69 +0,0 @@ -private import csharp as CS -private import ControlFlowGraphImpl as Impl -private import Completion as Comp -private import Splitting as Splitting -private import SuccessorType as ST -private import semmle.code.csharp.Caching - -class ControlFlowTreeBase = Impl::ControlFlowTree::Range; - -class ControlFlowElement = CS::ControlFlowElement; - -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 CfgScope = Impl::CfgScope; - -/** Gets the CFG scope for `e`. */ -CfgScope getCfgScope(ControlFlowElement e) { - Stages::ControlFlowStage::forceCachingInSameStage() and - result = e.getEnclosingCallable() -} - -predicate scopeFirst = Impl::scopeFirst/2; - -predicate scopeLast = Impl::scopeLast/3; - -/** The maximum number of splits allowed for a given node. */ -int maxSplits() { result = 5 } - -class SplitKindBase = Splitting::TSplitKind; - -class Split = Splitting::Split; - -class SuccessorType = ST::SuccessorType; - -/** Gets a successor type that matches completion `c`. */ -SuccessorType getAMatchingSuccessorType(Completion c) { result = c.getAMatchingSuccessorType() } - -/** - * Hold if `c` represents simple (normal) evaluation of a statement or an - * expression. - */ -predicate successorTypeIsSimple(SuccessorType t) { - t instanceof ST::SuccessorTypes::NormalSuccessor -} - -/** Holds if `t` is an abnormal exit type out of a callable. */ -predicate isAbnormalExitType(SuccessorType t) { - t instanceof ST::SuccessorTypes::ExceptionSuccessor or - t instanceof ST::SuccessorTypes::ExitSuccessor -} - -class Location = CS::Location; - -class Node = CS::ControlFlow::Node; diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll index 095c4e69498fd..6a507fb8014a5 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/PreBasicBlocks.qll @@ -12,7 +12,7 @@ import csharp private import Completion private import ControlFlowGraphImpl -private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow +private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg private predicate startsBB(ControlFlowElement cfe) { not succ(_, cfe, _) and @@ -55,7 +55,7 @@ private predicate bbIDominates(PreBasicBlock dom, PreBasicBlock bb) = class PreBasicBlock extends ControlFlowElement { PreBasicBlock() { startsBB(this) } - PreBasicBlock getASuccessorByType(SuccessorType t) { + PreBasicBlock getASuccessorByType(Cfg::SuccessorType t) { succ(this.getLastElement(), result, any(Completion c | t = c.getAMatchingSuccessorType())) } @@ -116,7 +116,7 @@ class ConditionBlock extends PreBasicBlock { } pragma[nomagic] - predicate controls(PreBasicBlock controlled, SuccessorTypes::ConditionalSuccessor s) { + predicate controls(PreBasicBlock controlled, Cfg::SuccessorTypes::ConditionalSuccessor s) { exists(PreBasicBlock succ, ConditionalCompletion c | this.immediatelyControls(succ, c) | succ.dominates(controlled) and s = c.getAMatchingSuccessorType() diff --git a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll index f7c84dbac5fe1..416fefdfbb6f3 100644 --- a/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll +++ b/csharp/ql/lib/semmle/code/csharp/controlflow/internal/Splitting.qll @@ -7,8 +7,7 @@ import csharp private import Completion private import ControlFlowGraphImpl -private import SuccessorTypes -private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow +private import semmle.code.csharp.controlflow.ControlFlowGraph::ControlFlow as Cfg private import semmle.code.csharp.controlflow.internal.PreSsa cached @@ -83,7 +82,7 @@ module InitializerSplitting { * Gets a control flow element that is a syntactic descendant of the * initializer expression. */ - ControlFlowElement getAnInitializerDescendant() { + AstNode getAnInitializerDescendant() { result = this.getInitializer() or result = this.getAnInitializerDescendant().getAChild() @@ -193,49 +192,49 @@ module InitializerSplitting { private class InitializerSplitKind extends SplitKind, TInitializerSplitKind { override int getListOrder() { result = 0 } - override predicate isEnabled(ControlFlowElement cfe) { this.appliesTo(cfe) } + override predicate isEnabled(AstNode cfe) { this.appliesTo(cfe) } override string toString() { result = "Initializer" } } int getNextListOrder() { result = 1 } - private class InitializerSplitImpl extends SplitImpl, InitializerSplit { + private class InitializerSplitImpl extends SplitImpl instanceof InitializerSplit { override InitializerSplitKind getKind() { any() } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { exists(ConstructorInitializer ci | last(ci, pred, c) and succ(pred, succ, c) and succ = any(InitializedInstanceMember m).getAnInitializerDescendant() and - this.getConstructor() = ci.getConstructor() + super.getConstructor() = ci.getConstructor() ) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { + override predicate hasEntryScope(CfgScope scope, AstNode first) { scopeFirst(scope, first) and - scope = this.getConstructor() and + scope = super.getConstructor() and first = any(InitializedInstanceMember m).getAnInitializerDescendant() } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { this.appliesTo(pred) and succ(pred, succ, c) and not succ = any(InitializedInstanceMember m).getAnInitializerDescendant() and - succ.getEnclosingCallable() = this.getConstructor() + succ.(ControlFlowElement).getEnclosingCallable() = super.getConstructor() } - override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesTo(last) and scopeLast(scope, last, c) and - scope = this.getConstructor() + scope = super.getConstructor() } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { this.appliesSucc(pred, succ, c) and succ = any(InitializedInstanceMember m | - constructorInitializes(this.getConstructor(), m.getInitializer()) + constructorInitializes(super.getConstructor(), m.getInitializer()) ).getAnInitializerDescendant() } } @@ -267,17 +266,22 @@ module ConditionalCompletionSplitting { private class ConditionalCompletionSplitKind extends SplitKind, TConditionalCompletionSplitKind { override int getListOrder() { result = InitializerSplitting::getNextListOrder() } - override predicate isEnabled(ControlFlowElement cfe) { this.appliesTo(cfe) } + override predicate isEnabled(AstNode cfe) { this.appliesTo(cfe) } override string toString() { result = "ConditionalCompletion" } } int getNextListOrder() { result = InitializerSplitting::getNextListOrder() + 1 } - private class ConditionalCompletionSplitImpl extends SplitImpl, ConditionalCompletionSplit { + private class ConditionalCompletionSplitImpl extends SplitImpl instanceof ConditionalCompletionSplit + { + ConditionalCompletion completion; + + ConditionalCompletionSplitImpl() { this = TConditionalCompletionSplit(completion) } + override ConditionalCompletionSplitKind getKind() { any() } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { succ(pred, succ, c) and last(succ, _, completion) and ( @@ -334,23 +338,21 @@ module ConditionalCompletionSplitting { ) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { none() } + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode 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, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesTo(last) and scopeLast(scope, last, c) and if c instanceof ConditionalCompletion then completion = c else any() } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - none() - } + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { none() } } } @@ -358,7 +360,7 @@ module AssertionSplitting { import semmle.code.csharp.commons.Assertions private import semmle.code.csharp.ExprOrStmtParent - private ControlFlowElement getAnAssertionDescendant(Assertion a) { + private AstNode getAnAssertionDescendant(Assertion a) { result = a or result = getAnAssertionDescendant(a).getAChild() @@ -401,17 +403,23 @@ module AssertionSplitting { private class AssertionSplitKind extends SplitKind, TAssertionSplitKind { override int getListOrder() { result = ConditionalCompletionSplitting::getNextListOrder() } - override predicate isEnabled(ControlFlowElement cfe) { this.appliesTo(cfe) } + override predicate isEnabled(AstNode cfe) { this.appliesTo(cfe) } override string toString() { result = "Assertion" } } int getNextListOrder() { result = ConditionalCompletionSplitting::getNextListOrder() + 1 } - private class AssertionSplitImpl extends SplitImpl, AssertionSplit { + private class AssertionSplitImpl extends SplitImpl instanceof AssertionSplit { + Assertion a; + boolean success; + int i; + + AssertionSplitImpl() { this = TAssertionSplit(a, i, success) } + override AssertionSplitKind getKind() { any() } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { exists(AssertMethod m | last(a.getExpr(i), pred, c) and succ(pred, succ, c) and @@ -440,9 +448,9 @@ module AssertionSplitting { ) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { none() } + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { this.appliesTo(pred) and pred = a and succ(pred, succ, c) and @@ -455,7 +463,7 @@ module AssertionSplitting { ) } - override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesTo(last) and last = a and scopeLast(scope, last, c) and @@ -468,7 +476,7 @@ module AssertionSplitting { ) } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { this.appliesSucc(pred, succ, c) and succ = getAnAssertionDescendant(a) } @@ -484,8 +492,8 @@ module FinallySplitting { * then the `finally` block must end with a `return` as well (provided that * the `finally` block exits normally). */ - class FinallySplitType extends SuccessorType { - FinallySplitType() { not this instanceof ConditionalSuccessor } + class FinallySplitType extends Cfg::SuccessorType { + FinallySplitType() { not this instanceof Cfg::SuccessorTypes::ConditionalSuccessor } /** Holds if this split type matches entry into a `finally` block with completion `c`. */ predicate isSplitForEntryCompletion(Completion c) { @@ -493,22 +501,22 @@ module FinallySplitting { then // If the entry into the `finally` block completes with any normal completion, // it simply means normal execution after the `finally` block - this instanceof NormalSuccessor + this instanceof Cfg::SuccessorTypes::NormalSuccessor else this = c.getAMatchingSuccessorType() } } /** A control flow element that belongs to a `finally` block. */ - private class FinallyControlFlowElement extends ControlFlowElement { + private class FinallyAstNode extends AstNode { private Statements::TryStmtTree try; - FinallyControlFlowElement() { this = try.getAFinallyDescendant() } + FinallyAstNode() { this = try.getAFinallyDescendant() } /** Gets the immediate `try` block that this node belongs to. */ Statements::TryStmtTree getTryStmt() { result = try } /** Holds if this node is the entry node in the `finally` block it belongs to. */ - predicate isEntryNode() { first(try.getFinally(), this) } + predicate isEntryNode() { first(try.(TryStmt).getFinally(), this) } } /** @@ -547,7 +555,7 @@ module FinallySplitting { int getNestLevel() { result = nestLevel } override string toString() { - if type instanceof NormalSuccessor + if type instanceof Cfg::SuccessorTypes::NormalSuccessor then result = "" else if nestLevel > 0 @@ -577,38 +585,34 @@ module FinallySplitting { override string toString() { result = "Finally (" + nestLevel + ")" } } - pragma[noinline] - private predicate hasEntry0( - ControlFlowElement pred, FinallyControlFlowElement succ, int nestLevel, Completion c - ) { + pragma[nomagic] + private predicate hasEntry0(AstNode pred, FinallyAstNode succ, int nestLevel, Completion c) { succ.isEntryNode() and nestLevel = succ.getTryStmt().nestLevel() and succ(pred, succ, c) } - private class FinallySplitImpl extends SplitImpl, FinallySplit { - override FinallySplitKind getKind() { result.getNestLevel() = this.getNestLevel() } + private class FinallySplitImpl extends SplitImpl instanceof FinallySplit { + override FinallySplitKind getKind() { result.getNestLevel() = super.getNestLevel() } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - hasEntry0(pred, succ, this.getNestLevel(), c) and - this.getType().isSplitForEntryCompletion(c) + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { + hasEntry0(pred, succ, super.getNestLevel(), c) and + super.getType().isSplitForEntryCompletion(c) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { none() } + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } /** * Holds if this split applies to control flow element `pred`, where `pred` * is a valid predecessor. */ - private predicate appliesToPredecessor(ControlFlowElement pred) { + private predicate appliesToPredecessor(AstNode pred) { this.appliesTo(pred) and (succ(pred, _, _) or scopeLast(_, pred, _)) } pragma[noinline] - private predicate exit0( - ControlFlowElement pred, Statements::TryStmtTree try, int nestLevel, Completion c - ) { + private predicate exit0(AstNode pred, Statements::TryStmtTree try, int nestLevel, Completion c) { this.appliesToPredecessor(pred) and nestLevel = try.nestLevel() and last(try, pred, c) @@ -619,10 +623,10 @@ module FinallySplitting { * `inherited` indicates whether `c` is an inherited completion from a `try`/ * `catch` block. */ - private predicate exit(ControlFlowElement pred, Completion c, boolean inherited) { + private predicate exit(AstNode pred, Completion c, boolean inherited) { exists(TryStmt try, FinallySplitType type | - this.exit0(pred, try, this.getNestLevel(), c) and - type = this.getType() + this.exit0(pred, try, super.getNestLevel(), c) and + type = super.getType() | if last(try.getFinally(), pred, c) then @@ -635,14 +639,14 @@ module FinallySplitting { or not c instanceof NormalCompletion or - type instanceof NormalSuccessor + type instanceof Cfg::SuccessorTypes::NormalSuccessor ) else ( // Finally block can exit with completion `c` inherited from try/catch // block: must match this split inherited = true and type = c.getAMatchingSuccessorType() and - not type instanceof NormalSuccessor + not type instanceof Cfg::SuccessorTypes::NormalSuccessor ) ) or @@ -672,15 +676,15 @@ module FinallySplitting { // is "normal" (corresponding to `b1 = true` and `b2 = false`), then the inner // split must be able to exit with an `ExceptionA` completion. this.appliesToPredecessor(pred) and - exists(FinallySplitImpl outer | - outer.getNestLevel() = this.getNestLevel() - 1 and - outer.exit(pred, c, inherited) and - this.getType() instanceof NormalSuccessor and + exists(FinallySplit outer | + outer.getNestLevel() = super.getNestLevel() - 1 and + outer.(FinallySplitImpl).exit(pred, c, inherited) and + super.getType() instanceof Cfg::SuccessorTypes::NormalSuccessor and inherited = true ) } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { succ(pred, succ, c) and ( this.exit(pred, c, _) @@ -689,7 +693,7 @@ module FinallySplitting { ) } - override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { scopeLast(scope, last, c) and ( this.exit(last, c, _) @@ -698,17 +702,17 @@ module FinallySplitting { ) } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { this.appliesSucc(pred, succ, c) and succ = - any(FinallyControlFlowElement fcfe | + any(FinallyAstNode fcfe | if fcfe.isEntryNode() then // entering a nested `finally` block - fcfe.getTryStmt().nestLevel() > this.getNestLevel() + fcfe.getTryStmt().nestLevel() > super.getNestLevel() else // staying in the same (possibly nested) `finally` block as `pred` - fcfe.getTryStmt().nestLevel() >= this.getNestLevel() + fcfe.getTryStmt().nestLevel() >= super.getNestLevel() ) } } @@ -774,20 +778,20 @@ module ExceptionHandlerSplitting { int getNextListOrder() { result = FinallySplitting::getNextListOrder() + 1 } - private class ExceptionHandlerSplitImpl extends SplitImpl, ExceptionHandlerSplit { + private class ExceptionHandlerSplitImpl extends SplitImpl instanceof ExceptionHandlerSplit { override ExceptionHandlerSplitKind getKind() { any() } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { // Entry into first catch clause exists(Statements::TryStmtTree ts | - this.getExceptionClass() = ts.getAThrownException(pred, c) + super.getExceptionClass() = ts.getAThrownException(pred, c) | succ(pred, succ, c) and - succ = ts.getCatchClause(0).(SpecificCatchClause) + succ = ts.(TryStmt).getCatchClause(0).(SpecificCatchClause) ) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { none() } + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } /** * Holds if this split applies to catch clause `scc`. The parameter `match` @@ -796,9 +800,9 @@ module ExceptionHandlerSplitting { */ private predicate appliesToCatchClause(SpecificCatchClause scc, TMatch match) { exists(Statements::TryStmtTree ts, ExceptionClass ec | - ec = this.getExceptionClass() and + ec = super.getExceptionClass() and ec = ts.getAThrownException(_, _) and - scc = ts.getACatchClause() + scc = ts.(TryStmt).getACatchClause() | if scc.getCaughtExceptionType() = ec.getABaseType*() then match = TAlways() @@ -813,7 +817,7 @@ module ExceptionHandlerSplitting { * Holds if this split applies to control flow element `pred`, where `pred` * is a valid predecessor with completion `c`. */ - private predicate appliesToPredecessor(ControlFlowElement pred, Completion c) { + private predicate appliesToPredecessor(AstNode pred, Completion c) { this.appliesTo(pred) and (succ(pred, _, c) or scopeLast(_, pred, c)) and ( @@ -843,18 +847,18 @@ module ExceptionHandlerSplitting { * with throw completion `c`, because it belongs to the last `catch` clause * in a `try` statement. */ - private predicate hasLastExit(ControlFlowElement pred, ThrowCompletion c) { + private predicate hasLastExit(AstNode pred, ThrowCompletion c) { this.appliesToPredecessor(pred, c) and exists(TryStmt ts, SpecificCatchClause scc, int last | last(ts.getCatchClause(last), pred, c) | ts.getCatchClause(last) = scc and scc.isLast() and - c.getExceptionClass() = this.getExceptionClass() + c.getExceptionClass() = super.getExceptionClass() ) } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { this.appliesToPredecessor(pred, c) and succ(pred, succ, c) and ( @@ -869,13 +873,13 @@ module ExceptionHandlerSplitting { ) } - override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { // Exit out from last `catch` clause (no catch clauses match) this.hasLastExit(last, c) and scopeLast(scope, last, c) } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { this.appliesToPredecessor(pred, c) and this.appliesSucc(pred, succ, c) and not first(any(SpecificCatchClause scc).getBlock(), succ) and @@ -916,7 +920,7 @@ module BooleanSplitting { abstract predicate correlatesConditions(ConditionBlock cb1, ConditionBlock cb2, boolean inverted); /** Holds if control flow element `cfe` starts a split of this kind. */ - predicate startsSplit(ControlFlowElement cfe) { + predicate startsSplit(AstNode cfe) { this.correlatesConditions(any(ConditionBlock cb | cb.getLastElement() = cfe), _, _) } @@ -1078,27 +1082,26 @@ module BooleanSplitting { override string toString() { result = kind.toString() } } - pragma[noinline] + pragma[nomagic] private predicate hasEntry0( - ControlFlowElement pred, ControlFlowElement succ, BooleanSplitSubKind kind, boolean b, - Completion c + AstNode pred, AstNode succ, BooleanSplitSubKind kind, boolean b, Completion c ) { kind.startsSplit(pred) and succ(pred, succ, c) and b = c.getInnerCompletion().(BooleanCompletion).getValue() } - private class BooleanSplitImpl extends SplitImpl, BooleanSplit { - override BooleanSplitKind getKind() { result.getSubKind() = this.getSubKind() } + private class BooleanSplitImpl extends SplitImpl instanceof BooleanSplit { + override BooleanSplitKind getKind() { result.getSubKind() = super.getSubKind() } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { - hasEntry0(pred, succ, this.getSubKind(), this.getBranch(), c) + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { + hasEntry0(pred, succ, super.getSubKind(), super.getBranch(), c) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { none() } + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } private ConditionBlock getACorrelatedCondition(boolean inverted) { - this.getSubKind().correlatesConditions(_, result, inverted) + super.getSubKind().correlatesConditions(_, result, inverted) } /** @@ -1107,35 +1110,35 @@ module BooleanSplitting { */ private predicate appliesToBlock(PreBasicBlock bb, Completion c) { this.appliesTo(bb) and - exists(ControlFlowElement last | last = bb.getLastElement() | + exists(AstNode last | last = bb.getLastElement() | (succ(last, _, c) or scopeLast(_, last, c)) and // Respect the value recorded in this split for all correlated conditions forall(boolean inverted | bb = this.getACorrelatedCondition(inverted) | c.getInnerCompletion() instanceof BooleanCompletion implies c.getInnerCompletion().(BooleanCompletion).getValue() = - this.getBranch().booleanXor(inverted) + super.getBranch().booleanXor(inverted) ) ) } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { exists(PreBasicBlock bb | this.appliesToBlock(bb, c) | pred = bb.getLastElement() and succ(pred, succ, c) and // Exit this split if we can no longer reach a correlated condition - not this.getSubKind().canReachCorrelatedCondition(succ) + not super.getSubKind().canReachCorrelatedCondition(succ) ) } - override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { exists(PreBasicBlock bb | this.appliesToBlock(bb, c) | last = bb.getLastElement() and scopeLast(scope, last, c) ) } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { exists(PreBasicBlock bb, Completion c0 | this.appliesToBlock(bb, c0) | pred = bb.getAnElement() and this.appliesSucc(pred, succ, c) and @@ -1144,7 +1147,7 @@ module BooleanSplitting { implies ( // We must still be able to reach a correlated condition to stay in this split - this.getSubKind().canReachCorrelatedCondition(succ) and + super.getSubKind().canReachCorrelatedCondition(succ) and c = c0 ) ) @@ -1174,15 +1177,15 @@ module LoopSplitting { */ abstract class AnalyzableLoopStmt extends LoopStmt { /** Holds if the step `pred --c--> succ` should start the split. */ - abstract predicate start(ControlFlowElement pred, ControlFlowElement succ, Completion c); + abstract predicate start(AstNode pred, AstNode succ, Completion c); /** Holds if the step `pred --c--> succ` should stop the split. */ - abstract predicate stop(ControlFlowElement pred, ControlFlowElement succ, Completion c); + abstract predicate stop(AstNode pred, AstNode succ, Completion c); /** * Holds if any step `pred --c--> _` should be pruned from the control flow graph. */ - abstract predicate pruneLoopCondition(ControlFlowElement pred, ConditionalCompletion c); + abstract predicate pruneLoopCondition(AstNode pred, ConditionalCompletion c); /** * Holds if the body is guaranteed to be executed at least once. If not, the @@ -1219,17 +1222,17 @@ module LoopSplitting { ) } - override predicate start(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate start(AstNode pred, AstNode succ, Completion c) { last(this.getIterableExpr(), pred, c) and succ = this } - override predicate stop(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate stop(AstNode pred, AstNode succ, Completion c) { pred = this and succ(pred, succ, c) } - override predicate pruneLoopCondition(ControlFlowElement pred, ConditionalCompletion c) { + override predicate pruneLoopCondition(AstNode pred, ConditionalCompletion c) { pred = this and c = any(EmptinessCompletion ec | if v.isEmpty() then not ec.isEmpty() else ec.isEmpty()) } @@ -1298,36 +1301,40 @@ module LoopSplitting { override string toString() { result = "Unroll" } } - private class LoopUnrollingSplitImpl extends SplitImpl, LoopSplit { + private class LoopUnrollingSplitImpl extends SplitImpl instanceof LoopSplit { + AnalyzableLoopStmt loop; + + LoopUnrollingSplitImpl() { this = TLoopSplit(loop) } + override LoopSplitKind getKind() { result = TLoopSplitKind(loop) } - override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasEntry(AstNode pred, AstNode succ, Completion c) { loop.start(pred, succ, c) } - override predicate hasEntryScope(CfgScope scope, ControlFlowElement first) { none() } + override predicate hasEntryScope(CfgScope scope, AstNode first) { none() } /** * Holds if this split applies to control flow element `pred`, where `pred` * is a valid predecessor. */ - private predicate appliesToPredecessor(ControlFlowElement pred, Completion c) { + private predicate appliesToPredecessor(AstNode pred, Completion c) { this.appliesTo(pred) and (succ(pred, _, c) or scopeLast(_, pred, c)) and not loop.pruneLoopCondition(pred, c) } - override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasExit(AstNode pred, AstNode succ, Completion c) { this.appliesToPredecessor(pred, c) and loop.stop(pred, succ, c) } - override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) { + override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) { this.appliesToPredecessor(last, c) and scopeLast(scope, last, c) } - override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) { + override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { this.appliesToPredecessor(pred, c) and this.appliesSucc(pred, succ, c) and not loop.stop(pred, succ, c) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll index e692721f0581a..2afa2936048dd 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/SSA.qll @@ -411,7 +411,7 @@ module Ssa { * This is either an expression, for example `x = 0`, a parameter, or a * callable. Phi nodes have no associated syntax element. */ - Element getElement() { result = this.getControlFlowNode().getElement() } + Element getElement() { result = this.getControlFlowNode().getAstNode() } /** Gets the callable to which this SSA definition belongs. */ final Callable getEnclosingCallable() { diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll index a83e91a9c4645..30b5a366ba55b 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/ControlFlowReachability.qll @@ -24,11 +24,11 @@ private ControlFlowElement getANonExactScopeChild(ControlFlowScope scope) { pragma[noinline] private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) { - result.getANode().getElement() = getANonExactScopeChild(scope) and + result.getANode().getAstNode() = getANonExactScopeChild(scope) and exactScope = false or scope.isExact() and - result.getANode().getElement() = scope and + result.getANode().getAstNode() = scope and exactScope = true } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll index 339965785975d..9f6ff2ce17ff8 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowDispatch.qll @@ -104,10 +104,10 @@ private module Cached { newtype TDataFlowCall = TNonDelegateCall(ControlFlow::Nodes::ElementNode cfn, DispatchCall dc) { DataFlowImplCommon::forceCachingInSameStage() and - cfn.getElement() = dc.getCall() + cfn.getAstNode() = dc.getCall() } or TExplicitDelegateLikeCall(ControlFlow::Nodes::ElementNode cfn, DelegateLikeCall dc) { - cfn.getElement() = dc + cfn.getAstNode() = dc } or TTransitiveCapturedCall(ControlFlow::Nodes::ElementNode cfn, Callable target) { transitiveCapturedCallTarget(cfn, target) diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll index 58f923623ee98..e7c11557a4c80 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPrivate.qll @@ -819,7 +819,7 @@ private module Cached { cached newtype TNode = - TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof Expr } or + TExprNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getAstNode() instanceof Expr } or TCilExprNode(CIL::Expr e) { e.getImplementation() instanceof CIL::BestImplementation } or TCilSsaDefinitionExtNode(CilSsaImpl::DefinitionExt def) or TSsaDefinitionExtNode(SsaImpl::DefinitionExt def) { @@ -835,19 +835,19 @@ private module Cached { not c.(Modifiable).isStatic() } or TYieldReturnNode(ControlFlow::Nodes::ElementNode cfn) { - any(Callable c).canYieldReturn(cfn.getElement()) + any(Callable c).canYieldReturn(cfn.getAstNode()) } or TAsyncReturnNode(ControlFlow::Nodes::ElementNode cfn) { - any(Callable c | c.(Modifiable).isAsync()).canReturn(cfn.getElement()) + any(Callable c | c.(Modifiable).isAsync()).canReturn(cfn.getAstNode()) } or TImplicitCapturedArgumentNode(ControlFlow::Nodes::ElementNode cfn, LocalScopeVariable v) { exists(Ssa::ExplicitDefinition def | def.isCapturedVariableDefinitionFlowIn(_, cfn, _) | v = def.getSourceVariable().getAssignable() ) } or - TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getElement() instanceof ObjectCreation } or + TMallocNode(ControlFlow::Nodes::ElementNode cfn) { cfn.getAstNode() instanceof ObjectCreation } or TObjectInitializerNode(ControlFlow::Nodes::ElementNode cfn) { - cfn.getElement().(ObjectCreation).hasInitializer() + cfn.getAstNode().(ObjectCreation).hasInitializer() } or TExprPostUpdateNode(ControlFlow::Nodes::ExprNode cfn) { cfn = LocalFlow::getAPostUpdateNodeForArg(_) @@ -1256,7 +1256,7 @@ private module ArgumentNodes { result.asCallable() = cfn.getEnclosingCallable() } - override Type getTypeImpl() { result = cfn.getElement().(Expr).getType() } + override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() } override Location getLocationImpl() { result = cfn.getLocation() } @@ -1397,7 +1397,7 @@ private module ReturnNodes { private ControlFlow::Nodes::ElementNode cfn; private Expr expr; - AsyncReturnNode() { this = TAsyncReturnNode(cfn) and expr = cfn.getElement() } + AsyncReturnNode() { this = TAsyncReturnNode(cfn) and expr = cfn.getAstNode() } Expr getExpr() { result = expr } @@ -2106,7 +2106,7 @@ private module PostUpdateNodes { result.asCallable() = cfn.getEnclosingCallable() } - override Type getTypeImpl() { result = cfn.getElement().(Expr).getType() } + override Type getTypeImpl() { result = cfn.getAstNode().(Expr).getType() } override ControlFlow::Node getControlFlowNodeImpl() { none() } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll index b3599e3404ef6..b297ec6b4237c 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/DataFlowPublic.qll @@ -90,7 +90,7 @@ class ExprNode extends Node, TExprNode_ { */ Expr getExprAtNode(ControlFlow::Nodes::ElementNode cfn) { this = TExprNode(cfn) and - result = cfn.getElement() + result = cfn.getAstNode() } } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll index 1346e4be30c02..511855b1eae32 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/SsaImpl.qll @@ -71,7 +71,7 @@ module ExposedForTestingOnly { * Holds if the `i`th node of basic block `bb` reads source variable `v`. */ private predicate variableReadActual(ControlFlow::BasicBlock bb, int i, Ssa::SourceVariable v) { - v.getAnAccess().(AssignableRead) = bb.getNode(i).getElement() + v.getAnAccess().(AssignableRead) = bb.getNode(i).getAstNode() } private module SourceVariableImpl { @@ -939,7 +939,7 @@ private module CapturedVariableLivenessImpl { CapturedReadLocalScopeVariable captured, Callable c, boolean libraryDelegateCall ) { implicitReadCandidate(v, call) and - c = getARuntimeTarget(call.getElement(), libraryDelegateCall) and + c = getARuntimeTarget(call.getAstNode(), libraryDelegateCall) and captured = v.getAssignable() and capturerReads(_, captured) } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll index a83e91a9c4645..30b5a366ba55b 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/ControlFlowReachability.qll @@ -24,11 +24,11 @@ private ControlFlowElement getANonExactScopeChild(ControlFlowScope scope) { pragma[noinline] private ControlFlow::BasicBlock getABasicBlockInScope(ControlFlowScope scope, boolean exactScope) { - result.getANode().getElement() = getANonExactScopeChild(scope) and + result.getANode().getAstNode() = getANonExactScopeChild(scope) and exactScope = false or scope.isExact() and - result.getANode().getElement() = scope and + result.getANode().getAstNode() = scope and exactScope = true } diff --git a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll index 6b91651691d5f..4b04a52be5f85 100644 --- a/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll +++ b/csharp/ql/lib/semmle/code/csharp/dataflow/internal/rangeanalysis/SsaReadPositionSpecific.qll @@ -22,8 +22,8 @@ private class PhiInputEdgeBlock extends BasicBlock { } private int getId(PhiInputEdgeBlock bb) { - exists(CfgImpl::ControlFlowTree::Range_ t | CfgImpl::ControlFlowTree::idOf(t, result) | - t = bb.getFirstNode().getElement() + exists(CfgImpl::ControlFlowTreeBase t | result = t.getId() | + t = bb.getFirstNode().getAstNode() or t = bb.(ControlFlow::BasicBlocks::EntryBlock).getCallable() ) diff --git a/csharp/ql/src/Concurrency/FutileSyncOnField.ql b/csharp/ql/src/Concurrency/FutileSyncOnField.ql index 19b23b88a30b0..b06c2ef9f8f27 100644 --- a/csharp/ql/src/Concurrency/FutileSyncOnField.ql +++ b/csharp/ql/src/Concurrency/FutileSyncOnField.ql @@ -16,7 +16,7 @@ import csharp predicate lockedFieldUpdate(LockStmt lock, Field f, AssignableDefinition def) { - lock.getAChild+() = def.getAControlFlowNode().getElement() and + lock.getAChild+() = def.getAControlFlowNode().getAstNode() and def.getTarget() = f } diff --git a/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql b/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql index 650cf9a1f0c5f..1ce94edee3d2a 100644 --- a/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql +++ b/csharp/ql/src/Concurrency/UnsynchronizedStaticAccess.ql @@ -28,7 +28,7 @@ ControlFlow::Node unlockedReachable(Callable a) { result = a.getEntryPoint() or exists(ControlFlow::Node mid | mid = unlockedReachable(a) | - not mid.getElement() instanceof LockingCall and + not mid.getAstNode() instanceof LockingCall and result = mid.getASuccessor() ) } diff --git a/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql b/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql index b231f6417780f..848246a0b4621 100644 --- a/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql +++ b/csharp/ql/src/Likely Bugs/NestedLoopsSameVariable.ql @@ -81,20 +81,20 @@ class NestedForLoopSameVariable extends ForStmt { /** Finds elements inside the outer loop that are no longer guarded by the loop invariant. */ private ControlFlow::Node getAnUnguardedNode() { - hasChild(this.getOuterForStmt().getBody(), result.getElement()) and + hasChild(this.getOuterForStmt().getBody(), result.getAstNode()) and ( result = this.getCondition().(ControlFlowElement).getAControlFlowExitNode().getAFalseSuccessor() or exists(ControlFlow::Node mid | mid = this.getAnUnguardedNode() | mid.getASuccessor() = result and - not exists(this.getAComparisonTest(result.getElement())) + not exists(this.getAComparisonTest(result.getAstNode())) ) ) } private VariableAccess getAnUnguardedAccess() { - result = this.getAnUnguardedNode().getElement() and + result = this.getAnUnguardedNode().getAstNode() and result.getTarget() = iteration } } diff --git a/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql b/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql index a557200e8eae3..ba64dc1bf949f 100644 --- a/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql +++ b/csharp/ql/src/Likely Bugs/Statements/UseBraces.ql @@ -15,9 +15,9 @@ import csharp // Iterate the control flow until we reach a Stmt Stmt findSuccessorStmt(ControlFlow::Node n) { - result = n.getElement() + result = n.getAstNode() or - not n.getElement() instanceof Stmt and result = findSuccessorStmt(n.getASuccessor()) + not n.getAstNode() instanceof Stmt and result = findSuccessorStmt(n.getASuccessor()) } // Return a successor statement to s diff --git a/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql b/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql index 5b490190a0e1f..aa244147e3edc 100644 --- a/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql +++ b/csharp/ql/src/Likely Bugs/UncheckedCastInEquals.ql @@ -19,13 +19,13 @@ private predicate equalsMethodChild(EqualsMethod equals, Element child) { } predicate nodeBeforeParameterAccess(ControlFlow::Node node) { - exists(EqualsMethod equals | equals.getBody() = node.getElement()) + exists(EqualsMethod equals | equals.getBody() = node.getAstNode()) or exists(EqualsMethod equals, Parameter param, ControlFlow::Node mid | equals.getParameter(0) = param and - equalsMethodChild(equals, mid.getElement()) and + equalsMethodChild(equals, mid.getAstNode()) and nodeBeforeParameterAccess(mid) and - not param.getAnAccess() = mid.getElement() and + not param.getAnAccess() = mid.getAstNode() and mid.getASuccessor() = node ) } diff --git a/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql b/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql index e158ac6b36690..368c294561cdd 100644 --- a/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql +++ b/csharp/ql/src/Security Features/CWE-384/AbandonSession.ql @@ -58,16 +58,16 @@ predicate sessionUse(MemberAccess ma) { /** A control flow step that is not sanitised by a call to clear the session. */ predicate controlStep(ControlFlow::Node s1, ControlFlow::Node s2) { s2 = s1.getASuccessor() and - not sessionEndMethod(s2.getElement().(MethodCall).getTarget()) + not sessionEndMethod(s2.getAstNode().(MethodCall).getTarget()) } from ControlFlow::Node loginCall, Method loginMethod, ControlFlow::Node sessionUse, ControlFlow::SuccessorType fromLoginFlow where - loginMethod = loginCall.getElement().(MethodCall).getTarget() and + loginMethod = loginCall.getAstNode().(MethodCall).getTarget() and loginMethod(loginMethod, fromLoginFlow) and - sessionUse(sessionUse.getElement()) and + sessionUse(sessionUse.getAstNode()) and controlStep+(loginCall.getASuccessorByType(fromLoginFlow), sessionUse) select sessionUse, "This session has not been invalidated following the call to $@.", loginCall, loginMethod.getName() diff --git a/csharp/ql/test/library-tests/cil/dataflow/ControlFlow.ql b/csharp/ql/test/library-tests/cil/dataflow/ControlFlow.ql index c064829b5e7e8..9b187f66f7ae5 100644 --- a/csharp/ql/test/library-tests/cil/dataflow/ControlFlow.ql +++ b/csharp/ql/test/library-tests/cil/dataflow/ControlFlow.ql @@ -2,5 +2,5 @@ import csharp query predicate deadCode(MethodCall c) { c.getTarget().getName() = "DeadCode" and - not exists(ControlFlow::Node node | node.getElement() = c) + not exists(ControlFlow::Node node | node.getAstNode() = c) } diff --git a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.ql b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.ql index cf1ba3f959845..dd6c58d68f73f 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.ql +++ b/csharp/ql/test/library-tests/controlflow/graph/NodeGraph.ql @@ -4,6 +4,6 @@ import csharp import Common -import semmle.code.csharp.controlflow.internal.ControlFlowGraphImplShared::TestOutput +import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl::TestOutput private class MyRelevantNode extends RelevantNode, SourceControlFlowNode { } diff --git a/csharp/ql/test/library-tests/controlflow/graph/Nodes.ql b/csharp/ql/test/library-tests/controlflow/graph/Nodes.ql index b391aaa32598e..fb937bfbd9f73 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Nodes.ql +++ b/csharp/ql/test/library-tests/controlflow/graph/Nodes.ql @@ -1,7 +1,7 @@ import csharp import ControlFlow import Common -import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl +import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl as Impl import semmle.code.csharp.controlflow.internal.Splitting as Splitting import Nodes @@ -16,11 +16,11 @@ class MyFinallySplitControlFlowNode extends ElementNode { ) } - Statements::TryStmtTree getTryStmt() { this.getElement() = result.getAFinallyDescendant() } + Impl::Statements::TryStmtTree getTryStmt() { this.getAstNode() = result.getAFinallyDescendant() } } query predicate finallyNode(MyFinallySplitControlFlowNode f, TryStmt try) { try = f.getTryStmt() } query predicate entryPoint(Callable c, SourceControlFlowElement cfn) { - c.getEntryPoint().getASuccessor().getElement() = cfn + c.getEntryPoint().getASuccessor().getAstNode() = cfn } diff --git a/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql b/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql index 2c637ff0db1aa..c1b5633912d92 100644 --- a/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql +++ b/csharp/ql/test/library-tests/controlflow/splits/SplittingStressTest.ql @@ -2,7 +2,7 @@ import csharp query predicate countSplits(ControlFlowElement cfe, int i) { not cfe.fromLibrary() and - i = strictcount(ControlFlow::Nodes::ElementNode n | n.getElement() = cfe) + i = strictcount(ControlFlow::Nodes::ElementNode n | n.getAstNode() = cfe) } query predicate ssaDef(Ssa::Definition def) { any() } diff --git a/csharp/ql/test/library-tests/dataflow/ssa/PreSsaConsistency.ql b/csharp/ql/test/library-tests/dataflow/ssa/PreSsaConsistency.ql index 5ee4c6e27a9fe..de7357d14b64b 100644 --- a/csharp/ql/test/library-tests/dataflow/ssa/PreSsaConsistency.ql +++ b/csharp/ql/test/library-tests/dataflow/ssa/PreSsaConsistency.ql @@ -4,7 +4,7 @@ import semmle.code.csharp.controlflow.internal.ControlFlowGraphImpl import semmle.code.csharp.dataflow.internal.SsaImpl as SsaImpl class CallableWithSplitting extends Callable { - CallableWithSplitting() { this = any(SplitControlFlowElement e).getEnclosingCallable() } + CallableWithSplitting() { this = any(SplitAstNode n).getEnclosingCallable() } } query predicate defReadInconsistency( @@ -57,8 +57,8 @@ query predicate readReadInconsistency( not PreSsa::adjacentReadPairSameVar(read1, read2) and // Exclude split CFG elements because SSA may be more precise than pre-SSA // in those cases - not read1 instanceof SplitControlFlowElement and - not read2 instanceof SplitControlFlowElement + not read1 instanceof SplitAstNode and + not read2 instanceof SplitAstNode ) } @@ -78,7 +78,7 @@ query predicate phiInconsistency( | edef.getADefinition() = adef and phi.definesAt(_, bb, _) and - cfe = bb.getFirstNode().getElement() + cfe = bb.getFirstNode().getAstNode() ) ) or @@ -89,7 +89,7 @@ query predicate phiInconsistency( edef = phi.getAnUltimateDefinition() and edef.getADefinition() = adef and phi.definesAt(_, bb, _) and - cfe = bb.getFirstNode().getElement() and + cfe = bb.getFirstNode().getAstNode() and not exists(PreSsa::PhiNode prePhi | adef = prePhi.getAnInput+().getDefinition() and cfe = prePhi.getBasicBlock().getFirstElement() diff --git a/csharp/ql/test/library-tests/goto/Goto1.ql b/csharp/ql/test/library-tests/goto/Goto1.ql index 97dd1fa2af02f..1a90e6eb1fb6b 100644 --- a/csharp/ql/test/library-tests/goto/Goto1.ql +++ b/csharp/ql/test/library-tests/goto/Goto1.ql @@ -1,7 +1,7 @@ import csharp query predicate edges(ControlFlow::Node node, ControlFlow::Node successor, string attr, string val) { - not node.getElement().fromLibrary() and + not node.getAstNode().fromLibrary() and exists(ControlFlow::SuccessorType t | successor = node.getASuccessorByType(t) | attr = "semmle.label" and val = t.toString() diff --git a/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql b/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql index 4758aaebe3bc7..f596ac41629a0 100644 --- a/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql +++ b/csharp/ql/test/library-tests/standalone/controlflow/cfg.ql @@ -12,5 +12,5 @@ class UnknownCall extends Call { } query predicate edges(ControlFlow::Node n1, ControlFlow::Node n2) { - not n1.getElement().fromLibrary() and n2 = n1.getASuccessor() + not n1.getAstNode().fromLibrary() and n2 = n1.getASuccessor() } diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index 26429c1c7ab04..b89f925e1d039 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -937,23 +937,6 @@ module Make Input> { ) } - /** - * Gets the CFG scope of node `n`. Unlike `getCfgScope`, this predicate - * is calculated based on reachability from an entry node, and it may - * yield different results for AST elements that are split into multiple - * scopes. - */ - cached - CfgScope getNodeCfgScope(Node n) { - n = TEntryNode(result) - or - n = TAnnotatedExitNode(result, _) - or - n = TExitNode(result) - or - n = TAstNode(result, _, _) - } - cached module Public { /** @@ -974,6 +957,23 @@ module Make Input> { */ cached AstNode getAControlFlowExitNode(AstNode n) { last(n, result, _) } + + /** + * Gets the CFG scope of node `n`. Unlike `getCfgScope`, this predicate + * is calculated based on reachability from an entry node, and it may + * yield different results for AST elements that are split into multiple + * scopes. + */ + cached + CfgScope getNodeCfgScope(Node n) { + n = TEntryNode(result) + or + n = TAnnotatedExitNode(result, _) + or + n = TExitNode(result) + or + n = TAstNode(result, _, _) + } } }