diff --git a/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll b/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll index dc52abb25ab..a0ba4e00417 100644 --- a/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll +++ b/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll @@ -5,66 +5,27 @@ overlay[local] module; import go -private import ControlFlowGraphImpl -private import codeql.controlflow.BasicBlock as BB -private import codeql.controlflow.SuccessorType +private import ControlFlowGraphShared -private module Input implements BB::InputSig { - /** A delineated part of the AST with its own CFG. */ - class CfgScope = ControlFlow::Root; +/** A basic block in the control-flow graph. */ +class BasicBlock = GoCfg::Cfg::BasicBlock; - /** The class of control flow nodes. */ - class Node = ControlFlowNode; - - /** Gets the CFG scope in which this node occurs. */ - CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result } - - /** Gets an immediate successor of this node. */ - Node nodeGetASuccessor(Node node, SuccessorType t) { - result = node.getASuccessor() and - ( - not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor - or - t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome() - ) - } - - /** - * Holds if `node` represents an entry node to be used when calculating - * dominance. - */ - predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode } - - /** - * Holds if `node` represents an exit node to be used when calculating - * post dominance. - */ - predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode } -} - -private module BbImpl = BB::Make; - -class BasicBlock = BbImpl::BasicBlock; - -class EntryBasicBlock = BbImpl::EntryBasicBlock; - -cached -private predicate reachableBB(BasicBlock bb) { - bb instanceof EntryBasicBlock - or - exists(BasicBlock predBB | predBB.getASuccessor(_) = bb | reachableBB(predBB)) -} +/** An entry basic block. */ +class EntryBasicBlock = GoCfg::Cfg::EntryBasicBlock; /** * A basic block that is reachable from an entry basic block. + * + * Since the shared CFG library only creates nodes for reachable code, + * all basic blocks are reachable by construction. */ class ReachableBasicBlock extends BasicBlock { - ReachableBasicBlock() { reachableBB(this) } + ReachableBasicBlock() { any() } } /** * A reachable basic block with more than one predecessor. */ class ReachableJoinBlock extends ReachableBasicBlock { - ReachableJoinBlock() { this.getFirstNode().isJoin() } + ReachableJoinBlock() { this.getFirstNode().(ControlFlow::Node).isJoin() } } diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll index ebd8605c117..49d11e3c561 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll @@ -5,47 +5,35 @@ overlay[local] module; import go -private import ControlFlowGraphImpl private import ControlFlowGraphShared -/** Provides helper predicates for mapping btween CFG nodes and the AST. */ +/** Provides helper predicates for mapping between CFG nodes and the AST. */ module ControlFlow { - /** A file or function with which a CFG is associated. */ + /** A function with which a CFG is associated. */ class Root extends AstNode { - Root() { exists(this.(File).getADecl()) or exists(this.(FuncDef).getBody()) } + Root() { exists(this.(FuncDef).getBody()) } - /** Holds if `nd` belongs to this file or function. */ - predicate isRootOf(AstNode nd) { - this = nd.getEnclosingFunction() - or - not exists(nd.getEnclosingFunction()) and - this = nd.getFile() - } + /** Holds if `nd` belongs to this function. */ + predicate isRootOf(AstNode nd) { this = nd.getEnclosingFunction() } - /** Gets the synthetic entry node of the CFG for this file or function. */ + /** Gets the synthetic entry node of the CFG for this function. */ EntryNode getEntryNode() { result = ControlFlow::entryNode(this) } - /** Gets the synthetic exit node of the CFG for this file or function. */ + /** Gets the synthetic exit node of the CFG for this function. */ ExitNode getExitNode() { result = ControlFlow::exitNode(this) } } /** - * A node in the intra-procedural control-flow graph of a Go function or file. + * A node in the intra-procedural control-flow graph of a Go function. * * Nodes correspond to expressions and statements that compute a value or perform * an operation (as opposed to providing syntactic structure or type information). * - * There are also synthetic entry and exit nodes for each Go function and file + * There are also synthetic entry and exit nodes for each Go function * that mark the beginning and the end, respectively, of the execution of the - * function and the loading of the file. + * function. */ - class Node extends TControlFlowNode { - /** Gets a node that directly follows this one in the control-flow graph. */ - Node getASuccessor() { result = CFG::succ(this) } - - /** Gets a node that directly precedes this one in the control-flow graph. */ - Node getAPredecessor() { this = result.getASuccessor() } - + class Node extends GoCfg::ControlFlowNode { /** Holds if this is a node with more than one successor. */ predicate isBranch() { strictcount(this.getASuccessor()) > 1 } @@ -53,22 +41,23 @@ module ControlFlow { predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } /** Holds if this is the first control-flow node in `subtree`. */ - predicate isFirstNodeOf(AstNode subtree) { CFG::firstNode(subtree, this) } + predicate isFirstNodeOf(AstNode subtree) { + this.isBefore(subtree) + or + this.injects(subtree) + } - /** Holds if this node is the (unique) entry node of a function or file. */ - predicate isEntryNode() { this instanceof MkEntryNode } + /** Holds if this node is the (unique) entry node of a function. */ + predicate isEntryNode() { this instanceof GoCfg::ControlFlow::EntryNode } - /** Holds if this node is the (unique) exit node of a function or file. */ - predicate isExitNode() { this instanceof MkExitNode } - - /** Gets the basic block to which this node belongs. */ - BasicBlock getBasicBlock() { result.getANode() = this } + /** Holds if this node is the (unique) exit node of a function. */ + predicate isExitNode() { this instanceof GoCfg::ControlFlow::ExitNode } /** Holds if this node dominates `dominee` in the control-flow graph. */ overlay[caller?] pragma[inline] predicate dominatesNode(ControlFlow::Node dominee) { - exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j | + exists(GoCfg::Cfg::BasicBlock thisbb, GoCfg::Cfg::BasicBlock dbb, int i, int j | this = thisbb.getNode(i) and dominee = dbb.getNode(j) | thisbb.strictlyDominates(dbb) @@ -77,20 +66,12 @@ module ControlFlow { ) } - /** Gets the innermost function or file to which this node belongs. */ - Root getRoot() { none() } + /** Gets the innermost function to which this node belongs. */ + Root getRoot() { result = this.getEnclosingCallable() } /** Gets the file to which this node belongs. */ File getFile() { result = this.getLocation().getFile() } - /** - * Gets a textual representation of this control flow node. - */ - string toString() { result = "control-flow node" } - - /** Gets the source location for this element. */ - Location getLocation() { none() } - /** * DEPRECATED: Use `getLocation()` instead. * @@ -114,6 +95,12 @@ module ControlFlow { } } + /** A synthetic entry node for a function. */ + class EntryNode extends Node instanceof GoCfg::ControlFlow::EntryNode { } + + /** A synthetic exit node for a function. */ + class ExitNode extends Node instanceof GoCfg::ControlFlow::ExitNode { } + /** * A control-flow node that initializes or updates the value of a constant, a variable, * a field, or an (array, slice, or map) element. @@ -173,7 +160,7 @@ module ControlFlow { exists(IR::FieldTarget trg | trg = super.getLhs() | ( trg.getBase() = base or - trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr()) + trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr()) ) and trg.getField() = f and super.getRhs() = rhs @@ -221,7 +208,7 @@ module ControlFlow { exists(IR::ElementTarget trg | trg = super.getLhs() | ( trg.getBase() = base or - trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr()) + trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr()) ) and trg.getIndex() = index and super.getRhs() = rhs @@ -251,11 +238,15 @@ module ControlFlow { * A control-flow node recording the fact that a certain expression has a known * Boolean value at this point in the program. */ - class ConditionGuardNode extends IR::Instruction, MkConditionGuardNode { + class ConditionGuardNode extends Node { Expr cond; boolean outcome; - ConditionGuardNode() { this = MkConditionGuardNode(cond, outcome) } + ConditionGuardNode() { + this.isAfterTrue(cond) and outcome = true + or + this.isAfterFalse(cond) and outcome = false + } private predicate ensuresAux(Expr expr, boolean b) { expr = cond and b = outcome @@ -321,21 +312,17 @@ module ControlFlow { boolean getOutcome() { result = outcome } override Root getRoot() { result.isRootOf(cond) } - - override string toString() { result = cond + " is " + outcome } - - override Location getLocation() { result = cond.getLocation() } } /** - * Gets the entry node of function or file `root`. + * Gets the entry node of function `root`. */ - Node entryNode(Root root) { result = MkEntryNode(root) } + EntryNode entryNode(Root root) { result.getEnclosingCallable() = root } /** - * Gets the exit node of function or file `root`. + * Gets the exit node of function `root`. */ - Node exitNode(Root root) { result = MkExitNode(root) } + ExitNode exitNode(Root root) { result.getEnclosingCallable() = root } /** * Holds if the function `f` may return without panicking, exiting the process, or looping forever. @@ -343,7 +330,9 @@ module ControlFlow { * This is defined conservatively, and so may also hold of a function that in fact * cannot return normally, but never fails to hold of a function that can return normally. */ - predicate mayReturnNormally(FuncDecl f) { CFG::mayReturnNormally(f.getBody()) } + predicate mayReturnNormally(FuncDecl f) { + exists(GoCfg::ControlFlow::NormalExitNode exit | exit.getEnclosingCallable() = f) + } /** * Holds if `pred` is the node for the case `testExpr` in an expression @@ -353,20 +342,16 @@ module ControlFlow { predicate isSwitchCaseTestPassingEdge( ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr ) { - CFG::isSwitchCaseTestPassingEdge(pred, succ, switchExpr, testExpr) + exists(ExpressionSwitchStmt ess, CaseClause cc, int i | + ess.getExpr() = switchExpr and + cc = ess.getACase() and + testExpr = cc.getExpr(i) and + pred.isAfter(testExpr) and + succ.isFirstNodeOf(cc.getStmt(0)) + ) } } class ControlFlowNode = ControlFlow::Node; class Write = ControlFlow::WriteNode; - -/** - * Provides the shared CFG library types for Go. - * - * These types are generated by the shared `codeql.controlflow.ControlFlowGraph` - * library and coexist with the existing Go CFG types during the transition. - * Use `SharedCfg::ControlFlowNode` to access the shared library's node type, - * `SharedCfg::ControlFlow::EntryNode` for entry nodes, etc. - */ -module SharedCfg = GoCfg; diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll index c9795f9d93e..147468122fd 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -25,6 +25,11 @@ module GoCfg { private import Cfg2 import Public + /** Holds if `e` has an implicit field selection at `index` for `implicitField`. */ + predicate implicitFieldSelection(Go::AstNode e, int index, Go::Field implicitField) { + Input::implicitFieldSelection(e, index, implicitField) + } + /** Provides an implementation of the AST signature for Go. */ private module Ast implements CfgLib::AstSig { class AstNode = Go::AstNode; @@ -63,7 +68,11 @@ module GoCfg { AstNode callableGetBody(Callable c) { result = c.(Go::FuncDef).getBody() } - Callable getEnclosingCallable(AstNode node) { result = node.getEnclosingFunction() } + Callable getEnclosingCallable(AstNode node) { + result = node and node instanceof Callable + or + not node instanceof Callable and result = node.getEnclosingFunction() + } class Stmt = Go::Stmt; @@ -515,7 +524,7 @@ module GoCfg { private predicate notBlankIdent(Go::Expr e) { not e instanceof Go::BlankIdent } /** Helper: implicit field selection for promoted selectors */ - private predicate implicitFieldSelection(Ast::AstNode e, int index, Go::Field implicitField) { + predicate implicitFieldSelection(Ast::AstNode e, int index, Go::Field implicitField) { exists(Go::StructType baseType, Go::PromotedField child, int implicitFieldDepth | baseType = e.(Go::PromotedSelector).getSelectedStructType() and ( diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index a4c73004108..0dd57d11c7d 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -7,13 +7,13 @@ * structure or type information). * * Each instruction is also a control-flow node, but there are control-flow nodes that are not - * instructions (synthetic entry and exit nodes, as well as no-op skip nodes). + * instructions (synthetic entry and exit nodes, as well as before/after nodes). */ overlay[local] module; import go -private import semmle.go.controlflow.ControlFlowGraphImpl +private import ControlFlowGraphShared /** Provides predicates and classes for working with IR constructs. */ module IR { @@ -22,37 +22,13 @@ module IR { */ class Instruction extends ControlFlow::Node { Instruction() { - this instanceof MkExprNode or - this instanceof MkLiteralElementInitNode or - this instanceof MkImplicitLiteralElementIndex or - this instanceof MkAssignNode or - this instanceof MkCompoundAssignRhsNode or - this instanceof MkExtractNode or - this instanceof MkZeroInitNode or - this instanceof MkFuncDeclNode or - this instanceof MkDeferNode or - this instanceof MkGoNode or - this instanceof MkConditionGuardNode or - this instanceof MkIncDecNode or - this instanceof MkIncDecRhs or - this instanceof MkImplicitOne or - this instanceof MkReturnNode or - this instanceof MkResultWriteNode or - this instanceof MkResultReadNode or - this instanceof MkSelectNode or - this instanceof MkSendNode or - this instanceof MkParameterInit or - this instanceof MkArgumentNode or - this instanceof MkResultInit or - this instanceof MkNextNode or - this instanceof MkImplicitTrue or - this instanceof MkCaseCheckNode or - this instanceof MkTypeSwitchImplicitVariable or - this instanceof MkImplicitLowerSliceBound or - this instanceof MkImplicitUpperSliceBound or - this instanceof MkImplicitMaxSliceBound or - this instanceof MkImplicitDeref or - this instanceof MkImplicitFieldSelection + this.isIn(_) + or + this.isAdditional(_, _) + or + this.isAfterTrue(_) and not this.isIn(_) + or + this.isAfterFalse(_) and not this.isIn(_) } /** Holds if this instruction reads the value of variable or constant `v`. */ @@ -120,78 +96,87 @@ module IR { /** Gets a textual representation of the kind of this instruction. */ string getInsnKind() { - this instanceof MkExprNode and result = "expression" + this instanceof EvalInstruction and result = "expression" or - this instanceof MkLiteralElementInitNode and result = "element init" + this instanceof InitLiteralComponentInstruction and result = "element init" or - this instanceof MkImplicitLiteralElementIndex and result = "element index" + this instanceof ImplicitLiteralElementIndexInstruction and result = "element index" or - this instanceof MkAssignNode and result = "assignment" + this instanceof AssignInstruction and result = "assignment" or - this instanceof MkCompoundAssignRhsNode and result = "right-hand side of compound assignment" + this instanceof EvalCompoundAssignRhsInstruction and + result = "right-hand side of compound assignment" or - this instanceof MkExtractNode and result = "tuple element extraction" + this instanceof ExtractTupleElementInstruction and result = "tuple element extraction" or - this instanceof MkZeroInitNode and result = "zero value" + this instanceof EvalImplicitInitInstruction and result = "zero value" or - this instanceof MkFuncDeclNode and result = "function declaration" + this instanceof DeclareFunctionInstruction and result = "function declaration" or - this instanceof MkDeferNode and result = "defer" + this instanceof DeferInstruction and result = "defer" or - this instanceof MkGoNode and result = "go" + this instanceof GoInstruction and result = "go" or - this instanceof MkConditionGuardNode and result = "condition guard" + this instanceof ConditionGuardInstruction and result = "condition guard" or - this instanceof MkIncDecNode and result = "increment/decrement" + this instanceof IncDecInstruction and result = "increment/decrement" or - this instanceof MkIncDecRhs and result = "right-hand side of increment/decrement" + this instanceof EvalIncDecRhsInstruction and + result = "right-hand side of increment/decrement" or - this instanceof MkImplicitOne and result = "implicit 1" + this instanceof EvalImplicitOneInstruction and result = "implicit 1" or - this instanceof MkReturnNode and result = "return" + this instanceof ReturnInstruction and result = "return" or - this instanceof MkResultWriteNode and result = "result write" + this instanceof WriteResultInstruction and result = "result write" or - this instanceof MkResultReadNode and result = "result read" + this instanceof ReadResultInstruction and result = "result read" or - this instanceof MkSelectNode and result = "select" + this instanceof SendInstruction and result = "send" or - this instanceof MkSendNode and result = "send" + this instanceof InitParameterInstruction and result = "parameter initialization" or - this instanceof MkParameterInit and result = "parameter initialization" + this instanceof ReadArgumentInstruction and result = "argument" or - this instanceof MkArgumentNode and result = "argument" + this instanceof InitResultInstruction and result = "result initialization" or - this instanceof MkResultInit and result = "result initialization" + this instanceof GetNextEntryInstruction and result = "next key-value pair" or - this instanceof MkNextNode and result = "next key-value pair" + this instanceof EvalImplicitTrueInstruction and result = "implicit true" or - this instanceof MkImplicitTrue and result = "implicit true" + this instanceof CaseInstruction and result = "case" or - this instanceof MkCaseCheckNode and result = "case" - or - this instanceof MkTypeSwitchImplicitVariable and + this instanceof TypeSwitchImplicitVariableInstruction and result = "type switch implicit variable declaration" or - this instanceof MkImplicitLowerSliceBound and result = "implicit lower bound" + this instanceof EvalImplicitLowerSliceBoundInstruction and result = "implicit lower bound" or - this instanceof MkImplicitUpperSliceBound and result = "implicit upper bound" + this instanceof EvalImplicitUpperSliceBoundInstruction and result = "implicit upper bound" or - this instanceof MkImplicitMaxSliceBound and result = "implicit maximum" + this instanceof EvalImplicitMaxSliceBoundInstruction and result = "implicit maximum" or - this instanceof MkImplicitDeref and result = "implicit dereference" + this instanceof EvalImplicitDerefInstruction and result = "implicit dereference" or - this instanceof MkImplicitFieldSelection and result = "implicit field selection" + 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, MkExprNode { + class EvalInstruction extends Instruction { Expr e; - EvalInstruction() { this = MkExprNode(e) } + EvalInstruction() { this.isIn(e) and e instanceof Expr } /** Gets the expression underlying this instruction. */ Expr getExpr() { result = e } @@ -217,10 +202,6 @@ module IR { override predicate isConst() { e.isConst() } override predicate isPlatformIndependentConstant() { e.isPlatformIndependentConstant() } - - override string toString() { result = e.toString() } - - override Location getLocation() { result = e.getLocation() } } /** @@ -236,17 +217,13 @@ module IR { or this instanceof ReadResultInstruction or - this instanceof MkImplicitFieldSelection + this instanceof ImplicitFieldReadInstruction } } /** * Gets the effective base of a selector, index or slice expression, taking implicit dereferences * and implicit field reads into account. - * - * For a selector expression `b.f`, this could be the implicit dereference `*b`, or the implicit - * field access `b.Embedded` if the field `f` is promoted from an embedded type `Embedded`, or a - * combination of both `*(b.Embedded)`, or simply `b` if neither case applies. */ private Instruction selectorBase(Expr e) { exists(ImplicitFieldReadInstruction fri | fri.getSelectorExpr() = e and fri.getIndex() = 1 | @@ -261,17 +238,15 @@ module IR { or base = e.(SliceExpr).getBase() | - result = MkImplicitDeref(base) + result = implicitDerefInstruction(base) or - not exists(MkImplicitDeref(base)) and + not exists(implicitDerefInstruction(base)) and result = evalExprInstruction(base) ) } /** * An IR instruction that reads a component from a composite object. - * - * This is either a field of a struct, or an element of an array, map, slice or string. */ class ComponentReadInstruction extends ReadInstruction { ComponentReadInstruction() { @@ -282,7 +257,7 @@ module IR { not e.(SelectorExpr).getSelector() = any(Method method).getAReference() ) or - this instanceof MkImplicitFieldSelection + this instanceof ImplicitFieldReadInstruction } /** Gets the instruction computing the base value on which the field or element is read. */ @@ -295,9 +270,6 @@ module IR { /** * An IR instruction that reads the value of a field. - * - * On databases with incomplete type information, method expressions may sometimes be - * misclassified as field reads. */ class FieldReadInstruction extends ComponentReadInstruction { SelectorExpr e; @@ -309,7 +281,9 @@ module IR { index = 0 and field.getAReference() = e.getSelector() or - this = MkImplicitFieldSelection(e, index, field) + this.(ImplicitFieldReadInstruction).getSelectorExpr() = e and + this.(ImplicitFieldReadInstruction).getIndex() = index and + this.(ImplicitFieldReadInstruction).getField() = field } /** Gets the `SelectorExpr` of this field read. */ @@ -332,9 +306,9 @@ module IR { fri.getSelectorExpr() = e and fri.getIndex() = pragma[only_bind_into](index + 1) ) and ( - result = MkImplicitDeref(e.getBase()) + result = implicitDerefInstruction(e.getBase()) or - not exists(MkImplicitDeref(e.getBase())) and + not exists(implicitDerefInstruction(e.getBase())) and result = evalExprInstruction(e.getBase()) ) } @@ -345,24 +319,50 @@ module IR { } /** - * An IR instruction for an implicit field read as part of reading a - * promoted field. - * - * If the field that is being implicitly read has a pointer type then this - * instruction represents an implicit dereference of it. + * An IR instruction for an implicit field read as part of reading a promoted field. */ - class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection { - ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) } + class ImplicitFieldReadInstruction extends Instruction { + SelectorExpr sel; + int idx; + Field fld; - override predicate reads(ValueEntity v) { v = field } + ImplicitFieldReadInstruction() { + this.isAdditional(sel, "implicit-field:" + idx.toString()) and + GoCfg::implicitFieldSelection(sel, idx, fld) + } - override Type getResultType() { result = lookThroughPointerType(field.getType()) } + /** Gets the `SelectorExpr` for which this is an implicit field read. */ + SelectorExpr getSelectorExpr() { result = sel } - override ControlFlow::Root getRoot() { result.isRootOf(e) } + /** Gets the index of this implicit field read. */ + int getIndex() { result = idx } - override string toString() { result = "implicit read of field " + field.toString() } + /** Gets the field being read. */ + Field getField() { result = fld } - override Location getLocation() { result = e.getBase().getLocation() } + Instruction getBaseInstruction() { + exists(ImplicitFieldReadInstruction fri | + fri.getSelectorExpr() = sel and fri.getIndex() = pragma[only_bind_into](idx + 1) + | + result = fri + ) + or + not exists(ImplicitFieldReadInstruction fri | + fri.getSelectorExpr() = sel and fri.getIndex() = pragma[only_bind_into](idx + 1) + ) and + ( + result = implicitDerefInstruction(sel.getBase()) + or + not exists(implicitDerefInstruction(sel.getBase())) and + result = evalExprInstruction(sel.getBase()) + ) + } + + override predicate reads(ValueEntity v) { v = fld } + + override Type getResultType() { result = lookThroughPointerType(fld.getType()) } + + override ControlFlow::Root getRoot() { result.isRootOf(sel) } } /** @@ -374,10 +374,8 @@ module IR { 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) { @@ -391,7 +389,6 @@ module IR { 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) { @@ -405,22 +402,18 @@ module IR { 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) @@ -445,13 +438,10 @@ module IR { 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) { @@ -463,16 +453,16 @@ module IR { /** * An IR instruction that initializes a component of a composite literal. */ - class InitLiteralComponentInstruction extends WriteInstruction, MkLiteralElementInitNode { + class InitLiteralComponentInstruction extends WriteInstruction { CompositeLit lit; - int i; + int litIdx; Expr elt; InitLiteralComponentInstruction() { - this = MkLiteralElementInitNode(elt) and elt = lit.getElement(i) + this.isAdditional(elt, "lit-init") and + elt = lit.getElement(litIdx) } - /** Gets the instruction allocating the composite literal. */ Instruction getBase() { result = evalExprInstruction(lit) } override Instruction getRhs() { @@ -481,10 +471,6 @@ module IR { } override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override string toString() { result = "init of " + elt } - - override Location getLocation() { result = elt.getLocation() } } /** @@ -493,15 +479,13 @@ module IR { 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, _, _) + else pragma[only_bind_out](lit.getStructType()).hasOwnField(litIdx, result, _, _) } - /** Gets the initialized field. */ Field getField() { result.getDeclaringType() = lit.getStructType() and result.getName() = this.getFieldName() @@ -523,85 +507,59 @@ module IR { ) } - /** Gets the instruction computing the index of the initialized element. */ Instruction getIndex() { result = evalExprInstruction(elt.(KeyValueExpr).getKey()) or - result = MkImplicitLiteralElementIndex(elt) + result.(ImplicitLiteralElementIndexInstruction).isAdditional(elt, "lit-index") } } - /** - * An IR instruction that initializes an element of an array literal. - */ 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 + 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 + this.getBase() = base and this.getIndex() = index } } - /** Holds if `lit` does not specify any explicit keys. */ private predicate noExplicitKeys(CompositeLit lit) { not lit.getAnElement() instanceof KeyValueExpr } - /** Gets the index of the `i`th element in (array or slice) literal `lit`. */ private int getElementIndex(CompositeLit lit, int i) { ( lit.getType().getUnderlyingType() instanceof ArrayType or lit.getType().getUnderlyingType() instanceof SliceType ) and exists(Expr elt | elt = lit.getElement(i) | - // short-circuit computation for literals without any explicit keys noExplicitKeys(lit) and result = i or result = elt.(KeyValueExpr).getKey().getIntValue() @@ -618,10 +576,10 @@ module IR { /** * An IR instruction computing the implicit index of an element in an array or slice literal. */ - class ImplicitLiteralElementIndexInstruction extends Instruction, MkImplicitLiteralElementIndex { + class ImplicitLiteralElementIndexInstruction extends Instruction { Expr elt; - ImplicitLiteralElementIndexInstruction() { this = MkImplicitLiteralElementIndex(elt) } + ImplicitLiteralElementIndexInstruction() { this.isAdditional(elt, "lit-index") } override Type getResultType() { result instanceof IntType } @@ -638,20 +596,25 @@ module IR { override predicate isPlatformIndependentConstant() { any() } override predicate isConst() { any() } - - override string toString() { result = "element index" } - - override Location getLocation() { result = elt.getLocation() } } /** * An instruction assigning to a variable or field. */ - class AssignInstruction extends WriteInstruction, MkAssignNode { + class AssignInstruction extends WriteInstruction { AstNode assgn; int i; - AssignInstruction() { this = MkAssignNode(assgn, i) } + AssignInstruction() { + this.isAdditional(assgn, "assign:" + i.toString()) and + ( + exists(assgn.(Assignment).getLhs(i)) + or + exists(assgn.(ValueSpec).getNameExpr(i)) + or + assgn instanceof RangeStmt and i in [0, 1] + ) + } override Instruction getRhs() { exists(SimpleAssignStmt a | a = assgn | @@ -663,51 +626,57 @@ module IR { spec.getNumName() = spec.getNumInit() and result = evalExprInstruction(spec.getInit(i)) or - result = MkZeroInitNode(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) + result = + implicitInitInstruction(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) ) or - result = MkCompoundAssignRhsNode(assgn) + result.(EvalCompoundAssignRhsInstruction).isAdditional(assgn, "compound-rhs") or - result = MkExtractNode(assgn, i) + result.(ExtractTupleElementInstruction).isAdditional(assgn, "extract:" + i.toString()) } override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = "assignment to " + this.getLhs() } - - override Location getLocation() { result = this.getLhs().getLocation() } } - /** An instruction computing the value of the right-hand side of a compound assignment. */ - class EvalCompoundAssignRhsInstruction extends Instruction, MkCompoundAssignRhsNode { + class EvalCompoundAssignRhsInstruction extends Instruction { CompoundAssignStmt assgn; - EvalCompoundAssignRhsInstruction() { this = MkCompoundAssignRhsNode(assgn) } + EvalCompoundAssignRhsInstruction() { this.isAdditional(assgn, "compound-rhs") } - /** 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 { + class ExtractTupleElementInstruction extends Instruction { AstNode s; int i; - ExtractTupleElementInstruction() { this = MkExtractNode(s, i) } + ExtractTupleElementInstruction() { + this.isAdditional(s, "extract:" + i.toString()) and + ( + exists(s.(Assignment).getLhs(i)) + or + exists(s.(ValueSpec).getNameExpr(i)) + or + s instanceof RangeStmt and i in [0, 1] + or + exists(s.(ReturnStmt).getEnclosingFunction().getType().(SignatureType).getResultType(i)) + or + exists( + s.(CallExpr) + .getArgument(0) + .stripParens() + .(CallExpr) + .getType() + .(TupleType) + .getComponentType(i) + ) + ) + } - /** Gets the instruction computing the tuple value from which one value is extracted. */ Instruction getBase() { exists(Expr baseExpr | baseExpr = s.(Assignment).getRhs() or @@ -716,14 +685,13 @@ module IR { result = evalExprInstruction(baseExpr) ) or - result = MkNextNode(s) + result.(GetNextEntryInstruction).isAdditional(s, "next") or result = evalExprInstruction(s.(ReturnStmt).getExpr()) or result = evalExprInstruction(s.(CallExpr).getArgument(0).stripParens()) } - /** Holds if this extracts the `idx`th value of the result of `base`. */ predicate extractsElement(Instruction base, int idx) { base = this.getBase() and idx = i } override Type getResultType() { @@ -738,51 +706,42 @@ module IR { rangeType.(PointerType).getBaseType().getUnderlyingType().(ArrayType).getElementType() or baseType = rangeType.(SliceType).getElementType() | - i = 0 and - result instanceof IntType + i = 0 and result instanceof IntType or - i = 1 and - result = baseType + i = 1 and result = baseType ) or rangeType instanceof StringType and ( - i = 0 and - result instanceof IntType + i = 0 and result instanceof IntType or result = Builtin::rune().getType() ) or exists(MapType map | map = rangeType | - i = 0 and - result = map.getKeyType() + i = 0 and result = map.getKeyType() or - i = 1 and - result = map.getValueType() + i = 1 and result = map.getValueType() ) or - i = 0 and - result = rangeType.(RecvChanType).getElementType() + i = 0 and result = rangeType.(RecvChanType).getElementType() or - i = 0 and - result = rangeType.(SendRecvChanType).getElementType() + i = 0 and result = rangeType.(SendRecvChanType).getElementType() ) } override ControlFlow::Root getRoot() { result.isRootOf(s) } - - override string toString() { result = s + "[" + i + "]" } - - override Location getLocation() { result = s.getLocation() } } - /** - * An instruction that computes the zero value for a variable or constant. - */ - class EvalImplicitInitInstruction extends Instruction, MkZeroInitNode { + class EvalImplicitInitInstruction extends Instruction { ValueEntity v; + int idx; + ValueSpec spec; - EvalImplicitInitInstruction() { this = MkZeroInitNode(v) } + EvalImplicitInitInstruction() { + this.isAdditional(spec, "zero-init:" + idx.toString()) and + spec.getNameExpr(idx) = v.getDeclaration() + } override Type getResultType() { result = v.getType() } @@ -814,104 +773,61 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "zero value for " + v } - - override Location getLocation() { result = v.getDeclaration().getLocation() } } - /** - * An instruction that corresponds to the declaration of a function. - */ - class DeclareFunctionInstruction extends Instruction, MkFuncDeclNode { + class DeclareFunctionInstruction extends Instruction { FuncDecl fd; - DeclareFunctionInstruction() { this = MkFuncDeclNode(fd) } + DeclareFunctionInstruction() { this.isIn(fd) and fd instanceof FuncDecl } 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 { + class DeferInstruction extends Instruction { DeferStmt defer; - DeferInstruction() { this = MkDeferNode(defer) } + DeferInstruction() { this.isIn(defer) } override ControlFlow::Root getRoot() { result.isRootOf(defer) } - - override string toString() { result = defer.toString() } - - override Location getLocation() { result = defer.getLocation() } } - /** - * An instruction that corresponds to a `go` statement. - */ - class GoInstruction extends Instruction, MkGoNode { + class GoInstruction extends Instruction { GoStmt go; - GoInstruction() { this = MkGoNode(go) } + GoInstruction() { this.isIn(go) } override ControlFlow::Root getRoot() { result.isRootOf(go) } - - override string toString() { result = go.toString() } - - override Location getLocation() { result = go.getLocation() } } - /** - * An instruction that corresponds to an increment or decrement statement. - */ - class IncDecInstruction extends WriteInstruction, MkIncDecNode { + class IncDecInstruction extends WriteInstruction { IncDecStmt ids; - IncDecInstruction() { this = MkIncDecNode(ids) } + IncDecInstruction() { this.isIn(ids) } - override Instruction getRhs() { result = MkIncDecRhs(ids) } + override Instruction getRhs() { + result.(EvalIncDecRhsInstruction).isAdditional(ids, "incdec-rhs") + } override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = ids.toString() } - - override Location getLocation() { result = ids.getLocation() } } - /** - * An instruction that computes the (implicit) right-hand side of an increment or - * decrement statement. - */ - class EvalIncDecRhsInstruction extends Instruction, MkIncDecRhs { + class EvalIncDecRhsInstruction extends Instruction { IncDecStmt ids; - EvalIncDecRhsInstruction() { this = MkIncDecRhs(ids) } + EvalIncDecRhsInstruction() { this.isAdditional(ids, "incdec-rhs") } - /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } 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 { + class EvalImplicitOneInstruction extends Instruction { IncDecStmt ids; - EvalImplicitOneInstruction() { this = MkImplicitOne(ids) } + EvalImplicitOneInstruction() { this.isAdditional(ids, "implicit-one") } - /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } override Type getResultType() { result = ids.getOperand().getType() } @@ -925,205 +841,184 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "1" } - - override Location getLocation() { result = ids.getLocation() } } - /** - * An instruction corresponding to a return from a function. - */ - class ReturnInstruction extends Instruction, MkReturnNode { + class ReturnInstruction extends Instruction { ReturnStmt ret; - ReturnInstruction() { this = MkReturnNode(ret) } + ReturnInstruction() { this.isAdditional(ret, "return") } - /** Gets the corresponding `ReturnStmt`. */ ReturnStmt getReturnStmt() { result = ret } - /** Holds if this statement returns multiple results. */ - predicate returnsMultipleResults() { exists(MkExtractNode(ret, _)) or ret.getNumExpr() > 1 } + predicate returnsMultipleResults() { + exists(ExtractTupleElementInstruction ext | ext.isAdditional(ret, _)) + or + ret.getNumExpr() > 1 + } - /** Gets the instruction whose result is the (unique) result returned by this statement. */ Instruction getResult() { 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) + result.isAdditional(ret, _) and + result.(ExtractTupleElementInstruction).extractsElement(_, i) or - not exists(MkExtractNode(ret, _)) and + not exists(ExtractTupleElementInstruction ext | ext.isAdditional(ret, _)) and result = evalExprInstruction(ret.getExpr(i)) } override ControlFlow::Root getRoot() { result.isRootOf(ret) } - - override string toString() { result = ret.toString() } - - override Location getLocation() { result = ret.getLocation() } } - /** - * An instruction that represents the implicit assignment to a result variable - * performed by a return statement. - */ - class WriteResultInstruction extends WriteInstruction, MkResultWriteNode { + class WriteResultInstruction extends WriteInstruction { ResultVariable var; - int i; - ReturnInstruction ret; + int idx; + ReturnStmt retStmt; WriteResultInstruction() { - exists(ReturnStmt retstmt | - this = MkResultWriteNode(var, i, retstmt) and - ret = MkReturnNode(retstmt) - ) + this.isAdditional(retStmt, "result-write:" + idx.toString()) and + var = retStmt.getEnclosingFunction().getResultVar(idx) and + exists(retStmt.getAnExpr()) } - override Instruction getRhs() { result = ret.getResult(i) } + private ReturnInstruction getReturnInstruction() { + result.(ReturnInstruction).isAdditional(retStmt, "return") + } + + override Instruction getRhs() { result = this.getReturnInstruction().getResult(idx) } - /** Gets the result variable being assigned. */ ResultVariable getResultVariable() { result = var } override Type getResultType() { result = var.getType() } override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit write of " + var } - - override Location getLocation() { result = ret.getResult(i).getLocation() } } - /** - * An instruction that reads the final value of a result variable upon returning - * from a function. - */ - class ReadResultInstruction extends Instruction, MkResultReadNode { + class ReadResultInstruction extends Instruction { ResultVariable var; + int idx; + FuncDef fd; - ReadResultInstruction() { this = MkResultReadNode(var) } + ReadResultInstruction() { + this.isAdditional(fd, "result-read:" + idx.toString()) and + var = fd.getResultVar(idx) + } override predicate reads(ValueEntity v) { v = var } override Type getResultType() { result = var.getType() } override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit read of " + var } - - override Location getLocation() { result = var.getDeclaration().getLocation() } } - /** - * An instruction corresponding to a `select` statement. - */ - class SelectInstruction extends Instruction, MkSelectNode { - SelectStmt sel; - - SelectInstruction() { this = MkSelectNode(sel) } - - override ControlFlow::Root getRoot() { result.isRootOf(sel) } - - override string toString() { result = sel.toString() } - - override Location getLocation() { result = sel.getLocation() } - } - - /** - * An instruction corresponding to a send statement. - */ - class SendInstruction extends Instruction, MkSendNode { + class SendInstruction extends Instruction { SendStmt send; - SendInstruction() { this = MkSendNode(send) } + SendInstruction() { this.isAdditional(send, "send") } override ControlFlow::Root getRoot() { result.isRootOf(send) } - - override string toString() { result = send.toString() } - - override Location getLocation() { result = send.getLocation() } } - /** - * An instruction initializing a parameter to the corresponding argument. - */ - class InitParameterInstruction extends WriteInstruction, MkParameterInit { + class InitParameterInstruction extends WriteInstruction { Parameter parm; + int idx; + FuncDef fd; - InitParameterInstruction() { this = MkParameterInit(parm) } + InitParameterInstruction() { + this.isAdditional(fd, "param-init:" + idx.toString()) and + parm = fd.getParameter(idx) + } - override Instruction getRhs() { result = MkArgumentNode(parm) } + override Instruction getRhs() { + result.(ReadArgumentInstruction).isAdditional(fd, "arg:" + idx.toString()) + } override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "initialization of " + parm } - - override Location getLocation() { result = parm.getDeclaration().getLocation() } } - /** - * An instruction reading the value of a function argument. - */ - class ReadArgumentInstruction extends Instruction, MkArgumentNode { + class ReadArgumentInstruction extends Instruction { Parameter parm; + int idx; + FuncDef fd; - ReadArgumentInstruction() { this = MkArgumentNode(parm) } + ReadArgumentInstruction() { + this.isAdditional(fd, "arg:" + idx.toString()) and + parm = fd.getParameter(idx) + } override Type getResultType() { result = parm.getType() } override ControlFlow::Root getRoot() { result = parm.getFunction() } - - 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 { + class InitResultInstruction extends WriteInstruction { ResultVariable res; + int idx; + FuncDef fd; - InitResultInstruction() { this = MkResultInit(res) } + InitResultInstruction() { + this.isAdditional(fd, "result-init:" + idx.toString()) and + res = fd.getResultVar(idx) + } - override Instruction getRhs() { result = MkZeroInitNode(res) } + override Instruction getRhs() { + result.(ResultZeroInitInstruction).isAdditional(fd, "result-zero-init:" + idx.toString()) + } 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 { + private class ResultZeroInitInstruction extends Instruction { + ResultVariable res; + int idx; + FuncDef fd; + + ResultZeroInitInstruction() { + this.isAdditional(fd, "result-zero-init:" + idx.toString()) and + res = fd.getResultVar(idx) + } + + override Type getResultType() { result = res.getType() } + + override ControlFlow::Root getRoot() { result.isRootOf(fd) } + + override int getIntValue() { + res.getType().getUnderlyingType() instanceof IntegerType and result = 0 + } + + override float getFloatValue() { + res.getType().getUnderlyingType() instanceof FloatType and result = 0.0 + } + + override string getStringValue() { + res.getType().getUnderlyingType() instanceof StringType and result = "" + } + + override boolean getBoolValue() { + res.getType().getUnderlyingType() instanceof BoolType and result = false + } + + override predicate isConst() { any() } + + override predicate isPlatformIndependentConstant() { any() } + } + + class GetNextEntryInstruction extends Instruction { RangeStmt rs; - GetNextEntryInstruction() { this = MkNextNode(rs) } + GetNextEntryInstruction() { this.isAdditional(rs, "next") } - /** - * Gets the instruction computing the value whose key-value pairs this instruction reads. - */ Instruction getDomain() { result = evalExprInstruction(rs.getDomain()) } override ControlFlow::Root getRoot() { result.isRootOf(rs) } - - override string toString() { result = "next key-value pair in range" } - - override Location getLocation() { result = rs.getDomain().getLocation() } } - /** - * An instruction computing the implicit `true` value in an expression-less `switch` statement. - */ - class EvalImplicitTrueInstruction extends Instruction, MkImplicitTrue { - Stmt stmt; + class EvalImplicitTrueInstruction extends Instruction { + ExpressionSwitchStmt stmt; - EvalImplicitTrueInstruction() { this = MkImplicitTrue(stmt) } + EvalImplicitTrueInstruction() { this.isAdditional(stmt, "implicit-true") } override Type getResultType() { result instanceof BoolType } @@ -1136,91 +1031,37 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "true" } - - override Location getLocation() { result = stmt.getLocation() } } - /** - * An instruction corresponding to the implicit comparison or type check performed by an - * expression in a `case` clause. - * - * For example, consider this `switch` statement: - * - * ```go - * switch x { - * case 2, y+1: - * ... - * } - * ``` - * - * The expressions `2` and `y+1` are implicitly compared to `x`. These comparisons are - * represented by case instructions. - */ - class CaseInstruction extends Instruction, MkCaseCheckNode { + class CaseInstruction extends Instruction { CaseClause cc; int i; - CaseInstruction() { this = MkCaseCheckNode(cc, i) } + CaseInstruction() { + this.isAdditional(cc, "case-check:" + i.toString()) and + exists(cc.getExpr(i)) + } override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "case " + cc.getExpr(i) } - - override Location getLocation() { result = cc.getExpr(i).getLocation() } } - /** - * An instruction corresponding to the implicit declaration of the variable - * `lv` in case clause `cc` and its assignment of the value `switchExpr` from - * the guard. This only occurs in case clauses in a type switch statement - * which declares a variable in its guard. - * - * For example, consider this type switch statement: - * - * ```go - * switch y := x.(type) { - * case Type1: - * f(y) - * ... - * } - * ``` - * - * The `y` inside the case clause is actually a local variable with type - * `Type1` that is implicitly declared at the top of the case clause. In - * default clauses and case clauses which list more than one type, the type - * of the implicitly declared variable is the type of `switchExpr`. - */ - class TypeSwitchImplicitVariableInstruction extends Instruction, MkTypeSwitchImplicitVariable { + class TypeSwitchImplicitVariableInstruction extends Instruction { CaseClause cc; - LocalVariable lv; - Expr switchExpr; - TypeSwitchImplicitVariableInstruction() { - this = MkTypeSwitchImplicitVariable(cc, lv, switchExpr) - } + TypeSwitchImplicitVariableInstruction() { this.isAdditional(cc, "type-switch-var") } override predicate writes(ValueEntity v, Instruction rhs) { - v = lv and - rhs = evalExprInstruction(switchExpr) + v = cc.getImplicitlyDeclaredVariable() and + exists(TypeSwitchStmt ts | cc = ts.getACase() | rhs = evalExprInstruction(ts.getExpr())) } override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "implicit type switch variable declaration" } - - override Location getLocation() { result = cc.getLocation() } } - /** - * An instruction computing the implicit lower slice bound of zero in a slice expression without - * an explicit lower bound. - */ - class EvalImplicitLowerSliceBoundInstruction extends Instruction, MkImplicitLowerSliceBound { + class EvalImplicitLowerSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitLowerSliceBoundInstruction() { this = MkImplicitLowerSliceBound(slice) } + EvalImplicitLowerSliceBoundInstruction() { this.isAdditional(slice, "implicit-low") } override Type getResultType() { result instanceof IntType } @@ -1233,58 +1074,33 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "0" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction computing the implicit upper slice bound in a slice expression without an - * explicit upper bound. - */ - class EvalImplicitUpperSliceBoundInstruction extends Instruction, MkImplicitUpperSliceBound { + class EvalImplicitUpperSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitUpperSliceBoundInstruction() { this = MkImplicitUpperSliceBound(slice) } + EvalImplicitUpperSliceBoundInstruction() { this.isAdditional(slice, "implicit-high") } override ControlFlow::Root getRoot() { result.isRootOf(slice) } override Type getResultType() { result instanceof IntType } - - override string toString() { result = "len" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction computing the implicit maximum slice bound in a slice expression without an - * explicit maximum bound. - */ - class EvalImplicitMaxSliceBoundInstruction extends Instruction, MkImplicitMaxSliceBound { + class EvalImplicitMaxSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitMaxSliceBoundInstruction() { this = MkImplicitMaxSliceBound(slice) } + EvalImplicitMaxSliceBoundInstruction() { this.isAdditional(slice, "implicit-max") } override ControlFlow::Root getRoot() { result.isRootOf(slice) } override Type getResultType() { result instanceof IntType } - - override string toString() { result = "cap" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction implicitly dereferencing the base in a field or method reference through a - * pointer, or the base in an element or slice reference through a pointer. - */ - class EvalImplicitDerefInstruction extends Instruction, MkImplicitDeref { + class EvalImplicitDerefInstruction extends Instruction { Expr e; - EvalImplicitDerefInstruction() { this = MkImplicitDeref(e) } + EvalImplicitDerefInstruction() { this.isAdditional(e, "implicit-deref") } - /** Gets the operand that is being dereferenced. */ Expr getOperand() { result = e } override Type getResultType() { @@ -1292,13 +1108,38 @@ module IR { } override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit dereference" } - - override Location getLocation() { result = e.getLocation() } } /** A representation of the target of a write instruction. */ + newtype TWriteTarget = + MkLhs(ControlFlow::Node write, Expr lhs) { + exists(AstNode assgn, int i | write.isAdditional(assgn, "assign:" + i.toString()) | + lhs = assgn.(Assignment).getLhs(i).stripParens() + or + lhs = assgn.(ValueSpec).getNameExpr(i) + or + exists(RangeStmt rs | rs = assgn | + i = 0 and lhs = rs.getKey().stripParens() + or + i = 1 and lhs = rs.getValue().stripParens() + ) + ) + or + exists(IncDecStmt ids | write.isIn(ids) | lhs = ids.getOperand().stripParens()) + or + exists(FuncDef fd, int idx | + write.isAdditional(fd, "param-init:" + idx.toString()) and + lhs = fd.getParameter(idx).getDeclaration() + ) + or + exists(FuncDef fd, int idx | + write.isAdditional(fd, "result-init:" + idx.toString()) and + lhs = fd.getResultVar(idx).getDeclaration() + ) + } or + MkLiteralElementTarget(InitLiteralComponentInstruction elt) or + MkResultWriteTarget(WriteResultInstruction w) + class WriteTarget extends TWriteTarget { ControlFlow::Node w; @@ -1306,35 +1147,20 @@ module IR { 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 ) { @@ -1349,7 +1175,6 @@ module IR { } } - /** A reference to a variable or constant, used as the target of a write. */ class VarOrConstTarget extends WriteTarget { Expr loc; @@ -1390,18 +1215,11 @@ module IR { ) } - /** 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) @@ -1409,14 +1227,12 @@ module IR { 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) { @@ -1427,27 +1243,9 @@ module IR { 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)) @@ -1455,32 +1253,19 @@ module IR { 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; @@ -1489,109 +1274,75 @@ module IR { (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.(EvalInstruction).getExpr() = e + or result = evalExprInstruction(e.(ParenExpr).getExpr()) } - /** - * Gets the instruction corresponding to the initialization of `r`. - */ - InitParameterInstruction initRecvInstruction(ReceiverVariable r) { result = MkParameterInit(r) } + InitParameterInstruction initRecvInstruction(ReceiverVariable r) { + exists(FuncDef fd, int i | + fd.getParameter(i) = r and result.isAdditional(fd, "param-init:" + i.toString()) + ) + } - /** - * Gets the instruction corresponding to the initialization of `p`. - */ - InitParameterInstruction initParamInstruction(Parameter p) { result = MkParameterInit(p) } + InitParameterInstruction initParamInstruction(Parameter p) { + exists(FuncDef fd, int i | + fd.getParameter(i) = p and result.isAdditional(fd, "param-init:" + i.toString()) + ) + } - /** - * Gets the instruction corresponding to the `i`th assignment happening at - * `assgn` (0-based). - */ - AssignInstruction assignInstruction(Assignment assgn, int i) { result = MkAssignNode(assgn, i) } + AssignInstruction assignInstruction(Assignment assgn, int i) { + result.isAdditional(assgn, "assign:" + i.toString()) and + exists(assgn.getLhs(i)) + } - /** - * Gets the instruction corresponding to the `i`th initialization happening - * at `spec` (0-based). - */ - AssignInstruction initInstruction(ValueSpec spec, int i) { result = MkAssignNode(spec, i) } + AssignInstruction initInstruction(ValueSpec spec, int i) { + result.isAdditional(spec, "assign:" + i.toString()) and + exists(spec.getNameExpr(i)) + } - /** - * Gets the instruction corresponding to the assignment of the key variable - * of range statement `rs`. - */ - AssignInstruction assignKeyInstruction(RangeStmt rs) { result = MkAssignNode(rs, 0) } + AssignInstruction assignKeyInstruction(RangeStmt rs) { result.isAdditional(rs, "assign:0") } - /** - * Gets the instruction corresponding to the assignment of the value variable - * of range statement `rs`. - */ - AssignInstruction assignValueInstruction(RangeStmt rs) { result = MkAssignNode(rs, 1) } + AssignInstruction assignValueInstruction(RangeStmt rs) { result.isAdditional(rs, "assign:1") } - /** - * Gets the instruction corresponding to the implicit initialization of `v` - * to its zero value. - */ - EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { result = MkZeroInitNode(v) } + EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { + exists(ValueSpec spec, int i | + spec.getNameExpr(i) = v.getDeclaration() and + result.isAdditional(spec, "zero-init:" + i.toString()) + ) + } - /** - * Gets the instruction corresponding to the extraction of the `idx`th element - * 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) + result.isAdditional(e, "implicit-low") } - /** - * Gets the instruction corresponding to the implicit upper bound of slice `e`, if any. - */ EvalImplicitUpperSliceBoundInstruction implicitUpperSliceBoundInstruction(SliceExpr e) { - result = MkImplicitUpperSliceBound(e) + result.isAdditional(e, "implicit-high") } - /** - * Gets the instruction corresponding to the implicit maximum bound of slice `e`, if any. - */ EvalImplicitMaxSliceBoundInstruction implicitMaxSliceBoundInstruction(SliceExpr e) { - result = MkImplicitMaxSliceBound(e) + result.isAdditional(e, "implicit-max") } - /** - * Gets the implicit dereference instruction for `e`, where `e` is a pointer used as the base - * in a field/method access, element access, or slice expression. - */ - EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { result = MkImplicitDeref(e) } + EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { + result.isAdditional(e, "implicit-deref") + } - /** Gets the base of `insn`, if `insn` is an implicit field read. */ Instruction lookThroughImplicitFieldRead(Instruction insn) { result = insn.(ImplicitFieldReadInstruction).getBaseInstruction() } diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll.bak b/go/ql/lib/semmle/go/controlflow/IR.qll.bak new file mode 100644 index 00000000000..807fbf76d1f --- /dev/null +++ b/go/ql/lib/semmle/go/controlflow/IR.qll.bak @@ -0,0 +1,1580 @@ +/** + * 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 new file mode 100644 index 00000000000..807fbf76d1f --- /dev/null +++ b/go/ql/lib/semmle/go/controlflow/IR.qll.bak2 @@ -0,0 +1,1580 @@ +/** + * 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/dataflow/SsaImpl.qll b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll index 9648335a6dd..8a66103ba5e 100644 --- a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll @@ -89,7 +89,7 @@ private module Internal { /** Holds if the `i`th node of `bb` in function `f` is an entry node. */ private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) { f = bb.getScope() and - bb.getNode(i).isEntryNode() + bb.getNode(i).(ControlFlow::Node).isEntryNode() } /** diff --git a/go/ql/lib/semmle/go/frameworks/Revel.qll b/go/ql/lib/semmle/go/frameworks/Revel.qll index c6250c2f8a5..085db1d2b0a 100644 --- a/go/ql/lib/semmle/go/frameworks/Revel.qll +++ b/go/ql/lib/semmle/go/frameworks/Revel.qll @@ -154,7 +154,7 @@ module Revel { private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) { result = insn or - result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase()) + result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBaseInstruction()) } /** A call to `Controller.Render`. */