From 515e5c13ca6bad5d37d1f6abdf21d77b7d16ba0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 15:29:14 +0000 Subject: [PATCH] Continue shared CFG library migration: fix prologue/epilogue, implicit fields, cleanup Agent-Logs-Url: https://github.com/github/codeql/sessions/011f8465-5ad3-44e2-9d84-f5314ea1afb2 Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com> --- .../2026-03-30-shared-cfg-library.md | 4 + .../go/controlflow/ControlFlowGraphImpl.qll | 2133 ----------------- .../go/controlflow/ControlFlowGraphShared.qll | 149 +- go/ql/lib/semmle/go/controlflow/IR.qll.bak | 1580 ------------ go/ql/lib/semmle/go/controlflow/IR.qll.bak2 | 1580 ------------ 5 files changed, 146 insertions(+), 5300 deletions(-) create mode 100644 go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md delete mode 100644 go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll delete mode 100644 go/ql/lib/semmle/go/controlflow/IR.qll.bak delete mode 100644 go/ql/lib/semmle/go/controlflow/IR.qll.bak2 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 00000000000..896f23a8a14 --- /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/ControlFlowGraphImpl.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll deleted file mode 100644 index a26ab3adaf5..00000000000 --- 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 index 147468122fd..70be225eff3 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -66,7 +66,7 @@ module GoCfg { Callable() { exists(this.(Go::FuncDef).getBody()) } } - AstNode callableGetBody(Callable c) { result = c.(Go::FuncDef).getBody() } + AstNode callableGetBody(Callable c) { result = c } Callable getEnclosingCallable(AstNode node) { result = node and node instanceof Callable @@ -648,7 +648,8 @@ module GoCfg { sliceExprStep(n1, n2) or selectorExprStep(n1, n2) or compositeLitStep(n1, n2) or - sendStmtStep(n1, n2) + sendStmtStep(n1, n2) or + funcDefStep(n1, n2) } /** @@ -967,18 +968,57 @@ module GoCfg { 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())) 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 ( - if implicitDerefCondition(sel.getBase()) - then n2.isAdditional(sel.getBase(), "implicit-deref") - else n2.isIn(sel) + // 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 - n1.isAdditional(sel.getBase(), "implicit-deref") and n2.isIn(sel) + // 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) ) @@ -1292,5 +1332,100 @@ module GoCfg { 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.bak b/go/ql/lib/semmle/go/controlflow/IR.qll.bak deleted file mode 100644 index 807fbf76d1f..00000000000 --- a/go/ql/lib/semmle/go/controlflow/IR.qll.bak +++ /dev/null @@ -1,1580 +0,0 @@ -/** - * Provides classes and predicates for working with an intermediate representation (IR) of Go - * programs that is used as the foundation of the control flow and data flow graphs. - * - * In the IR, the program is represented as a set of instructions, which correspond to expressions - * and statements that compute a value or perform an operation (as opposed to providing syntactic - * 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 before/after nodes). - */ -overlay[local] -module; - -import go -private import ControlFlowGraphShared - -/** Provides predicates and classes for working with IR constructs. */ -module IR { - /** - * An IR instruction. - */ - class Instruction extends ControlFlow::Node { - Instruction() { - this.isIn(_) or - this.isAdditional(_, _) or - this.isAfterTrue(_) and not this.isIn(_) or - this.isAfterFalse(_) and not this.isIn(_) - } - - /** Holds if this instruction reads the value of variable or constant `v`. */ - predicate reads(ValueEntity v) { this.readsField(_, v) or this.readsMethod(_, v) } - - /** Holds if this instruction updates variable or constant `v` to the value of `rhs`. */ - predicate writes(ValueEntity v, Instruction rhs) { this.writesField(_, v, rhs) } - - /** Holds if this instruction reads the value of field `f` on the value of `base`. */ - predicate readsField(Instruction base, Field f) { none() } - - /** Holds if this instruction updates the value of field `f` on the value of `base`. */ - predicate writesField(Instruction base, Field f, Instruction rhs) { none() } - - /** Holds if this instruction looks up method `m` on the value of `receiver`. */ - predicate readsMethod(Instruction receiver, Method m) { none() } - - /** Holds if this instruction reads the value of element `index` on the value of `base`. */ - predicate readsElement(Instruction base, Instruction index) { none() } - - /** Holds if this instruction updates the value of element `index` on the value of `base`. */ - predicate writesElement(Instruction base, Instruction index) { none() } - - /** Gets the type of the result of this instruction, if any. */ - Type getResultType() { none() } - - /** Gets the float value of the result of this instruction, if it can be determined. */ - float getFloatValue() { none() } - - /** Gets the int value of the result of this instruction, if it can be determined. */ - int getIntValue() { none() } - - /** - * Holds if the complex value of the result of this instruction has real part `real` and - * imaginary part `imag`. - */ - predicate hasComplexValue(float real, float imag) { none() } - - /** Gets either `getFloatValue` or `getIntValue` */ - float getNumericValue() { result = this.getFloatValue() or result = this.getIntValue() } - - /** - * Gets the string representation of the exact value of the result of this instruction, - * if any. - * - * For example, for the constant 3.141592653589793238462, this will - * result in 1570796326794896619231/500000000000000000000 - */ - string getExactValue() { none() } - - /** Gets the string value of the result of this instruction, if it can be determined. */ - string getStringValue() { none() } - - /** Gets the Boolean value of the result of this instruction, if it can be determined. */ - boolean getBoolValue() { none() } - - /** Holds if the result of this instruction is known at compile time. */ - predicate isConst() { none() } - - /** - * Holds if the result of this instruction is known at compile time, and is guaranteed not to - * depend on the platform where it is evaluated. - */ - predicate isPlatformIndependentConstant() { none() } - - /** Gets a textual representation of the kind of this instruction. */ - string getInsnKind() { - this instanceof EvalInstruction and result = "expression" - or - this instanceof InitLiteralComponentInstruction and result = "element init" - or - this instanceof ImplicitLiteralElementIndexInstruction and result = "element index" - or - this instanceof AssignInstruction and result = "assignment" - or - this instanceof EvalCompoundAssignRhsInstruction and - result = "right-hand side of compound assignment" - or - this instanceof ExtractTupleElementInstruction and result = "tuple element extraction" - or - this instanceof EvalImplicitInitInstruction and result = "zero value" - or - this instanceof DeclareFunctionInstruction and result = "function declaration" - or - this instanceof DeferInstruction and result = "defer" - or - this instanceof GoInstruction and result = "go" - or - this instanceof ConditionGuardInstruction and result = "condition guard" - or - this instanceof IncDecInstruction and result = "increment/decrement" - or - this instanceof EvalIncDecRhsInstruction and - result = "right-hand side of increment/decrement" - or - this instanceof EvalImplicitOneInstruction and result = "implicit 1" - or - this instanceof ReturnInstruction and result = "return" - or - this instanceof WriteResultInstruction and result = "result write" - or - this instanceof ReadResultInstruction and result = "result read" - or - this instanceof SendInstruction and result = "send" - or - this instanceof InitParameterInstruction and result = "parameter initialization" - or - this instanceof ReadArgumentInstruction and result = "argument" - or - this instanceof InitResultInstruction and result = "result initialization" - or - this instanceof GetNextEntryInstruction and result = "next key-value pair" - or - this instanceof EvalImplicitTrueInstruction and result = "implicit true" - or - this instanceof CaseInstruction and result = "case" - or - this instanceof TypeSwitchImplicitVariableInstruction and - result = "type switch implicit variable declaration" - or - this instanceof EvalImplicitLowerSliceBoundInstruction and result = "implicit lower bound" - or - this instanceof EvalImplicitUpperSliceBoundInstruction and result = "implicit upper bound" - or - this instanceof EvalImplicitMaxSliceBoundInstruction and result = "implicit maximum" - or - 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(_) and not this.isIn(_) - or - this.isAfterFalse(_) and not this.isIn(_) - } - } - - /** - * An IR instruction representing the evaluation of an expression. - */ - class EvalInstruction extends Instruction { - Expr e; - - EvalInstruction() { this.isIn(e) and e instanceof Expr } - - /** Gets the expression underlying this instruction. */ - Expr getExpr() { result = e } - - override predicate reads(ValueEntity v) { e = v.getAReference() } - - override Type getResultType() { result = e.getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override float getFloatValue() { result = e.getFloatValue() } - - override int getIntValue() { result = e.getIntValue() } - - override predicate hasComplexValue(float real, float imag) { e.hasComplexValue(real, imag) } - - override string getExactValue() { result = e.getExactValue() } - - override string getStringValue() { result = e.getStringValue() } - - override boolean getBoolValue() { result = e.getBoolValue() } - - override predicate isConst() { e.isConst() } - - override predicate isPlatformIndependentConstant() { e.isPlatformIndependentConstant() } - - override string toString() { result = e.toString() } - - override Location getLocation() { result = e.getLocation() } - } - - /** - * An IR instruction that reads the value of a variable, constant, field or array element, - * or refers to a function. - */ - class ReadInstruction extends Instruction { - ReadInstruction() { - exists(Expr e | e = this.(EvalInstruction).getExpr() | - (e instanceof ValueName or e instanceof IndexExpr) and - e.(ReferenceExpr).isRvalue() - ) - or - this instanceof ReadResultInstruction - or - 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 | - result = fri - ) - or - not exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1) and - exists(Expr base | - base = e.(SelectorExpr).getBase() - or - base = e.(IndexExpr).getBase() - or - base = e.(SliceExpr).getBase() - | - result = MkImplicitDeref(base) - or - not exists(MkImplicitDeref(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() { - exists(Expr e | e = this.(EvalInstruction).getExpr() | - e instanceof IndexExpr - or - e.(SelectorExpr).getBase() instanceof ValueExpr and - not e.(SelectorExpr).getSelector() = any(Method method).getAReference() - ) - or - this instanceof MkImplicitFieldSelection - } - - /** Gets the instruction computing the base value on which the field or element is read. */ - Instruction getBase() { - result = this.(ImplicitFieldReadInstruction).getBaseInstruction() - or - result = selectorBase(this.(EvalInstruction).getExpr()) - } - } - - /** - * 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; - int index; - Field field; - - FieldReadInstruction() { - e = this.(EvalInstruction).getExpr() and - index = 0 and - field.getAReference() = e.getSelector() - or - this = MkImplicitFieldSelection(e, index, field) - } - - /** Gets the `SelectorExpr` of this field read. */ - SelectorExpr getSelectorExpr() { result = e } - - /** Gets the index of this field read. */ - int getIndex() { result = index } - - /** Gets the field being read. */ - Field getField() { result = field } - - Instruction getBaseInstruction() { - exists(ImplicitFieldReadInstruction fri | - fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) - | - result = fri - ) - or - not exists(ImplicitFieldReadInstruction fri | - fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) - ) and - ( - result = MkImplicitDeref(e.getBase()) - or - not exists(MkImplicitDeref(e.getBase())) and - result = evalExprInstruction(e.getBase()) - ) - } - - override predicate readsField(Instruction base, Field f) { - base = this.getBaseInstruction() and f = field - } - } - - /** - * 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. - */ - class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection { - ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) } - - override predicate reads(ValueEntity v) { v = field } - - override Type getResultType() { result = lookThroughPointerType(field.getType()) } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit read of field " + field.toString() } - - override Location getLocation() { result = e.getBase().getLocation() } - } - - /** - * An IR instruction that looks up a method. - */ - class MethodReadInstruction extends ReadInstruction, EvalInstruction { - Method method; - override SelectorExpr e; - - MethodReadInstruction() { e.getSelector() = method.getAReference() } - - /** Gets the instruction computing the receiver value on which the method is looked up. */ - Instruction getReceiver() { result = selectorBase(e) } - - /** Gets the method being looked up. */ - Method getMethod() { result = method } - - override predicate readsMethod(Instruction receiver, Method m) { - receiver = this.getReceiver() and m = this.getMethod() - } - } - - /** - * An IR instruction that reads an element of an array, slice, map or string. - */ - class ElementReadInstruction extends ComponentReadInstruction, EvalInstruction { - override IndexExpr e; - - /** Gets the instruction computing the index of the element being looked up. */ - Instruction getIndex() { result = evalExprInstruction(e.getIndex()) } - - override predicate readsElement(Instruction base, Instruction index) { - base = this.getBase() and index = this.getIndex() - } - } - - /** - * An IR instruction that constructs a slice. - */ - class SliceInstruction extends EvalInstruction { - override SliceExpr e; - - /** Gets the instruction computing the base value from which the slice is constructed. */ - Instruction getBase() { result = selectorBase(e) } - - /** Gets the instruction computing the lower bound of the slice. */ - Instruction getLow() { - result = evalExprInstruction(e.getLow()) or - result = implicitLowerSliceBoundInstruction(e) - } - - /** Gets the instruction computing the upper bound of the slice. */ - Instruction getHigh() { - result = evalExprInstruction(e.getHigh()) or - result = implicitUpperSliceBoundInstruction(e) - } - - /** Gets the instruction computing the capacity of the slice. */ - Instruction getMax() { - result = evalExprInstruction(e.getMax()) or - result = implicitMaxSliceBoundInstruction(e) - } - } - - /** - * An IR instruction that writes a memory location. - */ - class WriteInstruction extends Instruction { - WriteTarget lhs; - Boolean initialization; - - WriteInstruction() { - ( - lhs = MkLhs(this, _) - or - lhs = MkResultWriteTarget(this) - ) and - initialization = false - or - lhs = MkLiteralElementTarget(this) and initialization = true - } - - /** Gets the target to which this instruction writes. */ - WriteTarget getLhs() { result = lhs } - - /** Holds if this instruction initializes a literal. */ - predicate isInitialization() { initialization = true } - - /** Gets the instruction computing the value this instruction writes. */ - Instruction getRhs() { none() } - - override predicate writes(ValueEntity v, Instruction rhs) { - this.getLhs().refersTo(v) and - rhs = this.getRhs() - } - } - - /** - * An IR instruction that initializes a component of a composite literal. - */ - class InitLiteralComponentInstruction extends WriteInstruction, MkLiteralElementInitNode { - CompositeLit lit; - int i; - Expr elt; - - InitLiteralComponentInstruction() { - this = MkLiteralElementInitNode(elt) and elt = lit.getElement(i) - } - - /** Gets the instruction allocating the composite literal. */ - Instruction getBase() { result = evalExprInstruction(lit) } - - override Instruction getRhs() { - result = evalExprInstruction(elt) or - result = evalExprInstruction(elt.(KeyValueExpr).getValue()) - } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override string toString() { result = "init of " + elt } - - override Location getLocation() { result = elt.getLocation() } - } - - /** - * An IR instruction that initializes a field of a struct literal. - */ - class InitLiteralStructFieldInstruction extends InitLiteralComponentInstruction { - override StructLit lit; - - /** Gets the name of the initialized field. */ - pragma[nomagic] - string getFieldName() { - if elt instanceof KeyValueExpr - then result = elt.(KeyValueExpr).getKey().(Ident).getName() - else pragma[only_bind_out](lit.getStructType()).hasOwnField(i, result, _, _) - } - - /** Gets the initialized field. */ - Field getField() { - result.getDeclaringType() = lit.getStructType() and - result.getName() = this.getFieldName() - } - } - - /** - * An IR instruction that initializes an element of an array, slice or map literal. - */ - class InitLiteralElementInstruction extends InitLiteralComponentInstruction { - Type literalType; - - InitLiteralElementInstruction() { - literalType = lit.getType().getUnderlyingType() and - ( - literalType instanceof ArrayType or - literalType instanceof SliceType or - literalType instanceof MapType - ) - } - - /** Gets the instruction computing the index of the initialized element. */ - Instruction getIndex() { - result = evalExprInstruction(elt.(KeyValueExpr).getKey()) - or - result = MkImplicitLiteralElementIndex(elt) - } - } - - /** - * 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. - */ - class InitLiteralSliceElementInstruction extends InitLiteralElementInstruction { - override SliceType literalType; - } - - /** - * 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. - */ - class FieldWriteInstruction extends WriteInstruction { - override FieldTarget lhs; - - /** Gets the instruction computing the base value on which the field is written. */ - Instruction getBase() { result = lhs.getBase() } - - /** Gets the field being written. */ - 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 - } - } - - /** - * 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. */ - 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 - } - } - - /** 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() - or - not elt instanceof KeyValueExpr and - ( - i = 0 and result = 0 - or - result = getElementIndex(lit, i - 1) + 1 - ) - ) - } - - /** - * An IR instruction computing the implicit index of an element in an array or slice literal. - */ - class ImplicitLiteralElementIndexInstruction extends Instruction, MkImplicitLiteralElementIndex { - Expr elt; - - ImplicitLiteralElementIndexInstruction() { this = MkImplicitLiteralElementIndex(elt) } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override int getIntValue() { - exists(CompositeLit lit, int i | elt = lit.getElement(i) | result = getElementIndex(lit, i)) - } - - override string getStringValue() { none() } - - override string getExactValue() { result = this.getIntValue().toString() } - - 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 { - AstNode assgn; - int i; - - AssignInstruction() { this = MkAssignNode(assgn, i) } - - override Instruction getRhs() { - exists(SimpleAssignStmt a | a = assgn | - a.getNumLhs() = a.getNumRhs() and - result = evalExprInstruction(a.getRhs(i)) - ) - or - exists(ValueSpec spec | spec = assgn | - spec.getNumName() = spec.getNumInit() and - result = evalExprInstruction(spec.getInit(i)) - or - result = MkZeroInitNode(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) - ) - or - result = MkCompoundAssignRhsNode(assgn) - or - result = MkExtractNode(assgn, i) - } - - 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 { - CompoundAssignStmt assgn; - - EvalCompoundAssignRhsInstruction() { this = MkCompoundAssignRhsNode(assgn) } - - /** Gets the underlying assignment of this instruction. */ - 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 { - AstNode s; - int i; - - ExtractTupleElementInstruction() { this = MkExtractNode(s, i) } - - /** Gets the instruction computing the tuple value from which one value is extracted. */ - Instruction getBase() { - exists(Expr baseExpr | - baseExpr = s.(Assignment).getRhs() or - baseExpr = s.(ValueSpec).getInit() - | - result = evalExprInstruction(baseExpr) - ) - or - result = MkNextNode(s) - 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`. */ - predicate extractsElement(Instruction base, int idx) { base = this.getBase() and idx = i } - - override Type getResultType() { - exists(Expr e | this.getBase() = evalExprInstruction(e) | - result = e.getType().(TupleType).getComponentType(pragma[only_bind_into](i)) - ) - or - exists(Type rangeType | rangeType = s.(RangeStmt).getDomain().getType().getUnderlyingType() | - exists(Type baseType | - baseType = rangeType.(ArrayType).getElementType() or - baseType = - rangeType.(PointerType).getBaseType().getUnderlyingType().(ArrayType).getElementType() or - baseType = rangeType.(SliceType).getElementType() - | - i = 0 and - result instanceof IntType - or - i = 1 and - result = baseType - ) - or - rangeType instanceof StringType and - ( - i = 0 and - result instanceof IntType - or - result = Builtin::rune().getType() - ) - or - exists(MapType map | map = rangeType | - i = 0 and - result = map.getKeyType() - or - i = 1 and - result = map.getValueType() - ) - or - i = 0 and - result = rangeType.(RecvChanType).getElementType() - or - 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. - */ - class EvalImplicitInitInstruction extends Instruction, MkZeroInitNode { - ValueEntity v; - - EvalImplicitInitInstruction() { this = MkZeroInitNode(v) } - - override Type getResultType() { result = v.getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(v.getDeclaration()) } - - override int getIntValue() { - v.getType().getUnderlyingType() instanceof IntegerType and result = 0 - } - - override float getFloatValue() { - v.getType().getUnderlyingType() instanceof FloatType and result = 0.0 - } - - override string getStringValue() { - v.getType().getUnderlyingType() instanceof StringType and result = "" - } - - override boolean getBoolValue() { - v.getType().getUnderlyingType() instanceof BoolType and result = false - } - - override string getExactValue() { - result = this.getIntValue().toString() or - result = this.getFloatValue().toString() or - result = this.getStringValue().toString() or - result = this.getBoolValue().toString() - } - - 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 { - FuncDecl fd; - - DeclareFunctionInstruction() { this = MkFuncDeclNode(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 { - DeferStmt defer; - - DeferInstruction() { this = MkDeferNode(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 { - GoStmt go; - - GoInstruction() { this = MkGoNode(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 { - IncDecStmt ids; - - IncDecInstruction() { this = MkIncDecNode(ids) } - - override Instruction getRhs() { result = MkIncDecRhs(ids) } - - 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 { - IncDecStmt ids; - - EvalIncDecRhsInstruction() { this = MkIncDecRhs(ids) } - - /** Gets the corresponding increment or decrement statement. */ - IncDecStmt getStmt() { result = ids } - - 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 { - IncDecStmt ids; - - EvalImplicitOneInstruction() { this = MkImplicitOne(ids) } - - /** Gets the corresponding increment or decrement statement. */ - IncDecStmt getStmt() { result = ids } - - override Type getResultType() { result = ids.getOperand().getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override int getIntValue() { result = 1 } - - override string getExactValue() { result = "1" } - - 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 { - ReturnStmt ret; - - ReturnInstruction() { this = MkReturnNode(ret) } - - /** Gets the corresponding `ReturnStmt`. */ - ReturnStmt getReturnStmt() { result = ret } - - /** Holds if this statement returns multiple results. */ - predicate returnsMultipleResults() { exists(MkExtractNode(ret, _)) or ret.getNumExpr() > 1 } - - /** Gets the instruction whose result is the (unique) result returned by this statement. */ - Instruction getResult() { - not this.returnsMultipleResults() and - result = evalExprInstruction(ret.getExpr()) - } - - /** Gets the instruction whose result is the `i`th result returned by this statement. */ - Instruction getResult(int i) { - result = MkExtractNode(ret, i) - or - not exists(MkExtractNode(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 { - ResultVariable var; - int i; - ReturnInstruction ret; - - WriteResultInstruction() { - exists(ReturnStmt retstmt | - this = MkResultWriteNode(var, i, retstmt) and - ret = MkReturnNode(retstmt) - ) - } - - override Instruction getRhs() { result = ret.getResult(i) } - - /** Gets the result variable being assigned. */ - ResultVariable getResultVariable() { result = var } - - 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 { - ResultVariable var; - - ReadResultInstruction() { this = MkResultReadNode(var) } - - 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 { - SendStmt send; - - SendInstruction() { this = MkSendNode(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 { - Parameter parm; - - InitParameterInstruction() { this = MkParameterInit(parm) } - - override Instruction getRhs() { result = MkArgumentNode(parm) } - - 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 { - Parameter parm; - - ReadArgumentInstruction() { this = MkArgumentNode(parm) } - - override Type getResultType() { result = parm.getType() } - - override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "argument corresponding to " + parm } - - override Location getLocation() { result = parm.getDeclaration().getLocation() } - } - - /** - * An instruction initializing a result variable to its zero value. - */ - class InitResultInstruction extends WriteInstruction, MkResultInit { - ResultVariable res; - - InitResultInstruction() { this = MkResultInit(res) } - - override Instruction getRhs() { result = MkZeroInitNode(res) } - - override ControlFlow::Root getRoot() { result = res.getFunction() } - - override string toString() { result = "initialization of " + res } - - override Location getLocation() { result = res.getDeclaration().getLocation() } - } - - /** - * An instruction that gets the next key-value pair in a range loop. - */ - class GetNextEntryInstruction extends Instruction, MkNextNode { - RangeStmt rs; - - GetNextEntryInstruction() { this = MkNextNode(rs) } - - /** - * Gets the instruction computing the value whose key-value pairs this instruction reads. - */ - 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; - - EvalImplicitTrueInstruction() { this = MkImplicitTrue(stmt) } - - override Type getResultType() { result instanceof BoolType } - - override ControlFlow::Root getRoot() { result.isRootOf(stmt) } - - override boolean getBoolValue() { result = true } - - override string getExactValue() { result = "true" } - - 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 { - CaseClause cc; - int i; - - CaseInstruction() { this = MkCaseCheckNode(cc, 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`. - */ - class TypeSwitchImplicitVariableInstruction extends Instruction, MkTypeSwitchImplicitVariable { - CaseClause cc; - LocalVariable lv; - Expr switchExpr; - - TypeSwitchImplicitVariableInstruction() { - this = MkTypeSwitchImplicitVariable(cc, lv, switchExpr) - } - - override predicate writes(ValueEntity v, Instruction rhs) { - v = lv and - rhs = evalExprInstruction(switchExpr) - } - - 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 { - SliceExpr slice; - - EvalImplicitLowerSliceBoundInstruction() { this = MkImplicitLowerSliceBound(slice) } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(slice) } - - override int getIntValue() { result = 0 } - - override string getExactValue() { result = "0" } - - 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 { - SliceExpr slice; - - EvalImplicitUpperSliceBoundInstruction() { this = MkImplicitUpperSliceBound(slice) } - - 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 { - SliceExpr slice; - - EvalImplicitMaxSliceBoundInstruction() { this = MkImplicitMaxSliceBound(slice) } - - 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. - */ - class EvalImplicitDerefInstruction extends Instruction, MkImplicitDeref { - Expr e; - - EvalImplicitDerefInstruction() { this = MkImplicitDeref(e) } - - /** Gets the operand that is being dereferenced. */ - Expr getOperand() { result = e } - - override Type getResultType() { - result = e.getType().getUnderlyingType().(PointerType).getBaseType() - } - - 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. */ - class WriteTarget extends TWriteTarget { - ControlFlow::Node w; - - WriteTarget() { - this = MkLhs(w, _) or this = MkLiteralElementTarget(w) or this = MkResultWriteTarget(w) - } - - /** Gets the write instruction of which this is the target. */ - WriteInstruction getWrite() { result = w } - - /** Gets the name of the variable or field being written to, if any. */ - string getName() { none() } - - /** Gets the SSA variable being written to, if any. */ - SsaVariable asSsaVariable() { - this.getWrite() = result.getDefinition().(SsaExplicitDefinition).getInstruction() - } - - /** Holds if `e` is the variable or field being written to. */ - predicate refersTo(ValueEntity e) { none() } - - /** Gets a textual representation of this target. */ - string toString() { result = "write target" } - - /** Gets the source location for this element. */ - Location getLocation() { none() } - - /** - * 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 - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not exists(this.getLocation()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } - } - - /** A reference to a variable or constant, used as the target of a write. */ - class VarOrConstTarget extends WriteTarget { - Expr loc; - - VarOrConstTarget() { - this = MkLhs(_, loc) and - ( - loc instanceof Ident - or - loc instanceof SelectorExpr and - not loc.(SelectorExpr).getBase() instanceof ReferenceExpr - ) - or - exists(WriteResultInstruction wr | - this = MkResultWriteTarget(wr) and - evalExprInstruction(loc) = wr.getRhs() - ) - } - - override predicate refersTo(ValueEntity e) { - this instanceof MkLhs and - pragma[only_bind_out](loc) = e.getAReference() - or - exists(WriteResultInstruction wr | this = MkResultWriteTarget(wr) | - e = wr.getResultVariable() - ) - } - - override string getName() { - this = MkLhs(_, loc) and - ( - result = loc.(Ident).getName() - or - result = loc.(SelectorExpr).getSelector().getName() - ) - or - exists(WriteResultInstruction wr | this = MkResultWriteTarget(wr) | - result = wr.getResultVariable().getName() - ) - } - - /** Gets the variable this refers to, if any. */ - Variable getVariable() { this.refersTo(result) } - - /** 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. */ - class FieldTarget extends WriteTarget { - FieldTarget() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.getBase() instanceof ValueExpr) - or - w instanceof InitLiteralStructFieldInstruction - } - - /** Gets the instruction computing the base value on which this field is accessed. */ - Instruction getBase() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | result = selectorBase(sel)) - or - result = w.(InitLiteralStructFieldInstruction).getBase() - } - - /** Get 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) { - exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.uses(e)) - or - e = w.(InitLiteralStructFieldInstruction).getField() - } - - override string getName() { exists(Field f | this.refersTo(f) | result = f.getName()) } - - /** 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. - */ - class ElementTarget extends WriteTarget { - ElementTarget() { - this = MkLhs(_, any(IndexExpr idx)) - or - w instanceof InitLiteralElementInstruction - } - - /** Gets the instruction computing the base value of this element reference. */ - Instruction getBase() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = selectorBase(idx)) - or - result = w.(InitLiteralComponentInstruction).getBase() - } - - /** Gets the instruction computing the index of this element reference. */ - Instruction getIndex() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = evalExprInstruction(idx.getIndex())) - 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() - } - } - - /** - * A pointer dereference, used as the target of a write. - */ - class PointerTarget extends WriteTarget { - Expr lhs; - - PointerTarget() { - this = MkLhs(_, lhs) and - (lhs instanceof StarExpr or lhs instanceof DerefExpr) - } - - /** Gets the instruction computing the pointer value being dereferenced. */ - Instruction getBase() { - exists(Expr base | base = lhs.(StarExpr).getBase() or base = lhs.(DerefExpr).getOperand() | - 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 = evalExprInstruction(e.(ParenExpr).getExpr()) - } - - /** - * Gets the instruction corresponding to the initialization of `r`. - */ - InitParameterInstruction initRecvInstruction(ReceiverVariable r) { result = MkParameterInit(r) } - - /** - * Gets the instruction corresponding to the initialization of `p`. - */ - InitParameterInstruction initParamInstruction(Parameter p) { result = MkParameterInit(p) } - - /** - * Gets the instruction corresponding to the `i`th assignment happening at - * `assgn` (0-based). - */ - AssignInstruction assignInstruction(Assignment assgn, int i) { result = MkAssignNode(assgn, 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) } - - /** - * Gets the instruction corresponding to the assignment of the key variable - * of range statement `rs`. - */ - AssignInstruction assignKeyInstruction(RangeStmt rs) { result = MkAssignNode(rs, 0) } - - /** - * Gets the instruction corresponding to the assignment of the value variable - * of range statement `rs`. - */ - AssignInstruction assignValueInstruction(RangeStmt rs) { result = MkAssignNode(rs, 1) } - - /** - * Gets the instruction corresponding to the implicit initialization of `v` - * to its zero value. - */ - EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { result = MkZeroInitNode(v) } - - /** - * Gets the instruction corresponding to the extraction of the `idx`th element - * of the tuple produced by `base`. - */ - ExtractTupleElementInstruction extractTupleElement(Instruction base, int idx) { - result.extractsElement(base, idx) - } - - /** - * Gets the instruction corresponding to the implicit lower bound of slice `e`, if any. - */ - EvalImplicitLowerSliceBoundInstruction implicitLowerSliceBoundInstruction(SliceExpr e) { - result = MkImplicitLowerSliceBound(e) - } - - /** - * Gets the instruction corresponding to the implicit upper bound of slice `e`, if any. - */ - EvalImplicitUpperSliceBoundInstruction implicitUpperSliceBoundInstruction(SliceExpr e) { - result = MkImplicitUpperSliceBound(e) - } - - /** - * Gets the instruction corresponding to the implicit maximum bound of slice `e`, if any. - */ - EvalImplicitMaxSliceBoundInstruction implicitMaxSliceBoundInstruction(SliceExpr e) { - result = MkImplicitMaxSliceBound(e) - } - - /** - * 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) } - - /** Gets the base of `insn`, if `insn` is an implicit field read. */ - Instruction lookThroughImplicitFieldRead(Instruction insn) { - result = insn.(ImplicitFieldReadInstruction).getBaseInstruction() - } -} diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll.bak2 b/go/ql/lib/semmle/go/controlflow/IR.qll.bak2 deleted file mode 100644 index 807fbf76d1f..00000000000 --- a/go/ql/lib/semmle/go/controlflow/IR.qll.bak2 +++ /dev/null @@ -1,1580 +0,0 @@ -/** - * Provides classes and predicates for working with an intermediate representation (IR) of Go - * programs that is used as the foundation of the control flow and data flow graphs. - * - * In the IR, the program is represented as a set of instructions, which correspond to expressions - * and statements that compute a value or perform an operation (as opposed to providing syntactic - * 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 before/after nodes). - */ -overlay[local] -module; - -import go -private import ControlFlowGraphShared - -/** Provides predicates and classes for working with IR constructs. */ -module IR { - /** - * An IR instruction. - */ - class Instruction extends ControlFlow::Node { - Instruction() { - this.isIn(_) or - this.isAdditional(_, _) or - this.isAfterTrue(_) and not this.isIn(_) or - this.isAfterFalse(_) and not this.isIn(_) - } - - /** Holds if this instruction reads the value of variable or constant `v`. */ - predicate reads(ValueEntity v) { this.readsField(_, v) or this.readsMethod(_, v) } - - /** Holds if this instruction updates variable or constant `v` to the value of `rhs`. */ - predicate writes(ValueEntity v, Instruction rhs) { this.writesField(_, v, rhs) } - - /** Holds if this instruction reads the value of field `f` on the value of `base`. */ - predicate readsField(Instruction base, Field f) { none() } - - /** Holds if this instruction updates the value of field `f` on the value of `base`. */ - predicate writesField(Instruction base, Field f, Instruction rhs) { none() } - - /** Holds if this instruction looks up method `m` on the value of `receiver`. */ - predicate readsMethod(Instruction receiver, Method m) { none() } - - /** Holds if this instruction reads the value of element `index` on the value of `base`. */ - predicate readsElement(Instruction base, Instruction index) { none() } - - /** Holds if this instruction updates the value of element `index` on the value of `base`. */ - predicate writesElement(Instruction base, Instruction index) { none() } - - /** Gets the type of the result of this instruction, if any. */ - Type getResultType() { none() } - - /** Gets the float value of the result of this instruction, if it can be determined. */ - float getFloatValue() { none() } - - /** Gets the int value of the result of this instruction, if it can be determined. */ - int getIntValue() { none() } - - /** - * Holds if the complex value of the result of this instruction has real part `real` and - * imaginary part `imag`. - */ - predicate hasComplexValue(float real, float imag) { none() } - - /** Gets either `getFloatValue` or `getIntValue` */ - float getNumericValue() { result = this.getFloatValue() or result = this.getIntValue() } - - /** - * Gets the string representation of the exact value of the result of this instruction, - * if any. - * - * For example, for the constant 3.141592653589793238462, this will - * result in 1570796326794896619231/500000000000000000000 - */ - string getExactValue() { none() } - - /** Gets the string value of the result of this instruction, if it can be determined. */ - string getStringValue() { none() } - - /** Gets the Boolean value of the result of this instruction, if it can be determined. */ - boolean getBoolValue() { none() } - - /** Holds if the result of this instruction is known at compile time. */ - predicate isConst() { none() } - - /** - * Holds if the result of this instruction is known at compile time, and is guaranteed not to - * depend on the platform where it is evaluated. - */ - predicate isPlatformIndependentConstant() { none() } - - /** Gets a textual representation of the kind of this instruction. */ - string getInsnKind() { - this instanceof EvalInstruction and result = "expression" - or - this instanceof InitLiteralComponentInstruction and result = "element init" - or - this instanceof ImplicitLiteralElementIndexInstruction and result = "element index" - or - this instanceof AssignInstruction and result = "assignment" - or - this instanceof EvalCompoundAssignRhsInstruction and - result = "right-hand side of compound assignment" - or - this instanceof ExtractTupleElementInstruction and result = "tuple element extraction" - or - this instanceof EvalImplicitInitInstruction and result = "zero value" - or - this instanceof DeclareFunctionInstruction and result = "function declaration" - or - this instanceof DeferInstruction and result = "defer" - or - this instanceof GoInstruction and result = "go" - or - this instanceof ConditionGuardInstruction and result = "condition guard" - or - this instanceof IncDecInstruction and result = "increment/decrement" - or - this instanceof EvalIncDecRhsInstruction and - result = "right-hand side of increment/decrement" - or - this instanceof EvalImplicitOneInstruction and result = "implicit 1" - or - this instanceof ReturnInstruction and result = "return" - or - this instanceof WriteResultInstruction and result = "result write" - or - this instanceof ReadResultInstruction and result = "result read" - or - this instanceof SendInstruction and result = "send" - or - this instanceof InitParameterInstruction and result = "parameter initialization" - or - this instanceof ReadArgumentInstruction and result = "argument" - or - this instanceof InitResultInstruction and result = "result initialization" - or - this instanceof GetNextEntryInstruction and result = "next key-value pair" - or - this instanceof EvalImplicitTrueInstruction and result = "implicit true" - or - this instanceof CaseInstruction and result = "case" - or - this instanceof TypeSwitchImplicitVariableInstruction and - result = "type switch implicit variable declaration" - or - this instanceof EvalImplicitLowerSliceBoundInstruction and result = "implicit lower bound" - or - this instanceof EvalImplicitUpperSliceBoundInstruction and result = "implicit upper bound" - or - this instanceof EvalImplicitMaxSliceBoundInstruction and result = "implicit maximum" - or - 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(_) and not this.isIn(_) - or - this.isAfterFalse(_) and not this.isIn(_) - } - } - - /** - * An IR instruction representing the evaluation of an expression. - */ - class EvalInstruction extends Instruction { - Expr e; - - EvalInstruction() { this.isIn(e) and e instanceof Expr } - - /** Gets the expression underlying this instruction. */ - Expr getExpr() { result = e } - - override predicate reads(ValueEntity v) { e = v.getAReference() } - - override Type getResultType() { result = e.getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override float getFloatValue() { result = e.getFloatValue() } - - override int getIntValue() { result = e.getIntValue() } - - override predicate hasComplexValue(float real, float imag) { e.hasComplexValue(real, imag) } - - override string getExactValue() { result = e.getExactValue() } - - override string getStringValue() { result = e.getStringValue() } - - override boolean getBoolValue() { result = e.getBoolValue() } - - override predicate isConst() { e.isConst() } - - override predicate isPlatformIndependentConstant() { e.isPlatformIndependentConstant() } - - override string toString() { result = e.toString() } - - override Location getLocation() { result = e.getLocation() } - } - - /** - * An IR instruction that reads the value of a variable, constant, field or array element, - * or refers to a function. - */ - class ReadInstruction extends Instruction { - ReadInstruction() { - exists(Expr e | e = this.(EvalInstruction).getExpr() | - (e instanceof ValueName or e instanceof IndexExpr) and - e.(ReferenceExpr).isRvalue() - ) - or - this instanceof ReadResultInstruction - or - 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 | - result = fri - ) - or - not exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1) and - exists(Expr base | - base = e.(SelectorExpr).getBase() - or - base = e.(IndexExpr).getBase() - or - base = e.(SliceExpr).getBase() - | - result = MkImplicitDeref(base) - or - not exists(MkImplicitDeref(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() { - exists(Expr e | e = this.(EvalInstruction).getExpr() | - e instanceof IndexExpr - or - e.(SelectorExpr).getBase() instanceof ValueExpr and - not e.(SelectorExpr).getSelector() = any(Method method).getAReference() - ) - or - this instanceof MkImplicitFieldSelection - } - - /** Gets the instruction computing the base value on which the field or element is read. */ - Instruction getBase() { - result = this.(ImplicitFieldReadInstruction).getBaseInstruction() - or - result = selectorBase(this.(EvalInstruction).getExpr()) - } - } - - /** - * 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; - int index; - Field field; - - FieldReadInstruction() { - e = this.(EvalInstruction).getExpr() and - index = 0 and - field.getAReference() = e.getSelector() - or - this = MkImplicitFieldSelection(e, index, field) - } - - /** Gets the `SelectorExpr` of this field read. */ - SelectorExpr getSelectorExpr() { result = e } - - /** Gets the index of this field read. */ - int getIndex() { result = index } - - /** Gets the field being read. */ - Field getField() { result = field } - - Instruction getBaseInstruction() { - exists(ImplicitFieldReadInstruction fri | - fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) - | - result = fri - ) - or - not exists(ImplicitFieldReadInstruction fri | - fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) - ) and - ( - result = MkImplicitDeref(e.getBase()) - or - not exists(MkImplicitDeref(e.getBase())) and - result = evalExprInstruction(e.getBase()) - ) - } - - override predicate readsField(Instruction base, Field f) { - base = this.getBaseInstruction() and f = field - } - } - - /** - * 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. - */ - class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection { - ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) } - - override predicate reads(ValueEntity v) { v = field } - - override Type getResultType() { result = lookThroughPointerType(field.getType()) } - - override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit read of field " + field.toString() } - - override Location getLocation() { result = e.getBase().getLocation() } - } - - /** - * An IR instruction that looks up a method. - */ - class MethodReadInstruction extends ReadInstruction, EvalInstruction { - Method method; - override SelectorExpr e; - - MethodReadInstruction() { e.getSelector() = method.getAReference() } - - /** Gets the instruction computing the receiver value on which the method is looked up. */ - Instruction getReceiver() { result = selectorBase(e) } - - /** Gets the method being looked up. */ - Method getMethod() { result = method } - - override predicate readsMethod(Instruction receiver, Method m) { - receiver = this.getReceiver() and m = this.getMethod() - } - } - - /** - * An IR instruction that reads an element of an array, slice, map or string. - */ - class ElementReadInstruction extends ComponentReadInstruction, EvalInstruction { - override IndexExpr e; - - /** Gets the instruction computing the index of the element being looked up. */ - Instruction getIndex() { result = evalExprInstruction(e.getIndex()) } - - override predicate readsElement(Instruction base, Instruction index) { - base = this.getBase() and index = this.getIndex() - } - } - - /** - * An IR instruction that constructs a slice. - */ - class SliceInstruction extends EvalInstruction { - override SliceExpr e; - - /** Gets the instruction computing the base value from which the slice is constructed. */ - Instruction getBase() { result = selectorBase(e) } - - /** Gets the instruction computing the lower bound of the slice. */ - Instruction getLow() { - result = evalExprInstruction(e.getLow()) or - result = implicitLowerSliceBoundInstruction(e) - } - - /** Gets the instruction computing the upper bound of the slice. */ - Instruction getHigh() { - result = evalExprInstruction(e.getHigh()) or - result = implicitUpperSliceBoundInstruction(e) - } - - /** Gets the instruction computing the capacity of the slice. */ - Instruction getMax() { - result = evalExprInstruction(e.getMax()) or - result = implicitMaxSliceBoundInstruction(e) - } - } - - /** - * An IR instruction that writes a memory location. - */ - class WriteInstruction extends Instruction { - WriteTarget lhs; - Boolean initialization; - - WriteInstruction() { - ( - lhs = MkLhs(this, _) - or - lhs = MkResultWriteTarget(this) - ) and - initialization = false - or - lhs = MkLiteralElementTarget(this) and initialization = true - } - - /** Gets the target to which this instruction writes. */ - WriteTarget getLhs() { result = lhs } - - /** Holds if this instruction initializes a literal. */ - predicate isInitialization() { initialization = true } - - /** Gets the instruction computing the value this instruction writes. */ - Instruction getRhs() { none() } - - override predicate writes(ValueEntity v, Instruction rhs) { - this.getLhs().refersTo(v) and - rhs = this.getRhs() - } - } - - /** - * An IR instruction that initializes a component of a composite literal. - */ - class InitLiteralComponentInstruction extends WriteInstruction, MkLiteralElementInitNode { - CompositeLit lit; - int i; - Expr elt; - - InitLiteralComponentInstruction() { - this = MkLiteralElementInitNode(elt) and elt = lit.getElement(i) - } - - /** Gets the instruction allocating the composite literal. */ - Instruction getBase() { result = evalExprInstruction(lit) } - - override Instruction getRhs() { - result = evalExprInstruction(elt) or - result = evalExprInstruction(elt.(KeyValueExpr).getValue()) - } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override string toString() { result = "init of " + elt } - - override Location getLocation() { result = elt.getLocation() } - } - - /** - * An IR instruction that initializes a field of a struct literal. - */ - class InitLiteralStructFieldInstruction extends InitLiteralComponentInstruction { - override StructLit lit; - - /** Gets the name of the initialized field. */ - pragma[nomagic] - string getFieldName() { - if elt instanceof KeyValueExpr - then result = elt.(KeyValueExpr).getKey().(Ident).getName() - else pragma[only_bind_out](lit.getStructType()).hasOwnField(i, result, _, _) - } - - /** Gets the initialized field. */ - Field getField() { - result.getDeclaringType() = lit.getStructType() and - result.getName() = this.getFieldName() - } - } - - /** - * An IR instruction that initializes an element of an array, slice or map literal. - */ - class InitLiteralElementInstruction extends InitLiteralComponentInstruction { - Type literalType; - - InitLiteralElementInstruction() { - literalType = lit.getType().getUnderlyingType() and - ( - literalType instanceof ArrayType or - literalType instanceof SliceType or - literalType instanceof MapType - ) - } - - /** Gets the instruction computing the index of the initialized element. */ - Instruction getIndex() { - result = evalExprInstruction(elt.(KeyValueExpr).getKey()) - or - result = MkImplicitLiteralElementIndex(elt) - } - } - - /** - * 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. - */ - class InitLiteralSliceElementInstruction extends InitLiteralElementInstruction { - override SliceType literalType; - } - - /** - * 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. - */ - class FieldWriteInstruction extends WriteInstruction { - override FieldTarget lhs; - - /** Gets the instruction computing the base value on which the field is written. */ - Instruction getBase() { result = lhs.getBase() } - - /** Gets the field being written. */ - 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 - } - } - - /** - * 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. */ - 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 - } - } - - /** 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() - or - not elt instanceof KeyValueExpr and - ( - i = 0 and result = 0 - or - result = getElementIndex(lit, i - 1) + 1 - ) - ) - } - - /** - * An IR instruction computing the implicit index of an element in an array or slice literal. - */ - class ImplicitLiteralElementIndexInstruction extends Instruction, MkImplicitLiteralElementIndex { - Expr elt; - - ImplicitLiteralElementIndexInstruction() { this = MkImplicitLiteralElementIndex(elt) } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override int getIntValue() { - exists(CompositeLit lit, int i | elt = lit.getElement(i) | result = getElementIndex(lit, i)) - } - - override string getStringValue() { none() } - - override string getExactValue() { result = this.getIntValue().toString() } - - 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 { - AstNode assgn; - int i; - - AssignInstruction() { this = MkAssignNode(assgn, i) } - - override Instruction getRhs() { - exists(SimpleAssignStmt a | a = assgn | - a.getNumLhs() = a.getNumRhs() and - result = evalExprInstruction(a.getRhs(i)) - ) - or - exists(ValueSpec spec | spec = assgn | - spec.getNumName() = spec.getNumInit() and - result = evalExprInstruction(spec.getInit(i)) - or - result = MkZeroInitNode(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) - ) - or - result = MkCompoundAssignRhsNode(assgn) - or - result = MkExtractNode(assgn, i) - } - - 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 { - CompoundAssignStmt assgn; - - EvalCompoundAssignRhsInstruction() { this = MkCompoundAssignRhsNode(assgn) } - - /** Gets the underlying assignment of this instruction. */ - 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 { - AstNode s; - int i; - - ExtractTupleElementInstruction() { this = MkExtractNode(s, i) } - - /** Gets the instruction computing the tuple value from which one value is extracted. */ - Instruction getBase() { - exists(Expr baseExpr | - baseExpr = s.(Assignment).getRhs() or - baseExpr = s.(ValueSpec).getInit() - | - result = evalExprInstruction(baseExpr) - ) - or - result = MkNextNode(s) - 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`. */ - predicate extractsElement(Instruction base, int idx) { base = this.getBase() and idx = i } - - override Type getResultType() { - exists(Expr e | this.getBase() = evalExprInstruction(e) | - result = e.getType().(TupleType).getComponentType(pragma[only_bind_into](i)) - ) - or - exists(Type rangeType | rangeType = s.(RangeStmt).getDomain().getType().getUnderlyingType() | - exists(Type baseType | - baseType = rangeType.(ArrayType).getElementType() or - baseType = - rangeType.(PointerType).getBaseType().getUnderlyingType().(ArrayType).getElementType() or - baseType = rangeType.(SliceType).getElementType() - | - i = 0 and - result instanceof IntType - or - i = 1 and - result = baseType - ) - or - rangeType instanceof StringType and - ( - i = 0 and - result instanceof IntType - or - result = Builtin::rune().getType() - ) - or - exists(MapType map | map = rangeType | - i = 0 and - result = map.getKeyType() - or - i = 1 and - result = map.getValueType() - ) - or - i = 0 and - result = rangeType.(RecvChanType).getElementType() - or - 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. - */ - class EvalImplicitInitInstruction extends Instruction, MkZeroInitNode { - ValueEntity v; - - EvalImplicitInitInstruction() { this = MkZeroInitNode(v) } - - override Type getResultType() { result = v.getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(v.getDeclaration()) } - - override int getIntValue() { - v.getType().getUnderlyingType() instanceof IntegerType and result = 0 - } - - override float getFloatValue() { - v.getType().getUnderlyingType() instanceof FloatType and result = 0.0 - } - - override string getStringValue() { - v.getType().getUnderlyingType() instanceof StringType and result = "" - } - - override boolean getBoolValue() { - v.getType().getUnderlyingType() instanceof BoolType and result = false - } - - override string getExactValue() { - result = this.getIntValue().toString() or - result = this.getFloatValue().toString() or - result = this.getStringValue().toString() or - result = this.getBoolValue().toString() - } - - 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 { - FuncDecl fd; - - DeclareFunctionInstruction() { this = MkFuncDeclNode(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 { - DeferStmt defer; - - DeferInstruction() { this = MkDeferNode(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 { - GoStmt go; - - GoInstruction() { this = MkGoNode(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 { - IncDecStmt ids; - - IncDecInstruction() { this = MkIncDecNode(ids) } - - override Instruction getRhs() { result = MkIncDecRhs(ids) } - - 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 { - IncDecStmt ids; - - EvalIncDecRhsInstruction() { this = MkIncDecRhs(ids) } - - /** Gets the corresponding increment or decrement statement. */ - IncDecStmt getStmt() { result = ids } - - 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 { - IncDecStmt ids; - - EvalImplicitOneInstruction() { this = MkImplicitOne(ids) } - - /** Gets the corresponding increment or decrement statement. */ - IncDecStmt getStmt() { result = ids } - - override Type getResultType() { result = ids.getOperand().getType() } - - override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override int getIntValue() { result = 1 } - - override string getExactValue() { result = "1" } - - 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 { - ReturnStmt ret; - - ReturnInstruction() { this = MkReturnNode(ret) } - - /** Gets the corresponding `ReturnStmt`. */ - ReturnStmt getReturnStmt() { result = ret } - - /** Holds if this statement returns multiple results. */ - predicate returnsMultipleResults() { exists(MkExtractNode(ret, _)) or ret.getNumExpr() > 1 } - - /** Gets the instruction whose result is the (unique) result returned by this statement. */ - Instruction getResult() { - not this.returnsMultipleResults() and - result = evalExprInstruction(ret.getExpr()) - } - - /** Gets the instruction whose result is the `i`th result returned by this statement. */ - Instruction getResult(int i) { - result = MkExtractNode(ret, i) - or - not exists(MkExtractNode(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 { - ResultVariable var; - int i; - ReturnInstruction ret; - - WriteResultInstruction() { - exists(ReturnStmt retstmt | - this = MkResultWriteNode(var, i, retstmt) and - ret = MkReturnNode(retstmt) - ) - } - - override Instruction getRhs() { result = ret.getResult(i) } - - /** Gets the result variable being assigned. */ - ResultVariable getResultVariable() { result = var } - - 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 { - ResultVariable var; - - ReadResultInstruction() { this = MkResultReadNode(var) } - - 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 { - SendStmt send; - - SendInstruction() { this = MkSendNode(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 { - Parameter parm; - - InitParameterInstruction() { this = MkParameterInit(parm) } - - override Instruction getRhs() { result = MkArgumentNode(parm) } - - 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 { - Parameter parm; - - ReadArgumentInstruction() { this = MkArgumentNode(parm) } - - override Type getResultType() { result = parm.getType() } - - override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "argument corresponding to " + parm } - - override Location getLocation() { result = parm.getDeclaration().getLocation() } - } - - /** - * An instruction initializing a result variable to its zero value. - */ - class InitResultInstruction extends WriteInstruction, MkResultInit { - ResultVariable res; - - InitResultInstruction() { this = MkResultInit(res) } - - override Instruction getRhs() { result = MkZeroInitNode(res) } - - override ControlFlow::Root getRoot() { result = res.getFunction() } - - override string toString() { result = "initialization of " + res } - - override Location getLocation() { result = res.getDeclaration().getLocation() } - } - - /** - * An instruction that gets the next key-value pair in a range loop. - */ - class GetNextEntryInstruction extends Instruction, MkNextNode { - RangeStmt rs; - - GetNextEntryInstruction() { this = MkNextNode(rs) } - - /** - * Gets the instruction computing the value whose key-value pairs this instruction reads. - */ - 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; - - EvalImplicitTrueInstruction() { this = MkImplicitTrue(stmt) } - - override Type getResultType() { result instanceof BoolType } - - override ControlFlow::Root getRoot() { result.isRootOf(stmt) } - - override boolean getBoolValue() { result = true } - - override string getExactValue() { result = "true" } - - 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 { - CaseClause cc; - int i; - - CaseInstruction() { this = MkCaseCheckNode(cc, 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`. - */ - class TypeSwitchImplicitVariableInstruction extends Instruction, MkTypeSwitchImplicitVariable { - CaseClause cc; - LocalVariable lv; - Expr switchExpr; - - TypeSwitchImplicitVariableInstruction() { - this = MkTypeSwitchImplicitVariable(cc, lv, switchExpr) - } - - override predicate writes(ValueEntity v, Instruction rhs) { - v = lv and - rhs = evalExprInstruction(switchExpr) - } - - 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 { - SliceExpr slice; - - EvalImplicitLowerSliceBoundInstruction() { this = MkImplicitLowerSliceBound(slice) } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(slice) } - - override int getIntValue() { result = 0 } - - override string getExactValue() { result = "0" } - - 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 { - SliceExpr slice; - - EvalImplicitUpperSliceBoundInstruction() { this = MkImplicitUpperSliceBound(slice) } - - 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 { - SliceExpr slice; - - EvalImplicitMaxSliceBoundInstruction() { this = MkImplicitMaxSliceBound(slice) } - - 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. - */ - class EvalImplicitDerefInstruction extends Instruction, MkImplicitDeref { - Expr e; - - EvalImplicitDerefInstruction() { this = MkImplicitDeref(e) } - - /** Gets the operand that is being dereferenced. */ - Expr getOperand() { result = e } - - override Type getResultType() { - result = e.getType().getUnderlyingType().(PointerType).getBaseType() - } - - 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. */ - class WriteTarget extends TWriteTarget { - ControlFlow::Node w; - - WriteTarget() { - this = MkLhs(w, _) or this = MkLiteralElementTarget(w) or this = MkResultWriteTarget(w) - } - - /** Gets the write instruction of which this is the target. */ - WriteInstruction getWrite() { result = w } - - /** Gets the name of the variable or field being written to, if any. */ - string getName() { none() } - - /** Gets the SSA variable being written to, if any. */ - SsaVariable asSsaVariable() { - this.getWrite() = result.getDefinition().(SsaExplicitDefinition).getInstruction() - } - - /** Holds if `e` is the variable or field being written to. */ - predicate refersTo(ValueEntity e) { none() } - - /** Gets a textual representation of this target. */ - string toString() { result = "write target" } - - /** Gets the source location for this element. */ - Location getLocation() { none() } - - /** - * 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 - ) { - this.getLocation().hasLocationInfo(filepath, startline, startcolumn, endline, endcolumn) - or - not exists(this.getLocation()) and - filepath = "" and - startline = 0 and - startcolumn = 0 and - endline = 0 and - endcolumn = 0 - } - } - - /** A reference to a variable or constant, used as the target of a write. */ - class VarOrConstTarget extends WriteTarget { - Expr loc; - - VarOrConstTarget() { - this = MkLhs(_, loc) and - ( - loc instanceof Ident - or - loc instanceof SelectorExpr and - not loc.(SelectorExpr).getBase() instanceof ReferenceExpr - ) - or - exists(WriteResultInstruction wr | - this = MkResultWriteTarget(wr) and - evalExprInstruction(loc) = wr.getRhs() - ) - } - - override predicate refersTo(ValueEntity e) { - this instanceof MkLhs and - pragma[only_bind_out](loc) = e.getAReference() - or - exists(WriteResultInstruction wr | this = MkResultWriteTarget(wr) | - e = wr.getResultVariable() - ) - } - - override string getName() { - this = MkLhs(_, loc) and - ( - result = loc.(Ident).getName() - or - result = loc.(SelectorExpr).getSelector().getName() - ) - or - exists(WriteResultInstruction wr | this = MkResultWriteTarget(wr) | - result = wr.getResultVariable().getName() - ) - } - - /** Gets the variable this refers to, if any. */ - Variable getVariable() { this.refersTo(result) } - - /** 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. */ - class FieldTarget extends WriteTarget { - FieldTarget() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.getBase() instanceof ValueExpr) - or - w instanceof InitLiteralStructFieldInstruction - } - - /** Gets the instruction computing the base value on which this field is accessed. */ - Instruction getBase() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | result = selectorBase(sel)) - or - result = w.(InitLiteralStructFieldInstruction).getBase() - } - - /** Get 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) { - exists(SelectorExpr sel | this = MkLhs(_, sel) | sel.uses(e)) - or - e = w.(InitLiteralStructFieldInstruction).getField() - } - - override string getName() { exists(Field f | this.refersTo(f) | result = f.getName()) } - - /** 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. - */ - class ElementTarget extends WriteTarget { - ElementTarget() { - this = MkLhs(_, any(IndexExpr idx)) - or - w instanceof InitLiteralElementInstruction - } - - /** Gets the instruction computing the base value of this element reference. */ - Instruction getBase() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = selectorBase(idx)) - or - result = w.(InitLiteralComponentInstruction).getBase() - } - - /** Gets the instruction computing the index of this element reference. */ - Instruction getIndex() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = evalExprInstruction(idx.getIndex())) - 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() - } - } - - /** - * A pointer dereference, used as the target of a write. - */ - class PointerTarget extends WriteTarget { - Expr lhs; - - PointerTarget() { - this = MkLhs(_, lhs) and - (lhs instanceof StarExpr or lhs instanceof DerefExpr) - } - - /** Gets the instruction computing the pointer value being dereferenced. */ - Instruction getBase() { - exists(Expr base | base = lhs.(StarExpr).getBase() or base = lhs.(DerefExpr).getOperand() | - 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 = evalExprInstruction(e.(ParenExpr).getExpr()) - } - - /** - * Gets the instruction corresponding to the initialization of `r`. - */ - InitParameterInstruction initRecvInstruction(ReceiverVariable r) { result = MkParameterInit(r) } - - /** - * Gets the instruction corresponding to the initialization of `p`. - */ - InitParameterInstruction initParamInstruction(Parameter p) { result = MkParameterInit(p) } - - /** - * Gets the instruction corresponding to the `i`th assignment happening at - * `assgn` (0-based). - */ - AssignInstruction assignInstruction(Assignment assgn, int i) { result = MkAssignNode(assgn, 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) } - - /** - * Gets the instruction corresponding to the assignment of the key variable - * of range statement `rs`. - */ - AssignInstruction assignKeyInstruction(RangeStmt rs) { result = MkAssignNode(rs, 0) } - - /** - * Gets the instruction corresponding to the assignment of the value variable - * of range statement `rs`. - */ - AssignInstruction assignValueInstruction(RangeStmt rs) { result = MkAssignNode(rs, 1) } - - /** - * Gets the instruction corresponding to the implicit initialization of `v` - * to its zero value. - */ - EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { result = MkZeroInitNode(v) } - - /** - * Gets the instruction corresponding to the extraction of the `idx`th element - * of the tuple produced by `base`. - */ - ExtractTupleElementInstruction extractTupleElement(Instruction base, int idx) { - result.extractsElement(base, idx) - } - - /** - * Gets the instruction corresponding to the implicit lower bound of slice `e`, if any. - */ - EvalImplicitLowerSliceBoundInstruction implicitLowerSliceBoundInstruction(SliceExpr e) { - result = MkImplicitLowerSliceBound(e) - } - - /** - * Gets the instruction corresponding to the implicit upper bound of slice `e`, if any. - */ - EvalImplicitUpperSliceBoundInstruction implicitUpperSliceBoundInstruction(SliceExpr e) { - result = MkImplicitUpperSliceBound(e) - } - - /** - * Gets the instruction corresponding to the implicit maximum bound of slice `e`, if any. - */ - EvalImplicitMaxSliceBoundInstruction implicitMaxSliceBoundInstruction(SliceExpr e) { - result = MkImplicitMaxSliceBound(e) - } - - /** - * 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) } - - /** Gets the base of `insn`, if `insn` is an implicit field read. */ - Instruction lookThroughImplicitFieldRead(Instruction insn) { - result = insn.(ImplicitFieldReadInstruction).getBaseInstruction() - } -}