diff --git a/go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md b/go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md new file mode 100644 index 000000000000..896f23a8a14d --- /dev/null +++ b/go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The Go control flow graph implementation has been migrated to use the shared CFG library. This is an internal change with no user-visible API changes. diff --git a/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll b/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll index dc52abb25abf..a0ba4e00417c 100644 --- a/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll +++ b/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll @@ -5,66 +5,27 @@ overlay[local] module; import go -private import ControlFlowGraphImpl -private import codeql.controlflow.BasicBlock as BB -private import codeql.controlflow.SuccessorType +private import ControlFlowGraphShared -private module Input implements BB::InputSig { - /** A delineated part of the AST with its own CFG. */ - class CfgScope = ControlFlow::Root; +/** A basic block in the control-flow graph. */ +class BasicBlock = GoCfg::Cfg::BasicBlock; - /** The class of control flow nodes. */ - class Node = ControlFlowNode; - - /** Gets the CFG scope in which this node occurs. */ - CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result } - - /** Gets an immediate successor of this node. */ - Node nodeGetASuccessor(Node node, SuccessorType t) { - result = node.getASuccessor() and - ( - not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor - or - t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome() - ) - } - - /** - * Holds if `node` represents an entry node to be used when calculating - * dominance. - */ - predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode } - - /** - * Holds if `node` represents an exit node to be used when calculating - * post dominance. - */ - predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode } -} - -private module BbImpl = BB::Make; - -class BasicBlock = BbImpl::BasicBlock; - -class EntryBasicBlock = BbImpl::EntryBasicBlock; - -cached -private predicate reachableBB(BasicBlock bb) { - bb instanceof EntryBasicBlock - or - exists(BasicBlock predBB | predBB.getASuccessor(_) = bb | reachableBB(predBB)) -} +/** An entry basic block. */ +class EntryBasicBlock = GoCfg::Cfg::EntryBasicBlock; /** * A basic block that is reachable from an entry basic block. + * + * Since the shared CFG library only creates nodes for reachable code, + * all basic blocks are reachable by construction. */ class ReachableBasicBlock extends BasicBlock { - ReachableBasicBlock() { reachableBB(this) } + ReachableBasicBlock() { any() } } /** * A reachable basic block with more than one predecessor. */ class ReachableJoinBlock extends ReachableBasicBlock { - ReachableJoinBlock() { this.getFirstNode().isJoin() } + ReachableJoinBlock() { this.getFirstNode().(ControlFlow::Node).isJoin() } } diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll index 77bb94d89f8c..0bbb3045d2df 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll @@ -5,13 +5,17 @@ overlay[local] module; import go -private import ControlFlowGraphImpl +private import ControlFlowGraphShared -/** Provides helper predicates for mapping btween CFG nodes and the AST. */ +/** Provides helper predicates for mapping between CFG nodes and the AST. */ module ControlFlow { /** A file or function with which a CFG is associated. */ class Root extends AstNode { - Root() { exists(this.(File).getADecl()) or exists(this.(FuncDef).getBody()) } + Root() { + exists(this.(FuncDef).getBody()) + or + exists(this.(File).getADecl()) + } /** Holds if `nd` belongs to this file or function. */ predicate isRootOf(AstNode nd) { @@ -29,22 +33,16 @@ module ControlFlow { } /** - * A node in the intra-procedural control-flow graph of a Go function or file. + * A node in the intra-procedural control-flow graph of a Go function. * * Nodes correspond to expressions and statements that compute a value or perform * an operation (as opposed to providing syntactic structure or type information). * - * There are also synthetic entry and exit nodes for each Go function and file + * There are also synthetic entry and exit nodes for each Go function * that mark the beginning and the end, respectively, of the execution of the - * function and the loading of the file. + * function. */ - class Node extends TControlFlowNode { - /** Gets a node that directly follows this one in the control-flow graph. */ - Node getASuccessor() { result = CFG::succ(this) } - - /** Gets a node that directly precedes this one in the control-flow graph. */ - Node getAPredecessor() { this = result.getASuccessor() } - + class Node extends GoCfg::ControlFlowNode { /** Holds if this is a node with more than one successor. */ predicate isBranch() { strictcount(this.getASuccessor()) > 1 } @@ -52,22 +50,23 @@ module ControlFlow { predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } /** Holds if this is the first control-flow node in `subtree`. */ - predicate isFirstNodeOf(AstNode subtree) { CFG::firstNode(subtree, this) } - - /** Holds if this node is the (unique) entry node of a function or file. */ - predicate isEntryNode() { this instanceof MkEntryNode } + predicate isFirstNodeOf(AstNode subtree) { + this.isBefore(subtree) + or + this.injects(subtree) + } - /** Holds if this node is the (unique) exit node of a function or file. */ - predicate isExitNode() { this instanceof MkExitNode } + /** Holds if this node is the (unique) entry node of a function. */ + predicate isEntryNode() { this instanceof GoCfg::ControlFlow::EntryNode } - /** Gets the basic block to which this node belongs. */ - BasicBlock getBasicBlock() { result.getANode() = this } + /** Holds if this node is the (unique) exit node of a function. */ + predicate isExitNode() { this instanceof GoCfg::ControlFlow::ExitNode } /** Holds if this node dominates `dominee` in the control-flow graph. */ overlay[caller?] pragma[inline] predicate dominatesNode(ControlFlow::Node dominee) { - exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j | + exists(GoCfg::Cfg::BasicBlock thisbb, GoCfg::Cfg::BasicBlock dbb, int i, int j | this = thisbb.getNode(i) and dominee = dbb.getNode(j) | thisbb.strictlyDominates(dbb) @@ -76,20 +75,12 @@ module ControlFlow { ) } - /** Gets the innermost function or file to which this node belongs. */ - Root getRoot() { none() } + /** Gets the innermost function to which this node belongs. */ + Root getRoot() { result = this.getEnclosingCallable() } /** Gets the file to which this node belongs. */ File getFile() { result = this.getLocation().getFile() } - /** - * Gets a textual representation of this control flow node. - */ - string toString() { result = "control-flow node" } - - /** Gets the source location for this element. */ - Location getLocation() { none() } - /** * DEPRECATED: Use `getLocation()` instead. * @@ -113,6 +104,12 @@ module ControlFlow { } } + /** A synthetic entry node for a function. */ + class EntryNode extends Node instanceof GoCfg::ControlFlow::EntryNode { } + + /** A synthetic exit node for a function. */ + class ExitNode extends Node instanceof GoCfg::ControlFlow::ExitNode { } + /** * A control-flow node that initializes or updates the value of a constant, a variable, * a field, or an (array, slice, or map) element. @@ -172,7 +169,7 @@ module ControlFlow { exists(IR::FieldTarget trg | trg = super.getLhs() | ( trg.getBase() = base or - trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr()) + trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr()) ) and trg.getField() = f and super.getRhs() = rhs @@ -220,7 +217,7 @@ module ControlFlow { exists(IR::ElementTarget trg | trg = super.getLhs() | ( trg.getBase() = base or - trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr()) + trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr()) ) and trg.getIndex() = index and super.getRhs() = rhs @@ -250,11 +247,15 @@ module ControlFlow { * A control-flow node recording the fact that a certain expression has a known * Boolean value at this point in the program. */ - class ConditionGuardNode extends IR::Instruction, MkConditionGuardNode { + class ConditionGuardNode extends Node { Expr cond; boolean outcome; - ConditionGuardNode() { this = MkConditionGuardNode(cond, outcome) } + ConditionGuardNode() { + this.isAfterTrue(cond) and outcome = true + or + this.isAfterFalse(cond) and outcome = false + } private predicate ensuresAux(Expr expr, boolean b) { expr = cond and b = outcome @@ -320,21 +321,17 @@ module ControlFlow { boolean getOutcome() { result = outcome } override Root getRoot() { result.isRootOf(cond) } - - override string toString() { result = cond + " is " + outcome } - - override Location getLocation() { result = cond.getLocation() } } /** - * Gets the entry node of function or file `root`. + * Gets the entry node of function `root`. */ - Node entryNode(Root root) { result = MkEntryNode(root) } + EntryNode entryNode(Root root) { result.getEnclosingCallable() = root } /** - * Gets the exit node of function or file `root`. + * Gets the exit node of function `root`. */ - Node exitNode(Root root) { result = MkExitNode(root) } + ExitNode exitNode(Root root) { result.getEnclosingCallable() = root } /** * Holds if the function `f` may return without panicking, exiting the process, or looping forever. @@ -342,7 +339,9 @@ module ControlFlow { * This is defined conservatively, and so may also hold of a function that in fact * cannot return normally, but never fails to hold of a function that can return normally. */ - predicate mayReturnNormally(FuncDecl f) { CFG::mayReturnNormally(f.getBody()) } + predicate mayReturnNormally(FuncDecl f) { + exists(GoCfg::ControlFlow::NormalExitNode exit | exit.getEnclosingCallable() = f) + } /** * Holds if `pred` is the node for the case `testExpr` in an expression @@ -352,7 +351,13 @@ module ControlFlow { predicate isSwitchCaseTestPassingEdge( ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr ) { - CFG::isSwitchCaseTestPassingEdge(pred, succ, switchExpr, testExpr) + exists(ExpressionSwitchStmt ess, CaseClause cc, int i | + ess.getExpr() = switchExpr and + cc = ess.getACase() and + testExpr = cc.getExpr(i) and + pred.isAfter(testExpr) and + succ.isFirstNodeOf(cc.getStmt(0)) + ) } } diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll deleted file mode 100644 index a26ab3adaf5f..000000000000 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ /dev/null @@ -1,2133 +0,0 @@ -/** - * INTERNAL: Analyses should use module `ControlFlowGraph` instead. - * - * Provides predicates for building intra-procedural CFGs. - */ -overlay[local] -module; - -import go - -/** A block statement that is not the body of a `switch` or `select` statement. */ -class PlainBlock extends BlockStmt { - PlainBlock() { - not this = any(SwitchStmt sw).getBody() and not this = any(SelectStmt sel).getBody() - } -} - -private predicate notBlankIdent(Expr e) { not e instanceof BlankIdent } - -private predicate pureLvalue(ReferenceExpr e) { not e.isRvalue() } - -/** - * Holds if `e` is a branch condition, including the LHS of a short-circuiting binary operator. - */ -private predicate isCondRoot(Expr e) { - e = any(LogicalBinaryExpr lbe).getLeftOperand() - or - e = any(ForStmt fs).getCond() - or - e = any(IfStmt is).getCond() - or - e = any(ExpressionSwitchStmt ess | not exists(ess.getExpr())).getACase().getAnExpr() -} - -/** - * Holds if `e` is a branch condition or part of a logical binary expression contributing to a - * branch condition. - * - * For example, in `v := (x && y) || (z && w)`, `x` and `(x && y)` and `z` are branch conditions - * (`isCondRoot` holds of them), whereas this predicate also holds of `y` (contributes to condition - * `x && y`) but not of `w` (contributes to the value `v`, but not to any branch condition). - * - * In the context `if (x && y) || (z && w)` then the whole `(x && y) || (z && w)` is a branch condition - * as well as `x` and `(x && y)` and `z` as previously, and this predicate holds of all their - * subexpressions. - */ -private predicate isCond(Expr e) { - isCondRoot(e) or - e = any(LogicalBinaryExpr lbe | isCond(lbe)).getRightOperand() or - e = any(ParenExpr par | isCond(par)).getExpr() -} - -/** - * Holds if `e` implicitly reads the embedded field `implicitField`. - * - * The `index` is the distance from the promoted field. For example, if `A` contains an embedded - * field `B`, `B` contains an embedded field `C` and `C` contains the non-embedded field `x`. - * Then `a.x` implicitly reads `C` with index 1 and `B` with index 2. - */ -private predicate implicitFieldSelectionForField(PromotedSelector e, int index, Field implicitField) { - exists(StructType baseType, PromotedField child, int implicitFieldDepth | - baseType = e.getSelectedStructType() and - ( - e.refersTo(child) - or - implicitFieldSelectionForField(e, implicitFieldDepth + 1, child) - ) - | - child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and - exists(PromotedField explicitField, int explicitFieldDepth | - e.refersTo(explicitField) and baseType.getFieldAtDepth(_, explicitFieldDepth) = explicitField - | - index = explicitFieldDepth - implicitFieldDepth - ) - ) -} - -private predicate implicitFieldSelectionForMethod(PromotedSelector e, int index, Field implicitField) { - exists(StructType baseType, PromotedMethod method, int mDepth, int implicitFieldDepth | - baseType = e.getSelectedStructType() and - e.refersTo(method) and - baseType.getMethodAtDepth(_, mDepth) = method and - index = mDepth - implicitFieldDepth - | - method = baseType.getMethodOfEmbedded(implicitField, _, implicitFieldDepth + 1) - or - exists(PromotedField child | - child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and - implicitFieldSelectionForMethod(e, implicitFieldDepth + 1, child) - ) - ) -} - -/** - * A node in the intra-procedural control-flow graph of a Go function or file. - * - * There are two kinds of control-flow nodes: - * - * 1. Instructions: these are nodes that correspond to expressions and statements - * that compute a value or perform an operation (as opposed to providing syntactic - * structure or type information). - * 2. Synthetic nodes: - * - Entry and exit nodes for each Go function and file that mark the beginning and the end, - * respectively, of the execution of the function and the loading of the file; - * - Skip nodes that are semantic no-ops, but make CFG construction easier. - */ -cached -newtype TControlFlowNode = - /** - * A control-flow node that represents the evaluation of an expression. - */ - MkExprNode(Expr e) { CFG::hasEvaluationNode(e) } or - /** - * A control-flow node that represents the initialization of an element of a composite literal. - */ - MkLiteralElementInitNode(Expr e) { e = any(CompositeLit lit).getAnElement() } or - /** - * A control-flow node that represents the implicit index of an element in a slice or array literal. - */ - MkImplicitLiteralElementIndex(Expr e) { - exists(CompositeLit lit | not lit instanceof StructLit | - e = lit.getAnElement() and - not e instanceof KeyValueExpr - ) - } or - /** - * A control-flow node that represents a (single) assignment. - * - * Assignments with multiple left-hand sides are split up into multiple assignment nodes, - * one for each left-hand side. Assignments to `_` are not represented in the control-flow graph. - */ - MkAssignNode(AstNode assgn, int i) { - // the `i`th assignment in a (possibly multi-)assignment - notBlankIdent(assgn.(Assignment).getLhs(i)) - or - // the `i`th name declared in a (possibly multi-)declaration specifier - notBlankIdent(assgn.(ValueSpec).getNameExpr(i)) - or - // the assignment to the "key" variable in a `range` statement - notBlankIdent(assgn.(RangeStmt).getKey()) and i = 0 - or - // the assignment to the "value" variable in a `range` statement - notBlankIdent(assgn.(RangeStmt).getValue()) and i = 1 - } or - /** - * A control-flow node that represents the implicit right-hand side of a compound assignment. - * - * For example, the compound assignment `x += 1` has an implicit right-hand side `x + 1`. - */ - MkCompoundAssignRhsNode(CompoundAssignStmt assgn) or - /** - * A control-flow node that represents the `i`th component of a tuple expression `s`. - */ - MkExtractNode(AstNode s, int i) { - // in an assignment `x, y, z = tuple` - exists(Assignment assgn | - s = assgn and - exists(assgn.getRhs()) and - assgn.getNumLhs() > 1 and - exists(assgn.getLhs(i)) - ) - or - // in a declaration `var x, y, z = tuple` - exists(ValueSpec spec | - s = spec and - exists(spec.getInit()) and - spec.getNumName() > 1 and - exists(spec.getNameExpr(i)) - ) - or - // in a `range` statement - exists(RangeStmt rs | s = rs | - exists(rs.getKey()) and i = 0 - or - exists(rs.getValue()) and i = 1 - ) - or - // in a return statement `return f()` where `f` has multiple return values - exists(ReturnStmt ret, SignatureType rettp | - s = ret and - // the return statement has a single expression - exists(ret.getExpr()) and - // but the enclosing function has multiple results - rettp = ret.getEnclosingFunction().getType() and - rettp.getNumResult() > 1 and - exists(rettp.getResultType(i)) - ) - or - // in a call `f(g())` where `g` has multiple return values - exists(CallExpr outer, CallExpr inner | s = outer | - inner = outer.getArgument(0).stripParens() and - outer.getNumArgument() = 1 and - exists(inner.getType().(TupleType).getComponentType(i)) - ) - } or - /** - * A control-flow node that represents the zero value to which a variable without an initializer - * expression is initialized. - */ - MkZeroInitNode(ValueEntity v) { - exists(ValueSpec spec | - not exists(spec.getAnInit()) and - spec.getNameExpr(_) = v.getDeclaration() - ) - or - exists(v.(ResultVariable).getFunction().getBody()) - } or - /** - * A control-flow node that represents a function declaration. - */ - MkFuncDeclNode(FuncDecl fd) or - /** - * A control-flow node that represents a `defer` statement. - */ - MkDeferNode(DeferStmt def) or - /** - * A control-flow node that represents a `go` statement. - */ - MkGoNode(GoStmt go) or - /** - * A control-flow node that represents the fact that `e` is known to evaluate to - * `outcome`. - */ - MkConditionGuardNode(Expr e, Boolean outcome) { isCondRoot(e) } or - /** - * A control-flow node that represents an increment or decrement statement. - */ - MkIncDecNode(IncDecStmt ids) or - /** - * A control-flow node that represents the implicit right-hand side of an increment or decrement statement. - */ - MkIncDecRhs(IncDecStmt ids) or - /** - * A control-flow node that represents the implicit operand 1 of an increment or decrement statement. - */ - MkImplicitOne(IncDecStmt ids) or - /** - * A control-flow node that represents a return from a function. - */ - MkReturnNode(ReturnStmt ret) or - /** - * A control-flow node that represents the implicit write to a named result variable in a return statement. - */ - MkResultWriteNode(ResultVariable var, int i, ReturnStmt ret) { - ret.getEnclosingFunction().getResultVar(i) = var and - exists(ret.getAnExpr()) - } or - /** - * A control-flow node that represents the implicit read of a named result variable upon returning from - * a function (after any deferred calls have been executed). - */ - MkResultReadNode(ResultVariable var) or - /** - * A control-flow node that represents a no-op. - * - * These control-flow nodes correspond to Go statements that have no runtime semantics other than potentially - * influencing control flow: the branching statements `continue`, `break`, `fallthrough` and `goto`; empty - * blocks; empty statements; and import and type declarations. - */ - MkSkipNode(AstNode skip) { - skip instanceof BranchStmt - or - skip instanceof EmptyStmt - or - skip.(PlainBlock).getNumStmt() = 0 - or - skip instanceof ImportDecl - or - skip instanceof TypeDecl - or - pureLvalue(skip) - or - skip.(CaseClause).getNumStmt() = 0 - or - skip.(CommClause).getNumStmt() = 0 - } or - /** - * A control-flow node that represents a `select` operation. - */ - MkSelectNode(SelectStmt sel) or - /** - * A control-flow node that represents a `send` operation. - */ - MkSendNode(SendStmt send) or - /** - * A control-flow node that represents the initialization of a parameter to its corresponding argument. - */ - MkParameterInit(Parameter parm) { exists(parm.getFunction().getBody()) } or - /** - * A control-flow node that represents the argument corresponding to a parameter. - */ - MkArgumentNode(Parameter parm) { exists(parm.getFunction().getBody()) } or - /** - * A control-flow node that represents the initialization of a result variable to its zero value. - */ - MkResultInit(ResultVariable rv) { exists(rv.getFunction().getBody()) } or - /** - * A control-flow node that represents the operation of retrieving the next (key, value) pair in a - * `range` statement, if any. - */ - MkNextNode(RangeStmt rs) or - /** - * A control-flow node that represents the implicit `true` expression in `switch { ... }`. - */ - MkImplicitTrue(ExpressionSwitchStmt stmt) { not exists(stmt.getExpr()) } or - /** - * A control-flow node that represents the implicit comparison or type check performed by - * the `i`th expression of a case clause `cc`. - */ - MkCaseCheckNode(CaseClause cc, int i) { exists(cc.getExpr(i)) } or - /** - * A control-flow node that represents the implicit declaration of the - * variable `lv` in case clause `cc` and its assignment of the value - * `switchExpr` from the guard. This only occurs in case clauses in a type - * switch statement which declares a variable in its guard. - */ - MkTypeSwitchImplicitVariable(CaseClause cc, LocalVariable lv, Expr switchExpr) { - exists(TypeSwitchStmt ts, DefineStmt ds | ds = ts.getAssign() | - cc = ts.getACase() and - lv = cc.getImplicitlyDeclaredVariable() and - switchExpr = ds.getRhs().(TypeAssertExpr).getExpr() - ) - } or - /** - * A control-flow node that represents the implicit lower bound of a slice expression. - */ - MkImplicitLowerSliceBound(SliceExpr sl) { not exists(sl.getLow()) } or - /** - * A control-flow node that represents the implicit upper bound of a simple slice expression. - */ - MkImplicitUpperSliceBound(SliceExpr sl) { not exists(sl.getHigh()) } or - /** - * A control-flow node that represents the implicit max bound of a simple slice expression. - */ - MkImplicitMaxSliceBound(SliceExpr sl) { not exists(sl.getMax()) } or - /** - * A control-flow node that represents the implicit dereference of the base in a field/method - * access, element access, or slice expression. - */ - MkImplicitDeref(Expr e) { - e.getType().getUnderlyingType() instanceof PointerType and - ( - exists(SelectorExpr sel | e = sel.getBase() | - // field accesses through a pointer always implicitly dereference - sel = any(Field f).getAReference() - or - // method accesses only dereference if the receiver is _not_ a pointer - exists(Method m, Type tp | - sel = m.getAReference() and - tp = m.getReceiver().getType().getUnderlyingType() and - not tp instanceof PointerType - ) - ) - or - e = any(IndexExpr ie).getBase() - or - e = any(SliceExpr se).getBase() - ) - } or - /** - * A control-flow node that represents the implicit selection of a field when - * accessing a promoted field. - * - * If that field has a pointer type then this control-flow node also - * represents an implicit dereference of it. - */ - MkImplicitFieldSelection(PromotedSelector e, int i, Field implicitField) { - implicitFieldSelectionForField(e, i, implicitField) or - implicitFieldSelectionForMethod(e, i, implicitField) - } or - /** - * A control-flow node that represents the start of the execution of a function or file. - */ - MkEntryNode(ControlFlow::Root root) or - /** - * A control-flow node that represents the end of the execution of a function or file. - */ - MkExitNode(ControlFlow::Root root) - -/** A representation of the target of a write. */ -newtype TWriteTarget = - /** A write target that is represented explicitly in the AST. */ - MkLhs(TControlFlowNode write, Expr lhs) { - exists(AstNode assgn, int i | write = MkAssignNode(assgn, i) | - lhs = assgn.(Assignment).getLhs(i).stripParens() - or - lhs = assgn.(ValueSpec).getNameExpr(i) - or - exists(RangeStmt rs | rs = assgn | - i = 0 and lhs = rs.getKey().stripParens() - or - i = 1 and lhs = rs.getValue().stripParens() - ) - ) - or - exists(IncDecStmt ids | write = MkIncDecNode(ids) | lhs = ids.getOperand().stripParens()) - or - exists(Parameter parm | write = MkParameterInit(parm) | lhs = parm.getDeclaration()) - or - exists(ResultVariable res | write = MkResultInit(res) | lhs = res.getDeclaration()) - } or - /** A write target for an element in a compound literal, viewed as a field write. */ - MkLiteralElementTarget(MkLiteralElementInitNode elt) or - /** A write target for a returned expression, viewed as a write to the corresponding result variable. */ - MkResultWriteTarget(MkResultWriteNode w) - -/** - * A control-flow node that represents a no-op. - * - * These control-flow nodes correspond to Go statements that have no runtime semantics other than - * potentially influencing control flow: the branching statements `continue`, `break`, - * `fallthrough` and `goto`; empty blocks; empty statements; and import and type declarations. - */ -class SkipNode extends ControlFlow::Node, MkSkipNode { - AstNode skip; - - SkipNode() { this = MkSkipNode(skip) } - - override ControlFlow::Root getRoot() { result.isRootOf(skip) } - - override string toString() { result = "skip" } - - override Location getLocation() { result = skip.getLocation() } -} - -/** - * A control-flow node that represents the start of the execution of a function or file. - */ -class EntryNode extends ControlFlow::Node, MkEntryNode { - ControlFlow::Root root; - - EntryNode() { this = MkEntryNode(root) } - - override ControlFlow::Root getRoot() { result = root } - - override string toString() { result = "entry" } - - override Location getLocation() { result = root.getLocation() } -} - -/** - * A control-flow node that represents the end of the execution of a function or file. - */ -class ExitNode extends ControlFlow::Node, MkExitNode { - ControlFlow::Root root; - - ExitNode() { this = MkExitNode(root) } - - override ControlFlow::Root getRoot() { result = root } - - override string toString() { result = "exit" } - - override Location getLocation() { result = root.getLocation() } -} - -/** - * Provides classes and predicates for computing the control-flow graph. - */ -cached -module CFG { - /** - * The target of a branch statement, which is either the label of a labeled statement or - * the special target `""` referring to the innermost enclosing loop or `switch`. - */ - private class BranchTarget extends string { - BranchTarget() { this = any(LabeledStmt ls).getLabel() or this = "" } - } - - private module BranchTarget { - /** Holds if this is the target of branch statement `stmt` or the label of compound statement `stmt`. */ - BranchTarget of(Stmt stmt) { - exists(BranchStmt bs | bs = stmt | - result = bs.getLabel() - or - not exists(bs.getLabel()) and result = "" - ) - or - exists(LabeledStmt ls | stmt = ls.getStmt() | result = ls.getLabel()) - or - (stmt instanceof LoopStmt or stmt instanceof SwitchStmt or stmt instanceof SelectStmt) and - result = "" - } - } - - private newtype TCompletion = - /** A completion indicating that an expression or statement was evaluated successfully. */ - Done() or - /** - * A completion indicating that an expression was successfully evaluated to Boolean value `b`. - * - * Note that many Boolean expressions are modeled as having completion `Done()` instead. - * Completion `Bool` is only used in contexts where the Boolean value can be determined. - */ - Bool(boolean b) { b = true or b = false } or - /** - * A completion indicating that execution of a (compound) statement ended with a `break` - * statement targeting the given label. - */ - Break(BranchTarget lbl) or - /** - * A completion indicating that execution of a (compound) statement ended with a `continue` - * statement targeting the given label. - */ - Continue(BranchTarget lbl) or - /** - * A completion indicating that execution of a (compound) statement ended with a `fallthrough` - * statement. - */ - Fallthrough() or - /** - * A completion indicating that execution of a (compound) statement ended with a `return` - * statement. - */ - Return() or - /** - * A completion indicating that execution of a statement or expression may have ended with - * a panic being raised. - */ - Panic() - - private Completion normalCompletion() { result.isNormal() } - - private class Completion extends TCompletion { - predicate isNormal() { this = Done() or this = Bool(_) } - - Boolean getOutcome() { this = Done() or this = Bool(result) } - - string toString() { - this = Done() and result = "normal" - or - exists(boolean b | this = Bool(b) | result = b.toString()) - or - exists(BranchTarget lbl | - this = Break(lbl) and result = "break " + lbl - or - this = Continue(lbl) and result = "continue " + lbl - ) - or - this = Fallthrough() and result = "fallthrough" - or - this = Return() and result = "return" - or - this = Panic() and result = "panic" - } - } - - /** - * Holds if `e` should have an evaluation node in the control-flow graph. - * - * Excluded expressions include those not evaluated at runtime (e.g. identifiers, type expressions) - * and some logical expressions that are expressed as control-flow edges rather than having a specific - * evaluation node. - */ - cached - predicate hasEvaluationNode(Expr e) { - // exclude expressions that do not denote a value - not e instanceof TypeExpr and - not e = any(FieldDecl f).getTag() and - not e instanceof KeyValueExpr and - not e = any(SelectorExpr sel).getSelector() and - not e = any(StructLit sl).getKey(_) and - not (e instanceof Ident and not e instanceof ReferenceExpr) and - not (e instanceof SelectorExpr and not e instanceof ReferenceExpr) and - not pureLvalue(e) and - // exclude parentheses, which are purely concrete syntax, and some logical binary expressions - // whose evaluation is implied by control-flow edges without requiring an evaluation node. - not isControlFlowStructural(e) and - // exclude expressions that are not evaluated at runtime - not e = any(ImportSpec is).getPathExpr() and - not e.getParent*() = any(ArrayTypeExpr ate).getLength() and - // sub-expressions of constant expressions are not evaluated (even if they don't look constant - // themselves) - not constRoot(e.getParent+()) - } - - /** - * Holds if `e` is an expression that purely serves grouping or control-flow purposes. - * - * Examples include parenthesized expressions and short-circuiting Boolean expressions used within - * a branch condition (`if` or `for` condition, or as part of a larger boolean expression, e.g. - * in `(x && y) || z`, the `&&` subexpression matches this predicate). - */ - private predicate isControlFlowStructural(Expr e) { - // Some logical binary operators do not need an evaluation node - // (for example, in `if x && y`, we evaluate `x` and then branch straight to either `y` or the - // `else` block, so there is no control-flow step where `x && y` is specifically calculated) - e instanceof LogicalBinaryExpr and - isCond(e) - or - // Purely concrete-syntactic structural expression: - e instanceof ParenExpr - } - - /** - * Gets a constant root, that is, an expression that is constant but whose parent expression is not. - * - * As an exception to the latter, for a control-flow structural expression such as `(c1)` or `c1 && c2` - * where `cn` are constants we still consider the `cn`s to be a constant roots, even though their parent - * expression is also constant. - */ - private predicate constRoot(Expr root) { - exists(Expr c | - c.isConst() and - not c.getParent().(Expr).isConst() and - root = stripStructural(c) - ) - } - - /** - * Strips off any control-flow structural components from `e`. - */ - private Expr stripStructural(Expr e) { - if isControlFlowStructural(e) then result = stripStructural(e.getAChildExpr()) else result = e - } - - private class ControlFlowTree extends AstNode { - predicate firstNode(ControlFlow::Node first) { none() } - - predicate lastNode(ControlFlow::Node last, Completion cmpl) { - // propagate abnormal completion from children - lastNode(this.getAChild(), last, cmpl) and - not cmpl.isNormal() - } - - /** - * Holds if `succ` is a successor of `pred`, ignoring the execution of any - * deferred functions when a function ends. - */ - pragma[nomagic] - predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - lastNode(this.getChildTreeRanked(i), pred, normalCompletion()) and - firstNode(this.getChildTreeRanked(i + 1), succ) - ) - } - - /** Holds if `succ` is a successor of `pred`. */ - predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { this.succ0(pred, succ) } - - final ControlFlowTree getChildTreeRanked(int i) { - exists(int j | - result = this.getChildTree(j) and - j = rank[i + 1](int k | exists(this.getChildTree(k))) - ) - } - - ControlFlowTree getFirstChildTree() { result = this.getChildTreeRanked(0) } - - ControlFlowTree getLastChildTree() { - result = max(ControlFlowTree ch, int j | ch = this.getChildTree(j) | ch order by j) - } - - ControlFlowTree getChildTree(int i) { none() } - } - - private class AtomicTree extends ControlFlowTree { - ControlFlow::Node nd; - Completion cmpl; - - AtomicTree() { - exists(Expr e | - e = this and - e.isConst() and - nd = mkExprOrSkipNode(this) - | - if e.isPlatformIndependentConstant() and exists(e.getBoolValue()) - then cmpl = Bool(e.getBoolValue()) - else cmpl = Done() - ) - or - this instanceof Ident and - not this.(Expr).isConst() and - nd = mkExprOrSkipNode(this) and - cmpl = Done() - or - this instanceof BreakStmt and - nd = MkSkipNode(this) and - cmpl = Break(BranchTarget::of(this)) - or - this instanceof ContinueStmt and - nd = MkSkipNode(this) and - cmpl = Continue(BranchTarget::of(this)) - or - this instanceof Decl and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof EmptyStmt and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof FallthroughStmt and - nd = MkSkipNode(this) and - cmpl = Fallthrough() - or - this instanceof FuncLit and - nd = MkExprNode(this) and - cmpl = Done() - or - this instanceof PlainBlock and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof SelectorExpr and - not this.(SelectorExpr).getBase() instanceof ValueExpr and - nd = mkExprOrSkipNode(this) and - cmpl = Done() - or - this instanceof GenericFunctionInstantiationExpr and - nd = MkExprNode(this) and - cmpl = Done() - } - - override predicate firstNode(ControlFlow::Node first) { first = nd } - - override predicate lastNode(ControlFlow::Node last, Completion c) { last = nd and c = cmpl } - } - - abstract private class PostOrderTree extends ControlFlowTree { - abstract ControlFlow::Node getNode(); - - Completion getCompletion() { result = Done() } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - or - not exists(this.getChildTree(_)) and - first = this.getNode() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - last = this.getNode() and cmpl = this.getCompletion() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - super.succ0(pred, succ) - or - lastNode(this.getLastChildTree(), pred, normalCompletion()) and - succ = this.getNode() - } - } - - abstract private class PreOrderTree extends ControlFlowTree { - abstract ControlFlow::Node getNode(); - - override predicate firstNode(ControlFlow::Node first) { first = this.getNode() } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - lastNode(this.getLastChildTree(), last, cmpl) - or - not exists(this.getChildTree(_)) and - last = this.getNode() and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - super.succ0(pred, succ) - or - pred = this.getNode() and - firstNode(this.getFirstChildTree(), succ) - } - } - - private class WrapperTree extends ControlFlowTree { - WrapperTree() { - this instanceof ConstDecl or - this instanceof DeclStmt or - this instanceof ExprStmt or - this instanceof KeyValueExpr or - this instanceof LabeledStmt or - this instanceof ParenExpr or - this instanceof PlainBlock or - this instanceof VarDecl - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - lastNode(this.getLastChildTree(), last, cmpl) - or - exists(LoopStmt ls | this = ls.getBody() | - lastNode(this, last, Continue(BranchTarget::of(ls))) and - cmpl = Done() - ) - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.(DeclStmt).getDecl() - or - i = 0 and result = this.(ExprStmt).getExpr() - or - result = this.(GenDecl).getSpec(i) - or - exists(KeyValueExpr kv | kv = this | - not kv.getLiteral() instanceof StructLit and - i = 0 and - result = kv.getKey() - or - i = 1 and result = kv.getValue() - ) - or - i = 0 and result = this.(LabeledStmt).getStmt() - or - i = 0 and result = this.(ParenExpr).getExpr() - or - result = this.(PlainBlock).getStmt(i) - } - } - - private class AssignmentTree extends ControlFlowTree { - AssignmentTree() { - this instanceof Assignment or - this instanceof ValueSpec - } - - Expr getLhs(int i) { - result = this.(Assignment).getLhs(i) or - result = this.(ValueSpec).getNameExpr(i) - } - - int getNumLhs() { - result = this.(Assignment).getNumLhs() or - result = this.(ValueSpec).getNumName() - } - - Expr getRhs(int i) { - result = this.(Assignment).getRhs(i) or - result = this.(ValueSpec).getInit(i) - } - - int getNumRhs() { - result = this.(Assignment).getNumRhs() or - result = this.(ValueSpec).getNumInit() - } - - predicate isExtractingAssign() { this.getNumRhs() = 1 and this.getNumLhs() > 1 } - - override predicate firstNode(ControlFlow::Node first) { - not this instanceof RecvStmt and - firstNode(this.getLhs(0), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - ( - last = max(int i | | this.epilogueNode(i) order by i) - or - not exists(this.epilogueNode(_)) and - lastNode(this.getLastSubExprInEvalOrder(), last, normalCompletion()) - ) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - exists(int i | lastNode(this.getLhs(i), pred, normalCompletion()) | - firstNode(this.getLhs(i + 1), succ) - or - not this instanceof RecvStmt and - i = this.getNumLhs() - 1 and - ( - firstNode(this.getRhs(0), succ) - or - not exists(this.getRhs(_)) and - succ = this.epilogueNodeRanked(0) - ) - ) - or - exists(int i | - lastNode(this.getRhs(i), pred, normalCompletion()) and - firstNode(this.getRhs(i + 1), succ) - ) - or - not this instanceof RecvStmt and - lastNode(this.getRhs(this.getNumRhs() - 1), pred, normalCompletion()) and - succ = this.epilogueNodeRanked(0) - or - exists(int i | - pred = this.epilogueNodeRanked(i) and - succ = this.epilogueNodeRanked(i + 1) - ) - } - - ControlFlow::Node epilogueNodeRanked(int i) { - exists(int j | - result = this.epilogueNode(j) and - j = rank[i + 1](int k | exists(this.epilogueNode(k))) - ) - } - - private Expr getSubExprInEvalOrder(int evalOrder) { - if evalOrder < this.getNumLhs() - then result = this.getLhs(evalOrder) - else result = this.getRhs(evalOrder - this.getNumLhs()) - } - - private Expr getLastSubExprInEvalOrder() { - result = max(int i | | this.getSubExprInEvalOrder(i) order by i) - } - - private ControlFlow::Node epilogueNode(int i) { - i = -1 and - result = MkCompoundAssignRhsNode(this) - or - exists(int j | - result = MkExtractNode(this, j) and - i = 2 * j - or - result = MkZeroInitNode(any(ValueEntity v | this.getLhs(j) = v.getDeclaration())) and - i = 2 * j - or - result = MkAssignNode(this, j) and - i = 2 * j + 1 - ) - } - } - - private class BinaryExprTree extends PostOrderTree, BinaryExpr { - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - private predicate equalityTestMayPanic() { - this instanceof EqualityTestExpr and - exists(Type t | - t = this.getAnOperand().getType().getUnderlyingType() and - ( - t instanceof InterfaceType or // panic due to comparison of incomparable interface values - t instanceof StructType or // may contain an interface-typed field - t instanceof ArrayType // may be an array of interface values - ) - ) - } - - override Completion getCompletion() { - result = PostOrderTree.super.getCompletion() - or - // runtime panic due to division by zero or comparison of incomparable interface values - (this instanceof DivExpr or this.equalityTestMayPanic()) and - not this.(Expr).isConst() and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getLeftOperand() - or - i = 1 and result = this.getRightOperand() - } - } - - private class LogicalBinaryExprTree extends BinaryExprTree, LogicalBinaryExpr { - boolean shortCircuit; - - LogicalBinaryExprTree() { - this instanceof LandExpr and shortCircuit = false - or - this instanceof LorExpr and shortCircuit = true - } - - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getLeftOperand(), outcome) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - lastNode(this.getAnOperand(), last, cmpl) and - not cmpl.isNormal() - or - if isCond(this) - then ( - last = this.getGuard(shortCircuit) and - cmpl = Bool(shortCircuit) - or - lastNode(this.getRightOperand(), last, cmpl) - ) else ( - last = MkExprNode(this) and - cmpl = Done() - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(Completion lcmpl | - lastNode(this.getLeftOperand(), pred, lcmpl) and - succ = this.getGuard(lcmpl.getOutcome()) - ) - or - pred = this.getGuard(shortCircuit.booleanNot()) and - firstNode(this.getRightOperand(), succ) - or - not isCond(this) and - ( - pred = this.getGuard(shortCircuit) and - succ = MkExprNode(this) - or - exists(Completion rcmpl | - lastNode(this.getRightOperand(), pred, rcmpl) and - rcmpl.isNormal() and - succ = MkExprNode(this) - ) - ) - } - } - - private class CallExprTree extends PostOrderTree, CallExpr { - private predicate isSpecial() { - this = any(DeferStmt defer).getCall() or - this = any(GoStmt go).getCall() - } - - override ControlFlow::Node getNode() { - not this.isSpecial() and - result = MkExprNode(this) - } - - override Completion getCompletion() { - (not exists(this.getTarget()) or this.getTarget().mayReturnNormally()) and - result = Done() - or - (not exists(this.getTarget()) or this.getTarget().mayPanic()) and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getCalleeExpr() - or - result = this.getArgument(i - 1) and - // calls to `make` and `new` can have type expressions as arguments - not result instanceof TypeExpr - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - // interpose implicit argument destructuring nodes between last argument - // and call itself; this is for cases like `f(g())` where `g` has multiple - // results - exists(ControlFlow::Node mid | PostOrderTree.super.succ0(pred, mid) | - if mid = this.getNode() then succ = this.getEpilogueNode(0) else succ = mid - ) - or - exists(int i | - pred = this.getEpilogueNode(i) and - succ = this.getEpilogueNode(i + 1) - ) - } - - private ControlFlow::Node getEpilogueNode(int i) { - result = MkExtractNode(this, i) - or - i = max(int j | exists(MkExtractNode(this, j))) + 1 and - result = this.getNode() - or - not exists(MkExtractNode(this, _)) and - i = 0 and - result = this.getNode() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - PostOrderTree.super.lastNode(last, cmpl) - or - this.isSpecial() and - lastNode(this.getLastChildTree(), last, cmpl) - } - } - - private class CaseClauseTree extends ControlFlowTree, CaseClause { - private ControlFlow::Node getExprStart(int i) { - firstNode(this.getExpr(i), result) - or - this.getExpr(i) instanceof TypeExpr and - result = MkCaseCheckNode(this, i) - } - - ControlFlow::Node getExprEnd(int i, Boolean outcome) { - exists(Expr e | e = this.getExpr(i) | - result = MkConditionGuardNode(e, outcome) - or - not exists(MkConditionGuardNode(e, _)) and - result = MkCaseCheckNode(this, i) - ) - } - - private ControlFlow::Node getBodyStart() { - firstNode(this.getStmt(0), result) or result = MkSkipNode(this) - } - - override predicate firstNode(ControlFlow::Node first) { - first = this.getExprStart(0) - or - not exists(this.getAnExpr()) and - first = MkTypeSwitchImplicitVariable(this, _, _) - or - not exists(this.getAnExpr()) and - not exists(MkTypeSwitchImplicitVariable(this, _, _)) and - first = this.getBodyStart() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // TODO: shouldn't be here - last = this.getExprEnd(this.getNumExpr() - 1, false) and - cmpl = Bool(false) - or - last = MkSkipNode(this) and - cmpl = Done() - or - lastNode(this.getStmt(this.getNumStmt() - 1), last, cmpl) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - pred = MkTypeSwitchImplicitVariable(this, _, _) and - succ = this.getBodyStart() - or - exists(int i | - lastNode(this.getExpr(i), pred, normalCompletion()) and - succ = MkCaseCheckNode(this, i) - or - // visit guard node if there is one - pred = MkCaseCheckNode(this, i) and - succ = this.getExprEnd(i, _) and - succ != pred // this avoids self-loops if there isn't a guard node - or - pred = this.getExprEnd(i, false) and - succ = this.getExprStart(i + 1) - or - this.isPassingEdge(i, pred, succ, _) - ) - } - - predicate isPassingEdge(int i, ControlFlow::Node pred, ControlFlow::Node succ, Expr testExpr) { - pred = this.getExprEnd(i, true) and - testExpr = this.getExpr(i) and - ( - succ = MkTypeSwitchImplicitVariable(this, _, _) - or - not exists(MkTypeSwitchImplicitVariable(this, _, _)) and - succ = this.getBodyStart() - ) - } - - override ControlFlowTree getChildTree(int i) { result = this.getStmt(i) } - } - - private class CommClauseTree extends ControlFlowTree, CommClause { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getComm(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkSkipNode(this) and - cmpl = Done() - or - lastNode(this.getStmt(this.getNumStmt() - 1), last, cmpl) - } - - override ControlFlowTree getChildTree(int i) { result = this.getStmt(i) } - } - - private class CompositeLiteralTree extends ControlFlowTree, CompositeLit { - private ControlFlow::Node getElementInit(int i) { - result = MkLiteralElementInitNode(this.getElement(i)) - } - - private ControlFlow::Node getElementStart(int i) { - exists(Expr elt | elt = this.getElement(i) | - result = MkImplicitLiteralElementIndex(elt) - or - (elt instanceof KeyValueExpr or this instanceof StructLit) and - firstNode(this.getElement(i), result) - ) - } - - override predicate firstNode(ControlFlow::Node first) { first = MkExprNode(this) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = this.getElementInit(this.getNumElement() - 1) and - cmpl = Done() - or - not exists(this.getElement(_)) and - last = MkExprNode(this) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - this.firstNode(pred) and - succ = this.getElementStart(0) - or - exists(int i | - pred = MkImplicitLiteralElementIndex(this.getElement(i)) and - firstNode(this.getElement(i), succ) - or - lastNode(this.getElement(i), pred, normalCompletion()) and - succ = this.getElementInit(i) - or - pred = this.getElementInit(i) and - succ = this.getElementStart(i + 1) - ) - } - } - - private class ConversionExprTree extends PostOrderTree, ConversionExpr { - override Completion getCompletion() { - // conversions of a slice to an array pointer are the only kind that may panic - this.getType().(PointerType).getBaseType() instanceof ArrayType and - result = Panic() - or - result = Done() - } - - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getOperand() } - } - - private class DeferStmtTree extends PostOrderTree, DeferStmt { - override ControlFlow::Node getNode() { result = MkDeferNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getCall() } - } - - private class FuncDeclTree extends PostOrderTree, FuncDecl { - override ControlFlow::Node getNode() { result = MkFuncDeclNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getNameExpr() } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - // override to prevent panic propagation out of function declarations - last = this.getNode() and cmpl = Done() - } - } - - private class GoStmtTree extends PostOrderTree, GoStmt { - override ControlFlow::Node getNode() { result = MkGoNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getCall() } - } - - private class IfStmtTree extends ControlFlowTree, IfStmt { - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getCond(), outcome) - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getInit(), first) - or - not exists(this.getInit()) and - firstNode(this.getCond(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - lastNode(this.getThen(), last, cmpl) - or - lastNode(this.getElse(), last, cmpl) - or - not exists(this.getElse()) and - last = this.getGuard(false) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getInit(), pred, normalCompletion()) and - firstNode(this.getCond(), succ) - or - exists(Completion condCmpl | - lastNode(this.getCond(), pred, condCmpl) and - succ = MkConditionGuardNode(this.getCond(), condCmpl.getOutcome()) - ) - or - pred = this.getGuard(true) and - firstNode(this.getThen(), succ) - or - pred = this.getGuard(false) and - firstNode(this.getElse(), succ) - } - } - - private class IndexExprTree extends ControlFlowTree, IndexExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = mkExprOrSkipNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getBase(), pred, normalCompletion()) and - ( - succ = MkImplicitDeref(this.getBase()) - or - not exists(MkImplicitDeref(this.getBase())) and - firstNode(this.getIndex(), succ) - ) - or - pred = MkImplicitDeref(this.getBase()) and - firstNode(this.getIndex(), succ) - or - lastNode(this.getIndex(), pred, normalCompletion()) and - succ = mkExprOrSkipNode(this) - } - } - - private class LoopTree extends ControlFlowTree, LoopStmt { - BranchTarget getLabel() { result = BranchTarget::of(this) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - exists(Completion inner | lastNode(this.getBody(), last, inner) and not inner.isNormal() | - if inner = Break(this.getLabel()) - then cmpl = Done() - else ( - not inner = Continue(this.getLabel()) and - cmpl = inner - ) - ) - } - } - - private class FileTree extends ControlFlowTree, File { - FileTree() { exists(this.getADecl()) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { none() } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - pred = MkEntryNode(this) and - firstNode(this.getDecl(0), succ) - or - exists(int i, Completion inner | lastNode(this.getDecl(i), pred, inner) | - not inner.isNormal() - or - i = this.getNumDecl() - 1 - ) and - succ = MkExitNode(this) - } - - override ControlFlowTree getChildTree(int i) { result = this.getDecl(i) } - } - - private class ForTree extends LoopTree, ForStmt { - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getCond(), outcome) - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - LoopTree.super.lastNode(last, cmpl) - or - lastNode(this.getInit(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getCond(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getPost(), last, cmpl) and - not cmpl.isNormal() - or - last = this.getGuard(false) and - cmpl = Done() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getInit() - or - i = 1 and result = this.getCond() - or - i = 2 and result = this.getBody() - or - i = 3 and result = this.getPost() - or - i = 4 and result = this.getCond() - or - i = 5 and result = this.getBody() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i, ControlFlowTree predTree, Completion cmpl | - predTree = this.getChildTreeRanked(i) and - lastNode(predTree, pred, cmpl) and - cmpl.isNormal() - | - if predTree = this.getCond() - then succ = this.getGuard(cmpl.getOutcome()) - else firstNode(this.getChildTreeRanked(i + 1), succ) - ) - or - pred = this.getGuard(true) and - firstNode(this.getBody(), succ) - } - } - - private class FuncDefTree extends ControlFlowTree, FuncDef { - FuncDefTree() { exists(this.getBody()) } - - pragma[noinline] - private MkEntryNode getEntry() { result = MkEntryNode(this) } - - private Parameter getParameterRanked(int i) { - result = rank[i + 1](Parameter p, int j | p = this.getParameter(j) | p order by j) - } - - private ControlFlow::Node getPrologueNode(int i) { - i = -1 and result = this.getEntry() - or - exists(int numParm, int numRes | - numParm = count(this.getParameter(_)) and - numRes = count(this.getResultVar(_)) - | - exists(int j, Parameter p | p = this.getParameterRanked(j) | - i = 2 * j and result = MkArgumentNode(p) - or - i = 2 * j + 1 and result = MkParameterInit(p) - ) - or - exists(int j, ResultVariable v | v = this.getResultVar(j) | - i = 2 * numParm + 2 * j and - result = MkZeroInitNode(v) - or - i = 2 * numParm + 2 * j + 1 and - result = MkResultInit(v) - ) - or - i = 2 * numParm + 2 * numRes and - firstNode(this.getBody(), result) - ) - } - - private ControlFlow::Node getEpilogueNode(int i) { - result = MkResultReadNode(this.getResultVar(i)) - or - i = count(this.getAResultVar()) and - result = MkExitNode(this) - } - - pragma[noinline] - private predicate firstDefer(ControlFlow::Node nd) { - exists(DeferStmt defer | - nd = MkExprNode(defer.getCall()) and - // `defer` can be the first `defer` statement executed - // there is always a predecessor node because the `defer`'s call is always - // evaluated before the defer statement itself - MkDeferNode(defer) = succ0(notDeferSucc0*(this.getEntry())) - ) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { none() } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - pred = this.getPrologueNode(i) and - succ = this.getPrologueNode(i + 1) - ) - or - exists(GotoStmt goto, LabeledStmt ls | - pred = MkSkipNode(goto) and - this = goto.getEnclosingFunction() and - this = ls.getEnclosingFunction() and - goto.getLabel() = ls.getLabel() and - firstNode(ls, succ) - ) - or - exists(int i | - pred = this.getEpilogueNode(i) and - succ = this.getEpilogueNode(i + 1) - ) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - this.succ0(pred, succ) - or - exists(Completion cmpl | - lastNode(this.getBody(), pred, cmpl) and - // last node of function body can be reached without going through a `defer` statement - pred = notDeferSucc0*(this.getEntry()) - | - // panic goes directly to exit, non-panic reads result variables first - if cmpl = Panic() then succ = MkExitNode(this) else succ = this.getEpilogueNode(0) - ) - or - lastNode(this.getBody(), pred, _) and - exists(DeferStmt defer | defer = this.getADeferStmt() | - succ = MkExprNode(defer.getCall()) and - // the last `DeferStmt` executed before pred is this `defer` - pred = notDeferSucc0*(MkDeferNode(defer)) - ) - or - exists(DeferStmt predDefer, DeferStmt succDefer | - predDefer = this.getADeferStmt() and - succDefer = this.getADeferStmt() - | - // reversed because `defer`s are executed in LIFO order - MkDeferNode(predDefer) = nextDefer(MkDeferNode(succDefer)) and - pred = MkExprNode(predDefer.getCall()) and - succ = MkExprNode(succDefer.getCall()) - ) - or - this.firstDefer(pred) and - ( - // conservatively assume that we might either panic (and hence skip the result reads) - // or not - succ = MkExitNode(this) - or - succ = this.getEpilogueNode(0) - ) - } - } - - private class GotoTree extends ControlFlowTree, GotoStmt { - override predicate firstNode(ControlFlow::Node first) { first = MkSkipNode(this) } - } - - private class IncDecTree extends ControlFlowTree, IncDecStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getOperand(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkIncDecNode(this) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getOperand(), pred, normalCompletion()) and - succ = MkImplicitOne(this) - or - pred = MkImplicitOne(this) and - succ = MkIncDecRhs(this) - or - pred = MkIncDecRhs(this) and - succ = MkIncDecNode(this) - } - } - - private class RangeTree extends LoopTree, RangeStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getDomain(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - LoopTree.super.lastNode(last, cmpl) - or - last = MkNextNode(this) and - cmpl = Done() - or - lastNode(this.getKey(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getValue(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getDomain(), last, cmpl) and - not cmpl.isNormal() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getDomain(), pred, normalCompletion()) and - succ = MkNextNode(this) - or - pred = MkNextNode(this) and - ( - firstNode(this.getKey(), succ) - or - not exists(this.getKey()) and - firstNode(this.getBody(), succ) - ) - or - lastNode(this.getKey(), pred, normalCompletion()) and - ( - firstNode(this.getValue(), succ) - or - not exists(this.getValue()) and - succ = MkExtractNode(this, 0) - ) - or - lastNode(this.getValue(), pred, normalCompletion()) and - succ = MkExtractNode(this, 0) - or - pred = MkExtractNode(this, 0) and - ( - if exists(this.getValue()) - then succ = MkExtractNode(this, 1) - else - if exists(MkAssignNode(this, 0)) - then succ = MkAssignNode(this, 0) - else - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkExtractNode(this, 1) and - ( - if exists(MkAssignNode(this, 0)) - then succ = MkAssignNode(this, 0) - else - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkAssignNode(this, 0) and - ( - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkAssignNode(this, 1) and - firstNode(this.getBody(), succ) - or - exists(Completion inner | - lastNode(this.getBody(), pred, inner) and - (inner.isNormal() or inner = Continue(BranchTarget::of(this))) and - succ = MkNextNode(this) - ) - } - } - - private class RecvStmtTree extends ControlFlowTree, RecvStmt { - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getExpr().getOperand(), first) - } - } - - private class ReturnStmtTree extends PostOrderTree, ReturnStmt { - override ControlFlow::Node getNode() { result = MkReturnNode(this) } - - override Completion getCompletion() { result = Return() } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - lastNode(this.getExpr(i), pred, normalCompletion()) and - succ = this.complete(i) - or - pred = MkExtractNode(this, i) and - succ = this.after(i) - or - pred = MkResultWriteNode(_, i, this) and - succ = this.next(i) - ) - } - - private ControlFlow::Node complete(int i) { - result = MkExtractNode(this, i) - or - not exists(MkExtractNode(this, _)) and - result = this.after(i) - } - - private ControlFlow::Node after(int i) { - result = MkResultWriteNode(_, i, this) - or - not exists(MkResultWriteNode(_, i, this)) and - result = this.next(i) - } - - private ControlFlow::Node next(int i) { - firstNode(this.getExpr(i + 1), result) - or - exists(MkExtractNode(this, _)) and - result = this.complete(i + 1) - or - i + 1 = this.getEnclosingFunction().getType().getNumResult() and - result = this.getNode() - } - - override ControlFlowTree getChildTree(int i) { result = this.getExpr(i) } - } - - private class SelectStmtTree extends ControlFlowTree, SelectStmt { - private BranchTarget getLabel() { result = BranchTarget::of(this) } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getNonDefaultCommClause(0), first) - or - this.getNumNonDefaultCommClause() = 0 and - first = MkSelectNode(this) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - exists(Completion inner | lastNode(this.getACommClause(), last, inner) | - if inner = Break(this.getLabel()) then cmpl = Done() else cmpl = inner - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - exists(CommClause cc, int i, Stmt comm | - cc = this.getNonDefaultCommClause(i) and - comm = cc.getComm() and - ( - comm instanceof RecvStmt and - lastNode(comm.(RecvStmt).getExpr().getOperand(), pred, normalCompletion()) - or - comm instanceof SendStmt and - lastNode(comm.(SendStmt).getValue(), pred, normalCompletion()) - ) - | - firstNode(this.getNonDefaultCommClause(i + 1), succ) - or - i = this.getNumNonDefaultCommClause() - 1 and - succ = MkSelectNode(this) - ) - or - pred = MkSelectNode(this) and - exists(CommClause cc, Stmt comm | - cc = this.getNonDefaultCommClause(_) and comm = cc.getComm() - | - comm instanceof RecvStmt and - succ = MkExprNode(comm.(RecvStmt).getExpr()) - or - comm instanceof SendStmt and - succ = MkSendNode(comm) - ) - or - pred = MkSelectNode(this) and - exists(CommClause cc | cc = this.getDefaultCommClause() | - firstNode(cc.getStmt(0), succ) - or - succ = MkSkipNode(cc) - ) - or - exists(CommClause cc, RecvStmt recv | cc = this.getCommClause(_) and recv = cc.getComm() | - pred = MkExprNode(recv.getExpr()) and - ( - firstNode(recv.getLhs(0), succ) - or - not exists(recv.getLhs(0)) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - lastNode(recv.getLhs(0), pred, normalCompletion()) and - not exists(recv.getLhs(1)) and - ( - succ = MkAssignNode(recv, 0) - or - not exists(MkAssignNode(recv, 0)) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - lastNode(recv.getLhs(1), pred, normalCompletion()) and - succ = MkExtractNode(recv, 0) - or - ( - pred = MkAssignNode(recv, 0) and - not exists(MkExtractNode(recv, 1)) - or - pred = MkExtractNode(recv, 1) and - not exists(MkAssignNode(recv, 1)) - or - pred = MkAssignNode(recv, 1) - ) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - exists(CommClause cc, SendStmt ss | - cc = this.getCommClause(_) and - ss = cc.getComm() and - pred = MkSendNode(ss) - | - firstNode(cc.getStmt(0), succ) - or - succ = MkSkipNode(cc) - ) - } - } - - private class SelectorExprTree extends ControlFlowTree, SelectorExpr { - SelectorExprTree() { this.getBase() instanceof ValueExpr } - - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = mkExprOrSkipNode(this) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | pred = this.getStepWithRank(i) and succ = this.getStepWithRank(i + 1)) - } - - private ControlFlow::Node getStepOrdered(int i) { - i = -2 and lastNode(this.getBase(), result, normalCompletion()) - or - i = -1 and result = MkImplicitDeref(this.getBase()) - or - exists(int maxIndex | - maxIndex = max(int k | k = 0 or exists(MkImplicitFieldSelection(this, k, _))) - | - result = MkImplicitFieldSelection(this, maxIndex - i, _) - or - i = maxIndex and - result = mkExprOrSkipNode(this) - ) - } - - private ControlFlow::Node getStepWithRank(int i) { - exists(int j | - result = this.getStepOrdered(j) and - j = rank[i + 1](int k | exists(this.getStepOrdered(k))) - ) - } - } - - private class SendStmtTree extends ControlFlowTree, SendStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getChannel(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkSendNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - not this = any(CommClause cc).getComm() and - lastNode(this.getValue(), pred, normalCompletion()) and - succ = MkSendNode(this) - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getChannel() - or - i = 1 and result = this.getValue() - } - } - - private class SliceExprTree extends ControlFlowTree, SliceExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = MkExprNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - lastNode(this.getBase(), pred, normalCompletion()) and - ( - succ = MkImplicitDeref(this.getBase()) - or - not exists(MkImplicitDeref(this.getBase())) and - (firstNode(this.getLow(), succ) or succ = MkImplicitLowerSliceBound(this)) - ) - or - pred = MkImplicitDeref(this.getBase()) and - (firstNode(this.getLow(), succ) or succ = MkImplicitLowerSliceBound(this)) - or - (lastNode(this.getLow(), pred, normalCompletion()) or pred = MkImplicitLowerSliceBound(this)) and - (firstNode(this.getHigh(), succ) or succ = MkImplicitUpperSliceBound(this)) - or - (lastNode(this.getHigh(), pred, normalCompletion()) or pred = MkImplicitUpperSliceBound(this)) and - (firstNode(this.getMax(), succ) or succ = MkImplicitMaxSliceBound(this)) - or - (lastNode(this.getMax(), pred, normalCompletion()) or pred = MkImplicitMaxSliceBound(this)) and - succ = MkExprNode(this) - } - } - - private class StarExprTree extends PostOrderTree, StarExpr { - override ControlFlow::Node getNode() { result = mkExprOrSkipNode(this) } - - override Completion getCompletion() { result = Done() or result = Panic() } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getBase() } - } - - private class SwitchTree extends ControlFlowTree, SwitchStmt { - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getInit(), first) - or - not exists(this.getInit()) and - ( - firstNode(this.(ExpressionSwitchStmt).getExpr(), first) - or - first = MkImplicitTrue(this) - or - firstNode(this.(TypeSwitchStmt).getTest(), first) - ) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - lastNode(this.getInit(), last, cmpl) and - not cmpl.isNormal() - or - ( - lastNode(this.(ExpressionSwitchStmt).getExpr(), last, cmpl) - or - lastNode(this.(TypeSwitchStmt).getTest(), last, cmpl) - ) and - ( - not cmpl.isNormal() - or - not exists(this.getDefault()) - ) - or - last = MkImplicitTrue(this) and - cmpl = Bool(true) and - this.getNumCase() = 0 - or - exists(CaseClause cc, int i, Completion inner | - cc = this.getCase(i) and lastNode(cc, last, inner) - | - not exists(this.getDefault()) and - i = this.getNumCase() - 1 and - last = cc.(CaseClauseTree).getExprEnd(cc.getNumExpr() - 1, false) and - inner.isNormal() and - cmpl = inner - or - not last = cc.(CaseClauseTree).getExprEnd(_, _) and - inner.isNormal() and - cmpl = inner - or - if inner = Break(BranchTarget::of(this)) - then cmpl = Done() - else ( - not inner.isNormal() and inner != Fallthrough() and cmpl = inner - ) - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - lastNode(this.getInit(), pred, normalCompletion()) and - ( - firstNode(this.(ExpressionSwitchStmt).getExpr(), succ) or - succ = MkImplicitTrue(this) or - firstNode(this.(TypeSwitchStmt).getTest(), succ) - ) - or - ( - lastNode(this.(ExpressionSwitchStmt).getExpr(), pred, normalCompletion()) or - pred = MkImplicitTrue(this) or - lastNode(this.(TypeSwitchStmt).getTest(), pred, normalCompletion()) - ) and - ( - firstNode(this.getNonDefaultCase(0), succ) - or - not exists(this.getANonDefaultCase()) and - firstNode(this.getDefault(), succ) - ) - or - exists(CaseClause cc, int i | - cc = this.getNonDefaultCase(i) and - lastNode(cc, pred, normalCompletion()) and - pred = cc.(CaseClauseTree).getExprEnd(_, false) - | - firstNode(this.getNonDefaultCase(i + 1), succ) - or - i = this.getNumNonDefaultCase() - 1 and - firstNode(this.getDefault(), succ) - ) - or - exists(CaseClause cc, int i, CaseClause next | - cc = this.getCase(i) and - lastNode(cc, pred, Fallthrough()) and - next = this.getCase(i + 1) - | - firstNode(next.getStmt(0), succ) - or - succ = MkSkipNode(next) - ) - } - } - - private class TypeAssertTree extends PostOrderTree, TypeAssertExpr { - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - override Completion getCompletion() { - result = Done() - or - // panic due to type mismatch, but not if the assertion appears in an assignment or - // initialization with two variables or a type-switch - not exists(Assignment assgn | assgn.getNumLhs() = 2 and this = assgn.getRhs().stripParens()) and - not exists(ValueSpec vs | vs.getNumName() = 2 and this = vs.getInit().stripParens()) and - not exists(TypeSwitchStmt ts | this = ts.getExpr()) and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getExpr() } - } - - private class UnaryExprTree extends ControlFlowTree, UnaryExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getOperand(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - last = MkExprNode(this) and - ( - cmpl = Done() - or - this instanceof DerefExpr and cmpl = Panic() - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - not this = any(RecvStmt recv).getExpr() and - lastNode(this.getOperand(), pred, normalCompletion()) and - succ = MkExprNode(this) - } - } - - private ControlFlow::Node mkExprOrSkipNode(Expr e) { - result = MkExprNode(e) or - result = MkSkipNode(e) - } - - /** Holds if evaluation of `root` may start at `first`. */ - cached - predicate firstNode(ControlFlowTree root, ControlFlow::Node first) { root.firstNode(first) } - - /** Holds if evaluation of `root` may complete normally after `last`. */ - cached - predicate lastNode(ControlFlowTree root, ControlFlow::Node last) { - lastNode(root, last, normalCompletion()) - } - - private predicate lastNode(ControlFlowTree root, ControlFlow::Node last, Completion cmpl) { - root.lastNode(last, cmpl) - } - - /** Gets a successor of `nd` that is not a `defer` node */ - private ControlFlow::Node notDeferSucc0(ControlFlow::Node nd) { - not result = MkDeferNode(_) and - result = succ0(nd) - } - - /** Gets `defer` statements that can be the first defer statement after `nd` in the CFG */ - private ControlFlow::Node nextDefer(ControlFlow::Node nd) { - nd = MkDeferNode(_) and - result = MkDeferNode(_) and - ( - result = succ0(nd) - or - result = succ0(notDeferSucc0+(nd)) - ) - } - - /** - * Holds if the function `f` may return without panicking, exiting the process, or looping forever. - * - * This is defined conservatively, and so may also hold of a function that in fact - * cannot return normally, but never fails to hold of a function that can return normally. - */ - cached - predicate mayReturnNormally(ControlFlowTree root) { - exists(Completion cmpl | lastNode(root, _, cmpl) and cmpl != Panic()) - } - - /** - * Holds if `pred` is the node for the case `testExpr` in an expression - * switch statement which is switching on `switchExpr`, and `succ` is the - * node to be executed next if the case test succeeds. - */ - cached - predicate isSwitchCaseTestPassingEdge( - ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr - ) { - exists(ExpressionSwitchStmt ess | ess.getExpr() = switchExpr | - ess.getACase().(CaseClauseTree).isPassingEdge(_, pred, succ, testExpr) - ) - } - - /** - * Gets a successor of `nd`, that is, a node that is executed after `nd`, - * ignoring the execution of any deferred functions when a function ends. - */ - pragma[nomagic] - private ControlFlow::Node succ0(ControlFlow::Node nd) { - any(ControlFlowTree tree).succ0(nd, result) - } - - /** Gets a successor of `nd`, that is, a node that is executed after `nd`. */ - cached - ControlFlow::Node succ(ControlFlow::Node nd) { any(ControlFlowTree tree).succ(nd, result) } -} diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll new file mode 100644 index 000000000000..00f366ea38c6 --- /dev/null +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -0,0 +1,1440 @@ +/** + * Provides the shared CFG library instantiation for Go. + * + * Everything is wrapped in `GoCfg` to avoid name conflicts with the existing + * CFG implementation during the transition. + */ +overlay[local] +module; + +private import codeql.controlflow.ControlFlowGraph as CfgLib +private import codeql.controlflow.SuccessorType + +/** Contains the shared CFG library instantiation for Go. */ +module GoCfg { + private import go as Go + + private module Cfg0 = CfgLib::Make0; + + private module Cfg1 = Cfg0::Make1; + + private module Cfg2 = Cfg1::Make2; + + private import Cfg0 + private import Cfg1 + private import Cfg2 + import Public + + /** Holds if `e` has an implicit field selection at `index` for `implicitField`. */ + predicate implicitFieldSelection(Go::AstNode e, int index, Go::Field implicitField) { + Input::implicitFieldSelection(e, index, implicitField) + } + + /** Provides an implementation of the AST signature for Go. */ + private module Ast implements CfgLib::AstSig { + class AstNode = Go::AstNode; + + private predicate skipCfg(AstNode e) { + e instanceof Go::TypeExpr and not e instanceof Go::FuncTypeExpr + or + e = any(Go::FieldDecl f).getTag() + or + e instanceof Go::KeyValueExpr and not e = any(Go::CompositeLit lit).getAnElement() + or + e = any(Go::SelectorExpr sel).getSelector() + or + e = any(Go::StructLit sl).getKey(_) + or + e instanceof Go::Ident and not e instanceof Go::ReferenceExpr + or + e instanceof Go::SelectorExpr and not e instanceof Go::ReferenceExpr + or + e instanceof Go::ReferenceExpr and not e.(Go::ReferenceExpr).isRvalue() + or + e instanceof Go::ParenExpr + or + e = any(Go::ImportSpec is).getPathExpr() + or + e.getParent*() = any(Go::ArrayTypeExpr ate).getLength() + } + + AstNode getChild(AstNode n, int index) { + not skipCfg(result) and not skipCfg(n) and result = n.getChild(index) + } + + class Callable extends AstNode { + Callable() { + exists(this.(Go::FuncDef).getBody()) + or + exists(this.(Go::File).getADecl()) + } + } + + AstNode callableGetBody(Callable c) { result = c } + + Callable getEnclosingCallable(AstNode node) { + result = node and node instanceof Callable + or + not node instanceof Callable and result = node.getEnclosingFunction() + or + not node instanceof Callable and + not exists(node.getEnclosingFunction()) and + result = node.getFile() and + result instanceof Callable + } + + class Stmt = Go::Stmt; + + class Expr = Go::Expr; + + class BlockStmt extends Go::BlockStmt { + BlockStmt() { + not this = any(Go::SwitchStmt sw).getBody() and + not this = any(Go::SelectStmt sel).getBody() + } + + override Stmt getStmt(int n) { result = Go::BlockStmt.super.getStmt(n) } + + Stmt getLastStmt() { + exists(int last | result = this.getStmt(last) and not exists(this.getStmt(last + 1))) + } + } + + class ExprStmt = Go::ExprStmt; + + /** If statements without init (those with init use custom steps). */ + class IfStmt extends Stmt { + IfStmt() { this instanceof Go::IfStmt and not exists(this.(Go::IfStmt).getInit()) } + + Expr getCondition() { result = this.(Go::IfStmt).getCond() } + + Stmt getThen() { result = this.(Go::IfStmt).getThen() } + + Stmt getElse() { result = this.(Go::IfStmt).getElse() } + } + + class LoopStmt extends Stmt { + LoopStmt() { this instanceof Go::LoopStmt } + + Stmt getBody() { result = this.(Go::LoopStmt).getBody() } + } + + class WhileStmt extends LoopStmt { + WhileStmt() { none() } + + Expr getCondition() { none() } + } + + class DoStmt extends LoopStmt { + DoStmt() { none() } + + Expr getCondition() { none() } + } + + class ForStmt extends LoopStmt { + ForStmt() { none() } + + Expr getInit(int index) { none() } + + Expr getCondition() { none() } + + Expr getUpdate(int index) { none() } + } + + class ForeachStmt extends LoopStmt { + ForeachStmt() { none() } + + Expr getVariable() { none() } + + Expr getCollection() { none() } + } + + class BreakStmt = Go::BreakStmt; + + class ContinueStmt = Go::ContinueStmt; + + class ReturnStmt extends Go::ReturnStmt { + override Expr getExpr() { result = Go::ReturnStmt.super.getExpr() } + } + + class ThrowStmt extends Stmt { + ThrowStmt() { none() } + + Expr getExpr() { none() } + } + + class TryStmt extends Stmt { + TryStmt() { none() } + + Stmt getBody() { none() } + + CatchClause getCatch(int index) { none() } + + Stmt getFinally() { none() } + } + + class CatchClause extends AstNode { + CatchClause() { none() } + + AstNode getVariable() { none() } + + Expr getCondition() { none() } + + Stmt getBody() { none() } + } + + class Switch extends AstNode { + Switch() { none() } + + Expr getExpr() { none() } + + Case getCase(int index) { none() } + + Stmt getStmt(int index) { none() } + } + + class Case extends AstNode { + Case() { none() } + + AstNode getAPattern() { none() } + + Expr getGuard() { none() } + + AstNode getBody() { none() } + } + + class DefaultCase extends Case { + DefaultCase() { none() } + } + + class ConditionalExpr extends Expr { + ConditionalExpr() { none() } + + Expr getCondition() { none() } + + Expr getThen() { none() } + + Expr getElse() { none() } + } + + class BinaryExpr = Go::BinaryExpr; + + class LogicalAndExpr = Go::LandExpr; + + class LogicalOrExpr = Go::LorExpr; + + class NullCoalescingExpr extends BinaryExpr { + NullCoalescingExpr() { none() } + } + + class UnaryExpr = Go::UnaryExpr; + + class LogicalNotExpr = Go::NotExpr; + + class BooleanLiteral extends Expr { + boolean val; + + BooleanLiteral() { + this.(Go::Ident).getName() = "true" and val = true + or + this.(Go::Ident).getName() = "false" and val = false + } + + boolean getValue() { result = val } + } + } + + /** The Input module implementing InputSig1 and InputSig2 for Go. */ + private module Input implements Cfg0::InputSig1, Cfg1::InputSig2 { + predicate cfgCachedStageRef() { CfgCachedStage::ref() } + + private newtype TLabel = + TGoLabel(string l) { exists(Go::LabeledStmt ls | l = ls.getLabel()) } or + TFallthrough() + + class Label extends TLabel { + string toString() { + exists(string l | this = TGoLabel(l) and result = l) + or + this = TFallthrough() and result = "fallthrough" + } + } + + private Label getLabelOfStmt(Go::Stmt s) { + exists(Go::LabeledStmt l | s = l.getStmt() | + result = TGoLabel(l.getLabel()) or result = getLabelOfStmt(l) + ) + } + + predicate hasLabel(Ast::AstNode n, Label l) { + l = getLabelOfStmt(n) + or + l = TGoLabel(n.(Go::BreakStmt).getLabel()) + or + l = TGoLabel(n.(Go::ContinueStmt).getLabel()) + or + l = TFallthrough() and n instanceof Go::FallthroughStmt + } + + predicate inConditionalContext(Ast::AstNode n, ConditionKind kind) { + kind.isBoolean() and + ( + exists(Go::IfStmt ifstmt | ifstmt.getCond() = n and exists(ifstmt.getInit())) + or + n = any(Go::ForStmt fs).getCond() + or + exists(Go::ExpressionSwitchStmt ess | + not exists(ess.getExpr()) and n = ess.getACase().(Go::CaseClause).getExpr(_) + ) + ) + } + + predicate preOrderExpr(Ast::Expr e) { none() } + + predicate postOrInOrder(Ast::AstNode n) { + n instanceof Go::CallExpr and + not n = any(Go::DeferStmt defer).getCall() and + not n = any(Go::GoStmt go_).getCall() + or + n instanceof Go::BinaryExpr and not n instanceof Go::LogicalBinaryExpr + or + n instanceof Go::UnaryExpr and not n instanceof Go::NotExpr + or + n instanceof Go::ConversionExpr + or + n instanceof Go::TypeAssertExpr + or + n instanceof Go::IndexExpr + or + n instanceof Go::SliceExpr + or + n instanceof Go::CompositeLit + or + n instanceof Go::ReturnStmt + or + n instanceof Go::DeferStmt + or + n instanceof Go::GoStmt + or + n instanceof Go::SendStmt + or + n instanceof Go::IncDecStmt + or + n instanceof Go::FuncDecl + or + n instanceof Go::SelectorExpr and + n.(Go::SelectorExpr).getBase() instanceof Go::ValueExpr + } + + predicate additionalNode(Ast::AstNode n, string tag, NormalSuccessor t) { + t instanceof DirectSuccessor and + ( + // Assignment write nodes: one per LHS + exists(int i | + ( + notBlankIdent(n.(Go::Assignment).getLhs(i)) + or + notBlankIdent(n.(Go::ValueSpec).getNameExpr(i)) + or + notBlankIdent(n.(Go::RangeStmt).getKey()) and i = 0 + or + notBlankIdent(n.(Go::RangeStmt).getValue()) and i = 1 + ) and + tag = "assign:" + i.toString() + ) + or + // Compound assignment implicit RHS + n instanceof Go::CompoundAssignStmt and tag = "compound-rhs" + or + // Tuple extraction nodes + exists(int i | + extractNodeCondition(n, i) and + tag = "extract:" + i.toString() + ) + or + // Zero initialization (on the ValueSpec) + exists(int i, Go::ValueSpec spec | + n = spec and + not exists(spec.getAnInit()) and + exists(spec.getNameExpr(i)) and + tag = "zero-init:" + i.toString() + ) + or + // Increment/decrement implicit operations + n instanceof Go::IncDecStmt and tag = "implicit-one" + or + n instanceof Go::IncDecStmt and tag = "incdec-rhs" + or + // Return node + n instanceof Go::ReturnStmt and tag = "return" + or + // Result write nodes in return statements + exists(int i, Go::ReturnStmt ret | + n = ret and + exists(ret.getEnclosingFunction().getResultVar(i)) and + exists(ret.getAnExpr()) and + tag = "result-write:" + i.toString() + ) + or + // Result read nodes (on the FuncDef) + exists(int i, Go::FuncDef fd | + n = fd and + exists(fd.getBody()) and + exists(fd.getResultVar(i)) and + tag = "result-read:" + i.toString() + ) + or + // Parameter init + argument nodes (on the FuncDef) + exists(int i, Go::FuncDef fd | + n = fd and + exists(fd.getBody()) and + exists(fd.getParameter(i)) and + (tag = "param-init:" + i.toString() or tag = "arg:" + i.toString()) + ) + or + // Result variable init (on the FuncDef) + exists(int i, Go::FuncDef fd | + n = fd and + exists(fd.getBody()) and + exists(fd.getResultVar(i)) and + tag = "result-init:" + i.toString() + ) + or + // Result variable zero init (on the FuncDef) + exists(int i, Go::FuncDef fd | + n = fd and + exists(fd.getBody()) and + exists(fd.getResultVar(i)) and + exists(fd.getResultVar(i).(Go::ResultVariable).getFunction().getBody()) and + tag = "result-zero-init:" + i.toString() + ) + or + // Next node for range statements + n instanceof Go::RangeStmt and tag = "next" + or + // Send node + n instanceof Go::SendStmt and + not n = any(Go::CommClause cc).getComm() and + tag = "send" + or + // Implicit deref + implicitDerefCondition(n) and tag = "implicit-deref" + or + // Implicit slice bounds + n instanceof Go::SliceExpr and + not exists(n.(Go::SliceExpr).getLow()) and + tag = "implicit-low" + or + n instanceof Go::SliceExpr and + not exists(n.(Go::SliceExpr).getHigh()) and + tag = "implicit-high" + or + n instanceof Go::SliceExpr and + not exists(n.(Go::SliceExpr).getMax()) and + tag = "implicit-max" + or + // Implicit true in tagless switch + n instanceof Go::ExpressionSwitchStmt and + not exists(n.(Go::ExpressionSwitchStmt).getExpr()) and + tag = "implicit-true" + or + // Case check nodes + exists(int i | + exists(n.(Go::CaseClause).getExpr(i)) and + tag = "case-check:" + i.toString() + ) + or + // Type switch implicit variable + exists(Go::TypeSwitchStmt ts, Go::DefineStmt ds | + ds = ts.getAssign() and + n.(Go::CaseClause) = ts.getACase() and + exists(n.(Go::CaseClause).getImplicitlyDeclaredVariable()) and + tag = "type-switch-var" + ) + or + // Literal element initialization + n = any(Go::CompositeLit lit).getAnElement() and + tag = "lit-init" + or + // Implicit literal element index + exists(Go::CompositeLit lit | + not lit instanceof Go::StructLit and + n = lit.getAnElement() and + not n instanceof Go::KeyValueExpr and + tag = "lit-index" + ) + or + // Implicit field selection for promoted fields + exists(int i, Go::Field implicitField | + implicitFieldSelection(n, i, implicitField) and + tag = "implicit-field:" + i.toString() + ) + ) + } + + /** Helper: condition for MkExtractNode */ + private predicate extractNodeCondition(Ast::AstNode s, int i) { + exists(Go::Assignment assgn | + s = assgn and + exists(assgn.getRhs()) and + assgn.getNumLhs() > 1 and + exists(assgn.getLhs(i)) + ) + or + exists(Go::ValueSpec spec | + s = spec and + exists(spec.getInit()) and + spec.getNumName() > 1 and + exists(spec.getNameExpr(i)) + ) + or + exists(Go::RangeStmt rs | s = rs | + exists(rs.getKey()) and i = 0 + or + exists(rs.getValue()) and i = 1 + ) + or + exists(Go::ReturnStmt ret, Go::SignatureType rettp | + s = ret and + exists(ret.getExpr()) and + rettp = ret.getEnclosingFunction().getType() and + rettp.getNumResult() > 1 and + exists(rettp.getResultType(i)) + ) + or + exists(Go::CallExpr outer, Go::CallExpr inner | s = outer | + inner = outer.getArgument(0).stripParens() and + outer.getNumArgument() = 1 and + exists(inner.getType().(Go::TupleType).getComponentType(i)) + ) + } + + /** Helper: condition for implicit dereference */ + private predicate implicitDerefCondition(Ast::AstNode e) { + e.(Go::Expr).getType().getUnderlyingType() instanceof Go::PointerType and + ( + exists(Go::SelectorExpr sel | e = sel.getBase() | + sel = any(Go::Field f).getAReference() + or + exists(Go::Method m, Go::Type tp | + sel = m.getAReference() and + tp = m.getReceiver().getType().getUnderlyingType() and + not tp instanceof Go::PointerType + ) + ) + or + e = any(Go::IndexExpr ie).getBase() + or + e = any(Go::SliceExpr se).getBase() + ) + } + + /** Helper: blank identifier check */ + private predicate notBlankIdent(Go::Expr e) { not e instanceof Go::BlankIdent } + + /** Helper: implicit field selection for promoted selectors */ + additional predicate implicitFieldSelection(Ast::AstNode e, int index, Go::Field implicitField) { + exists(Go::StructType baseType, Go::PromotedField child, int implicitFieldDepth | + baseType = e.(Go::PromotedSelector).getSelectedStructType() and + ( + e.(Go::PromotedSelector).refersTo(child) + or + implicitFieldSelection(e, implicitFieldDepth + 1, child) + ) + | + child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and + exists(Go::PromotedField explicitField, int explicitFieldDepth | + e.(Go::PromotedSelector).refersTo(explicitField) and + baseType.getFieldAtDepth(_, explicitFieldDepth) = explicitField + | + index = explicitFieldDepth - implicitFieldDepth + ) + ) + or + exists( + Go::StructType baseType, Go::PromotedMethod method, int mDepth, int implicitFieldDepth + | + baseType = e.(Go::PromotedSelector).getSelectedStructType() and + e.(Go::PromotedSelector).refersTo(method) and + baseType.getMethodAtDepth(_, mDepth) = method and + index = mDepth - implicitFieldDepth + | + method = baseType.getMethodOfEmbedded(implicitField, _, implicitFieldDepth + 1) + or + exists(Go::PromotedField child | + child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and + implicitFieldSelection(e, implicitFieldDepth + 1, child) + ) + ) + } + + predicate beginAbruptCompletion( + Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always + ) { + ast instanceof Go::CallExpr and + ( + not exists(ast.(Go::CallExpr).getTarget()) or + ast.(Go::CallExpr).getTarget().mayPanic() + ) and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::DivExpr and + not ast.(Go::Expr).isConst() and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::DerefExpr and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::TypeAssertExpr and + not exists(Go::Assignment assgn | + assgn.getNumLhs() = 2 and ast = assgn.getRhs().stripParens() + ) and + not exists(Go::ValueSpec vs | vs.getNumName() = 2 and ast = vs.getInit().stripParens()) and + not exists(Go::TypeSwitchStmt ts | ast = ts.getExpr()) and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::IndexExpr and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::ConversionExpr and + ast.(Go::ConversionExpr).getType().(Go::PointerType).getBaseType() instanceof Go::ArrayType and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::FallthroughStmt and + n.injects(ast) and + c.getSuccessorType() instanceof BreakSuccessor and + c.hasLabel(TFallthrough()) and + always = true + or + ast instanceof Go::GotoStmt and + n.injects(ast) and + c.getSuccessorType() instanceof GotoSuccessor and + c.hasLabel(TGoLabel(ast.(Go::GotoStmt).getLabel())) and + always = true + } + + predicate endAbruptCompletion(Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c) { + exists(Go::LabeledStmt lbl | + ast = lbl.getStmt() and + n.isAfter(lbl) and + c.getSuccessorType() instanceof BreakSuccessor and + c.hasLabel(TGoLabel(lbl.getLabel())) + ) + or + exists(Go::LabeledStmt lbl, Go::FuncDef fd | + ast = fd.getBody() and + n.isBefore(lbl) and + fd = lbl.getEnclosingFunction() and + c.getSuccessorType() instanceof GotoSuccessor and + c.hasLabel(TGoLabel(lbl.getLabel())) + ) + } + + predicate step(PreControlFlowNode n1, PreControlFlowNode n2) { + ifWithInit(n1, n2) or + forLoop(n1, n2) or + rangeLoop(n1, n2) or + switchStmt(n1, n2) or + selectStmt(n1, n2) or + deferStmt(n1, n2) or + goStmtStep(n1, n2) or + assignmentStep(n1, n2) or + incDecStep(n1, n2) or + returnStep(n1, n2) or + indexExprStep(n1, n2) or + sliceExprStep(n1, n2) or + selectorExprStep(n1, n2) or + compositeLitStep(n1, n2) or + sendStmtStep(n1, n2) or + funcDefStep(n1, n2) + } + + /** + * Gets the non-skipped child of `parent` at rank `rnk` (1-based). + * This mimics the shared library's getRankedChild but for use in explicit steps. + */ + private Ast::AstNode getRankedChild(Ast::AstNode parent, int rnk) { + result = rank[rnk](Ast::AstNode c, int ix | c = getChild(parent, ix) | c order by ix) + } + + private Ast::AstNode getChild(Ast::AstNode parent, int ix) { + result = Ast::getChild(parent, ix) + } + + /** + * Routes from isBefore(parent) through all non-skipped children in order, + * then to the first epilogue node (additionalNode or isIn/isAfter). + * This is for constructs where we manually override default flow. + */ + private predicate childSequenceStep( + Ast::AstNode parent, PreControlFlowNode n1, PreControlFlowNode n2 + ) { + // Before parent → Before first child + n1.isBefore(parent) and n2.isBefore(getRankedChild(parent, 1)) + or + // After child i → Before child i+1 + exists(int i | + n1.isAfter(getRankedChild(parent, i)) and + n2.isBefore(getRankedChild(parent, i + 1)) + ) + } + + /** Gets the last non-skipped child of `parent`, or fails if none. */ + private Ast::AstNode getLastRankedChild(Ast::AstNode parent) { + exists(int i | + result = getRankedChild(parent, i) and + not exists(getRankedChild(parent, i + 1)) + ) + } + + /** + * Assignment flow: routes through LHS/RHS children, then through + * additional nodes for compound-rhs, extract, zero-init, and assign + * operations. + */ + private predicate assignmentStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Ast::AstNode assgn | + ( + assgn instanceof Go::Assignment and not assgn instanceof Go::RecvStmt + or + assgn instanceof Go::ValueSpec + ) + | + // Route through children (LHS names, RHS expressions) + childSequenceStep(assgn, n1, n2) + or + // After last child → first epilogue node + exists(Ast::AstNode lastChild | lastChild = getLastRankedChild(assgn) | + n1.isAfter(lastChild) and n2.isAdditional(assgn, getFirstEpilogueTag(assgn)) + ) + or + // No children at all → before → first epilogue + not exists(getRankedChild(assgn, _)) and + n1.isBefore(assgn) and + n2.isAdditional(assgn, getFirstEpilogueTag(assgn)) + or + // Chain through epilogue nodes + exists(string tag1, string tag2 | + epilogueSucc(assgn, tag1, tag2) and + n1.isAdditional(assgn, tag1) and + n2.isAdditional(assgn, tag2) + ) + or + // Last epilogue → after the assignment + n1.isAdditional(assgn, getLastEpilogueTag(assgn)) and + n2.isAfter(assgn) + or + // No epilogue at all → after last child → after assignment + not exists(getFirstEpilogueTag(assgn)) and + n1.isAfter(getLastRankedChild(assgn)) and + n2.isAfter(assgn) + ) + } + + /** + * Gets the ordered epilogue tags for an assignment node, following the + * pattern: compound-rhs?, (extract:i, assign:i | zero-init:i, assign:i)* + */ + private string getEpilogueTag(Ast::AstNode assgn, int ord) { + // Compound RHS comes first + assgn instanceof Go::CompoundAssignStmt and + ord = -1 and + result = "compound-rhs" + or + exists(int j | + ( + extractNodeCondition(assgn, j) and result = "extract:" + j.toString() and ord = 2 * j + or + exists(Go::ValueSpec spec | + assgn = spec and + not exists(spec.getAnInit()) and + exists(spec.getNameExpr(j)) and + result = "zero-init:" + j.toString() and + ord = 2 * j + ) + or + ( + notBlankIdent(assgn.(Go::Assignment).getLhs(j)) + or + notBlankIdent(assgn.(Go::ValueSpec).getNameExpr(j)) + ) and + result = "assign:" + j.toString() and + ord = 2 * j + 1 + ) + ) + } + + private string getEpilogueTagRanked(Ast::AstNode assgn, int rnk) { + result = + rank[rnk](string tag, int ord | + tag = getEpilogueTag(assgn, ord) and + exists(tag) + | + tag order by ord + ) + } + + private string getFirstEpilogueTag(Ast::AstNode assgn) { + result = getEpilogueTagRanked(assgn, 1) + } + + private string getLastEpilogueTag(Ast::AstNode assgn) { + exists(int i | + result = getEpilogueTagRanked(assgn, i) and + not exists(getEpilogueTagRanked(assgn, i + 1)) + ) + } + + private predicate epilogueSucc(Ast::AstNode assgn, string tag1, string tag2) { + exists(int i | + tag1 = getEpilogueTagRanked(assgn, i) and + tag2 = getEpilogueTagRanked(assgn, i + 1) + ) + } + + /** + * Increment/decrement: operand → implicit-one → incdec-rhs → In(stmt) + * (IncDecStmt is in postOrInOrder, so In(stmt) is its evaluation point) + */ + private predicate incDecStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::IncDecStmt s | + // Before → Before operand + n1.isBefore(s) and n2.isBefore(s.getOperand()) + or + // After operand → implicit-one + n1.isAfter(s.getOperand()) and n2.isAdditional(s, "implicit-one") + or + // implicit-one → incdec-rhs + n1.isAdditional(s, "implicit-one") and n2.isAdditional(s, "incdec-rhs") + or + // incdec-rhs → In(stmt) (the assignment itself) + n1.isAdditional(s, "incdec-rhs") and n2.isIn(s) + or + // In(stmt) → After(stmt) + n1.isIn(s) and n2.isAfter(s) + ) + } + + /** + * Return statement: evaluate expressions, extract tuples, write results, + * then the return node. + */ + private predicate returnStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::ReturnStmt ret | + // Route through expression children + childSequenceStep(ret, n1, n2) + or + exists(Ast::AstNode lastChild | lastChild = getLastRankedChild(ret) | + // After last expr → first return epilogue + n1.isAfter(lastChild) and n2.isAdditional(ret, getFirstReturnEpilogueTag(ret)) + ) + or + // No expressions → before → return node directly + not exists(getRankedChild(ret, _)) and + n1.isBefore(ret) and + n2.isIn(ret) + or + // Chain return epilogue nodes + exists(string tag1, string tag2 | + returnEpilogueSucc(ret, tag1, tag2) and + n1.isAdditional(ret, tag1) and + n2.isAdditional(ret, tag2) + ) + or + // Last return epilogue → In(ret) (the return itself) + n1.isAdditional(ret, getLastReturnEpilogueTag(ret)) and + n2.isIn(ret) + ) + } + + private string getReturnEpilogueTag(Go::ReturnStmt ret, int ord) { + exists(int i | + extractNodeCondition(ret, i) and result = "extract:" + i.toString() and ord = 2 * i + or + exists(Go::ResultVariable rv | + ret.getEnclosingFunction().getResultVar(i) = rv and + exists(ret.getAnExpr()) and + result = "result-write:" + i.toString() and + ord = 2 * i + 1 + ) + ) + } + + private string getReturnEpilogueTagRanked(Go::ReturnStmt ret, int rnk) { + result = + rank[rnk](string tag, int ord | + tag = getReturnEpilogueTag(ret, ord) and + exists(tag) + | + tag order by ord + ) + } + + private string getFirstReturnEpilogueTag(Go::ReturnStmt ret) { + result = getReturnEpilogueTagRanked(ret, 1) + } + + private string getLastReturnEpilogueTag(Go::ReturnStmt ret) { + exists(int i | + result = getReturnEpilogueTagRanked(ret, i) and + not exists(getReturnEpilogueTagRanked(ret, i + 1)) + ) + } + + private predicate returnEpilogueSucc(Go::ReturnStmt ret, string tag1, string tag2) { + exists(int i | + tag1 = getReturnEpilogueTagRanked(ret, i) and + tag2 = getReturnEpilogueTagRanked(ret, i + 1) + ) + } + + /** + * Index expression: base → implicit-deref? → index → In(indexExpr) + */ + private predicate indexExprStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::IndexExpr ie | + implicitDerefCondition(ie.getBase()) and + ( + n1.isBefore(ie) and n2.isBefore(ie.getBase()) + or + n1.isAfter(ie.getBase()) and n2.isAdditional(ie.getBase(), "implicit-deref") + or + n1.isAdditional(ie.getBase(), "implicit-deref") and n2.isBefore(ie.getIndex()) + or + n1.isAfter(ie.getIndex()) and n2.isIn(ie) + or + n1.isIn(ie) and n2.isAfter(ie) + ) + ) + } + + /** + * Slice expression: base → implicit-deref? → low/implicit-low → + * high/implicit-high → max/implicit-max → In(sliceExpr) + */ + private predicate sliceExprStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SliceExpr se | + (implicitDerefCondition(se.getBase()) or exists(se.getLow()) or not exists(se.getLow())) and + ( + n1.isBefore(se) and n2.isBefore(se.getBase()) + or + // After base → implicit deref or low/implicit-low + n1.isAfter(se.getBase()) and + ( + if implicitDerefCondition(se.getBase()) + then n2.isAdditional(se.getBase(), "implicit-deref") + else + if exists(se.getLow()) + then n2.isBefore(se.getLow()) + else n2.isAdditional(se, "implicit-low") + ) + or + n1.isAdditional(se.getBase(), "implicit-deref") and + ( + if exists(se.getLow()) + then n2.isBefore(se.getLow()) + else n2.isAdditional(se, "implicit-low") + ) + or + (n1.isAfter(se.getLow()) or n1.isAdditional(se, "implicit-low")) and + ( + if exists(se.getHigh()) + then n2.isBefore(se.getHigh()) + else n2.isAdditional(se, "implicit-high") + ) + or + (n1.isAfter(se.getHigh()) or n1.isAdditional(se, "implicit-high")) and + ( + if exists(se.getMax()) + then n2.isBefore(se.getMax()) + else n2.isAdditional(se, "implicit-max") + ) + or + (n1.isAfter(se.getMax()) or n1.isAdditional(se, "implicit-max")) and + n2.isIn(se) + or + n1.isIn(se) and n2.isAfter(se) + ) + ) + } + + /** + * Selector expression with value base: base → implicit-deref? → + * implicit-field-selections → In(selector) + */ + private predicate selectorExprStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SelectorExpr sel | + sel.getBase() instanceof Go::ValueExpr and + ( + implicitDerefCondition(sel.getBase()) or + exists(Go::Field f | sel = f.getAReference()) or + implicitFieldSelection(sel, _, _) + ) and + ( + n1.isBefore(sel) and n2.isBefore(sel.getBase()) + or + // After base (no implicit-deref) → first implicit-field or In(sel) + n1.isAfter(sel.getBase()) and + not implicitDerefCondition(sel.getBase()) and + ( + // Has implicit field reads: go to outermost (highest index) + exists(int maxIdx | + maxIdx = max(int i | implicitFieldSelection(sel, i, _)) and + n2.isAdditional(sel, "implicit-field:" + maxIdx.toString()) + ) + or + // No implicit field reads: go directly to In(sel) + not implicitFieldSelection(sel, _, _) and n2.isIn(sel) + ) + or + // After base (has implicit-deref) → implicit-deref node + n1.isAfter(sel.getBase()) and + implicitDerefCondition(sel.getBase()) and + n2.isAdditional(sel.getBase(), "implicit-deref") + or + // After implicit-deref → first implicit-field or In(sel) + n1.isAdditional(sel.getBase(), "implicit-deref") and + ( + exists(int maxIdx | + maxIdx = max(int i | implicitFieldSelection(sel, i, _)) and + n2.isAdditional(sel, "implicit-field:" + maxIdx.toString()) + ) + or + not implicitFieldSelection(sel, _, _) and n2.isIn(sel) + ) + or + // Between implicit field reads: descend from index i to i-1 + exists(int i | + i > 1 and + implicitFieldSelection(sel, i, _) and + implicitFieldSelection(sel, i - 1, _) and + n1.isAdditional(sel, "implicit-field:" + i.toString()) and + n2.isAdditional(sel, "implicit-field:" + (i - 1).toString()) + ) + or + // Last implicit field read (index 1) → In(sel) + implicitFieldSelection(sel, 1, _) and + n1.isAdditional(sel, "implicit-field:1") and + n2.isIn(sel) + or + n1.isIn(sel) and n2.isAfter(sel) + ) + ) + } + + /** + * Composite literal: In(lit) → element-init chain → After(lit) + * CompositeLit evaluates the literal (allocation) first (pre-order), + * then initializes elements. + */ + private predicate compositeLitStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::CompositeLit lit | + // Before → In (the literal allocation) + n1.isBefore(lit) and n2.isIn(lit) + or + // In → first element, or After if no elements + n1.isIn(lit) and + ( + n2.isBefore(lit.getElement(0)) + or + not exists(lit.getElement(_)) and n2.isAfter(lit) + ) + or + // After element → lit-init → next element or After + exists(int i | + n1.isAfter(lit.getElement(i)) and n2.isAdditional(lit.getElement(i), "lit-init") + or + n1.isAdditional(lit.getElement(i), "lit-init") and + ( + n2.isBefore(lit.getElement(i + 1)) + or + not exists(lit.getElement(i + 1)) and n2.isAfter(lit) + ) + ) + ) + } + + /** + * Send statement (outside select): channel → value → In(send) + */ + private predicate sendStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SendStmt s | not s = any(Go::CommClause cc).getComm() | + n1.isBefore(s) and n2.isBefore(s.getChannel()) + or + n1.isAfter(s.getChannel()) and n2.isBefore(s.getValue()) + or + n1.isAfter(s.getValue()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + private predicate ifWithInit(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::IfStmt s | exists(s.getInit()) | + n1.isBefore(s) and n2.isBefore(s.getInit()) + or + n1.isAfter(s.getInit()) and n2.isBefore(s.getCond()) + or + n1.isAfterTrue(s.getCond()) and n2.isBefore(s.getThen()) + or + n1.isAfterFalse(s.getCond()) and + ( + n2.isBefore(s.getElse()) + or + not exists(s.getElse()) and n2.isAfter(s) + ) + or + n1.isAfter(s.getThen()) and n2.isAfter(s) + or + n1.isAfter(s.getElse()) and n2.isAfter(s) + ) + } + + private predicate forLoop(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::ForStmt s | + exists(PreControlFlowNode cond | + ( + cond.isBefore(s.getCond()) + or + not exists(s.getCond()) and cond.isBefore(s.getBody()) + ) + | + n1.isBefore(s) and + ( + n2.isBefore(s.getInit()) + or + not exists(s.getInit()) and n2 = cond + ) + or + n1.isAfter(s.getInit()) and n2 = cond + or + n1.isAfterTrue(s.getCond()) and n2.isBefore(s.getBody()) + or + n1.isAfterFalse(s.getCond()) and n2.isAfter(s) + or + not exists(s.getCond()) and + n1.isAfter(s.getBody()) and + ( + n2.isBefore(s.getPost()) + or + not exists(s.getPost()) and n2.isBefore(s.getBody()) + ) + or + exists(s.getCond()) and + n1.isAfter(s.getBody()) and + ( + n2.isBefore(s.getPost()) + or + not exists(s.getPost()) and n2 = cond + ) + or + n1.isAfter(s.getPost()) and n2 = cond + ) + ) + } + + private predicate rangeLoop(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::RangeStmt s | + n1.isBefore(s) and n2.isBefore(s.getDomain()) + or + n1.isAfter(s.getDomain()) and n2.isIn(s) + or + n1.isIn(s) and + ( + n2.isBefore(s.getKey()) + or + not exists(s.getKey()) and n2.isBefore(s.getBody()) + ) + or + n1.isAfter(s.getKey()) and + ( + n2.isBefore(s.getValue()) + or + not exists(s.getValue()) and n2.isBefore(s.getBody()) + ) + or + n1.isAfter(s.getValue()) and n2.isBefore(s.getBody()) + or + n1.isAfter(s.getBody()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + private predicate switchStmt(PreControlFlowNode n1, PreControlFlowNode n2) { + exprSwitch(n1, n2) or typeSwitch(n1, n2) or caseClause(n1, n2) + } + + private predicate exprSwitch(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::ExpressionSwitchStmt sw | + n1.isBefore(sw) and + ( + n2.isBefore(sw.getInit()) + or + not exists(sw.getInit()) and + ( + n2.isBefore(sw.getExpr()) + or + not exists(sw.getExpr()) and + ( + n2.isBefore(sw.getNonDefaultCase(0)) + or + not exists(sw.getANonDefaultCase()) and n2.isBefore(sw.getDefault()) + or + not exists(sw.getACase()) and n2.isAfter(sw) + ) + ) + ) + or + n1.isAfter(sw.getInit()) and + ( + n2.isBefore(sw.getExpr()) + or + not exists(sw.getExpr()) and + ( + n2.isBefore(sw.getNonDefaultCase(0)) + or + not exists(sw.getANonDefaultCase()) and n2.isBefore(sw.getDefault()) + ) + ) + or + n1.isAfter(sw.getExpr()) and + ( + n2.isBefore(sw.getNonDefaultCase(0)) + or + not exists(sw.getANonDefaultCase()) and n2.isBefore(sw.getDefault()) + ) + ) + } + + private predicate typeSwitch(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::TypeSwitchStmt sw | + n1.isBefore(sw) and + ( + n2.isBefore(sw.getInit()) + or + not exists(sw.getInit()) and n2.isBefore(sw.getTest()) + ) + or + n1.isAfter(sw.getInit()) and n2.isBefore(sw.getTest()) + or + n1.isAfter(sw.getTest()) and + ( + n2.isBefore(sw.getNonDefaultCase(0)) + or + not exists(sw.getANonDefaultCase()) and n2.isBefore(sw.getDefault()) + ) + ) + } + + private predicate caseClause(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SwitchStmt sw, Go::CaseClause cc, int i | cc = sw.getNonDefaultCase(i) | + n1.isBefore(cc) and n2.isBefore(cc.getExpr(0)) + or + exists(int j | n1.isAfter(cc.getExpr(j)) and n2.isBefore(cc.getExpr(j + 1))) + or + exists(int last | last = max(int j | exists(cc.getExpr(j))) | + n1.isAfter(cc.getExpr(last)) and + ( + n2.isBefore(cc.getStmt(0)) + or + not exists(cc.getStmt(0)) and n2.isAfter(sw) + or + n2.isBefore(sw.getNonDefaultCase(i + 1)) + or + not exists(sw.getNonDefaultCase(i + 1)) and n2.isBefore(sw.getDefault()) + or + not exists(sw.getNonDefaultCase(i + 1)) and + not exists(sw.getDefault()) and + n2.isAfter(sw) + ) + ) + ) + or + exists(Go::SwitchStmt sw, Go::CaseClause def | def = sw.getDefault() | + n1.isBefore(def) and + ( + n2.isBefore(def.getStmt(0)) + or + not exists(def.getStmt(0)) and n2.isAfter(sw) + ) + ) + or + exists(Go::CaseClause cc | + exists(int j | n1.isAfter(cc.getStmt(j)) and n2.isBefore(cc.getStmt(j + 1))) + or + exists(Go::SwitchStmt sw, int last | + sw.getACase() = cc and + last = max(int j | exists(cc.getStmt(j))) and + n1.isAfter(cc.getStmt(last)) and + n2.isAfter(sw) + ) + ) + } + + private predicate selectStmt(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SelectStmt sel | + n1.isBefore(sel) and + ( + n2.isBefore(sel.getNonDefaultCommClause(0).getComm()) + or + not exists(sel.getACommClause()) and n2.isAfter(sel) + ) + or + exists(Go::CommClause cc, int i | cc = sel.getNonDefaultCommClause(i) | + n1.isAfter(cc.getComm()) and + ( + n2.isBefore(sel.getNonDefaultCommClause(i + 1).getComm()) + or + not exists(sel.getNonDefaultCommClause(i + 1)) and + ( + n2.isBefore(sel.getACommClause().getStmt(0)) + or + exists(sel.getDefaultCommClause()) and + n2.isBefore(sel.getDefaultCommClause().getStmt(0)) + ) + ) + ) + or + exists(Go::CommClause cc | sel.getACommClause() = cc | + exists(int j | n1.isAfter(cc.getStmt(j)) and n2.isBefore(cc.getStmt(j + 1))) + or + exists(int last | + last = max(int j | exists(cc.getStmt(j))) and + n1.isAfter(cc.getStmt(last)) and + n2.isAfter(sel) + ) + or + not exists(cc.getStmt(_)) and n1.isBefore(cc) and n2.isAfter(sel) + ) + ) + } + + private predicate deferStmt(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::DeferStmt s | + n1.isBefore(s) and n2.isBefore(s.getCall()) + or + n1.isAfter(s.getCall()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + private predicate goStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::GoStmt s | + n1.isBefore(s) and n2.isBefore(s.getCall()) + or + n1.isAfter(s.getCall()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + /** + * Function definition prologue and epilogue: + * - Prologue: Before(fd) → arg:0 → param-init:0 → arg:1 → param-init:1 → ... + * → result-zero-init:0 → result-init:0 → ... → Before(body) + * - Epilogue: After(body) → result-read:0 → result-read:1 → ... → After(fd) + * + * `After(fd)` goes to `NormalExit(fd)` via the shared library's built-in step + * (since `callableGetBody(fd) = fd`). + */ + private predicate funcDefStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::FuncDef fd | exists(fd.getBody()) | + // Before(fd) → first prologue node, or Before(body) if no prologue + n1.isBefore(fd) and + ( + // Has parameters: start with arg:0 + exists(fd.getParameter(0)) and n2.isAdditional(fd, "arg:0") + or + // No parameters, has result vars: start with result-zero-init:0 + not exists(fd.getParameter(_)) and + exists(fd.getResultVar(0)) and + n2.isAdditional(fd, "result-zero-init:0") + or + // No parameters and no result vars: go directly to Before(body) + not exists(fd.getParameter(_)) and + not exists(fd.getResultVar(_)) and + n2.isBefore(fd.getBody()) + ) + or + // arg:i → param-init:i (for each parameter) + exists(int i | exists(fd.getParameter(i)) | + n1.isAdditional(fd, "arg:" + i.toString()) and + n2.isAdditional(fd, "param-init:" + i.toString()) + ) + or + // param-init:i → next: arg:(i+1), or result-zero-init:0, or Before(body) + exists(int i | exists(fd.getParameter(i)) | + n1.isAdditional(fd, "param-init:" + i.toString()) and + ( + // Next parameter exists + exists(fd.getParameter(i + 1)) and + n2.isAdditional(fd, "arg:" + (i + 1).toString()) + or + // No next parameter, has result vars: go to result-zero-init:0 + not exists(fd.getParameter(i + 1)) and + exists(fd.getResultVar(0)) and + n2.isAdditional(fd, "result-zero-init:0") + or + // No next parameter and no result vars: go to Before(body) + not exists(fd.getParameter(i + 1)) and + not exists(fd.getResultVar(_)) and + n2.isBefore(fd.getBody()) + ) + ) + or + // result-zero-init:j → result-init:j (for each result variable) + exists(int j | exists(fd.getResultVar(j)) | + n1.isAdditional(fd, "result-zero-init:" + j.toString()) and + n2.isAdditional(fd, "result-init:" + j.toString()) + ) + or + // result-init:j → next: result-zero-init:(j+1), or Before(body) + exists(int j | exists(fd.getResultVar(j)) | + n1.isAdditional(fd, "result-init:" + j.toString()) and + ( + // Next result var exists + exists(fd.getResultVar(j + 1)) and + n2.isAdditional(fd, "result-zero-init:" + (j + 1).toString()) + or + // No next result var: go to Before(body) + not exists(fd.getResultVar(j + 1)) and + n2.isBefore(fd.getBody()) + ) + ) + or + // After(body) → first epilogue or After(fd) if no result vars + n1.isAfter(fd.getBody()) and + ( + exists(fd.getResultVar(0)) and n2.isAdditional(fd, "result-read:0") + or + not exists(fd.getResultVar(_)) and n2.isAfter(fd) + ) + or + // result-read:j → result-read:(j+1) or After(fd) + exists(int j | exists(fd.getResultVar(j)) | + n1.isAdditional(fd, "result-read:" + j.toString()) and + ( + exists(fd.getResultVar(j + 1)) and + n2.isAdditional(fd, "result-read:" + (j + 1).toString()) + or + not exists(fd.getResultVar(j + 1)) and n2.isAfter(fd) + ) + ) + ) + } + } +} diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index a4c730041082..22300f3688ba 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -7,13 +7,13 @@ * structure or type information). * * Each instruction is also a control-flow node, but there are control-flow nodes that are not - * instructions (synthetic entry and exit nodes, as well as no-op skip nodes). + * instructions (synthetic entry and exit nodes, as well as before/after nodes). */ overlay[local] module; import go -private import semmle.go.controlflow.ControlFlowGraphImpl +private import ControlFlowGraphShared /** Provides predicates and classes for working with IR constructs. */ module IR { @@ -22,37 +22,13 @@ module IR { */ class Instruction extends ControlFlow::Node { Instruction() { - this instanceof MkExprNode or - this instanceof MkLiteralElementInitNode or - this instanceof MkImplicitLiteralElementIndex or - this instanceof MkAssignNode or - this instanceof MkCompoundAssignRhsNode or - this instanceof MkExtractNode or - this instanceof MkZeroInitNode or - this instanceof MkFuncDeclNode or - this instanceof MkDeferNode or - this instanceof MkGoNode or - this instanceof MkConditionGuardNode or - this instanceof MkIncDecNode or - this instanceof MkIncDecRhs or - this instanceof MkImplicitOne or - this instanceof MkReturnNode or - this instanceof MkResultWriteNode or - this instanceof MkResultReadNode or - this instanceof MkSelectNode or - this instanceof MkSendNode or - this instanceof MkParameterInit or - this instanceof MkArgumentNode or - this instanceof MkResultInit or - this instanceof MkNextNode or - this instanceof MkImplicitTrue or - this instanceof MkCaseCheckNode or - this instanceof MkTypeSwitchImplicitVariable or - this instanceof MkImplicitLowerSliceBound or - this instanceof MkImplicitUpperSliceBound or - this instanceof MkImplicitMaxSliceBound or - this instanceof MkImplicitDeref or - this instanceof MkImplicitFieldSelection + this.isIn(_) + or + this.isAdditional(_, _) + or + this.isAfterTrue(_) + or + this.isAfterFalse(_) } /** Holds if this instruction reads the value of variable or constant `v`. */ @@ -120,78 +96,87 @@ module IR { /** Gets a textual representation of the kind of this instruction. */ string getInsnKind() { - this instanceof MkExprNode and result = "expression" - or - this instanceof MkLiteralElementInitNode and result = "element init" + this instanceof EvalInstruction and result = "expression" or - this instanceof MkImplicitLiteralElementIndex and result = "element index" + this instanceof InitLiteralComponentInstruction and result = "element init" or - this instanceof MkAssignNode and result = "assignment" + this instanceof ImplicitLiteralElementIndexInstruction and result = "element index" or - this instanceof MkCompoundAssignRhsNode and result = "right-hand side of compound assignment" + this instanceof AssignInstruction and result = "assignment" or - this instanceof MkExtractNode and result = "tuple element extraction" + this instanceof EvalCompoundAssignRhsInstruction and + result = "right-hand side of compound assignment" or - this instanceof MkZeroInitNode and result = "zero value" + this instanceof ExtractTupleElementInstruction and result = "tuple element extraction" or - this instanceof MkFuncDeclNode and result = "function declaration" + this instanceof EvalImplicitInitInstruction and result = "zero value" or - this instanceof MkDeferNode and result = "defer" + this instanceof DeclareFunctionInstruction and result = "function declaration" or - this instanceof MkGoNode and result = "go" + this instanceof DeferInstruction and result = "defer" or - this instanceof MkConditionGuardNode and result = "condition guard" + this instanceof GoInstruction and result = "go" or - this instanceof MkIncDecNode and result = "increment/decrement" + this instanceof ConditionGuardInstruction and result = "condition guard" or - this instanceof MkIncDecRhs and result = "right-hand side of increment/decrement" + this instanceof IncDecInstruction and result = "increment/decrement" or - this instanceof MkImplicitOne and result = "implicit 1" + this instanceof EvalIncDecRhsInstruction and + result = "right-hand side of increment/decrement" or - this instanceof MkReturnNode and result = "return" + this instanceof EvalImplicitOneInstruction and result = "implicit 1" or - this instanceof MkResultWriteNode and result = "result write" + this instanceof ReturnInstruction and result = "return" or - this instanceof MkResultReadNode and result = "result read" + this instanceof WriteResultInstruction and result = "result write" or - this instanceof MkSelectNode and result = "select" + this instanceof ReadResultInstruction and result = "result read" or - this instanceof MkSendNode and result = "send" + this instanceof SendInstruction and result = "send" or - this instanceof MkParameterInit and result = "parameter initialization" + this instanceof InitParameterInstruction and result = "parameter initialization" or - this instanceof MkArgumentNode and result = "argument" + this instanceof ReadArgumentInstruction and result = "argument" or - this instanceof MkResultInit and result = "result initialization" + this instanceof InitResultInstruction and result = "result initialization" or - this instanceof MkNextNode and result = "next key-value pair" + this instanceof GetNextEntryInstruction and result = "next key-value pair" or - this instanceof MkImplicitTrue and result = "implicit true" + this instanceof EvalImplicitTrueInstruction and result = "implicit true" or - this instanceof MkCaseCheckNode and result = "case" + this instanceof CaseInstruction and result = "case" or - this instanceof MkTypeSwitchImplicitVariable and + this instanceof TypeSwitchImplicitVariableInstruction and result = "type switch implicit variable declaration" or - this instanceof MkImplicitLowerSliceBound and result = "implicit lower bound" + this instanceof EvalImplicitLowerSliceBoundInstruction and result = "implicit lower bound" or - this instanceof MkImplicitUpperSliceBound and result = "implicit upper bound" + this instanceof EvalImplicitUpperSliceBoundInstruction and result = "implicit upper bound" or - this instanceof MkImplicitMaxSliceBound and result = "implicit maximum" + this instanceof EvalImplicitMaxSliceBoundInstruction and result = "implicit maximum" or - this instanceof MkImplicitDeref and result = "implicit dereference" + this instanceof EvalImplicitDerefInstruction and result = "implicit dereference" + or + this instanceof ImplicitFieldReadInstruction and result = "implicit field selection" + } + } + + /** A condition guard instruction, representing a known boolean outcome for a condition. */ + private class ConditionGuardInstruction extends Instruction { + ConditionGuardInstruction() { + this.isAfterTrue(_) or - this instanceof MkImplicitFieldSelection and result = "implicit field selection" + this.isAfterFalse(_) } } /** * An IR instruction representing the evaluation of an expression. */ - class EvalInstruction extends Instruction, MkExprNode { + class EvalInstruction extends Instruction { Expr e; - EvalInstruction() { this = MkExprNode(e) } + EvalInstruction() { this.isIn(e) } /** Gets the expression underlying this instruction. */ Expr getExpr() { result = e } @@ -217,10 +202,6 @@ module IR { override predicate isConst() { e.isConst() } override predicate isPlatformIndependentConstant() { e.isPlatformIndependentConstant() } - - override string toString() { result = e.toString() } - - override Location getLocation() { result = e.getLocation() } } /** @@ -236,17 +217,13 @@ module IR { or this instanceof ReadResultInstruction or - this instanceof MkImplicitFieldSelection + this instanceof ImplicitFieldReadInstruction } } /** * Gets the effective base of a selector, index or slice expression, taking implicit dereferences * and implicit field reads into account. - * - * For a selector expression `b.f`, this could be the implicit dereference `*b`, or the implicit - * field access `b.Embedded` if the field `f` is promoted from an embedded type `Embedded`, or a - * combination of both `*(b.Embedded)`, or simply `b` if neither case applies. */ private Instruction selectorBase(Expr e) { exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1 | @@ -261,17 +238,15 @@ module IR { or base = e.(SliceExpr).getBase() | - result = MkImplicitDeref(base) + result = implicitDerefInstruction(base) or - not exists(MkImplicitDeref(base)) and + not exists(implicitDerefInstruction(base)) and result = evalExprInstruction(base) ) } /** * An IR instruction that reads a component from a composite object. - * - * This is either a field of a struct, or an element of an array, map, slice or string. */ class ComponentReadInstruction extends ReadInstruction { ComponentReadInstruction() { @@ -282,7 +257,7 @@ module IR { not e.(SelectorExpr).getSelector() = any(Method method).getAReference() ) or - this instanceof MkImplicitFieldSelection + this instanceof ImplicitFieldReadInstruction } /** Gets the instruction computing the base value on which the field or element is read. */ @@ -295,9 +270,6 @@ module IR { /** * An IR instruction that reads the value of a field. - * - * On databases with incomplete type information, method expressions may sometimes be - * misclassified as field reads. */ class FieldReadInstruction extends ComponentReadInstruction { SelectorExpr e; @@ -309,7 +281,9 @@ module IR { index = 0 and field.getAReference() = e.getSelector() or - this = MkImplicitFieldSelection(e, index, field) + this.(ImplicitFieldReadInstruction).getSelectorExpr() = e and + this.(ImplicitFieldReadInstruction).getIndex() = index and + this.(ImplicitFieldReadInstruction).getField() = field } /** Gets the `SelectorExpr` of this field read. */ @@ -332,9 +306,9 @@ module IR { fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) ) and ( - result = MkImplicitDeref(e.getBase()) + result = implicitDerefInstruction(e.getBase()) or - not exists(MkImplicitDeref(e.getBase())) and + not exists(implicitDerefInstruction(e.getBase())) and result = evalExprInstruction(e.getBase()) ) } @@ -345,24 +319,51 @@ module IR { } /** - * An IR instruction for an implicit field read as part of reading a - * promoted field. - * - * If the field that is being implicitly read has a pointer type then this - * instruction represents an implicit dereference of it. + * An IR instruction for an implicit field read as part of reading a promoted field. */ - class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection { - ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) } + class ImplicitFieldReadInstruction extends Instruction { + SelectorExpr sel; + int idx; + Field fld; + + ImplicitFieldReadInstruction() { + this.isAdditional(sel, "implicit-field:" + idx.toString()) and + GoCfg::implicitFieldSelection(sel, idx, fld) + } - override predicate reads(ValueEntity v) { v = field } + /** Gets the `SelectorExpr` for which this is an implicit field read. */ + SelectorExpr getSelectorExpr() { result = sel } - override Type getResultType() { result = lookThroughPointerType(field.getType()) } + /** Gets the index of this implicit field read. */ + int getIndex() { result = idx } - override ControlFlow::Root getRoot() { result.isRootOf(e) } + /** Gets the field being read. */ + Field getField() { result = fld } + + /** Gets the instruction computing the base value on which the field is read. */ + Instruction getBaseInstruction() { + exists(ImplicitFieldReadInstruction fri | + fri.getSelectorExpr() = sel and fri.getIndex() = pragma[only_bind_into](idx + 1) + | + result = fri + ) + or + not exists(ImplicitFieldReadInstruction fri | + fri.getSelectorExpr() = sel and fri.getIndex() = pragma[only_bind_into](idx + 1) + ) and + ( + result = implicitDerefInstruction(sel.getBase()) + or + not exists(implicitDerefInstruction(sel.getBase())) and + result = evalExprInstruction(sel.getBase()) + ) + } + + override predicate reads(ValueEntity v) { v = fld } - override string toString() { result = "implicit read of field " + field.toString() } + override Type getResultType() { result = lookThroughPointerType(fld.getType()) } - override Location getLocation() { result = e.getBase().getLocation() } + override ControlFlow::Root getRoot() { result.isRootOf(sel) } } /** @@ -463,13 +464,14 @@ module IR { /** * An IR instruction that initializes a component of a composite literal. */ - class InitLiteralComponentInstruction extends WriteInstruction, MkLiteralElementInitNode { + class InitLiteralComponentInstruction extends WriteInstruction { CompositeLit lit; - int i; + int litIdx; Expr elt; InitLiteralComponentInstruction() { - this = MkLiteralElementInitNode(elt) and elt = lit.getElement(i) + this.isAdditional(elt, "lit-init") and + elt = lit.getElement(litIdx) } /** Gets the instruction allocating the composite literal. */ @@ -481,10 +483,6 @@ module IR { } override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override string toString() { result = "init of " + elt } - - override Location getLocation() { result = elt.getLocation() } } /** @@ -498,7 +496,7 @@ module IR { string getFieldName() { if elt instanceof KeyValueExpr then result = elt.(KeyValueExpr).getKey().(Ident).getName() - else pragma[only_bind_out](lit.getStructType()).hasOwnField(i, result, _, _) + else pragma[only_bind_out](lit.getStructType()).hasOwnField(litIdx, result, _, _) } /** Gets the initialized field. */ @@ -527,34 +525,26 @@ module IR { Instruction getIndex() { result = evalExprInstruction(elt.(KeyValueExpr).getKey()) or - result = MkImplicitLiteralElementIndex(elt) + result.(ImplicitLiteralElementIndexInstruction).isAdditional(elt, "lit-index") } } - /** - * An IR instruction that initializes an element of an array literal. - */ + /** An IR instruction that initializes an element of an array literal. */ class InitLiteralArrayElementInstruction extends InitLiteralElementInstruction { override ArrayType literalType; } - /** - * An IR instruction that initializes an element of a slice literal. - */ + /** An IR instruction that initializes an element of a slice literal. */ class InitLiteralSliceElementInstruction extends InitLiteralElementInstruction { override SliceType literalType; } - /** - * An IR instruction that initializes an element of a map literal. - */ + /** An IR instruction that initializes an element of a map literal. */ class InitLiteralMapElementInstruction extends InitLiteralElementInstruction { override MapType literalType; } - /** - * An IR instruction that writes to a field. - */ + /** An IR instruction that writes to a field. */ class FieldWriteInstruction extends WriteInstruction { override FieldTarget lhs; @@ -565,43 +555,35 @@ module IR { Field getField() { result = lhs.getField() } override predicate writesField(Instruction base, Field f, Instruction rhs) { - this.getBase() = base and - this.getField() = f and - this.getRhs() = rhs + this.getBase() = base and this.getField() = f and this.getRhs() = rhs } } - /** - * An IR instruction that writes to an element of an array, slice, or map. - */ + /** An IR instruction that writes to an element of an array, slice, or map. */ class ElementWriteInstruction extends WriteInstruction { override ElementTarget lhs; - /** Gets the instruction computing the base value on which the field is written. */ + /** Gets the instruction computing the base value on which the element is written. */ Instruction getBase() { result = lhs.getBase() } /** Gets the instruction computing the element index being written. */ Instruction getIndex() { result = lhs.getIndex() } override predicate writesElement(Instruction base, Instruction index) { - this.getBase() = base and - this.getIndex() = index + this.getBase() = base and this.getIndex() = index } } - /** Holds if `lit` does not specify any explicit keys. */ private predicate noExplicitKeys(CompositeLit lit) { not lit.getAnElement() instanceof KeyValueExpr } - /** Gets the index of the `i`th element in (array or slice) literal `lit`. */ private int getElementIndex(CompositeLit lit, int i) { ( lit.getType().getUnderlyingType() instanceof ArrayType or lit.getType().getUnderlyingType() instanceof SliceType ) and exists(Expr elt | elt = lit.getElement(i) | - // short-circuit computation for literals without any explicit keys noExplicitKeys(lit) and result = i or result = elt.(KeyValueExpr).getKey().getIntValue() @@ -618,10 +600,10 @@ module IR { /** * An IR instruction computing the implicit index of an element in an array or slice literal. */ - class ImplicitLiteralElementIndexInstruction extends Instruction, MkImplicitLiteralElementIndex { + class ImplicitLiteralElementIndexInstruction extends Instruction { Expr elt; - ImplicitLiteralElementIndexInstruction() { this = MkImplicitLiteralElementIndex(elt) } + ImplicitLiteralElementIndexInstruction() { this.isAdditional(elt, "lit-index") } override Type getResultType() { result instanceof IntType } @@ -638,20 +620,25 @@ module IR { override predicate isPlatformIndependentConstant() { any() } override predicate isConst() { any() } - - override string toString() { result = "element index" } - - override Location getLocation() { result = elt.getLocation() } } /** * An instruction assigning to a variable or field. */ - class AssignInstruction extends WriteInstruction, MkAssignNode { + class AssignInstruction extends WriteInstruction { AstNode assgn; int i; - AssignInstruction() { this = MkAssignNode(assgn, i) } + AssignInstruction() { + this.isAdditional(assgn, "assign:" + i.toString()) and + ( + exists(assgn.(Assignment).getLhs(i)) + or + exists(assgn.(ValueSpec).getNameExpr(i)) + or + assgn instanceof RangeStmt and i in [0, 1] + ) + } override Instruction getRhs() { exists(SimpleAssignStmt a | a = assgn | @@ -663,51 +650,63 @@ module IR { spec.getNumName() = spec.getNumInit() and result = evalExprInstruction(spec.getInit(i)) or - result = MkZeroInitNode(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) + result = + implicitInitInstruction(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) ) or - result = MkCompoundAssignRhsNode(assgn) + result.(EvalCompoundAssignRhsInstruction).isAdditional(assgn, "compound-rhs") or - result = MkExtractNode(assgn, i) + result.(ExtractTupleElementInstruction).isAdditional(assgn, "extract:" + i.toString()) } override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = "assignment to " + this.getLhs() } - - override Location getLocation() { result = this.getLhs().getLocation() } } - /** An instruction computing the value of the right-hand side of a compound assignment. */ - class EvalCompoundAssignRhsInstruction extends Instruction, MkCompoundAssignRhsNode { + /** + * An instruction that computes the (implicit) right-hand side of a compound assignment. + */ + class EvalCompoundAssignRhsInstruction extends Instruction { CompoundAssignStmt assgn; - EvalCompoundAssignRhsInstruction() { this = MkCompoundAssignRhsNode(assgn) } + EvalCompoundAssignRhsInstruction() { this.isAdditional(assgn, "compound-rhs") } - /** Gets the underlying assignment of this instruction. */ + /** Gets the corresponding compound assignment statement. */ CompoundAssignStmt getAssignment() { result = assgn } override Type getResultType() { result = assgn.getRhs().getType() } override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = assgn.toString() } - - override Location getLocation() { result = assgn.getLocation() } } - /** - * An instruction selecting one of multiple values returned by a function, or either the key - * or the value of the iterator in a range loop, or the result or success value from a type - * assertion. - */ - class ExtractTupleElementInstruction extends Instruction, MkExtractNode { + /** An instruction extracting a component of a tuple value. */ + class ExtractTupleElementInstruction extends Instruction { AstNode s; int i; - ExtractTupleElementInstruction() { this = MkExtractNode(s, i) } + ExtractTupleElementInstruction() { + this.isAdditional(s, "extract:" + i.toString()) and + ( + exists(s.(Assignment).getLhs(i)) + or + exists(s.(ValueSpec).getNameExpr(i)) + or + s instanceof RangeStmt and i in [0, 1] + or + exists(s.(ReturnStmt).getEnclosingFunction().getType().(SignatureType).getResultType(i)) + or + exists( + s.(CallExpr) + .getArgument(0) + .stripParens() + .(CallExpr) + .getType() + .(TupleType) + .getComponentType(i) + ) + ) + } - /** Gets the instruction computing the tuple value from which one value is extracted. */ + /** Gets the instruction computing the tuple value from which the element is extracted. */ Instruction getBase() { exists(Expr baseExpr | baseExpr = s.(Assignment).getRhs() or @@ -716,14 +715,14 @@ module IR { result = evalExprInstruction(baseExpr) ) or - result = MkNextNode(s) + result.(GetNextEntryInstruction).isAdditional(s, "next") or result = evalExprInstruction(s.(ReturnStmt).getExpr()) or result = evalExprInstruction(s.(CallExpr).getArgument(0).stripParens()) } - /** Holds if this extracts the `idx`th value of the result of `base`. */ + /** Holds if this instruction extracts element `idx` from the tuple `base`. */ predicate extractsElement(Instruction base, int idx) { base = this.getBase() and idx = i } override Type getResultType() { @@ -738,51 +737,46 @@ module IR { rangeType.(PointerType).getBaseType().getUnderlyingType().(ArrayType).getElementType() or baseType = rangeType.(SliceType).getElementType() | - i = 0 and - result instanceof IntType + i = 0 and result instanceof IntType or - i = 1 and - result = baseType + i = 1 and result = baseType ) or rangeType instanceof StringType and ( - i = 0 and - result instanceof IntType + i = 0 and result instanceof IntType or result = Builtin::rune().getType() ) or exists(MapType map | map = rangeType | - i = 0 and - result = map.getKeyType() + i = 0 and result = map.getKeyType() or - i = 1 and - result = map.getValueType() + i = 1 and result = map.getValueType() ) or - i = 0 and - result = rangeType.(RecvChanType).getElementType() + i = 0 and result = rangeType.(RecvChanType).getElementType() or - i = 0 and - result = rangeType.(SendRecvChanType).getElementType() + i = 0 and result = rangeType.(SendRecvChanType).getElementType() ) } override ControlFlow::Root getRoot() { result.isRootOf(s) } - - override string toString() { result = s + "[" + i + "]" } - - override Location getLocation() { result = s.getLocation() } } /** - * An instruction that computes the zero value for a variable or constant. + * An instruction that computes the zero value to which a variable without an initializer + * expression is initialized. */ - class EvalImplicitInitInstruction extends Instruction, MkZeroInitNode { + class EvalImplicitInitInstruction extends Instruction { ValueEntity v; + int idx; + ValueSpec spec; - EvalImplicitInitInstruction() { this = MkZeroInitNode(v) } + EvalImplicitInitInstruction() { + this.isAdditional(spec, "zero-init:" + idx.toString()) and + spec.getNameExpr(idx) = v.getDeclaration() + } override Type getResultType() { result = v.getType() } @@ -814,82 +808,56 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "zero value for " + v } - - override Location getLocation() { result = v.getDeclaration().getLocation() } } - /** - * An instruction that corresponds to the declaration of a function. - */ - class DeclareFunctionInstruction extends Instruction, MkFuncDeclNode { + /** An instruction that declares a function. */ + class DeclareFunctionInstruction extends Instruction { FuncDecl fd; - DeclareFunctionInstruction() { this = MkFuncDeclNode(fd) } + DeclareFunctionInstruction() { this.isIn(fd) } override Type getResultType() { result = fd.getType() } - - override string toString() { result = fd.toString() } - - override Location getLocation() { result = fd.getLocation() } } - /** - * An instruction that corresponds to a `defer` statement. - */ - class DeferInstruction extends Instruction, MkDeferNode { + /** An instruction that corresponds to a `defer` statement. */ + class DeferInstruction extends Instruction { DeferStmt defer; - DeferInstruction() { this = MkDeferNode(defer) } + DeferInstruction() { this.isIn(defer) } override ControlFlow::Root getRoot() { result.isRootOf(defer) } - - override string toString() { result = defer.toString() } - - override Location getLocation() { result = defer.getLocation() } } - /** - * An instruction that corresponds to a `go` statement. - */ - class GoInstruction extends Instruction, MkGoNode { + /** An instruction that corresponds to a `go` statement. */ + class GoInstruction extends Instruction { GoStmt go; - GoInstruction() { this = MkGoNode(go) } + GoInstruction() { this.isIn(go) } override ControlFlow::Root getRoot() { result.isRootOf(go) } - - override string toString() { result = go.toString() } - - override Location getLocation() { result = go.getLocation() } } - /** - * An instruction that corresponds to an increment or decrement statement. - */ - class IncDecInstruction extends WriteInstruction, MkIncDecNode { + /** An instruction that corresponds to an increment or decrement statement. */ + class IncDecInstruction extends WriteInstruction { IncDecStmt ids; - IncDecInstruction() { this = MkIncDecNode(ids) } + IncDecInstruction() { this.isIn(ids) } - override Instruction getRhs() { result = MkIncDecRhs(ids) } + override Instruction getRhs() { + result.(EvalIncDecRhsInstruction).isAdditional(ids, "incdec-rhs") + } override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = ids.toString() } - - override Location getLocation() { result = ids.getLocation() } } /** * An instruction that computes the (implicit) right-hand side of an increment or * decrement statement. */ - class EvalIncDecRhsInstruction extends Instruction, MkIncDecRhs { + class EvalIncDecRhsInstruction extends Instruction { IncDecStmt ids; - EvalIncDecRhsInstruction() { this = MkIncDecRhs(ids) } + EvalIncDecRhsInstruction() { this.isAdditional(ids, "incdec-rhs") } /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } @@ -897,19 +865,13 @@ module IR { override Type getResultType() { result = ids.getOperand().getType() } override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = "rhs of " + ids } - - override Location getLocation() { result = ids.getLocation() } } - /** - * An instruction computing the implicit operand `1` in an increment or decrement statement. - */ - class EvalImplicitOneInstruction extends Instruction, MkImplicitOne { + /** An instruction computing the implicit operand `1` in an increment or decrement statement. */ + class EvalImplicitOneInstruction extends Instruction { IncDecStmt ids; - EvalImplicitOneInstruction() { this = MkImplicitOne(ids) } + EvalImplicitOneInstruction() { this.isAdditional(ids, "implicit-one") } /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } @@ -925,25 +887,23 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "1" } - - override Location getLocation() { result = ids.getLocation() } } - /** - * An instruction corresponding to a return from a function. - */ - class ReturnInstruction extends Instruction, MkReturnNode { + /** An instruction corresponding to a return from a function. */ + class ReturnInstruction extends Instruction { ReturnStmt ret; - ReturnInstruction() { this = MkReturnNode(ret) } + ReturnInstruction() { this.isAdditional(ret, "return") } /** Gets the corresponding `ReturnStmt`. */ ReturnStmt getReturnStmt() { result = ret } /** Holds if this statement returns multiple results. */ - predicate returnsMultipleResults() { exists(MkExtractNode(ret, _)) or ret.getNumExpr() > 1 } + predicate returnsMultipleResults() { + exists(ExtractTupleElementInstruction ext | ext.isAdditional(ret, _)) + or + ret.getNumExpr() > 1 + } /** Gets the instruction whose result is the (unique) result returned by this statement. */ Instruction getResult() { @@ -953,36 +913,36 @@ module IR { /** Gets the instruction whose result is the `i`th result returned by this statement. */ Instruction getResult(int i) { - result = MkExtractNode(ret, i) + result.isAdditional(ret, _) and + result.(ExtractTupleElementInstruction).extractsElement(_, i) or - not exists(MkExtractNode(ret, _)) and + not exists(ExtractTupleElementInstruction ext | ext.isAdditional(ret, _)) and result = evalExprInstruction(ret.getExpr(i)) } override ControlFlow::Root getRoot() { result.isRootOf(ret) } - - override string toString() { result = ret.toString() } - - override Location getLocation() { result = ret.getLocation() } } /** * An instruction that represents the implicit assignment to a result variable * performed by a return statement. */ - class WriteResultInstruction extends WriteInstruction, MkResultWriteNode { + class WriteResultInstruction extends WriteInstruction { ResultVariable var; - int i; - ReturnInstruction ret; + int idx; + ReturnStmt retStmt; WriteResultInstruction() { - exists(ReturnStmt retstmt | - this = MkResultWriteNode(var, i, retstmt) and - ret = MkReturnNode(retstmt) - ) + this.isAdditional(retStmt, "result-write:" + idx.toString()) and + var = retStmt.getEnclosingFunction().getResultVar(idx) and + exists(retStmt.getAnExpr()) } - override Instruction getRhs() { result = ret.getResult(i) } + private ReturnInstruction getReturnInstruction() { + result.(ReturnInstruction).isAdditional(retStmt, "return") + } + + override Instruction getRhs() { result = this.getReturnInstruction().getResult(idx) } /** Gets the result variable being assigned. */ ResultVariable getResultVariable() { result = var } @@ -990,120 +950,130 @@ module IR { override Type getResultType() { result = var.getType() } override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit write of " + var } - - override Location getLocation() { result = ret.getResult(i).getLocation() } } /** * An instruction that reads the final value of a result variable upon returning * from a function. */ - class ReadResultInstruction extends Instruction, MkResultReadNode { + class ReadResultInstruction extends Instruction { ResultVariable var; + int idx; + FuncDef fd; - ReadResultInstruction() { this = MkResultReadNode(var) } + ReadResultInstruction() { + this.isAdditional(fd, "result-read:" + idx.toString()) and + var = fd.getResultVar(idx) + } override predicate reads(ValueEntity v) { v = var } override Type getResultType() { result = var.getType() } override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit read of " + var } - - override Location getLocation() { result = var.getDeclaration().getLocation() } } - /** - * An instruction corresponding to a `select` statement. - */ - class SelectInstruction extends Instruction, MkSelectNode { - SelectStmt sel; - - SelectInstruction() { this = MkSelectNode(sel) } - - override ControlFlow::Root getRoot() { result.isRootOf(sel) } - - override string toString() { result = sel.toString() } - - override Location getLocation() { result = sel.getLocation() } - } - - /** - * An instruction corresponding to a send statement. - */ - class SendInstruction extends Instruction, MkSendNode { + /** An instruction corresponding to a send statement. */ + class SendInstruction extends Instruction { SendStmt send; - SendInstruction() { this = MkSendNode(send) } + SendInstruction() { this.isAdditional(send, "send") } override ControlFlow::Root getRoot() { result.isRootOf(send) } - - override string toString() { result = send.toString() } - - override Location getLocation() { result = send.getLocation() } } - /** - * An instruction initializing a parameter to the corresponding argument. - */ - class InitParameterInstruction extends WriteInstruction, MkParameterInit { + /** An instruction initializing a parameter to the corresponding argument. */ + class InitParameterInstruction extends WriteInstruction { Parameter parm; + int idx; + FuncDef fd; - InitParameterInstruction() { this = MkParameterInit(parm) } + InitParameterInstruction() { + this.isAdditional(fd, "param-init:" + idx.toString()) and + parm = fd.getParameter(idx) + } - override Instruction getRhs() { result = MkArgumentNode(parm) } + override Instruction getRhs() { + result.(ReadArgumentInstruction).isAdditional(fd, "arg:" + idx.toString()) + } override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "initialization of " + parm } - - override Location getLocation() { result = parm.getDeclaration().getLocation() } } - /** - * An instruction reading the value of a function argument. - */ - class ReadArgumentInstruction extends Instruction, MkArgumentNode { + /** An instruction reading the value of a function argument. */ + class ReadArgumentInstruction extends Instruction { Parameter parm; + int idx; + FuncDef fd; - ReadArgumentInstruction() { this = MkArgumentNode(parm) } + ReadArgumentInstruction() { + this.isAdditional(fd, "arg:" + idx.toString()) and + parm = fd.getParameter(idx) + } override Type getResultType() { result = parm.getType() } override ControlFlow::Root getRoot() { result = parm.getFunction() } + } + + /** An instruction initializing a result variable to its zero value. */ + class InitResultInstruction extends WriteInstruction { + ResultVariable res; + int idx; + FuncDef fd; - override string toString() { result = "argument corresponding to " + parm } + InitResultInstruction() { + this.isAdditional(fd, "result-init:" + idx.toString()) and + res = fd.getResultVar(idx) + } + + override Instruction getRhs() { + result.(ResultZeroInitInstruction).isAdditional(fd, "result-zero-init:" + idx.toString()) + } - override Location getLocation() { result = parm.getDeclaration().getLocation() } + override ControlFlow::Root getRoot() { result = res.getFunction() } } - /** - * An instruction initializing a result variable to its zero value. - */ - class InitResultInstruction extends WriteInstruction, MkResultInit { + private class ResultZeroInitInstruction extends Instruction { ResultVariable res; + int idx; + FuncDef fd; - InitResultInstruction() { this = MkResultInit(res) } + ResultZeroInitInstruction() { + this.isAdditional(fd, "result-zero-init:" + idx.toString()) and + res = fd.getResultVar(idx) + } - override Instruction getRhs() { result = MkZeroInitNode(res) } + override Type getResultType() { result = res.getType() } - override ControlFlow::Root getRoot() { result = res.getFunction() } + override ControlFlow::Root getRoot() { result.isRootOf(fd) } + + override int getIntValue() { + res.getType().getUnderlyingType() instanceof IntegerType and result = 0 + } - override string toString() { result = "initialization of " + res } + override float getFloatValue() { + res.getType().getUnderlyingType() instanceof FloatType and result = 0.0 + } - override Location getLocation() { result = res.getDeclaration().getLocation() } + override string getStringValue() { + res.getType().getUnderlyingType() instanceof StringType and result = "" + } + + override boolean getBoolValue() { + res.getType().getUnderlyingType() instanceof BoolType and result = false + } + + override predicate isConst() { any() } + + override predicate isPlatformIndependentConstant() { any() } } - /** - * An instruction that gets the next key-value pair in a range loop. - */ - class GetNextEntryInstruction extends Instruction, MkNextNode { + /** An instruction that gets the next key-value pair in a range loop. */ + class GetNextEntryInstruction extends Instruction { RangeStmt rs; - GetNextEntryInstruction() { this = MkNextNode(rs) } + GetNextEntryInstruction() { this.isAdditional(rs, "next") } /** * Gets the instruction computing the value whose key-value pairs this instruction reads. @@ -1111,19 +1081,15 @@ module IR { Instruction getDomain() { result = evalExprInstruction(rs.getDomain()) } override ControlFlow::Root getRoot() { result.isRootOf(rs) } - - override string toString() { result = "next key-value pair in range" } - - override Location getLocation() { result = rs.getDomain().getLocation() } } /** * An instruction computing the implicit `true` value in an expression-less `switch` statement. */ - class EvalImplicitTrueInstruction extends Instruction, MkImplicitTrue { - Stmt stmt; + class EvalImplicitTrueInstruction extends Instruction { + ExpressionSwitchStmt stmt; - EvalImplicitTrueInstruction() { this = MkImplicitTrue(stmt) } + EvalImplicitTrueInstruction() { this.isAdditional(stmt, "implicit-true") } override Type getResultType() { result instanceof BoolType } @@ -1136,91 +1102,46 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "true" } - - override Location getLocation() { result = stmt.getLocation() } } /** * An instruction corresponding to the implicit comparison or type check performed by an * expression in a `case` clause. - * - * For example, consider this `switch` statement: - * - * ```go - * switch x { - * case 2, y+1: - * ... - * } - * ``` - * - * The expressions `2` and `y+1` are implicitly compared to `x`. These comparisons are - * represented by case instructions. */ - class CaseInstruction extends Instruction, MkCaseCheckNode { + class CaseInstruction extends Instruction { CaseClause cc; int i; - CaseInstruction() { this = MkCaseCheckNode(cc, i) } + CaseInstruction() { + this.isAdditional(cc, "case-check:" + i.toString()) and + exists(cc.getExpr(i)) + } override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "case " + cc.getExpr(i) } - - override Location getLocation() { result = cc.getExpr(i).getLocation() } } /** - * An instruction corresponding to the implicit declaration of the variable - * `lv` in case clause `cc` and its assignment of the value `switchExpr` from - * the guard. This only occurs in case clauses in a type switch statement - * which declares a variable in its guard. - * - * For example, consider this type switch statement: - * - * ```go - * switch y := x.(type) { - * case Type1: - * f(y) - * ... - * } - * ``` - * - * The `y` inside the case clause is actually a local variable with type - * `Type1` that is implicitly declared at the top of the case clause. In - * default clauses and case clauses which list more than one type, the type - * of the implicitly declared variable is the type of `switchExpr`. + * An instruction corresponding to the implicit declaration and assignment of a variable + * in a type switch case clause. */ - class TypeSwitchImplicitVariableInstruction extends Instruction, MkTypeSwitchImplicitVariable { + class TypeSwitchImplicitVariableInstruction extends Instruction { CaseClause cc; - LocalVariable lv; - Expr switchExpr; - TypeSwitchImplicitVariableInstruction() { - this = MkTypeSwitchImplicitVariable(cc, lv, switchExpr) - } + TypeSwitchImplicitVariableInstruction() { this.isAdditional(cc, "type-switch-var") } override predicate writes(ValueEntity v, Instruction rhs) { - v = lv and - rhs = evalExprInstruction(switchExpr) + v = cc.getImplicitlyDeclaredVariable() and + exists(TypeSwitchStmt ts | cc = ts.getACase() | rhs = evalExprInstruction(ts.getExpr())) } override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "implicit type switch variable declaration" } - - override Location getLocation() { result = cc.getLocation() } } - /** - * An instruction computing the implicit lower slice bound of zero in a slice expression without - * an explicit lower bound. - */ - class EvalImplicitLowerSliceBoundInstruction extends Instruction, MkImplicitLowerSliceBound { + /** An instruction computing the implicit lower bound of a slice expression. */ + class EvalImplicitLowerSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitLowerSliceBoundInstruction() { this = MkImplicitLowerSliceBound(slice) } + EvalImplicitLowerSliceBoundInstruction() { this.isAdditional(slice, "implicit-low") } override Type getResultType() { result instanceof IntType } @@ -1233,56 +1154,38 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "0" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction computing the implicit upper slice bound in a slice expression without an - * explicit upper bound. - */ - class EvalImplicitUpperSliceBoundInstruction extends Instruction, MkImplicitUpperSliceBound { + /** An instruction computing the implicit upper bound of a slice expression. */ + class EvalImplicitUpperSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitUpperSliceBoundInstruction() { this = MkImplicitUpperSliceBound(slice) } + EvalImplicitUpperSliceBoundInstruction() { this.isAdditional(slice, "implicit-high") } override ControlFlow::Root getRoot() { result.isRootOf(slice) } override Type getResultType() { result instanceof IntType } - - override string toString() { result = "len" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction computing the implicit maximum slice bound in a slice expression without an - * explicit maximum bound. - */ - class EvalImplicitMaxSliceBoundInstruction extends Instruction, MkImplicitMaxSliceBound { + /** An instruction computing the implicit maximum bound of a slice expression. */ + class EvalImplicitMaxSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitMaxSliceBoundInstruction() { this = MkImplicitMaxSliceBound(slice) } + EvalImplicitMaxSliceBoundInstruction() { this.isAdditional(slice, "implicit-max") } override ControlFlow::Root getRoot() { result.isRootOf(slice) } override Type getResultType() { result instanceof IntType } - - override string toString() { result = "cap" } - - override Location getLocation() { result = slice.getLocation() } } /** - * An instruction implicitly dereferencing the base in a field or method reference through a - * pointer, or the base in an element or slice reference through a pointer. + * An instruction computing the implicit dereference of a pointer used as the base of a field + * or method access, element access, or slice expression. */ - class EvalImplicitDerefInstruction extends Instruction, MkImplicitDeref { + class EvalImplicitDerefInstruction extends Instruction { Expr e; - EvalImplicitDerefInstruction() { this = MkImplicitDeref(e) } + EvalImplicitDerefInstruction() { this.isAdditional(e, "implicit-deref") } /** Gets the operand that is being dereferenced. */ Expr getOperand() { result = e } @@ -1292,12 +1195,41 @@ module IR { } override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit dereference" } - - override Location getLocation() { result = e.getLocation() } } + /** A representation of the target of a write instruction. */ + newtype TWriteTarget = + /** A left-hand side of an assignment. */ + MkLhs(ControlFlow::Node write, Expr lhs) { + exists(AstNode assgn, int i | write.isAdditional(assgn, "assign:" + i.toString()) | + lhs = assgn.(Assignment).getLhs(i).stripParens() + or + lhs = assgn.(ValueSpec).getNameExpr(i) + or + exists(RangeStmt rs | rs = assgn | + i = 0 and lhs = rs.getKey().stripParens() + or + i = 1 and lhs = rs.getValue().stripParens() + ) + ) + or + exists(IncDecStmt ids | write.isIn(ids) | lhs = ids.getOperand().stripParens()) + or + exists(FuncDef fd, int idx | + write.isAdditional(fd, "param-init:" + idx.toString()) and + lhs = fd.getParameter(idx).getDeclaration() + ) + or + exists(FuncDef fd, int idx | + write.isAdditional(fd, "result-init:" + idx.toString()) and + lhs = fd.getResultVar(idx).getDeclaration() + ) + } or + /** A composite literal element target. */ + MkLiteralElementTarget(InitLiteralComponentInstruction elt) or + /** A result variable write target. */ + MkResultWriteTarget(WriteResultInstruction w) + /** A representation of the target of a write instruction. */ class WriteTarget extends TWriteTarget { ControlFlow::Node w; @@ -1330,10 +1262,6 @@ module IR { * DEPRECATED: Use `getLocation()` instead. * * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ deprecated predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -1395,10 +1323,6 @@ module IR { /** Gets the constant this refers to, if any. */ Constant getConstant() { this.refersTo(result) } - - override string toString() { result = this.getName() } - - override Location getLocation() { result = loc.getLocation() } } /** A reference to a field, used as the target of a write. */ @@ -1416,7 +1340,7 @@ module IR { result = w.(InitLiteralStructFieldInstruction).getBase() } - /** Get the type of the base of this field access, that is, the type that contains the field. */ + /** Gets the type of the base of this field access, that is, the type that contains the field. */ Type getBaseType() { result = this.getBase().getResultType() } override predicate refersTo(ValueEntity e) { @@ -1429,24 +1353,10 @@ module IR { /** Gets the field this refers to, if it can be determined. */ Field getField() { this.refersTo(result) } - - override string toString() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | - result = "field " + sel.getSelector().getName() - ) - or - result = "field " + w.(InitLiteralStructFieldInstruction).getFieldName() - } - - override Location getLocation() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | result = sel.getLocation()) - or - result = w.(InitLiteralStructFieldInstruction).getLocation() - } } /** - * A reference to an element of an array, slice or map, used as the target of a write. + * A reference to an element of an array, slice, or map, used as the target of a write. */ class ElementTarget extends WriteTarget { ElementTarget() { @@ -1468,14 +1378,6 @@ module IR { or result = w.(InitLiteralElementInstruction).getIndex() } - - override string toString() { result = "element" } - - override Location getLocation() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = idx.getLocation()) - or - result = w.(InitLiteralElementInstruction).getLocation() - } } /** @@ -1495,66 +1397,75 @@ module IR { result = evalExprInstruction(base) ) } - - override string toString() { result = lhs.toString() } - - override Location getLocation() { result = lhs.getLocation() } } /** * Gets the (final) instruction computing the value of `e`. - * - * Note that some expressions (such as type expressions or labels) have no corresponding - * instruction, so this predicate is undefined for them. - * - * Short-circuiting expressions that are purely used for control flow (meaning that their - * value is not stored in a variable or used to compute the value of a non-shortcircuiting - * expression) do not have a final instruction either. */ Instruction evalExprInstruction(Expr e) { - result = MkExprNode(e) or + result.(EvalInstruction).getExpr() = e + or result = evalExprInstruction(e.(ParenExpr).getExpr()) } /** * Gets the instruction corresponding to the initialization of `r`. */ - InitParameterInstruction initRecvInstruction(ReceiverVariable r) { result = MkParameterInit(r) } + InitParameterInstruction initRecvInstruction(ReceiverVariable r) { + exists(FuncDef fd, int i | + fd.getParameter(i) = r and result.isAdditional(fd, "param-init:" + i.toString()) + ) + } /** * Gets the instruction corresponding to the initialization of `p`. */ - InitParameterInstruction initParamInstruction(Parameter p) { result = MkParameterInit(p) } + InitParameterInstruction initParamInstruction(Parameter p) { + exists(FuncDef fd, int i | + fd.getParameter(i) = p and result.isAdditional(fd, "param-init:" + i.toString()) + ) + } /** * Gets the instruction corresponding to the `i`th assignment happening at * `assgn` (0-based). */ - AssignInstruction assignInstruction(Assignment assgn, int i) { result = MkAssignNode(assgn, i) } + AssignInstruction assignInstruction(Assignment assgn, int i) { + result.isAdditional(assgn, "assign:" + i.toString()) and + exists(assgn.getLhs(i)) + } /** * Gets the instruction corresponding to the `i`th initialization happening * at `spec` (0-based). */ - AssignInstruction initInstruction(ValueSpec spec, int i) { result = MkAssignNode(spec, i) } + AssignInstruction initInstruction(ValueSpec spec, int i) { + result.isAdditional(spec, "assign:" + i.toString()) and + exists(spec.getNameExpr(i)) + } /** * Gets the instruction corresponding to the assignment of the key variable * of range statement `rs`. */ - AssignInstruction assignKeyInstruction(RangeStmt rs) { result = MkAssignNode(rs, 0) } + AssignInstruction assignKeyInstruction(RangeStmt rs) { result.isAdditional(rs, "assign:0") } /** * Gets the instruction corresponding to the assignment of the value variable * of range statement `rs`. */ - AssignInstruction assignValueInstruction(RangeStmt rs) { result = MkAssignNode(rs, 1) } + AssignInstruction assignValueInstruction(RangeStmt rs) { result.isAdditional(rs, "assign:1") } /** * Gets the instruction corresponding to the implicit initialization of `v` * to its zero value. */ - EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { result = MkZeroInitNode(v) } + EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { + exists(ValueSpec spec, int i | + spec.getNameExpr(i) = v.getDeclaration() and + result.isAdditional(spec, "zero-init:" + i.toString()) + ) + } /** * Gets the instruction corresponding to the extraction of the `idx`th element @@ -1568,28 +1479,30 @@ module IR { * Gets the instruction corresponding to the implicit lower bound of slice `e`, if any. */ EvalImplicitLowerSliceBoundInstruction implicitLowerSliceBoundInstruction(SliceExpr e) { - result = MkImplicitLowerSliceBound(e) + result.isAdditional(e, "implicit-low") } /** * Gets the instruction corresponding to the implicit upper bound of slice `e`, if any. */ EvalImplicitUpperSliceBoundInstruction implicitUpperSliceBoundInstruction(SliceExpr e) { - result = MkImplicitUpperSliceBound(e) + result.isAdditional(e, "implicit-high") } /** * Gets the instruction corresponding to the implicit maximum bound of slice `e`, if any. */ EvalImplicitMaxSliceBoundInstruction implicitMaxSliceBoundInstruction(SliceExpr e) { - result = MkImplicitMaxSliceBound(e) + result.isAdditional(e, "implicit-max") } /** * Gets the implicit dereference instruction for `e`, where `e` is a pointer used as the base * in a field/method access, element access, or slice expression. */ - EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { result = MkImplicitDeref(e) } + EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { + result.isAdditional(e, "implicit-deref") + } /** Gets the base of `insn`, if `insn` is an implicit field read. */ Instruction lookThroughImplicitFieldRead(Instruction insn) { diff --git a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll index 9648335a6dde..8a66103ba5e9 100644 --- a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll @@ -89,7 +89,7 @@ private module Internal { /** Holds if the `i`th node of `bb` in function `f` is an entry node. */ private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) { f = bb.getScope() and - bb.getNode(i).isEntryNode() + bb.getNode(i).(ControlFlow::Node).isEntryNode() } /** diff --git a/go/ql/lib/semmle/go/frameworks/Revel.qll b/go/ql/lib/semmle/go/frameworks/Revel.qll index c6250c2f8a51..085db1d2b0a8 100644 --- a/go/ql/lib/semmle/go/frameworks/Revel.qll +++ b/go/ql/lib/semmle/go/frameworks/Revel.qll @@ -154,7 +154,7 @@ module Revel { private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) { result = insn or - result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase()) + result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBaseInstruction()) } /** A call to `Controller.Render`. */