diff --git a/go/ql/consistency-queries/CfgConsistency.ql b/go/ql/consistency-queries/CfgConsistency.ql new file mode 100644 index 000000000000..fec7fad646d3 --- /dev/null +++ b/go/ql/consistency-queries/CfgConsistency.ql @@ -0,0 +1,3 @@ +import go +private import semmle.go.controlflow.ControlFlowGraphShared +import GoCfg::ControlFlow::Consistency diff --git a/go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md b/go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md new file mode 100644 index 000000000000..896f23a8a14d --- /dev/null +++ b/go/ql/lib/change-notes/2026-03-30-shared-cfg-library.md @@ -0,0 +1,4 @@ +--- +category: fix +--- +* The Go control flow graph implementation has been migrated to use the shared CFG library. This is an internal change with no user-visible API changes. diff --git a/go/ql/lib/printCfg.ql b/go/ql/lib/printCfg.ql new file mode 100644 index 000000000000..f61d918ce98f --- /dev/null +++ b/go/ql/lib/printCfg.ql @@ -0,0 +1,53 @@ +/** + * @name Print CFG + * @description Produces a representation of a file's Control Flow Graph. + * This query is used by the VS Code extension. + * @id go/print-cfg + * @kind graph + * @tags ide-contextual-queries/print-cfg + */ + +import go +import semmle.go.controlflow.ControlFlowGraph +private import semmle.go.controlflow.ControlFlowGraphShared + +external string selectedSourceFile(); + +private predicate selectedSourceFileAlias = selectedSourceFile/0; + +external int selectedSourceLine(); + +private predicate selectedSourceLineAlias = selectedSourceLine/0; + +external int selectedSourceColumn(); + +private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + +module ViewCfgQueryInput implements GoCfg::ControlFlow::ViewCfgQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + predicate cfgScopeSpan( + CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn + ) { + file = scope.getFile() and + scope.getLocation().getStartLine() = startLine and + scope.getLocation().getStartColumn() = startColumn and + exists(Location loc | + loc.getEndLine() = endLine and + loc.getEndColumn() = endColumn and + loc = scope.(FuncDef).getBody().getLocation() + ) + or + file = scope.(File) and + startLine = 1 and + startColumn = 1 and + endLine = file.getNumberOfLines() and + endColumn = 999999 + } +} + +import GoCfg::ControlFlow::ViewCfgQuery diff --git a/go/ql/lib/semmle/go/Concepts.qll b/go/ql/lib/semmle/go/Concepts.qll index 302149149520..1194b2051f5a 100644 --- a/go/ql/lib/semmle/go/Concepts.qll +++ b/go/ql/lib/semmle/go/Concepts.qll @@ -431,7 +431,7 @@ private class HeuristicLoggerFunction extends Method { ) } - override predicate mayReturnNormally() { logFunctionPrefix != "Fatal" } + override predicate mustNotReturnNormally() { logFunctionPrefix = "Fatal" } override predicate mustPanic() { logFunctionPrefix = "Panic" } } diff --git a/go/ql/lib/semmle/go/PrintAst.qll b/go/ql/lib/semmle/go/PrintAst.qll index 6ea5fcc39719..c7c9bb5b4b7e 100644 --- a/go/ql/lib/semmle/go/PrintAst.qll +++ b/go/ql/lib/semmle/go/PrintAst.qll @@ -1,7 +1,7 @@ /** * Provides queries to pretty-print a Go AST as a graph. */ -overlay[local] +overlay[local?] module; import go diff --git a/go/ql/lib/semmle/go/Scopes.qll b/go/ql/lib/semmle/go/Scopes.qll index 9f18290fb011..735dbe80d590 100644 --- a/go/ql/lib/semmle/go/Scopes.qll +++ b/go/ql/lib/semmle/go/Scopes.qll @@ -437,11 +437,12 @@ class Function extends ValueEntity, @functionobject { * This predicate is an over-approximation: it may hold for functions that can never * return normally, but it never fails to hold for functions that can. * - * Note this is declared here and not in `DeclaredFunction` so that library models can override this - * by extending `Function` rather than having to remember to extend `DeclaredFunction`. + * Library models should not override this predicate; override `mustNotReturnNormally` + * instead, so that the control-flow graph construction can take the model into account. */ predicate mayReturnNormally() { not this.mustPanic() and + not this.mustNotReturnNormally() and (ControlFlow::mayReturnNormally(this.getFuncDecl()) or not exists(this.getBody())) } @@ -461,6 +462,16 @@ class Function extends ValueEntity, @functionobject { */ predicate mustPanic() { none() } + /** + * Holds if calling this function never returns normally (for example because it + * always panics, exits the process, or loops forever). + * + * Unlike `mayReturnNormally`, this predicate must be defined without reference to + * the control-flow graph, so that it can be used during CFG construction to + * suppress normal-flow successors of calls to this function. + */ + predicate mustNotReturnNormally() { none() } + /** Gets the number of parameters of this function. */ int getNumParameter() { result = this.getType().(SignatureType).getNumParameter() } diff --git a/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll b/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll index dc52abb25abf..a0ba4e00417c 100644 --- a/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll +++ b/go/ql/lib/semmle/go/controlflow/BasicBlocks.qll @@ -5,66 +5,27 @@ overlay[local] module; import go -private import ControlFlowGraphImpl -private import codeql.controlflow.BasicBlock as BB -private import codeql.controlflow.SuccessorType +private import ControlFlowGraphShared -private module Input implements BB::InputSig { - /** A delineated part of the AST with its own CFG. */ - class CfgScope = ControlFlow::Root; +/** A basic block in the control-flow graph. */ +class BasicBlock = GoCfg::Cfg::BasicBlock; - /** The class of control flow nodes. */ - class Node = ControlFlowNode; - - /** Gets the CFG scope in which this node occurs. */ - CfgScope nodeGetCfgScope(Node node) { node.getRoot() = result } - - /** Gets an immediate successor of this node. */ - Node nodeGetASuccessor(Node node, SuccessorType t) { - result = node.getASuccessor() and - ( - not result instanceof ControlFlow::ConditionGuardNode and t instanceof DirectSuccessor - or - t.(BooleanSuccessor).getValue() = result.(ControlFlow::ConditionGuardNode).getOutcome() - ) - } - - /** - * Holds if `node` represents an entry node to be used when calculating - * dominance. - */ - predicate nodeIsDominanceEntry(Node node) { node instanceof EntryNode } - - /** - * Holds if `node` represents an exit node to be used when calculating - * post dominance. - */ - predicate nodeIsPostDominanceExit(Node node) { node instanceof ExitNode } -} - -private module BbImpl = BB::Make; - -class BasicBlock = BbImpl::BasicBlock; - -class EntryBasicBlock = BbImpl::EntryBasicBlock; - -cached -private predicate reachableBB(BasicBlock bb) { - bb instanceof EntryBasicBlock - or - exists(BasicBlock predBB | predBB.getASuccessor(_) = bb | reachableBB(predBB)) -} +/** An entry basic block. */ +class EntryBasicBlock = GoCfg::Cfg::EntryBasicBlock; /** * A basic block that is reachable from an entry basic block. + * + * Since the shared CFG library only creates nodes for reachable code, + * all basic blocks are reachable by construction. */ class ReachableBasicBlock extends BasicBlock { - ReachableBasicBlock() { reachableBB(this) } + ReachableBasicBlock() { any() } } /** * A reachable basic block with more than one predecessor. */ class ReachableJoinBlock extends ReachableBasicBlock { - ReachableJoinBlock() { this.getFirstNode().isJoin() } + ReachableJoinBlock() { this.getFirstNode().(ControlFlow::Node).isJoin() } } diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll index 77bb94d89f8c..9ee373e25103 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll @@ -5,13 +5,17 @@ overlay[local] module; import go -private import ControlFlowGraphImpl +private import ControlFlowGraphShared -/** Provides helper predicates for mapping btween CFG nodes and the AST. */ +/** Provides helper predicates for mapping between CFG nodes and the AST. */ module ControlFlow { /** A file or function with which a CFG is associated. */ class Root extends AstNode { - Root() { exists(this.(File).getADecl()) or exists(this.(FuncDef).getBody()) } + Root() { + exists(this.(FuncDef).getBody()) + or + exists(this.(File).getADecl()) + } /** Holds if `nd` belongs to this file or function. */ predicate isRootOf(AstNode nd) { @@ -29,22 +33,16 @@ module ControlFlow { } /** - * A node in the intra-procedural control-flow graph of a Go function or file. + * A node in the intra-procedural control-flow graph of a Go function. * * Nodes correspond to expressions and statements that compute a value or perform * an operation (as opposed to providing syntactic structure or type information). * - * There are also synthetic entry and exit nodes for each Go function and file + * There are also synthetic entry and exit nodes for each Go function * that mark the beginning and the end, respectively, of the execution of the - * function and the loading of the file. + * function. */ - class Node extends TControlFlowNode { - /** Gets a node that directly follows this one in the control-flow graph. */ - Node getASuccessor() { result = CFG::succ(this) } - - /** Gets a node that directly precedes this one in the control-flow graph. */ - Node getAPredecessor() { this = result.getASuccessor() } - + class Node extends GoCfg::ControlFlowNode { /** Holds if this is a node with more than one successor. */ predicate isBranch() { strictcount(this.getASuccessor()) > 1 } @@ -52,22 +50,23 @@ module ControlFlow { predicate isJoin() { strictcount(this.getAPredecessor()) > 1 } /** Holds if this is the first control-flow node in `subtree`. */ - predicate isFirstNodeOf(AstNode subtree) { CFG::firstNode(subtree, this) } - - /** Holds if this node is the (unique) entry node of a function or file. */ - predicate isEntryNode() { this instanceof MkEntryNode } + predicate isFirstNodeOf(AstNode subtree) { + this.isBefore(subtree) + or + this.injects(subtree) + } - /** Holds if this node is the (unique) exit node of a function or file. */ - predicate isExitNode() { this instanceof MkExitNode } + /** Holds if this node is the (unique) entry node of a function. */ + predicate isEntryNode() { this instanceof GoCfg::ControlFlow::EntryNode } - /** Gets the basic block to which this node belongs. */ - BasicBlock getBasicBlock() { result.getANode() = this } + /** Holds if this node is the (unique) exit node of a function. */ + predicate isExitNode() { this instanceof GoCfg::ControlFlow::ExitNode } /** Holds if this node dominates `dominee` in the control-flow graph. */ overlay[caller?] pragma[inline] predicate dominatesNode(ControlFlow::Node dominee) { - exists(ReachableBasicBlock thisbb, ReachableBasicBlock dbb, int i, int j | + exists(GoCfg::Cfg::BasicBlock thisbb, GoCfg::Cfg::BasicBlock dbb, int i, int j | this = thisbb.getNode(i) and dominee = dbb.getNode(j) | thisbb.strictlyDominates(dbb) @@ -76,20 +75,12 @@ module ControlFlow { ) } - /** Gets the innermost function or file to which this node belongs. */ - Root getRoot() { none() } + /** Gets the innermost function to which this node belongs. */ + Root getRoot() { result = this.getEnclosingCallable() } /** Gets the file to which this node belongs. */ File getFile() { result = this.getLocation().getFile() } - /** - * Gets a textual representation of this control flow node. - */ - string toString() { result = "control-flow node" } - - /** Gets the source location for this element. */ - Location getLocation() { none() } - /** * DEPRECATED: Use `getLocation()` instead. * @@ -113,6 +104,22 @@ 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 { } + + private predicate isBranchConditionRoot(Expr expr) { + expr = any(LogicalBinaryExpr lbe).getLeftOperand() + or + expr = any(ForStmt fs).getCond() + or + expr = any(IfStmt is).getCond() + or + expr = any(ExpressionSwitchStmt ess | not exists(ess.getExpr())).getACase().getAnExpr() + } + /** * A control-flow node that initializes or updates the value of a constant, a variable, * a field, or an (array, slice, or map) element. @@ -172,7 +179,7 @@ module ControlFlow { exists(IR::FieldTarget trg | trg = super.getLhs() | ( trg.getBase() = base or - trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr()) + trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr()) ) and trg.getField() = f and super.getRhs() = rhs @@ -220,7 +227,7 @@ module ControlFlow { exists(IR::ElementTarget trg | trg = super.getLhs() | ( trg.getBase() = base or - trg.getBase() = MkImplicitDeref(base.(IR::EvalInstruction).getExpr()) + trg.getBase() = IR::implicitDerefInstruction(base.(IR::EvalInstruction).getExpr()) ) and trg.getIndex() = index and super.getRhs() = rhs @@ -250,11 +257,19 @@ 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 IR::Instruction { Expr cond; boolean outcome; - ConditionGuardNode() { this = MkConditionGuardNode(cond, outcome) } + ConditionGuardNode() { + isBranchConditionRoot(cond) and + this.isAfterTrue(cond) and + outcome = true + or + isBranchConditionRoot(cond) and + this.isAfterFalse(cond) and + outcome = false + } private predicate ensuresAux(Expr expr, boolean b) { expr = cond and b = outcome @@ -320,21 +335,17 @@ module ControlFlow { boolean getOutcome() { result = outcome } override Root getRoot() { result.isRootOf(cond) } - - override string toString() { result = cond + " is " + outcome } - - override Location getLocation() { result = cond.getLocation() } } /** - * Gets the entry node of function or file `root`. + * Gets the entry node of function `root`. */ - Node entryNode(Root root) { result = MkEntryNode(root) } + EntryNode entryNode(Root root) { result.getEnclosingCallable() = root } /** - * Gets the exit node of function or file `root`. + * Gets the exit node of function `root`. */ - Node exitNode(Root root) { result = MkExitNode(root) } + ExitNode exitNode(Root root) { result.getEnclosingCallable() = root } /** * Holds if the function `f` may return without panicking, exiting the process, or looping forever. @@ -342,7 +353,12 @@ 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 and + exists(exit.getAPredecessor()) + ) + } /** * Holds if `pred` is the node for the case `testExpr` in an expression @@ -352,10 +368,18 @@ 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 CfgScope = GoCfg::CfgScope; + class Write = ControlFlow::WriteNode; diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll deleted file mode 100644 index a26ab3adaf5f..000000000000 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ /dev/null @@ -1,2133 +0,0 @@ -/** - * INTERNAL: Analyses should use module `ControlFlowGraph` instead. - * - * Provides predicates for building intra-procedural CFGs. - */ -overlay[local] -module; - -import go - -/** A block statement that is not the body of a `switch` or `select` statement. */ -class PlainBlock extends BlockStmt { - PlainBlock() { - not this = any(SwitchStmt sw).getBody() and not this = any(SelectStmt sel).getBody() - } -} - -private predicate notBlankIdent(Expr e) { not e instanceof BlankIdent } - -private predicate pureLvalue(ReferenceExpr e) { not e.isRvalue() } - -/** - * Holds if `e` is a branch condition, including the LHS of a short-circuiting binary operator. - */ -private predicate isCondRoot(Expr e) { - e = any(LogicalBinaryExpr lbe).getLeftOperand() - or - e = any(ForStmt fs).getCond() - or - e = any(IfStmt is).getCond() - or - e = any(ExpressionSwitchStmt ess | not exists(ess.getExpr())).getACase().getAnExpr() -} - -/** - * Holds if `e` is a branch condition or part of a logical binary expression contributing to a - * branch condition. - * - * For example, in `v := (x && y) || (z && w)`, `x` and `(x && y)` and `z` are branch conditions - * (`isCondRoot` holds of them), whereas this predicate also holds of `y` (contributes to condition - * `x && y`) but not of `w` (contributes to the value `v`, but not to any branch condition). - * - * In the context `if (x && y) || (z && w)` then the whole `(x && y) || (z && w)` is a branch condition - * as well as `x` and `(x && y)` and `z` as previously, and this predicate holds of all their - * subexpressions. - */ -private predicate isCond(Expr e) { - isCondRoot(e) or - e = any(LogicalBinaryExpr lbe | isCond(lbe)).getRightOperand() or - e = any(ParenExpr par | isCond(par)).getExpr() -} - -/** - * Holds if `e` implicitly reads the embedded field `implicitField`. - * - * The `index` is the distance from the promoted field. For example, if `A` contains an embedded - * field `B`, `B` contains an embedded field `C` and `C` contains the non-embedded field `x`. - * Then `a.x` implicitly reads `C` with index 1 and `B` with index 2. - */ -private predicate implicitFieldSelectionForField(PromotedSelector e, int index, Field implicitField) { - exists(StructType baseType, PromotedField child, int implicitFieldDepth | - baseType = e.getSelectedStructType() and - ( - e.refersTo(child) - or - implicitFieldSelectionForField(e, implicitFieldDepth + 1, child) - ) - | - child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and - exists(PromotedField explicitField, int explicitFieldDepth | - e.refersTo(explicitField) and baseType.getFieldAtDepth(_, explicitFieldDepth) = explicitField - | - index = explicitFieldDepth - implicitFieldDepth - ) - ) -} - -private predicate implicitFieldSelectionForMethod(PromotedSelector e, int index, Field implicitField) { - exists(StructType baseType, PromotedMethod method, int mDepth, int implicitFieldDepth | - baseType = e.getSelectedStructType() and - e.refersTo(method) and - baseType.getMethodAtDepth(_, mDepth) = method and - index = mDepth - implicitFieldDepth - | - method = baseType.getMethodOfEmbedded(implicitField, _, implicitFieldDepth + 1) - or - exists(PromotedField child | - child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and - implicitFieldSelectionForMethod(e, implicitFieldDepth + 1, child) - ) - ) -} - -/** - * A node in the intra-procedural control-flow graph of a Go function or file. - * - * There are two kinds of control-flow nodes: - * - * 1. Instructions: these are nodes that correspond to expressions and statements - * that compute a value or perform an operation (as opposed to providing syntactic - * structure or type information). - * 2. Synthetic nodes: - * - Entry and exit nodes for each Go function and file that mark the beginning and the end, - * respectively, of the execution of the function and the loading of the file; - * - Skip nodes that are semantic no-ops, but make CFG construction easier. - */ -cached -newtype TControlFlowNode = - /** - * A control-flow node that represents the evaluation of an expression. - */ - MkExprNode(Expr e) { CFG::hasEvaluationNode(e) } or - /** - * A control-flow node that represents the initialization of an element of a composite literal. - */ - MkLiteralElementInitNode(Expr e) { e = any(CompositeLit lit).getAnElement() } or - /** - * A control-flow node that represents the implicit index of an element in a slice or array literal. - */ - MkImplicitLiteralElementIndex(Expr e) { - exists(CompositeLit lit | not lit instanceof StructLit | - e = lit.getAnElement() and - not e instanceof KeyValueExpr - ) - } or - /** - * A control-flow node that represents a (single) assignment. - * - * Assignments with multiple left-hand sides are split up into multiple assignment nodes, - * one for each left-hand side. Assignments to `_` are not represented in the control-flow graph. - */ - MkAssignNode(AstNode assgn, int i) { - // the `i`th assignment in a (possibly multi-)assignment - notBlankIdent(assgn.(Assignment).getLhs(i)) - or - // the `i`th name declared in a (possibly multi-)declaration specifier - notBlankIdent(assgn.(ValueSpec).getNameExpr(i)) - or - // the assignment to the "key" variable in a `range` statement - notBlankIdent(assgn.(RangeStmt).getKey()) and i = 0 - or - // the assignment to the "value" variable in a `range` statement - notBlankIdent(assgn.(RangeStmt).getValue()) and i = 1 - } or - /** - * A control-flow node that represents the implicit right-hand side of a compound assignment. - * - * For example, the compound assignment `x += 1` has an implicit right-hand side `x + 1`. - */ - MkCompoundAssignRhsNode(CompoundAssignStmt assgn) or - /** - * A control-flow node that represents the `i`th component of a tuple expression `s`. - */ - MkExtractNode(AstNode s, int i) { - // in an assignment `x, y, z = tuple` - exists(Assignment assgn | - s = assgn and - exists(assgn.getRhs()) and - assgn.getNumLhs() > 1 and - exists(assgn.getLhs(i)) - ) - or - // in a declaration `var x, y, z = tuple` - exists(ValueSpec spec | - s = spec and - exists(spec.getInit()) and - spec.getNumName() > 1 and - exists(spec.getNameExpr(i)) - ) - or - // in a `range` statement - exists(RangeStmt rs | s = rs | - exists(rs.getKey()) and i = 0 - or - exists(rs.getValue()) and i = 1 - ) - or - // in a return statement `return f()` where `f` has multiple return values - exists(ReturnStmt ret, SignatureType rettp | - s = ret and - // the return statement has a single expression - exists(ret.getExpr()) and - // but the enclosing function has multiple results - rettp = ret.getEnclosingFunction().getType() and - rettp.getNumResult() > 1 and - exists(rettp.getResultType(i)) - ) - or - // in a call `f(g())` where `g` has multiple return values - exists(CallExpr outer, CallExpr inner | s = outer | - inner = outer.getArgument(0).stripParens() and - outer.getNumArgument() = 1 and - exists(inner.getType().(TupleType).getComponentType(i)) - ) - } or - /** - * A control-flow node that represents the zero value to which a variable without an initializer - * expression is initialized. - */ - MkZeroInitNode(ValueEntity v) { - exists(ValueSpec spec | - not exists(spec.getAnInit()) and - spec.getNameExpr(_) = v.getDeclaration() - ) - or - exists(v.(ResultVariable).getFunction().getBody()) - } or - /** - * A control-flow node that represents a function declaration. - */ - MkFuncDeclNode(FuncDecl fd) or - /** - * A control-flow node that represents a `defer` statement. - */ - MkDeferNode(DeferStmt def) or - /** - * A control-flow node that represents a `go` statement. - */ - MkGoNode(GoStmt go) or - /** - * A control-flow node that represents the fact that `e` is known to evaluate to - * `outcome`. - */ - MkConditionGuardNode(Expr e, Boolean outcome) { isCondRoot(e) } or - /** - * A control-flow node that represents an increment or decrement statement. - */ - MkIncDecNode(IncDecStmt ids) or - /** - * A control-flow node that represents the implicit right-hand side of an increment or decrement statement. - */ - MkIncDecRhs(IncDecStmt ids) or - /** - * A control-flow node that represents the implicit operand 1 of an increment or decrement statement. - */ - MkImplicitOne(IncDecStmt ids) or - /** - * A control-flow node that represents a return from a function. - */ - MkReturnNode(ReturnStmt ret) or - /** - * A control-flow node that represents the implicit write to a named result variable in a return statement. - */ - MkResultWriteNode(ResultVariable var, int i, ReturnStmt ret) { - ret.getEnclosingFunction().getResultVar(i) = var and - exists(ret.getAnExpr()) - } or - /** - * A control-flow node that represents the implicit read of a named result variable upon returning from - * a function (after any deferred calls have been executed). - */ - MkResultReadNode(ResultVariable var) or - /** - * A control-flow node that represents a no-op. - * - * These control-flow nodes correspond to Go statements that have no runtime semantics other than potentially - * influencing control flow: the branching statements `continue`, `break`, `fallthrough` and `goto`; empty - * blocks; empty statements; and import and type declarations. - */ - MkSkipNode(AstNode skip) { - skip instanceof BranchStmt - or - skip instanceof EmptyStmt - or - skip.(PlainBlock).getNumStmt() = 0 - or - skip instanceof ImportDecl - or - skip instanceof TypeDecl - or - pureLvalue(skip) - or - skip.(CaseClause).getNumStmt() = 0 - or - skip.(CommClause).getNumStmt() = 0 - } or - /** - * A control-flow node that represents a `select` operation. - */ - MkSelectNode(SelectStmt sel) or - /** - * A control-flow node that represents a `send` operation. - */ - MkSendNode(SendStmt send) or - /** - * A control-flow node that represents the initialization of a parameter to its corresponding argument. - */ - MkParameterInit(Parameter parm) { exists(parm.getFunction().getBody()) } or - /** - * A control-flow node that represents the argument corresponding to a parameter. - */ - MkArgumentNode(Parameter parm) { exists(parm.getFunction().getBody()) } or - /** - * A control-flow node that represents the initialization of a result variable to its zero value. - */ - MkResultInit(ResultVariable rv) { exists(rv.getFunction().getBody()) } or - /** - * A control-flow node that represents the operation of retrieving the next (key, value) pair in a - * `range` statement, if any. - */ - MkNextNode(RangeStmt rs) or - /** - * A control-flow node that represents the implicit `true` expression in `switch { ... }`. - */ - MkImplicitTrue(ExpressionSwitchStmt stmt) { not exists(stmt.getExpr()) } or - /** - * A control-flow node that represents the implicit comparison or type check performed by - * the `i`th expression of a case clause `cc`. - */ - MkCaseCheckNode(CaseClause cc, int i) { exists(cc.getExpr(i)) } or - /** - * A control-flow node that represents the implicit declaration of the - * variable `lv` in case clause `cc` and its assignment of the value - * `switchExpr` from the guard. This only occurs in case clauses in a type - * switch statement which declares a variable in its guard. - */ - MkTypeSwitchImplicitVariable(CaseClause cc, LocalVariable lv, Expr switchExpr) { - exists(TypeSwitchStmt ts, DefineStmt ds | ds = ts.getAssign() | - cc = ts.getACase() and - lv = cc.getImplicitlyDeclaredVariable() and - switchExpr = ds.getRhs().(TypeAssertExpr).getExpr() - ) - } or - /** - * A control-flow node that represents the implicit lower bound of a slice expression. - */ - MkImplicitLowerSliceBound(SliceExpr sl) { not exists(sl.getLow()) } or - /** - * A control-flow node that represents the implicit upper bound of a simple slice expression. - */ - MkImplicitUpperSliceBound(SliceExpr sl) { not exists(sl.getHigh()) } or - /** - * A control-flow node that represents the implicit max bound of a simple slice expression. - */ - MkImplicitMaxSliceBound(SliceExpr sl) { not exists(sl.getMax()) } or - /** - * A control-flow node that represents the implicit dereference of the base in a field/method - * access, element access, or slice expression. - */ - MkImplicitDeref(Expr e) { - e.getType().getUnderlyingType() instanceof PointerType and - ( - exists(SelectorExpr sel | e = sel.getBase() | - // field accesses through a pointer always implicitly dereference - sel = any(Field f).getAReference() - or - // method accesses only dereference if the receiver is _not_ a pointer - exists(Method m, Type tp | - sel = m.getAReference() and - tp = m.getReceiver().getType().getUnderlyingType() and - not tp instanceof PointerType - ) - ) - or - e = any(IndexExpr ie).getBase() - or - e = any(SliceExpr se).getBase() - ) - } or - /** - * A control-flow node that represents the implicit selection of a field when - * accessing a promoted field. - * - * If that field has a pointer type then this control-flow node also - * represents an implicit dereference of it. - */ - MkImplicitFieldSelection(PromotedSelector e, int i, Field implicitField) { - implicitFieldSelectionForField(e, i, implicitField) or - implicitFieldSelectionForMethod(e, i, implicitField) - } or - /** - * A control-flow node that represents the start of the execution of a function or file. - */ - MkEntryNode(ControlFlow::Root root) or - /** - * A control-flow node that represents the end of the execution of a function or file. - */ - MkExitNode(ControlFlow::Root root) - -/** A representation of the target of a write. */ -newtype TWriteTarget = - /** A write target that is represented explicitly in the AST. */ - MkLhs(TControlFlowNode write, Expr lhs) { - exists(AstNode assgn, int i | write = MkAssignNode(assgn, i) | - lhs = assgn.(Assignment).getLhs(i).stripParens() - or - lhs = assgn.(ValueSpec).getNameExpr(i) - or - exists(RangeStmt rs | rs = assgn | - i = 0 and lhs = rs.getKey().stripParens() - or - i = 1 and lhs = rs.getValue().stripParens() - ) - ) - or - exists(IncDecStmt ids | write = MkIncDecNode(ids) | lhs = ids.getOperand().stripParens()) - or - exists(Parameter parm | write = MkParameterInit(parm) | lhs = parm.getDeclaration()) - or - exists(ResultVariable res | write = MkResultInit(res) | lhs = res.getDeclaration()) - } or - /** A write target for an element in a compound literal, viewed as a field write. */ - MkLiteralElementTarget(MkLiteralElementInitNode elt) or - /** A write target for a returned expression, viewed as a write to the corresponding result variable. */ - MkResultWriteTarget(MkResultWriteNode w) - -/** - * A control-flow node that represents a no-op. - * - * These control-flow nodes correspond to Go statements that have no runtime semantics other than - * potentially influencing control flow: the branching statements `continue`, `break`, - * `fallthrough` and `goto`; empty blocks; empty statements; and import and type declarations. - */ -class SkipNode extends ControlFlow::Node, MkSkipNode { - AstNode skip; - - SkipNode() { this = MkSkipNode(skip) } - - override ControlFlow::Root getRoot() { result.isRootOf(skip) } - - override string toString() { result = "skip" } - - override Location getLocation() { result = skip.getLocation() } -} - -/** - * A control-flow node that represents the start of the execution of a function or file. - */ -class EntryNode extends ControlFlow::Node, MkEntryNode { - ControlFlow::Root root; - - EntryNode() { this = MkEntryNode(root) } - - override ControlFlow::Root getRoot() { result = root } - - override string toString() { result = "entry" } - - override Location getLocation() { result = root.getLocation() } -} - -/** - * A control-flow node that represents the end of the execution of a function or file. - */ -class ExitNode extends ControlFlow::Node, MkExitNode { - ControlFlow::Root root; - - ExitNode() { this = MkExitNode(root) } - - override ControlFlow::Root getRoot() { result = root } - - override string toString() { result = "exit" } - - override Location getLocation() { result = root.getLocation() } -} - -/** - * Provides classes and predicates for computing the control-flow graph. - */ -cached -module CFG { - /** - * The target of a branch statement, which is either the label of a labeled statement or - * the special target `""` referring to the innermost enclosing loop or `switch`. - */ - private class BranchTarget extends string { - BranchTarget() { this = any(LabeledStmt ls).getLabel() or this = "" } - } - - private module BranchTarget { - /** Holds if this is the target of branch statement `stmt` or the label of compound statement `stmt`. */ - BranchTarget of(Stmt stmt) { - exists(BranchStmt bs | bs = stmt | - result = bs.getLabel() - or - not exists(bs.getLabel()) and result = "" - ) - or - exists(LabeledStmt ls | stmt = ls.getStmt() | result = ls.getLabel()) - or - (stmt instanceof LoopStmt or stmt instanceof SwitchStmt or stmt instanceof SelectStmt) and - result = "" - } - } - - private newtype TCompletion = - /** A completion indicating that an expression or statement was evaluated successfully. */ - Done() or - /** - * A completion indicating that an expression was successfully evaluated to Boolean value `b`. - * - * Note that many Boolean expressions are modeled as having completion `Done()` instead. - * Completion `Bool` is only used in contexts where the Boolean value can be determined. - */ - Bool(boolean b) { b = true or b = false } or - /** - * A completion indicating that execution of a (compound) statement ended with a `break` - * statement targeting the given label. - */ - Break(BranchTarget lbl) or - /** - * A completion indicating that execution of a (compound) statement ended with a `continue` - * statement targeting the given label. - */ - Continue(BranchTarget lbl) or - /** - * A completion indicating that execution of a (compound) statement ended with a `fallthrough` - * statement. - */ - Fallthrough() or - /** - * A completion indicating that execution of a (compound) statement ended with a `return` - * statement. - */ - Return() or - /** - * A completion indicating that execution of a statement or expression may have ended with - * a panic being raised. - */ - Panic() - - private Completion normalCompletion() { result.isNormal() } - - private class Completion extends TCompletion { - predicate isNormal() { this = Done() or this = Bool(_) } - - Boolean getOutcome() { this = Done() or this = Bool(result) } - - string toString() { - this = Done() and result = "normal" - or - exists(boolean b | this = Bool(b) | result = b.toString()) - or - exists(BranchTarget lbl | - this = Break(lbl) and result = "break " + lbl - or - this = Continue(lbl) and result = "continue " + lbl - ) - or - this = Fallthrough() and result = "fallthrough" - or - this = Return() and result = "return" - or - this = Panic() and result = "panic" - } - } - - /** - * Holds if `e` should have an evaluation node in the control-flow graph. - * - * Excluded expressions include those not evaluated at runtime (e.g. identifiers, type expressions) - * and some logical expressions that are expressed as control-flow edges rather than having a specific - * evaluation node. - */ - cached - predicate hasEvaluationNode(Expr e) { - // exclude expressions that do not denote a value - not e instanceof TypeExpr and - not e = any(FieldDecl f).getTag() and - not e instanceof KeyValueExpr and - not e = any(SelectorExpr sel).getSelector() and - not e = any(StructLit sl).getKey(_) and - not (e instanceof Ident and not e instanceof ReferenceExpr) and - not (e instanceof SelectorExpr and not e instanceof ReferenceExpr) and - not pureLvalue(e) and - // exclude parentheses, which are purely concrete syntax, and some logical binary expressions - // whose evaluation is implied by control-flow edges without requiring an evaluation node. - not isControlFlowStructural(e) and - // exclude expressions that are not evaluated at runtime - not e = any(ImportSpec is).getPathExpr() and - not e.getParent*() = any(ArrayTypeExpr ate).getLength() and - // sub-expressions of constant expressions are not evaluated (even if they don't look constant - // themselves) - not constRoot(e.getParent+()) - } - - /** - * Holds if `e` is an expression that purely serves grouping or control-flow purposes. - * - * Examples include parenthesized expressions and short-circuiting Boolean expressions used within - * a branch condition (`if` or `for` condition, or as part of a larger boolean expression, e.g. - * in `(x && y) || z`, the `&&` subexpression matches this predicate). - */ - private predicate isControlFlowStructural(Expr e) { - // Some logical binary operators do not need an evaluation node - // (for example, in `if x && y`, we evaluate `x` and then branch straight to either `y` or the - // `else` block, so there is no control-flow step where `x && y` is specifically calculated) - e instanceof LogicalBinaryExpr and - isCond(e) - or - // Purely concrete-syntactic structural expression: - e instanceof ParenExpr - } - - /** - * Gets a constant root, that is, an expression that is constant but whose parent expression is not. - * - * As an exception to the latter, for a control-flow structural expression such as `(c1)` or `c1 && c2` - * where `cn` are constants we still consider the `cn`s to be a constant roots, even though their parent - * expression is also constant. - */ - private predicate constRoot(Expr root) { - exists(Expr c | - c.isConst() and - not c.getParent().(Expr).isConst() and - root = stripStructural(c) - ) - } - - /** - * Strips off any control-flow structural components from `e`. - */ - private Expr stripStructural(Expr e) { - if isControlFlowStructural(e) then result = stripStructural(e.getAChildExpr()) else result = e - } - - private class ControlFlowTree extends AstNode { - predicate firstNode(ControlFlow::Node first) { none() } - - predicate lastNode(ControlFlow::Node last, Completion cmpl) { - // propagate abnormal completion from children - lastNode(this.getAChild(), last, cmpl) and - not cmpl.isNormal() - } - - /** - * Holds if `succ` is a successor of `pred`, ignoring the execution of any - * deferred functions when a function ends. - */ - pragma[nomagic] - predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - lastNode(this.getChildTreeRanked(i), pred, normalCompletion()) and - firstNode(this.getChildTreeRanked(i + 1), succ) - ) - } - - /** Holds if `succ` is a successor of `pred`. */ - predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { this.succ0(pred, succ) } - - final ControlFlowTree getChildTreeRanked(int i) { - exists(int j | - result = this.getChildTree(j) and - j = rank[i + 1](int k | exists(this.getChildTree(k))) - ) - } - - ControlFlowTree getFirstChildTree() { result = this.getChildTreeRanked(0) } - - ControlFlowTree getLastChildTree() { - result = max(ControlFlowTree ch, int j | ch = this.getChildTree(j) | ch order by j) - } - - ControlFlowTree getChildTree(int i) { none() } - } - - private class AtomicTree extends ControlFlowTree { - ControlFlow::Node nd; - Completion cmpl; - - AtomicTree() { - exists(Expr e | - e = this and - e.isConst() and - nd = mkExprOrSkipNode(this) - | - if e.isPlatformIndependentConstant() and exists(e.getBoolValue()) - then cmpl = Bool(e.getBoolValue()) - else cmpl = Done() - ) - or - this instanceof Ident and - not this.(Expr).isConst() and - nd = mkExprOrSkipNode(this) and - cmpl = Done() - or - this instanceof BreakStmt and - nd = MkSkipNode(this) and - cmpl = Break(BranchTarget::of(this)) - or - this instanceof ContinueStmt and - nd = MkSkipNode(this) and - cmpl = Continue(BranchTarget::of(this)) - or - this instanceof Decl and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof EmptyStmt and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof FallthroughStmt and - nd = MkSkipNode(this) and - cmpl = Fallthrough() - or - this instanceof FuncLit and - nd = MkExprNode(this) and - cmpl = Done() - or - this instanceof PlainBlock and - nd = MkSkipNode(this) and - cmpl = Done() - or - this instanceof SelectorExpr and - not this.(SelectorExpr).getBase() instanceof ValueExpr and - nd = mkExprOrSkipNode(this) and - cmpl = Done() - or - this instanceof GenericFunctionInstantiationExpr and - nd = MkExprNode(this) and - cmpl = Done() - } - - override predicate firstNode(ControlFlow::Node first) { first = nd } - - override predicate lastNode(ControlFlow::Node last, Completion c) { last = nd and c = cmpl } - } - - abstract private class PostOrderTree extends ControlFlowTree { - abstract ControlFlow::Node getNode(); - - Completion getCompletion() { result = Done() } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - or - not exists(this.getChildTree(_)) and - first = this.getNode() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - last = this.getNode() and cmpl = this.getCompletion() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - super.succ0(pred, succ) - or - lastNode(this.getLastChildTree(), pred, normalCompletion()) and - succ = this.getNode() - } - } - - abstract private class PreOrderTree extends ControlFlowTree { - abstract ControlFlow::Node getNode(); - - override predicate firstNode(ControlFlow::Node first) { first = this.getNode() } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - lastNode(this.getLastChildTree(), last, cmpl) - or - not exists(this.getChildTree(_)) and - last = this.getNode() and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - super.succ0(pred, succ) - or - pred = this.getNode() and - firstNode(this.getFirstChildTree(), succ) - } - } - - private class WrapperTree extends ControlFlowTree { - WrapperTree() { - this instanceof ConstDecl or - this instanceof DeclStmt or - this instanceof ExprStmt or - this instanceof KeyValueExpr or - this instanceof LabeledStmt or - this instanceof ParenExpr or - this instanceof PlainBlock or - this instanceof VarDecl - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - super.lastNode(last, cmpl) - or - lastNode(this.getLastChildTree(), last, cmpl) - or - exists(LoopStmt ls | this = ls.getBody() | - lastNode(this, last, Continue(BranchTarget::of(ls))) and - cmpl = Done() - ) - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.(DeclStmt).getDecl() - or - i = 0 and result = this.(ExprStmt).getExpr() - or - result = this.(GenDecl).getSpec(i) - or - exists(KeyValueExpr kv | kv = this | - not kv.getLiteral() instanceof StructLit and - i = 0 and - result = kv.getKey() - or - i = 1 and result = kv.getValue() - ) - or - i = 0 and result = this.(LabeledStmt).getStmt() - or - i = 0 and result = this.(ParenExpr).getExpr() - or - result = this.(PlainBlock).getStmt(i) - } - } - - private class AssignmentTree extends ControlFlowTree { - AssignmentTree() { - this instanceof Assignment or - this instanceof ValueSpec - } - - Expr getLhs(int i) { - result = this.(Assignment).getLhs(i) or - result = this.(ValueSpec).getNameExpr(i) - } - - int getNumLhs() { - result = this.(Assignment).getNumLhs() or - result = this.(ValueSpec).getNumName() - } - - Expr getRhs(int i) { - result = this.(Assignment).getRhs(i) or - result = this.(ValueSpec).getInit(i) - } - - int getNumRhs() { - result = this.(Assignment).getNumRhs() or - result = this.(ValueSpec).getNumInit() - } - - predicate isExtractingAssign() { this.getNumRhs() = 1 and this.getNumLhs() > 1 } - - override predicate firstNode(ControlFlow::Node first) { - not this instanceof RecvStmt and - firstNode(this.getLhs(0), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - ( - last = max(int i | | this.epilogueNode(i) order by i) - or - not exists(this.epilogueNode(_)) and - lastNode(this.getLastSubExprInEvalOrder(), last, normalCompletion()) - ) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - exists(int i | lastNode(this.getLhs(i), pred, normalCompletion()) | - firstNode(this.getLhs(i + 1), succ) - or - not this instanceof RecvStmt and - i = this.getNumLhs() - 1 and - ( - firstNode(this.getRhs(0), succ) - or - not exists(this.getRhs(_)) and - succ = this.epilogueNodeRanked(0) - ) - ) - or - exists(int i | - lastNode(this.getRhs(i), pred, normalCompletion()) and - firstNode(this.getRhs(i + 1), succ) - ) - or - not this instanceof RecvStmt and - lastNode(this.getRhs(this.getNumRhs() - 1), pred, normalCompletion()) and - succ = this.epilogueNodeRanked(0) - or - exists(int i | - pred = this.epilogueNodeRanked(i) and - succ = this.epilogueNodeRanked(i + 1) - ) - } - - ControlFlow::Node epilogueNodeRanked(int i) { - exists(int j | - result = this.epilogueNode(j) and - j = rank[i + 1](int k | exists(this.epilogueNode(k))) - ) - } - - private Expr getSubExprInEvalOrder(int evalOrder) { - if evalOrder < this.getNumLhs() - then result = this.getLhs(evalOrder) - else result = this.getRhs(evalOrder - this.getNumLhs()) - } - - private Expr getLastSubExprInEvalOrder() { - result = max(int i | | this.getSubExprInEvalOrder(i) order by i) - } - - private ControlFlow::Node epilogueNode(int i) { - i = -1 and - result = MkCompoundAssignRhsNode(this) - or - exists(int j | - result = MkExtractNode(this, j) and - i = 2 * j - or - result = MkZeroInitNode(any(ValueEntity v | this.getLhs(j) = v.getDeclaration())) and - i = 2 * j - or - result = MkAssignNode(this, j) and - i = 2 * j + 1 - ) - } - } - - private class BinaryExprTree extends PostOrderTree, BinaryExpr { - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - private predicate equalityTestMayPanic() { - this instanceof EqualityTestExpr and - exists(Type t | - t = this.getAnOperand().getType().getUnderlyingType() and - ( - t instanceof InterfaceType or // panic due to comparison of incomparable interface values - t instanceof StructType or // may contain an interface-typed field - t instanceof ArrayType // may be an array of interface values - ) - ) - } - - override Completion getCompletion() { - result = PostOrderTree.super.getCompletion() - or - // runtime panic due to division by zero or comparison of incomparable interface values - (this instanceof DivExpr or this.equalityTestMayPanic()) and - not this.(Expr).isConst() and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getLeftOperand() - or - i = 1 and result = this.getRightOperand() - } - } - - private class LogicalBinaryExprTree extends BinaryExprTree, LogicalBinaryExpr { - boolean shortCircuit; - - LogicalBinaryExprTree() { - this instanceof LandExpr and shortCircuit = false - or - this instanceof LorExpr and shortCircuit = true - } - - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getLeftOperand(), outcome) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - lastNode(this.getAnOperand(), last, cmpl) and - not cmpl.isNormal() - or - if isCond(this) - then ( - last = this.getGuard(shortCircuit) and - cmpl = Bool(shortCircuit) - or - lastNode(this.getRightOperand(), last, cmpl) - ) else ( - last = MkExprNode(this) and - cmpl = Done() - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(Completion lcmpl | - lastNode(this.getLeftOperand(), pred, lcmpl) and - succ = this.getGuard(lcmpl.getOutcome()) - ) - or - pred = this.getGuard(shortCircuit.booleanNot()) and - firstNode(this.getRightOperand(), succ) - or - not isCond(this) and - ( - pred = this.getGuard(shortCircuit) and - succ = MkExprNode(this) - or - exists(Completion rcmpl | - lastNode(this.getRightOperand(), pred, rcmpl) and - rcmpl.isNormal() and - succ = MkExprNode(this) - ) - ) - } - } - - private class CallExprTree extends PostOrderTree, CallExpr { - private predicate isSpecial() { - this = any(DeferStmt defer).getCall() or - this = any(GoStmt go).getCall() - } - - override ControlFlow::Node getNode() { - not this.isSpecial() and - result = MkExprNode(this) - } - - override Completion getCompletion() { - (not exists(this.getTarget()) or this.getTarget().mayReturnNormally()) and - result = Done() - or - (not exists(this.getTarget()) or this.getTarget().mayPanic()) and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getCalleeExpr() - or - result = this.getArgument(i - 1) and - // calls to `make` and `new` can have type expressions as arguments - not result instanceof TypeExpr - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - // interpose implicit argument destructuring nodes between last argument - // and call itself; this is for cases like `f(g())` where `g` has multiple - // results - exists(ControlFlow::Node mid | PostOrderTree.super.succ0(pred, mid) | - if mid = this.getNode() then succ = this.getEpilogueNode(0) else succ = mid - ) - or - exists(int i | - pred = this.getEpilogueNode(i) and - succ = this.getEpilogueNode(i + 1) - ) - } - - private ControlFlow::Node getEpilogueNode(int i) { - result = MkExtractNode(this, i) - or - i = max(int j | exists(MkExtractNode(this, j))) + 1 and - result = this.getNode() - or - not exists(MkExtractNode(this, _)) and - i = 0 and - result = this.getNode() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - PostOrderTree.super.lastNode(last, cmpl) - or - this.isSpecial() and - lastNode(this.getLastChildTree(), last, cmpl) - } - } - - private class CaseClauseTree extends ControlFlowTree, CaseClause { - private ControlFlow::Node getExprStart(int i) { - firstNode(this.getExpr(i), result) - or - this.getExpr(i) instanceof TypeExpr and - result = MkCaseCheckNode(this, i) - } - - ControlFlow::Node getExprEnd(int i, Boolean outcome) { - exists(Expr e | e = this.getExpr(i) | - result = MkConditionGuardNode(e, outcome) - or - not exists(MkConditionGuardNode(e, _)) and - result = MkCaseCheckNode(this, i) - ) - } - - private ControlFlow::Node getBodyStart() { - firstNode(this.getStmt(0), result) or result = MkSkipNode(this) - } - - override predicate firstNode(ControlFlow::Node first) { - first = this.getExprStart(0) - or - not exists(this.getAnExpr()) and - first = MkTypeSwitchImplicitVariable(this, _, _) - or - not exists(this.getAnExpr()) and - not exists(MkTypeSwitchImplicitVariable(this, _, _)) and - first = this.getBodyStart() - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // TODO: shouldn't be here - last = this.getExprEnd(this.getNumExpr() - 1, false) and - cmpl = Bool(false) - or - last = MkSkipNode(this) and - cmpl = Done() - or - lastNode(this.getStmt(this.getNumStmt() - 1), last, cmpl) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - pred = MkTypeSwitchImplicitVariable(this, _, _) and - succ = this.getBodyStart() - or - exists(int i | - lastNode(this.getExpr(i), pred, normalCompletion()) and - succ = MkCaseCheckNode(this, i) - or - // visit guard node if there is one - pred = MkCaseCheckNode(this, i) and - succ = this.getExprEnd(i, _) and - succ != pred // this avoids self-loops if there isn't a guard node - or - pred = this.getExprEnd(i, false) and - succ = this.getExprStart(i + 1) - or - this.isPassingEdge(i, pred, succ, _) - ) - } - - predicate isPassingEdge(int i, ControlFlow::Node pred, ControlFlow::Node succ, Expr testExpr) { - pred = this.getExprEnd(i, true) and - testExpr = this.getExpr(i) and - ( - succ = MkTypeSwitchImplicitVariable(this, _, _) - or - not exists(MkTypeSwitchImplicitVariable(this, _, _)) and - succ = this.getBodyStart() - ) - } - - override ControlFlowTree getChildTree(int i) { result = this.getStmt(i) } - } - - private class CommClauseTree extends ControlFlowTree, CommClause { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getComm(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkSkipNode(this) and - cmpl = Done() - or - lastNode(this.getStmt(this.getNumStmt() - 1), last, cmpl) - } - - override ControlFlowTree getChildTree(int i) { result = this.getStmt(i) } - } - - private class CompositeLiteralTree extends ControlFlowTree, CompositeLit { - private ControlFlow::Node getElementInit(int i) { - result = MkLiteralElementInitNode(this.getElement(i)) - } - - private ControlFlow::Node getElementStart(int i) { - exists(Expr elt | elt = this.getElement(i) | - result = MkImplicitLiteralElementIndex(elt) - or - (elt instanceof KeyValueExpr or this instanceof StructLit) and - firstNode(this.getElement(i), result) - ) - } - - override predicate firstNode(ControlFlow::Node first) { first = MkExprNode(this) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = this.getElementInit(this.getNumElement() - 1) and - cmpl = Done() - or - not exists(this.getElement(_)) and - last = MkExprNode(this) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - this.firstNode(pred) and - succ = this.getElementStart(0) - or - exists(int i | - pred = MkImplicitLiteralElementIndex(this.getElement(i)) and - firstNode(this.getElement(i), succ) - or - lastNode(this.getElement(i), pred, normalCompletion()) and - succ = this.getElementInit(i) - or - pred = this.getElementInit(i) and - succ = this.getElementStart(i + 1) - ) - } - } - - private class ConversionExprTree extends PostOrderTree, ConversionExpr { - override Completion getCompletion() { - // conversions of a slice to an array pointer are the only kind that may panic - this.getType().(PointerType).getBaseType() instanceof ArrayType and - result = Panic() - or - result = Done() - } - - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getOperand() } - } - - private class DeferStmtTree extends PostOrderTree, DeferStmt { - override ControlFlow::Node getNode() { result = MkDeferNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getCall() } - } - - private class FuncDeclTree extends PostOrderTree, FuncDecl { - override ControlFlow::Node getNode() { result = MkFuncDeclNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getNameExpr() } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - // override to prevent panic propagation out of function declarations - last = this.getNode() and cmpl = Done() - } - } - - private class GoStmtTree extends PostOrderTree, GoStmt { - override ControlFlow::Node getNode() { result = MkGoNode(this) } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getCall() } - } - - private class IfStmtTree extends ControlFlowTree, IfStmt { - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getCond(), outcome) - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getInit(), first) - or - not exists(this.getInit()) and - firstNode(this.getCond(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - lastNode(this.getThen(), last, cmpl) - or - lastNode(this.getElse(), last, cmpl) - or - not exists(this.getElse()) and - last = this.getGuard(false) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getInit(), pred, normalCompletion()) and - firstNode(this.getCond(), succ) - or - exists(Completion condCmpl | - lastNode(this.getCond(), pred, condCmpl) and - succ = MkConditionGuardNode(this.getCond(), condCmpl.getOutcome()) - ) - or - pred = this.getGuard(true) and - firstNode(this.getThen(), succ) - or - pred = this.getGuard(false) and - firstNode(this.getElse(), succ) - } - } - - private class IndexExprTree extends ControlFlowTree, IndexExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = mkExprOrSkipNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getBase(), pred, normalCompletion()) and - ( - succ = MkImplicitDeref(this.getBase()) - or - not exists(MkImplicitDeref(this.getBase())) and - firstNode(this.getIndex(), succ) - ) - or - pred = MkImplicitDeref(this.getBase()) and - firstNode(this.getIndex(), succ) - or - lastNode(this.getIndex(), pred, normalCompletion()) and - succ = mkExprOrSkipNode(this) - } - } - - private class LoopTree extends ControlFlowTree, LoopStmt { - BranchTarget getLabel() { result = BranchTarget::of(this) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - exists(Completion inner | lastNode(this.getBody(), last, inner) and not inner.isNormal() | - if inner = Break(this.getLabel()) - then cmpl = Done() - else ( - not inner = Continue(this.getLabel()) and - cmpl = inner - ) - ) - } - } - - private class FileTree extends ControlFlowTree, File { - FileTree() { exists(this.getADecl()) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { none() } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - pred = MkEntryNode(this) and - firstNode(this.getDecl(0), succ) - or - exists(int i, Completion inner | lastNode(this.getDecl(i), pred, inner) | - not inner.isNormal() - or - i = this.getNumDecl() - 1 - ) and - succ = MkExitNode(this) - } - - override ControlFlowTree getChildTree(int i) { result = this.getDecl(i) } - } - - private class ForTree extends LoopTree, ForStmt { - private ControlFlow::Node getGuard(boolean outcome) { - result = MkConditionGuardNode(this.getCond(), outcome) - } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getFirstChildTree(), first) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - LoopTree.super.lastNode(last, cmpl) - or - lastNode(this.getInit(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getCond(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getPost(), last, cmpl) and - not cmpl.isNormal() - or - last = this.getGuard(false) and - cmpl = Done() - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getInit() - or - i = 1 and result = this.getCond() - or - i = 2 and result = this.getBody() - or - i = 3 and result = this.getPost() - or - i = 4 and result = this.getCond() - or - i = 5 and result = this.getBody() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i, ControlFlowTree predTree, Completion cmpl | - predTree = this.getChildTreeRanked(i) and - lastNode(predTree, pred, cmpl) and - cmpl.isNormal() - | - if predTree = this.getCond() - then succ = this.getGuard(cmpl.getOutcome()) - else firstNode(this.getChildTreeRanked(i + 1), succ) - ) - or - pred = this.getGuard(true) and - firstNode(this.getBody(), succ) - } - } - - private class FuncDefTree extends ControlFlowTree, FuncDef { - FuncDefTree() { exists(this.getBody()) } - - pragma[noinline] - private MkEntryNode getEntry() { result = MkEntryNode(this) } - - private Parameter getParameterRanked(int i) { - result = rank[i + 1](Parameter p, int j | p = this.getParameter(j) | p order by j) - } - - private ControlFlow::Node getPrologueNode(int i) { - i = -1 and result = this.getEntry() - or - exists(int numParm, int numRes | - numParm = count(this.getParameter(_)) and - numRes = count(this.getResultVar(_)) - | - exists(int j, Parameter p | p = this.getParameterRanked(j) | - i = 2 * j and result = MkArgumentNode(p) - or - i = 2 * j + 1 and result = MkParameterInit(p) - ) - or - exists(int j, ResultVariable v | v = this.getResultVar(j) | - i = 2 * numParm + 2 * j and - result = MkZeroInitNode(v) - or - i = 2 * numParm + 2 * j + 1 and - result = MkResultInit(v) - ) - or - i = 2 * numParm + 2 * numRes and - firstNode(this.getBody(), result) - ) - } - - private ControlFlow::Node getEpilogueNode(int i) { - result = MkResultReadNode(this.getResultVar(i)) - or - i = count(this.getAResultVar()) and - result = MkExitNode(this) - } - - pragma[noinline] - private predicate firstDefer(ControlFlow::Node nd) { - exists(DeferStmt defer | - nd = MkExprNode(defer.getCall()) and - // `defer` can be the first `defer` statement executed - // there is always a predecessor node because the `defer`'s call is always - // evaluated before the defer statement itself - MkDeferNode(defer) = succ0(notDeferSucc0*(this.getEntry())) - ) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { none() } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - pred = this.getPrologueNode(i) and - succ = this.getPrologueNode(i + 1) - ) - or - exists(GotoStmt goto, LabeledStmt ls | - pred = MkSkipNode(goto) and - this = goto.getEnclosingFunction() and - this = ls.getEnclosingFunction() and - goto.getLabel() = ls.getLabel() and - firstNode(ls, succ) - ) - or - exists(int i | - pred = this.getEpilogueNode(i) and - succ = this.getEpilogueNode(i + 1) - ) - } - - override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - this.succ0(pred, succ) - or - exists(Completion cmpl | - lastNode(this.getBody(), pred, cmpl) and - // last node of function body can be reached without going through a `defer` statement - pred = notDeferSucc0*(this.getEntry()) - | - // panic goes directly to exit, non-panic reads result variables first - if cmpl = Panic() then succ = MkExitNode(this) else succ = this.getEpilogueNode(0) - ) - or - lastNode(this.getBody(), pred, _) and - exists(DeferStmt defer | defer = this.getADeferStmt() | - succ = MkExprNode(defer.getCall()) and - // the last `DeferStmt` executed before pred is this `defer` - pred = notDeferSucc0*(MkDeferNode(defer)) - ) - or - exists(DeferStmt predDefer, DeferStmt succDefer | - predDefer = this.getADeferStmt() and - succDefer = this.getADeferStmt() - | - // reversed because `defer`s are executed in LIFO order - MkDeferNode(predDefer) = nextDefer(MkDeferNode(succDefer)) and - pred = MkExprNode(predDefer.getCall()) and - succ = MkExprNode(succDefer.getCall()) - ) - or - this.firstDefer(pred) and - ( - // conservatively assume that we might either panic (and hence skip the result reads) - // or not - succ = MkExitNode(this) - or - succ = this.getEpilogueNode(0) - ) - } - } - - private class GotoTree extends ControlFlowTree, GotoStmt { - override predicate firstNode(ControlFlow::Node first) { first = MkSkipNode(this) } - } - - private class IncDecTree extends ControlFlowTree, IncDecStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getOperand(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkIncDecNode(this) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getOperand(), pred, normalCompletion()) and - succ = MkImplicitOne(this) - or - pred = MkImplicitOne(this) and - succ = MkIncDecRhs(this) - or - pred = MkIncDecRhs(this) and - succ = MkIncDecNode(this) - } - } - - private class RangeTree extends LoopTree, RangeStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getDomain(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - LoopTree.super.lastNode(last, cmpl) - or - last = MkNextNode(this) and - cmpl = Done() - or - lastNode(this.getKey(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getValue(), last, cmpl) and - not cmpl.isNormal() - or - lastNode(this.getDomain(), last, cmpl) and - not cmpl.isNormal() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(this.getDomain(), pred, normalCompletion()) and - succ = MkNextNode(this) - or - pred = MkNextNode(this) and - ( - firstNode(this.getKey(), succ) - or - not exists(this.getKey()) and - firstNode(this.getBody(), succ) - ) - or - lastNode(this.getKey(), pred, normalCompletion()) and - ( - firstNode(this.getValue(), succ) - or - not exists(this.getValue()) and - succ = MkExtractNode(this, 0) - ) - or - lastNode(this.getValue(), pred, normalCompletion()) and - succ = MkExtractNode(this, 0) - or - pred = MkExtractNode(this, 0) and - ( - if exists(this.getValue()) - then succ = MkExtractNode(this, 1) - else - if exists(MkAssignNode(this, 0)) - then succ = MkAssignNode(this, 0) - else - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkExtractNode(this, 1) and - ( - if exists(MkAssignNode(this, 0)) - then succ = MkAssignNode(this, 0) - else - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkAssignNode(this, 0) and - ( - if exists(MkAssignNode(this, 1)) - then succ = MkAssignNode(this, 1) - else firstNode(this.getBody(), succ) - ) - or - pred = MkAssignNode(this, 1) and - firstNode(this.getBody(), succ) - or - exists(Completion inner | - lastNode(this.getBody(), pred, inner) and - (inner.isNormal() or inner = Continue(BranchTarget::of(this))) and - succ = MkNextNode(this) - ) - } - } - - private class RecvStmtTree extends ControlFlowTree, RecvStmt { - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getExpr().getOperand(), first) - } - } - - private class ReturnStmtTree extends PostOrderTree, ReturnStmt { - override ControlFlow::Node getNode() { result = MkReturnNode(this) } - - override Completion getCompletion() { result = Return() } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | - lastNode(this.getExpr(i), pred, normalCompletion()) and - succ = this.complete(i) - or - pred = MkExtractNode(this, i) and - succ = this.after(i) - or - pred = MkResultWriteNode(_, i, this) and - succ = this.next(i) - ) - } - - private ControlFlow::Node complete(int i) { - result = MkExtractNode(this, i) - or - not exists(MkExtractNode(this, _)) and - result = this.after(i) - } - - private ControlFlow::Node after(int i) { - result = MkResultWriteNode(_, i, this) - or - not exists(MkResultWriteNode(_, i, this)) and - result = this.next(i) - } - - private ControlFlow::Node next(int i) { - firstNode(this.getExpr(i + 1), result) - or - exists(MkExtractNode(this, _)) and - result = this.complete(i + 1) - or - i + 1 = this.getEnclosingFunction().getType().getNumResult() and - result = this.getNode() - } - - override ControlFlowTree getChildTree(int i) { result = this.getExpr(i) } - } - - private class SelectStmtTree extends ControlFlowTree, SelectStmt { - private BranchTarget getLabel() { result = BranchTarget::of(this) } - - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getNonDefaultCommClause(0), first) - or - this.getNumNonDefaultCommClause() = 0 and - first = MkSelectNode(this) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - exists(Completion inner | lastNode(this.getACommClause(), last, inner) | - if inner = Break(this.getLabel()) then cmpl = Done() else cmpl = inner - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - exists(CommClause cc, int i, Stmt comm | - cc = this.getNonDefaultCommClause(i) and - comm = cc.getComm() and - ( - comm instanceof RecvStmt and - lastNode(comm.(RecvStmt).getExpr().getOperand(), pred, normalCompletion()) - or - comm instanceof SendStmt and - lastNode(comm.(SendStmt).getValue(), pred, normalCompletion()) - ) - | - firstNode(this.getNonDefaultCommClause(i + 1), succ) - or - i = this.getNumNonDefaultCommClause() - 1 and - succ = MkSelectNode(this) - ) - or - pred = MkSelectNode(this) and - exists(CommClause cc, Stmt comm | - cc = this.getNonDefaultCommClause(_) and comm = cc.getComm() - | - comm instanceof RecvStmt and - succ = MkExprNode(comm.(RecvStmt).getExpr()) - or - comm instanceof SendStmt and - succ = MkSendNode(comm) - ) - or - pred = MkSelectNode(this) and - exists(CommClause cc | cc = this.getDefaultCommClause() | - firstNode(cc.getStmt(0), succ) - or - succ = MkSkipNode(cc) - ) - or - exists(CommClause cc, RecvStmt recv | cc = this.getCommClause(_) and recv = cc.getComm() | - pred = MkExprNode(recv.getExpr()) and - ( - firstNode(recv.getLhs(0), succ) - or - not exists(recv.getLhs(0)) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - lastNode(recv.getLhs(0), pred, normalCompletion()) and - not exists(recv.getLhs(1)) and - ( - succ = MkAssignNode(recv, 0) - or - not exists(MkAssignNode(recv, 0)) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - lastNode(recv.getLhs(1), pred, normalCompletion()) and - succ = MkExtractNode(recv, 0) - or - ( - pred = MkAssignNode(recv, 0) and - not exists(MkExtractNode(recv, 1)) - or - pred = MkExtractNode(recv, 1) and - not exists(MkAssignNode(recv, 1)) - or - pred = MkAssignNode(recv, 1) - ) and - (firstNode(cc.getStmt(0), succ) or succ = MkSkipNode(cc)) - ) - or - exists(CommClause cc, SendStmt ss | - cc = this.getCommClause(_) and - ss = cc.getComm() and - pred = MkSendNode(ss) - | - firstNode(cc.getStmt(0), succ) - or - succ = MkSkipNode(cc) - ) - } - } - - private class SelectorExprTree extends ControlFlowTree, SelectorExpr { - SelectorExprTree() { this.getBase() instanceof ValueExpr } - - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = mkExprOrSkipNode(this) and - cmpl = Done() - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - exists(int i | pred = this.getStepWithRank(i) and succ = this.getStepWithRank(i + 1)) - } - - private ControlFlow::Node getStepOrdered(int i) { - i = -2 and lastNode(this.getBase(), result, normalCompletion()) - or - i = -1 and result = MkImplicitDeref(this.getBase()) - or - exists(int maxIndex | - maxIndex = max(int k | k = 0 or exists(MkImplicitFieldSelection(this, k, _))) - | - result = MkImplicitFieldSelection(this, maxIndex - i, _) - or - i = maxIndex and - result = mkExprOrSkipNode(this) - ) - } - - private ControlFlow::Node getStepWithRank(int i) { - exists(int j | - result = this.getStepOrdered(j) and - j = rank[i + 1](int k | exists(this.getStepOrdered(k))) - ) - } - } - - private class SendStmtTree extends ControlFlowTree, SendStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getChannel(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - last = MkSendNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - not this = any(CommClause cc).getComm() and - lastNode(this.getValue(), pred, normalCompletion()) and - succ = MkSendNode(this) - } - - override ControlFlowTree getChildTree(int i) { - i = 0 and result = this.getChannel() - or - i = 1 and result = this.getValue() - } - } - - private class SliceExprTree extends ControlFlowTree, SliceExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getBase(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - ControlFlowTree.super.lastNode(last, cmpl) - or - // panic due to `nil` dereference - last = MkImplicitDeref(this.getBase()) and - cmpl = Panic() - or - last = MkExprNode(this) and - (cmpl = Done() or cmpl = Panic()) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - lastNode(this.getBase(), pred, normalCompletion()) and - ( - succ = MkImplicitDeref(this.getBase()) - or - not exists(MkImplicitDeref(this.getBase())) and - (firstNode(this.getLow(), succ) or succ = MkImplicitLowerSliceBound(this)) - ) - or - pred = MkImplicitDeref(this.getBase()) and - (firstNode(this.getLow(), succ) or succ = MkImplicitLowerSliceBound(this)) - or - (lastNode(this.getLow(), pred, normalCompletion()) or pred = MkImplicitLowerSliceBound(this)) and - (firstNode(this.getHigh(), succ) or succ = MkImplicitUpperSliceBound(this)) - or - (lastNode(this.getHigh(), pred, normalCompletion()) or pred = MkImplicitUpperSliceBound(this)) and - (firstNode(this.getMax(), succ) or succ = MkImplicitMaxSliceBound(this)) - or - (lastNode(this.getMax(), pred, normalCompletion()) or pred = MkImplicitMaxSliceBound(this)) and - succ = MkExprNode(this) - } - } - - private class StarExprTree extends PostOrderTree, StarExpr { - override ControlFlow::Node getNode() { result = mkExprOrSkipNode(this) } - - override Completion getCompletion() { result = Done() or result = Panic() } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getBase() } - } - - private class SwitchTree extends ControlFlowTree, SwitchStmt { - override predicate firstNode(ControlFlow::Node first) { - firstNode(this.getInit(), first) - or - not exists(this.getInit()) and - ( - firstNode(this.(ExpressionSwitchStmt).getExpr(), first) - or - first = MkImplicitTrue(this) - or - firstNode(this.(TypeSwitchStmt).getTest(), first) - ) - } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - lastNode(this.getInit(), last, cmpl) and - not cmpl.isNormal() - or - ( - lastNode(this.(ExpressionSwitchStmt).getExpr(), last, cmpl) - or - lastNode(this.(TypeSwitchStmt).getTest(), last, cmpl) - ) and - ( - not cmpl.isNormal() - or - not exists(this.getDefault()) - ) - or - last = MkImplicitTrue(this) and - cmpl = Bool(true) and - this.getNumCase() = 0 - or - exists(CaseClause cc, int i, Completion inner | - cc = this.getCase(i) and lastNode(cc, last, inner) - | - not exists(this.getDefault()) and - i = this.getNumCase() - 1 and - last = cc.(CaseClauseTree).getExprEnd(cc.getNumExpr() - 1, false) and - inner.isNormal() and - cmpl = inner - or - not last = cc.(CaseClauseTree).getExprEnd(_, _) and - inner.isNormal() and - cmpl = inner - or - if inner = Break(BranchTarget::of(this)) - then cmpl = Done() - else ( - not inner.isNormal() and inner != Fallthrough() and cmpl = inner - ) - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - lastNode(this.getInit(), pred, normalCompletion()) and - ( - firstNode(this.(ExpressionSwitchStmt).getExpr(), succ) or - succ = MkImplicitTrue(this) or - firstNode(this.(TypeSwitchStmt).getTest(), succ) - ) - or - ( - lastNode(this.(ExpressionSwitchStmt).getExpr(), pred, normalCompletion()) or - pred = MkImplicitTrue(this) or - lastNode(this.(TypeSwitchStmt).getTest(), pred, normalCompletion()) - ) and - ( - firstNode(this.getNonDefaultCase(0), succ) - or - not exists(this.getANonDefaultCase()) and - firstNode(this.getDefault(), succ) - ) - or - exists(CaseClause cc, int i | - cc = this.getNonDefaultCase(i) and - lastNode(cc, pred, normalCompletion()) and - pred = cc.(CaseClauseTree).getExprEnd(_, false) - | - firstNode(this.getNonDefaultCase(i + 1), succ) - or - i = this.getNumNonDefaultCase() - 1 and - firstNode(this.getDefault(), succ) - ) - or - exists(CaseClause cc, int i, CaseClause next | - cc = this.getCase(i) and - lastNode(cc, pred, Fallthrough()) and - next = this.getCase(i + 1) - | - firstNode(next.getStmt(0), succ) - or - succ = MkSkipNode(next) - ) - } - } - - private class TypeAssertTree extends PostOrderTree, TypeAssertExpr { - override ControlFlow::Node getNode() { result = MkExprNode(this) } - - override Completion getCompletion() { - result = Done() - or - // panic due to type mismatch, but not if the assertion appears in an assignment or - // initialization with two variables or a type-switch - not exists(Assignment assgn | assgn.getNumLhs() = 2 and this = assgn.getRhs().stripParens()) and - not exists(ValueSpec vs | vs.getNumName() = 2 and this = vs.getInit().stripParens()) and - not exists(TypeSwitchStmt ts | this = ts.getExpr()) and - result = Panic() - } - - override ControlFlowTree getChildTree(int i) { i = 0 and result = this.getExpr() } - } - - private class UnaryExprTree extends ControlFlowTree, UnaryExpr { - override predicate firstNode(ControlFlow::Node first) { firstNode(this.getOperand(), first) } - - override predicate lastNode(ControlFlow::Node last, Completion cmpl) { - last = MkExprNode(this) and - ( - cmpl = Done() - or - this instanceof DerefExpr and cmpl = Panic() - ) - } - - pragma[nomagic] - override predicate succ0(ControlFlow::Node pred, ControlFlow::Node succ) { - ControlFlowTree.super.succ0(pred, succ) - or - not this = any(RecvStmt recv).getExpr() and - lastNode(this.getOperand(), pred, normalCompletion()) and - succ = MkExprNode(this) - } - } - - private ControlFlow::Node mkExprOrSkipNode(Expr e) { - result = MkExprNode(e) or - result = MkSkipNode(e) - } - - /** Holds if evaluation of `root` may start at `first`. */ - cached - predicate firstNode(ControlFlowTree root, ControlFlow::Node first) { root.firstNode(first) } - - /** Holds if evaluation of `root` may complete normally after `last`. */ - cached - predicate lastNode(ControlFlowTree root, ControlFlow::Node last) { - lastNode(root, last, normalCompletion()) - } - - private predicate lastNode(ControlFlowTree root, ControlFlow::Node last, Completion cmpl) { - root.lastNode(last, cmpl) - } - - /** Gets a successor of `nd` that is not a `defer` node */ - private ControlFlow::Node notDeferSucc0(ControlFlow::Node nd) { - not result = MkDeferNode(_) and - result = succ0(nd) - } - - /** Gets `defer` statements that can be the first defer statement after `nd` in the CFG */ - private ControlFlow::Node nextDefer(ControlFlow::Node nd) { - nd = MkDeferNode(_) and - result = MkDeferNode(_) and - ( - result = succ0(nd) - or - result = succ0(notDeferSucc0+(nd)) - ) - } - - /** - * Holds if the function `f` may return without panicking, exiting the process, or looping forever. - * - * This is defined conservatively, and so may also hold of a function that in fact - * cannot return normally, but never fails to hold of a function that can return normally. - */ - cached - predicate mayReturnNormally(ControlFlowTree root) { - exists(Completion cmpl | lastNode(root, _, cmpl) and cmpl != Panic()) - } - - /** - * Holds if `pred` is the node for the case `testExpr` in an expression - * switch statement which is switching on `switchExpr`, and `succ` is the - * node to be executed next if the case test succeeds. - */ - cached - predicate isSwitchCaseTestPassingEdge( - ControlFlow::Node pred, ControlFlow::Node succ, Expr switchExpr, Expr testExpr - ) { - exists(ExpressionSwitchStmt ess | ess.getExpr() = switchExpr | - ess.getACase().(CaseClauseTree).isPassingEdge(_, pred, succ, testExpr) - ) - } - - /** - * Gets a successor of `nd`, that is, a node that is executed after `nd`, - * ignoring the execution of any deferred functions when a function ends. - */ - pragma[nomagic] - private ControlFlow::Node succ0(ControlFlow::Node nd) { - any(ControlFlowTree tree).succ0(nd, result) - } - - /** Gets a successor of `nd`, that is, a node that is executed after `nd`. */ - cached - ControlFlow::Node succ(ControlFlow::Node nd) { any(ControlFlowTree tree).succ(nd, result) } -} diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll new file mode 100644 index 000000000000..1255b2ee3dd3 --- /dev/null +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -0,0 +1,1591 @@ +/** + * Provides the shared CFG library instantiation for Go. + * + * Everything is wrapped in `GoCfg` to avoid name conflicts with the existing + * CFG implementation during the transition. + */ +overlay[local] +module; + +private import codeql.controlflow.ControlFlowGraph as CfgLib +private import codeql.controlflow.SuccessorType +private import codeql.util.Void + +/** Contains the shared CFG library instantiation for Go. */ +module GoCfg { + private import go as Go + + private module Cfg0 = CfgLib::Make0; + + private module Cfg1 = Cfg0::Make1; + + private module Cfg2 = Cfg1::Make2; + + private import Cfg0 + private import Cfg1 + private import Cfg2 + import Public + + class CfgScope = Ast::Callable; + + /** Holds if `e` has an implicit field selection at `index` for `implicitField`. */ + predicate implicitFieldSelection(Go::AstNode e, int index, Go::Field implicitField) { + Input::implicitFieldSelection(e, index, implicitField) + } + + /** Provides an implementation of the AST signature for Go. */ + private module Ast implements CfgLib::AstSig { + class AstNode = Go::AstNode; + + private predicate skipCfg(AstNode e) { + e instanceof Go::TypeExpr and not e instanceof Go::FuncTypeExpr + or + e = any(Go::FieldDecl f).getTag() + or + e instanceof Go::KeyValueExpr and not e = any(Go::CompositeLit lit).getAnElement() + or + e = any(Go::SelectorExpr sel).getSelector() + or + e = any(Go::StructLit sl).getKey(_) + or + e instanceof Go::Ident and not e instanceof Go::ReferenceExpr + or + e instanceof Go::SelectorExpr and not e instanceof Go::ReferenceExpr + or + e instanceof Go::ReferenceExpr and + not e.(Go::ReferenceExpr).isRvalue() and + not e instanceof Go::SelectorExpr and + not e = any(Go::SelectorExpr sel).getBase() and + not e instanceof Go::IndexExpr and + not e = any(Go::IndexExpr idx).getBase() and + not e = any(Go::IndexExpr idx).getIndex() + or + e instanceof Go::CommentGroup + or + e instanceof Go::Comment + or + e = any(Go::ImportSpec is).getPathExpr() + or + e.getParent*() = any(Go::ArrayTypeExpr ate).getLength() + } + + AstNode getChild(AstNode n, int index) { + not n instanceof Go::FuncDef and + not skipCfg(n) and + not skipCfg(result) and + result = n.getChild(index) + } + + class Callable extends AstNode { + Callable() { + exists(this.(Go::FuncDef).getBody()) + or + exists(this.(Go::File).getADecl()) + } + } + + AstNode callableGetBody(Callable c) { + result = c.(Go::FuncDef).getBody() + or + result = c.(Go::File) + } + + class Parameter extends AstNode { + Parameter() { none() } + + Expr getDefaultValue() { none() } + } + + Parameter callableGetParameter(Callable c, int index) { none() } + + Callable getEnclosingCallable(AstNode node) { + result = node.getEnclosingFunction() + or + not exists(node.getEnclosingFunction()) and + result = node.getFile() + } + + class Stmt = Go::Stmt; + + class Expr = Go::Expr; + + class BlockStmt extends Go::BlockStmt { + BlockStmt() { + not this = any(Go::SwitchStmt sw).getBody() and + not this = any(Go::SelectStmt sel).getBody() + } + + override Stmt getStmt(int n) { result = Go::BlockStmt.super.getStmt(n) } + + Stmt getLastStmt() { + exists(int last | result = this.getStmt(last) and not exists(this.getStmt(last + 1))) + } + } + + class ExprStmt = Go::ExprStmt; + + class IfStmt extends Stmt { + IfStmt() { this instanceof Go::IfStmt } + + Expr getCondition() { result = this.(Go::IfStmt).getCond() } + + Stmt getThen() { result = this.(Go::IfStmt).getThen() } + + Stmt getElse() { result = this.(Go::IfStmt).getElse() } + } + + AstNode getIfInit(IfStmt ifstmt) { result = ifstmt.(Go::IfStmt).getInit() } + + class LoopStmt extends Stmt { + LoopStmt() { this instanceof Go::LoopStmt } + + Stmt getBody() { result = this.(Go::LoopStmt).getBody() } + } + + class WhileStmt extends LoopStmt { + WhileStmt() { none() } + + Expr getCondition() { none() } + } + + class DoStmt extends LoopStmt { + DoStmt() { none() } + + Expr getCondition() { none() } + } + + class ForStmt extends LoopStmt { + ForStmt() { this instanceof Go::ForStmt } + + AstNode getInit(int index) { index = 0 and result = this.(Go::ForStmt).getInit() } + + Expr getCondition() { result = this.(Go::ForStmt).getCond() } + + AstNode getUpdate(int index) { index = 0 and result = this.(Go::ForStmt).getPost() } + } + + class ForeachStmt extends LoopStmt { + ForeachStmt() { none() } + + Expr getVariable() { none() } + + Expr getCollection() { none() } + } + + class BreakStmt = Go::BreakStmt; + + class ContinueStmt = Go::ContinueStmt; + + class GotoStmt = Go::GotoStmt; + + class ReturnStmt extends Go::ReturnStmt { + override Expr getExpr() { result = Go::ReturnStmt.super.getExpr() } + } + + class Throw extends AstNode { + Throw() { none() } + + Expr getExpr() { none() } + } + + class TryStmt extends Stmt { + TryStmt() { none() } + + Stmt getBody() { none() } + + CatchClause getCatch(int index) { none() } + + Stmt getFinally() { none() } + } + + class CatchClause extends AstNode { + CatchClause() { none() } + + AstNode getVariable() { none() } + + Expr getCondition() { none() } + + Stmt getBody() { none() } + } + + class Switch extends AstNode { + Switch() { none() } + + Expr getExpr() { none() } + + Case getCase(int index) { none() } + + Stmt getStmt(int index) { none() } + } + + class Case extends AstNode { + Case() { none() } + + AstNode getPattern(int index) { none() } + + Expr getGuard() { none() } + + AstNode getBody() { none() } + } + + class DefaultCase extends Case { + DefaultCase() { none() } + } + + class ConditionalExpr extends Expr { + ConditionalExpr() { none() } + + Expr getCondition() { none() } + + Expr getThen() { none() } + + Expr getElse() { none() } + } + + class BinaryExpr = Go::BinaryExpr; + + class LogicalAndExpr = Go::LandExpr; + + class LogicalOrExpr = Go::LorExpr; + + class NullCoalescingExpr extends BinaryExpr { + NullCoalescingExpr() { none() } + } + + class UnaryExpr = Go::UnaryExpr; + + class LogicalNotExpr = Go::NotExpr; + + class BooleanLiteral extends Expr { + boolean val; + + BooleanLiteral() { + this.(Go::Ident).getName() = "true" and val = true + or + this.(Go::Ident).getName() = "false" and val = false + } + + boolean getValue() { result = val } + } + + class Assignment extends BinaryExpr { + Assignment() { none() } + } + + class AssignExpr extends Assignment { + AssignExpr() { none() } + } + + class CompoundAssignment extends Assignment { + CompoundAssignment() { none() } + } + + class AssignLogicalAndExpr extends CompoundAssignment { + AssignLogicalAndExpr() { none() } + } + + class AssignLogicalOrExpr extends CompoundAssignment { + AssignLogicalOrExpr() { none() } + } + + class AssignNullCoalescingExpr extends CompoundAssignment { + AssignNullCoalescingExpr() { none() } + } + + class PatternMatchExpr extends Expr { + PatternMatchExpr() { none() } + + Expr getExpr() { none() } + + AstNode getPattern() { none() } + } + } + + /** The Input module implementing InputSig1 and InputSig2 for Go. */ + private module Input implements Cfg0::InputSig1, Cfg1::InputSig2 { + predicate cfgCachedStageRef() { CfgCachedStage::ref() } + + class CallableContext = Void; + + private newtype TLabel = + TGoLabel(string l) { exists(Go::LabeledStmt ls | l = ls.getLabel()) } or + TFallthrough() + + class Label extends TLabel { + string toString() { + exists(string l | this = TGoLabel(l) and result = l) + or + this = TFallthrough() and result = "fallthrough" + } + } + + private Label getLabelOfStmt(Go::Stmt s) { + exists(Go::LabeledStmt l | s = l.getStmt() | + result = TGoLabel(l.getLabel()) or result = getLabelOfStmt(l) + ) + } + + predicate hasLabel(Ast::AstNode n, Label l) { + l = getLabelOfStmt(n) + or + l = TGoLabel(n.(Go::BreakStmt).getLabel()) + or + l = TGoLabel(n.(Go::ContinueStmt).getLabel()) + or + l = TFallthrough() and n instanceof Go::FallthroughStmt + } + + predicate inConditionalContext(Ast::AstNode n, ConditionKind kind) { + kind.isBoolean() and + ( + n = any(Go::ForStmt fs).getCond() + or + exists(Go::ExpressionSwitchStmt ess | + not exists(ess.getExpr()) and n = ess.getACase().(Go::CaseClause).getExpr(_) + ) + ) + } + + predicate preOrderExpr(Ast::Expr e) { none() } + + predicate propagatesValue(Ast::AstNode child, Ast::AstNode parent) { + child = parent.(Go::ParenExpr).getExpr() + } + + predicate postOrInOrder(Ast::AstNode n) { + n instanceof Go::ReferenceExpr + or + n instanceof Go::BasicLit + or + n instanceof Go::FuncLit + or + n instanceof Go::CallExpr and + not n = any(Go::DeferStmt defer).getCall() and + not n = any(Go::GoStmt go_).getCall() + or + n instanceof Go::BinaryExpr and not n instanceof Go::LogicalBinaryExpr + or + n instanceof Go::UnaryExpr and not n instanceof Go::NotExpr + or + n instanceof Go::ConversionExpr + or + n instanceof Go::TypeAssertExpr + or + n instanceof Go::IndexExpr + or + n instanceof Go::SliceExpr + or + n instanceof Go::CompositeLit + or + n instanceof Go::ReturnStmt + or + n instanceof Go::DeferStmt + or + n instanceof Go::GoStmt + or + n instanceof Go::SelectStmt + or + n instanceof Go::SendStmt + or + n instanceof Go::IncDecStmt + or + n instanceof Go::FuncDecl + or + n instanceof Go::SelectorExpr and + n.(Go::SelectorExpr).getBase() instanceof Go::ValueExpr + } + + predicate additionalNode(Ast::AstNode n, string tag, NormalSuccessor t) { + t instanceof DirectSuccessor and + ( + // Assignment write nodes: one per LHS + exists(int i | + ( + notBlankIdent(n.(Go::Assignment).getLhs(i)) + or + notBlankIdent(n.(Go::ValueSpec).getNameExpr(i)) + or + notBlankIdent(n.(Go::RangeStmt).getKey()) and i = 0 + or + notBlankIdent(n.(Go::RangeStmt).getValue()) and i = 1 + ) and + tag = "assign:" + i.toString() + ) + or + // Compound assignment implicit RHS + n instanceof Go::CompoundAssignStmt and tag = "compound-rhs" + or + // Tuple extraction nodes + exists(int i | + extractNodeCondition(n, i) and + tag = "extract:" + i.toString() + ) + or + // Zero initialization (on the ValueSpec) + exists(int i, Go::ValueSpec spec | + n = spec and + not exists(spec.getAnInit()) and + exists(spec.getNameExpr(i)) and + tag = "zero-init:" + i.toString() + ) + or + // Increment/decrement implicit operations + n instanceof Go::IncDecStmt and tag = "implicit-one" + or + n instanceof Go::IncDecStmt and tag = "incdec-rhs" + or + // Result write nodes in return statements + exists(int i, Go::ReturnStmt ret | + n = ret and + exists(ret.getEnclosingFunction().getResultVar(i)) and + exists(ret.getAnExpr()) and + tag = "result-write:" + i.toString() + ) + or + // Result read nodes (on the function body) + exists(int i, Go::FuncDef fd | + n = fd.getBody() and + exists(fd.getBody()) and + exists(fd.getResultVar(i)) and + tag = "result-read:" + i.toString() + ) + or + // Parameter init + argument nodes (on the function body) + exists(int i, Go::FuncDef fd | + n = fd.getBody() and + exists(fd.getBody()) and + exists(fd.getParameter(i)) and + (tag = "param-init:" + i.toString() or tag = "arg:" + i.toString()) + ) + or + // Result variable init (on the function body) + exists(int i, Go::FuncDef fd | + n = fd.getBody() and + exists(fd.getBody()) and + exists(fd.getResultVar(i)) and + tag = "result-init:" + i.toString() + ) + or + // Result variable zero init (on the function body) + exists(int i, Go::FuncDef fd | + n = fd.getBody() and + exists(fd.getBody()) and + exists(fd.getResultVar(i)) and + exists(fd.getResultVar(i).(Go::ResultVariable).getFunction().getBody()) and + tag = "result-zero-init:" + i.toString() + ) + or + // Next node for range statements + n instanceof Go::RangeStmt and tag = "next" + or + // Send node + n instanceof Go::SendStmt and + not n = any(Go::CommClause cc).getComm() and + tag = "send" + or + // Implicit deref + implicitDerefCondition(n) and tag = "implicit-deref" + or + // Implicit slice bounds + n instanceof Go::SliceExpr and + not exists(n.(Go::SliceExpr).getLow()) and + tag = "implicit-low" + or + n instanceof Go::SliceExpr and + not exists(n.(Go::SliceExpr).getHigh()) and + tag = "implicit-high" + or + n instanceof Go::SliceExpr and + not exists(n.(Go::SliceExpr).getMax()) and + tag = "implicit-max" + or + // Implicit true in tagless switch + n instanceof Go::ExpressionSwitchStmt and + not exists(n.(Go::ExpressionSwitchStmt).getExpr()) and + tag = "implicit-true" + or + // Case check nodes + exists(int i | + exists(n.(Go::CaseClause).getExpr(i)) and + tag = "case-check:" + i.toString() + ) + or + // Type switch implicit variable + exists(Go::TypeSwitchStmt ts, Go::DefineStmt ds | + ds = ts.getAssign() and + n.(Go::CaseClause) = ts.getACase() and + exists(n.(Go::CaseClause).getImplicitlyDeclaredVariable()) and + tag = "type-switch-var" + ) + or + // Literal element initialization + n = any(Go::CompositeLit lit).getAnElement() and + tag = "lit-init" + or + // Implicit literal element index + exists(Go::CompositeLit lit | + not lit instanceof Go::StructLit and + n = lit.getAnElement() and + not n instanceof Go::KeyValueExpr and + tag = "lit-index" + ) + or + // Implicit field selection for promoted fields + exists(int i, Go::Field implicitField | + implicitFieldSelection(n, i, implicitField) and + tag = "implicit-field:" + i.toString() + ) + ) + } + + /** Helper: condition for MkExtractNode */ + private predicate extractNodeCondition(Ast::AstNode s, int i) { + exists(Go::Assignment assgn | + s = assgn and + exists(assgn.getRhs()) and + assgn.getNumLhs() > 1 and + exists(assgn.getLhs(i)) + ) + or + exists(Go::ValueSpec spec | + s = spec and + exists(spec.getInit()) and + spec.getNumName() > 1 and + exists(spec.getNameExpr(i)) + ) + or + exists(Go::RangeStmt rs | s = rs | + exists(rs.getKey()) and i = 0 + or + exists(rs.getValue()) and i = 1 + ) + or + exists(Go::ReturnStmt ret, Go::SignatureType rettp | + s = ret and + exists(ret.getExpr()) and + rettp = ret.getEnclosingFunction().getType() and + rettp.getNumResult() > 1 and + exists(rettp.getResultType(i)) + ) + or + exists(Go::CallExpr outer, Go::CallExpr inner | s = outer | + inner = outer.getArgument(0).stripParens() and + outer.getNumArgument() = 1 and + exists(inner.getType().(Go::TupleType).getComponentType(i)) + ) + } + + /** Helper: condition for implicit dereference */ + private predicate implicitDerefCondition(Ast::AstNode e) { + e.(Go::Expr).getType().getUnderlyingType() instanceof Go::PointerType and + ( + exists(Go::SelectorExpr sel | e = sel.getBase() | + sel = any(Go::Field f).getAReference() + or + exists(Go::Method m, Go::Type tp | + sel = m.getAReference() and + tp = m.getReceiver().getType().getUnderlyingType() and + not tp instanceof Go::PointerType + ) + ) + or + e = any(Go::IndexExpr ie).getBase() + or + e = any(Go::SliceExpr se).getBase() + ) + } + + /** Helper: blank identifier check */ + private predicate notBlankIdent(Go::Expr e) { not e instanceof Go::BlankIdent } + + /** Helper: implicit field selection for promoted selectors */ + additional predicate implicitFieldSelection(Ast::AstNode e, int index, Go::Field implicitField) { + exists(Go::StructType baseType, Go::PromotedField child, int implicitFieldDepth | + baseType = e.(Go::PromotedSelector).getSelectedStructType() and + ( + e.(Go::PromotedSelector).refersTo(child) + or + implicitFieldSelection(e, implicitFieldDepth + 1, child) + ) + | + child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and + exists(Go::PromotedField explicitField, int explicitFieldDepth | + e.(Go::PromotedSelector).refersTo(explicitField) and + baseType.getFieldAtDepth(_, explicitFieldDepth) = explicitField + | + index = explicitFieldDepth - implicitFieldDepth + ) + ) + or + exists( + Go::StructType baseType, Go::PromotedMethod method, int mDepth, int implicitFieldDepth + | + baseType = e.(Go::PromotedSelector).getSelectedStructType() and + e.(Go::PromotedSelector).refersTo(method) and + baseType.getMethodAtDepth(_, mDepth) = method and + index = mDepth - implicitFieldDepth + | + method = baseType.getMethodOfEmbedded(implicitField, _, implicitFieldDepth + 1) + or + exists(Go::PromotedField child | + child = baseType.getFieldOfEmbedded(implicitField, _, implicitFieldDepth + 1, _) and + implicitFieldSelection(e, implicitFieldDepth + 1, child) + ) + ) + } + + predicate beginAbruptCompletion( + Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c, boolean always + ) { + ast instanceof Go::CallExpr and + ( + not exists(ast.(Go::CallExpr).getTarget()) or + ast.(Go::CallExpr).getTarget().mayPanic() + ) and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + // Calls to functions that never return normally (e.g. `os.Exit`, `log.Fatal`, + // `panic`) must suppress normal flow past the call site. We emit an `always` + // exception completion so that the shared library's default In->After step + // is suppressed. + ast instanceof Go::CallExpr and + exists(Go::Function target | target = ast.(Go::CallExpr).getTarget() | + target.mustPanic() or target.mustNotReturnNormally() + ) and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = true + or + ast instanceof Go::DivExpr and + not ast.(Go::Expr).isConst() and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::DerefExpr and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::TypeAssertExpr and + not exists(Go::Assignment assgn | + assgn.getNumLhs() = 2 and ast = assgn.getRhs().stripParens() + ) and + not exists(Go::ValueSpec vs | vs.getNumName() = 2 and ast = vs.getInit().stripParens()) and + not exists(Go::TypeSwitchStmt ts | ast = ts.getExpr()) and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::IndexExpr and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::ConversionExpr and + ast.(Go::ConversionExpr).getType().(Go::PointerType).getBaseType() instanceof Go::ArrayType and + n.isIn(ast) and + c.asSimpleAbruptCompletion() instanceof ExceptionSuccessor and + always = false + or + ast instanceof Go::FallthroughStmt and + n.injects(ast) and + c.getSuccessorType() instanceof BreakSuccessor and + c.hasLabel(TFallthrough()) and + always = true + or + ast instanceof Go::GotoStmt and + n.injects(ast) and + c.getSuccessorType() instanceof GotoSuccessor and + c.hasLabel(TGoLabel(ast.(Go::GotoStmt).getLabel())) and + always = true + } + + predicate endAbruptCompletion(Ast::AstNode ast, PreControlFlowNode n, AbruptCompletion c) { + exists(Go::LabeledStmt lbl | + ast = lbl.getStmt() and + n.isAfter(lbl) and + c.getSuccessorType() instanceof BreakSuccessor and + c.hasLabel(TGoLabel(lbl.getLabel())) + ) + or + exists(Go::FuncDef fd | + ast = fd.getBody() and + c.getSuccessorType() instanceof ReturnSuccessor and + ( + // If the function has result variables, route the return completion + // through the result-read epilogue before reaching the function exit. + exists(fd.getResultVar(0)) and n.isAdditional(fd.getBody(), "result-read:0") + or + not exists(fd.getResultVar(_)) and n.isAfter(fd.getBody()) + ) + ) + or + exists(Go::LabeledStmt lbl, Go::FuncDef fd | + ast = fd.getBody() and + n.isBefore(lbl) and + fd = lbl.getEnclosingFunction() and + c.getSuccessorType() instanceof GotoSuccessor and + c.hasLabel(TGoLabel(lbl.getLabel())) + ) + } + + predicate step(PreControlFlowNode n1, PreControlFlowNode n2) { + rangeLoop(n1, n2) or + switchStmt(n1, n2) or + selectStmt(n1, n2) or + deferStmt(n1, n2) or + goStmtStep(n1, n2) or + assignmentStep(n1, n2) or + incDecStep(n1, n2) or + returnStep(n1, n2) or + indexExprStep(n1, n2) or + sliceExprStep(n1, n2) or + selectorExprStep(n1, n2) or + compositeLitStep(n1, n2) or + sendStmtStep(n1, n2) or + funcDefStep(n1, n2) + } + + /** + * Gets the non-skipped child of `parent` at rank `rnk` (1-based). + * This mimics the shared library's getRankedChild but for use in explicit steps. + */ + private Ast::AstNode getRankedChild(Ast::AstNode parent, int rnk) { + result = rank[rnk](Ast::AstNode c, int ix | c = getChild(parent, ix) | c order by ix) + } + + private Ast::AstNode getChild(Ast::AstNode parent, int ix) { + result = Ast::getChild(parent, ix) + } + + /** + * Routes from isBefore(parent) through all non-skipped children in order, + * then to the first epilogue node (additionalNode or isIn/isAfter). + * This is for constructs where we manually override default flow. + */ + private predicate childSequenceStep( + Ast::AstNode parent, PreControlFlowNode n1, PreControlFlowNode n2 + ) { + // Before parent → Before first child + n1.isBefore(parent) and n2.isBefore(getRankedChild(parent, 1)) + or + // After child i → Before child i+1 + exists(int i | + n1.isAfter(getRankedChild(parent, i)) and + n2.isBefore(getRankedChild(parent, i + 1)) + ) + } + + /** Gets the last non-skipped child of `parent`, or fails if none. */ + private Ast::AstNode getLastRankedChild(Ast::AstNode parent) { + exists(int i | + result = getRankedChild(parent, i) and + not exists(getRankedChild(parent, i + 1)) + ) + } + + /** + * Assignment flow: routes through LHS/RHS children, then through + * additional nodes for compound-rhs, extract, zero-init, and assign + * operations. + */ + private predicate assignmentStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Ast::AstNode assgn | + ( + assgn instanceof Go::Assignment and not assgn instanceof Go::RecvStmt + or + assgn instanceof Go::ValueSpec + ) + | + // Route through children (LHS names, RHS expressions) + childSequenceStep(assgn, n1, n2) + or + // After last child → first epilogue node + exists(Ast::AstNode lastChild | lastChild = getLastRankedChild(assgn) | + n1.isAfter(lastChild) and n2.isAdditional(assgn, getFirstEpilogueTag(assgn)) + ) + or + // No children at all → before → first epilogue + not exists(getRankedChild(assgn, _)) and + n1.isBefore(assgn) and + n2.isAdditional(assgn, getFirstEpilogueTag(assgn)) + or + // Chain through epilogue nodes + exists(string tag1, string tag2 | + epilogueSucc(assgn, tag1, tag2) and + n1.isAdditional(assgn, tag1) and + n2.isAdditional(assgn, tag2) + ) + or + // Last epilogue → after the assignment + n1.isAdditional(assgn, getLastEpilogueTag(assgn)) and + n2.isAfter(assgn) + or + // No epilogue at all → after last child → after assignment + not exists(getFirstEpilogueTag(assgn)) and + n1.isAfter(getLastRankedChild(assgn)) and + n2.isAfter(assgn) + ) + } + + /** + * Gets the ordered epilogue tags for an assignment node, following the + * pattern: compound-rhs?, (extract:i, assign:i | zero-init:i, assign:i)* + */ + private string getEpilogueTag(Ast::AstNode assgn, int ord) { + // Compound RHS comes first + assgn instanceof Go::CompoundAssignStmt and + ord = -1 and + result = "compound-rhs" + or + exists(int j | + ( + extractNodeCondition(assgn, j) and result = "extract:" + j.toString() and ord = 2 * j + or + exists(Go::ValueSpec spec | + assgn = spec and + not exists(spec.getAnInit()) and + exists(spec.getNameExpr(j)) and + result = "zero-init:" + j.toString() and + ord = 2 * j + ) + or + ( + notBlankIdent(assgn.(Go::Assignment).getLhs(j)) + or + notBlankIdent(assgn.(Go::ValueSpec).getNameExpr(j)) + or + notBlankIdent(assgn.(Go::RangeStmt).getKey()) and j = 0 + or + notBlankIdent(assgn.(Go::RangeStmt).getValue()) and j = 1 + ) and + result = "assign:" + j.toString() and + ord = 2 * j + 1 + ) + ) + } + + private string getEpilogueTagRanked(Ast::AstNode assgn, int rnk) { + result = + rank[rnk](string tag, int ord | + tag = getEpilogueTag(assgn, ord) and + exists(tag) + | + tag order by ord + ) + } + + private string getFirstEpilogueTag(Ast::AstNode assgn) { + result = getEpilogueTagRanked(assgn, 1) + } + + private string getLastEpilogueTag(Ast::AstNode assgn) { + exists(int i | + result = getEpilogueTagRanked(assgn, i) and + not exists(getEpilogueTagRanked(assgn, i + 1)) + ) + } + + private predicate epilogueSucc(Ast::AstNode assgn, string tag1, string tag2) { + exists(int i | + tag1 = getEpilogueTagRanked(assgn, i) and + tag2 = getEpilogueTagRanked(assgn, i + 1) + ) + } + + /** + * Increment/decrement: operand → implicit-one → incdec-rhs → In(stmt) + * (IncDecStmt is in postOrInOrder, so In(stmt) is its evaluation point) + */ + private predicate incDecStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::IncDecStmt s | + // Before → Before operand + n1.isBefore(s) and n2.isBefore(s.getOperand()) + or + // After operand → implicit-one + n1.isAfter(s.getOperand()) and n2.isAdditional(s, "implicit-one") + or + // implicit-one → incdec-rhs + n1.isAdditional(s, "implicit-one") and n2.isAdditional(s, "incdec-rhs") + or + // incdec-rhs → In(stmt) (the assignment itself) + n1.isAdditional(s, "incdec-rhs") and n2.isIn(s) + or + // In(stmt) → After(stmt) + n1.isIn(s) and n2.isAfter(s) + ) + } + + /** + * Return statement: evaluate expressions, extract tuples, write results, + * then the return node. + */ + private predicate returnStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::ReturnStmt ret | + // Route through expression children + childSequenceStep(ret, n1, n2) + or + exists(Ast::AstNode lastChild | lastChild = getLastRankedChild(ret) | + // After last expr → first return epilogue + n1.isAfter(lastChild) and n2.isAdditional(ret, getFirstReturnEpilogueTag(ret)) + ) + or + exists(Ast::AstNode lastChild | lastChild = getLastRankedChild(ret) | + // After last expr → return node directly if there is no return epilogue + not exists(getFirstReturnEpilogueTag(ret)) and + n1.isAfter(lastChild) and + n2.isIn(ret) + ) + or + // No expressions → before → return node directly + not exists(getRankedChild(ret, _)) and + n1.isBefore(ret) and + n2.isIn(ret) + or + // Chain return epilogue nodes + exists(string tag1, string tag2 | + returnEpilogueSucc(ret, tag1, tag2) and + n1.isAdditional(ret, tag1) and + n2.isAdditional(ret, tag2) + ) + or + // Last return epilogue → return node + n1.isAdditional(ret, getLastReturnEpilogueTag(ret)) and + n2.isIn(ret) + ) + } + + private string getReturnEpilogueTag(Go::ReturnStmt ret, int ord) { + exists(int i | + extractNodeCondition(ret, i) and result = "extract:" + i.toString() and ord = 2 * i + or + exists(Go::ResultVariable rv | + ret.getEnclosingFunction().getResultVar(i) = rv and + exists(ret.getAnExpr()) and + result = "result-write:" + i.toString() and + ord = 2 * i + 1 + ) + ) + } + + private string getReturnEpilogueTagRanked(Go::ReturnStmt ret, int rnk) { + result = + rank[rnk](string tag, int ord | + tag = getReturnEpilogueTag(ret, ord) and + exists(tag) + | + tag order by ord + ) + } + + private string getFirstReturnEpilogueTag(Go::ReturnStmt ret) { + result = getReturnEpilogueTagRanked(ret, 1) + } + + private string getLastReturnEpilogueTag(Go::ReturnStmt ret) { + exists(int i | + result = getReturnEpilogueTagRanked(ret, i) and + not exists(getReturnEpilogueTagRanked(ret, i + 1)) + ) + } + + private predicate returnEpilogueSucc(Go::ReturnStmt ret, string tag1, string tag2) { + exists(int i | + tag1 = getReturnEpilogueTagRanked(ret, i) and + tag2 = getReturnEpilogueTagRanked(ret, i + 1) + ) + } + + /** + * Index expression: base → implicit-deref? → index → In(indexExpr) + */ + private predicate indexExprStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::IndexExpr ie | + implicitDerefCondition(ie.getBase()) and + ( + n1.isBefore(ie) and n2.isBefore(ie.getBase()) + or + n1.isAfter(ie.getBase()) and n2.isAdditional(ie.getBase(), "implicit-deref") + or + n1.isAdditional(ie.getBase(), "implicit-deref") and n2.isBefore(ie.getIndex()) + or + n1.isAfter(ie.getIndex()) and n2.isIn(ie) + or + n1.isIn(ie) and n2.isAfter(ie) + ) + ) + } + + /** + * Slice expression: base → implicit-deref? → low/implicit-low → + * high/implicit-high → max/implicit-max → In(sliceExpr) + */ + private predicate sliceExprStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SliceExpr se | + (implicitDerefCondition(se.getBase()) or exists(se.getLow()) or not exists(se.getLow())) and + ( + n1.isBefore(se) and n2.isBefore(se.getBase()) + or + // After base → implicit deref or low/implicit-low + n1.isAfter(se.getBase()) and + ( + if implicitDerefCondition(se.getBase()) + then n2.isAdditional(se.getBase(), "implicit-deref") + else + if exists(se.getLow()) + then n2.isBefore(se.getLow()) + else n2.isAdditional(se, "implicit-low") + ) + or + n1.isAdditional(se.getBase(), "implicit-deref") and + ( + if exists(se.getLow()) + then n2.isBefore(se.getLow()) + else n2.isAdditional(se, "implicit-low") + ) + or + (n1.isAfter(se.getLow()) or n1.isAdditional(se, "implicit-low")) and + ( + if exists(se.getHigh()) + then n2.isBefore(se.getHigh()) + else n2.isAdditional(se, "implicit-high") + ) + or + (n1.isAfter(se.getHigh()) or n1.isAdditional(se, "implicit-high")) and + ( + if exists(se.getMax()) + then n2.isBefore(se.getMax()) + else n2.isAdditional(se, "implicit-max") + ) + or + (n1.isAfter(se.getMax()) or n1.isAdditional(se, "implicit-max")) and + n2.isIn(se) + or + n1.isIn(se) and n2.isAfter(se) + ) + ) + } + + /** + * Selector expression with value base: base → implicit-deref? → + * implicit-field-selections → In(selector) + */ + private predicate selectorExprStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SelectorExpr sel | + sel.getBase() instanceof Go::ValueExpr and + ( + implicitDerefCondition(sel.getBase()) or + exists(Go::Field f | sel = f.getAReference()) or + implicitFieldSelection(sel, _, _) + ) and + ( + n1.isBefore(sel) and n2.isBefore(sel.getBase()) + or + // After base (no implicit-deref) → first implicit-field or In(sel) + n1.isAfter(sel.getBase()) and + not implicitDerefCondition(sel.getBase()) and + ( + // Has implicit field reads: go to outermost (highest index) + exists(int maxIdx | + maxIdx = max(int i | implicitFieldSelection(sel, i, _)) and + n2.isAdditional(sel, "implicit-field:" + maxIdx.toString()) + ) + or + // No implicit field reads: go directly to In(sel) + not implicitFieldSelection(sel, _, _) and n2.isIn(sel) + ) + or + // After base (has implicit-deref) → implicit-deref node + n1.isAfter(sel.getBase()) and + implicitDerefCondition(sel.getBase()) and + n2.isAdditional(sel.getBase(), "implicit-deref") + or + // After implicit-deref → first implicit-field or In(sel) + n1.isAdditional(sel.getBase(), "implicit-deref") and + ( + exists(int maxIdx | + maxIdx = max(int i | implicitFieldSelection(sel, i, _)) and + n2.isAdditional(sel, "implicit-field:" + maxIdx.toString()) + ) + or + not implicitFieldSelection(sel, _, _) and n2.isIn(sel) + ) + or + // Between implicit field reads: descend from index i to i-1 + exists(int i | + i > 1 and + implicitFieldSelection(sel, i, _) and + implicitFieldSelection(sel, i - 1, _) and + n1.isAdditional(sel, "implicit-field:" + i.toString()) and + n2.isAdditional(sel, "implicit-field:" + (i - 1).toString()) + ) + or + // Last implicit field read (index 1) → In(sel) + implicitFieldSelection(sel, 1, _) and + n1.isAdditional(sel, "implicit-field:1") and + n2.isIn(sel) + or + n1.isIn(sel) and n2.isAfter(sel) + ) + ) + } + + /** + * Composite literal: In(lit) → element-init chain → After(lit) + * CompositeLit evaluates the literal (allocation) first (pre-order), + * then initializes elements. + */ + private predicate compositeLitStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::CompositeLit lit | + // Before → In (the literal allocation) + n1.isBefore(lit) and n2.isIn(lit) + or + // In → first element, or After if no elements + n1.isIn(lit) and + ( + n2.isBefore(lit.getElement(0)) + or + not exists(lit.getElement(_)) and n2.isAfter(lit) + ) + or + // After element → optional lit-index → lit-init → next element or After + exists(int i | + n1.isAfter(lit.getElement(i)) and + ( + n2.isAdditional(lit.getElement(i), "lit-index") + or + not exists(PreControlFlowNode idx | idx.isAdditional(lit.getElement(i), "lit-index")) and + n2.isAdditional(lit.getElement(i), "lit-init") + ) + or + n1.isAdditional(lit.getElement(i), "lit-index") and + n2.isAdditional(lit.getElement(i), "lit-init") + or + n1.isAdditional(lit.getElement(i), "lit-init") and + ( + n2.isBefore(lit.getElement(i + 1)) + or + not exists(lit.getElement(i + 1)) and n2.isAfter(lit) + ) + ) + ) + } + + /** + * Send statement (outside select): channel → value → In(send) + */ + private predicate sendStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SendStmt s | not s = any(Go::CommClause cc).getComm() | + n1.isBefore(s) and n2.isBefore(s.getChannel()) + or + n1.isAfter(s.getChannel()) and n2.isBefore(s.getValue()) + or + n1.isAfter(s.getValue()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + private predicate rangeLoop(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::RangeStmt s | + // Use the shared library's auto-created `[LoopHeader]` additional node + // (created for every `LoopStmt`) as the join/branch point of the range loop. + n1.isBefore(s) and n2.isBefore(s.getDomain()) + or + n1.isAfter(s.getDomain()) and n2.isAdditional(s, "[LoopHeader]") + or + n1.isAdditional(s, "[LoopHeader]") and n2.isAdditional(s, "next") + or + n1.isAdditional(s, "next") and + ( + exists(getFirstEpilogueTag(s)) and n2.isAdditional(s, getFirstEpilogueTag(s)) + or + not exists(getFirstEpilogueTag(s)) and n2.isBefore(s.getBody()) + ) + or + exists(string tag1, string tag2 | + epilogueSucc(s, tag1, tag2) and + n1.isAdditional(s, tag1) and + n2.isAdditional(s, tag2) + ) + or + n1.isAdditional(s, getLastEpilogueTag(s)) and n2.isBefore(s.getBody()) + or + n1.isAfter(s.getBody()) and n2.isAdditional(s, "[LoopHeader]") + or + n1.isAdditional(s, "[LoopHeader]") and n2.isAfter(s) + ) + } + + private predicate switchStmt(PreControlFlowNode n1, PreControlFlowNode n2) { + exprSwitch(n1, n2) or typeSwitch(n1, n2) or caseClause(n1, n2) + } + + private predicate switchCasesStartOrAfter(Go::SwitchStmt sw, PreControlFlowNode n) { + n.isBefore(sw.getNonDefaultCase(0)) + or + not exists(sw.getANonDefaultCase()) and n.isBefore(sw.getDefault()) + or + not exists(sw.getACase()) and n.isAfter(sw) + } + + private predicate exprSwitch(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::ExpressionSwitchStmt sw | + n1.isBefore(sw) and + ( + n2.isBefore(sw.getInit()) + or + not exists(sw.getInit()) and + ( + n2.isBefore(sw.getExpr()) + or + not exists(sw.getExpr()) and switchCasesStartOrAfter(sw, n2) + ) + ) + or + n1.isAfter(sw.getInit()) and + ( + n2.isBefore(sw.getExpr()) + or + not exists(sw.getExpr()) and switchCasesStartOrAfter(sw, n2) + ) + or + n1.isAfter(sw.getExpr()) and switchCasesStartOrAfter(sw, n2) + ) + } + + private predicate typeSwitch(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::TypeSwitchStmt sw | + n1.isBefore(sw) and + ( + n2.isBefore(sw.getInit()) + or + not exists(sw.getInit()) and n2.isBefore(sw.getTest()) + ) + or + n1.isAfter(sw.getInit()) and n2.isBefore(sw.getTest()) + or + n1.isAfter(sw.getTest()) and switchCasesStartOrAfter(sw, n2) + ) + } + + private predicate caseClause(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SwitchStmt sw, Go::CaseClause cc, int i | cc = sw.getNonDefaultCase(i) | + n1.isBefore(cc) and n2.isBefore(cc.getExpr(0)) + or + exists(int j | n1.isAfter(cc.getExpr(j)) and n2.isBefore(cc.getExpr(j + 1))) + or + exists(int last | last = max(int j | exists(cc.getExpr(j))) | + n1.isAfter(cc.getExpr(last)) and + ( + n2.isBefore(cc.getStmt(0)) + or + not exists(cc.getStmt(0)) and n2.isAfter(sw) + or + n2.isBefore(sw.getNonDefaultCase(i + 1)) + or + not exists(sw.getNonDefaultCase(i + 1)) and n2.isBefore(sw.getDefault()) + or + not exists(sw.getNonDefaultCase(i + 1)) and + not exists(sw.getDefault()) and + n2.isAfter(sw) + ) + ) + ) + or + exists(Go::SwitchStmt sw, Go::CaseClause def | def = sw.getDefault() | + n1.isBefore(def) and + ( + n2.isBefore(def.getStmt(0)) + or + not exists(def.getStmt(0)) and n2.isAfter(sw) + ) + ) + or + exists(Go::CaseClause cc | + exists(int j | n1.isAfter(cc.getStmt(j)) and n2.isBefore(cc.getStmt(j + 1))) + or + exists(Go::SwitchStmt sw, int last | + sw.getACase() = cc and + last = max(int j | exists(cc.getStmt(j))) and + n1.isAfter(cc.getStmt(last)) and + n2.isAfter(sw) + ) + ) + } + + private predicate commClauseBodyStart( + Go::SelectStmt sel, Go::CommClause cc, PreControlFlowNode n + ) { + n.isBefore(cc.getStmt(0)) + or + not exists(cc.getStmt(0)) and n.isAfter(sel) + } + + private predicate selectCommPrepStart(Go::CommClause cc, PreControlFlowNode n) { + exists(Go::RecvStmt recv | recv = cc.getComm() | n.isBefore(recv.getExpr().getOperand())) + or + exists(Go::SendStmt send | send = cc.getComm() | n.isBefore(send.getChannel())) + } + + private predicate selectCommPrepEnd(Go::CommClause cc, PreControlFlowNode n) { + exists(Go::RecvStmt recv | recv = cc.getComm() | n.isAfter(recv.getExpr().getOperand())) + or + exists(Go::SendStmt send | send = cc.getComm() | n.isAfter(send.getValue())) + } + + private predicate selectCommPrepStep( + Go::CommClause cc, PreControlFlowNode n1, PreControlFlowNode n2 + ) { + exists(Go::SendStmt send | send = cc.getComm() | + n1.isAfter(send.getChannel()) and n2.isBefore(send.getValue()) + ) + } + + private predicate selectedCommStep( + Go::SelectStmt sel, PreControlFlowNode n1, PreControlFlowNode n2 + ) { + exists(Go::RecvStmt recv | recv = sel.getACommClause().getComm() | + n1.isBefore(recv) and n2.isIn(recv.getExpr()) + or + n1.isBefore(recv.getExpr()) and n2.isBefore(recv.getExpr().getOperand()) + ) + or + exists(Go::SendStmt send | send = sel.getACommClause().getComm() | + n1.isBefore(send) and n2.isBefore(send.getChannel()) + or + n1.isAfter(send.getChannel()) and n2.isBefore(send.getValue()) + or + n1.isIn(send) and n2.isAfter(send) + ) + } + + private predicate selectRecvStmtStep( + Go::SelectStmt sel, Go::CommClause cc, Go::RecvStmt recv, PreControlFlowNode n1, + PreControlFlowNode n2 + ) { + cc = sel.getACommClause() and + recv = cc.getComm() and + ( + n1.isIn(recv.getExpr()) and + ( + n2.isBefore(recv.getLhs(0)) + or + not exists(recv.getLhs(0)) and commClauseBodyStart(sel, cc, n2) + ) + or + exists(int j | n1.isAfter(recv.getLhs(j)) and n2.isBefore(recv.getLhs(j + 1))) + or + exists(int last | exists(recv.getLhs(last)) and not exists(recv.getLhs(last + 1)) | + n1.isAfter(recv.getLhs(last)) and n2.isAdditional(recv, getFirstEpilogueTag(recv)) + ) + or + exists(int last | exists(recv.getLhs(last)) and not exists(recv.getLhs(last + 1)) | + not exists(getFirstEpilogueTag(recv)) and + n1.isAfter(recv.getLhs(last)) and + commClauseBodyStart(sel, cc, n2) + ) + or + exists(string tag1, string tag2 | + epilogueSucc(recv, tag1, tag2) and + n1.isAdditional(recv, tag1) and + n2.isAdditional(recv, tag2) + ) + or + n1.isAdditional(recv, getLastEpilogueTag(recv)) and commClauseBodyStart(sel, cc, n2) + ) + } + + private predicate selectStmt(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::SelectStmt sel | + selectedCommStep(sel, n1, n2) + or + n1.isBefore(sel) and + ( + selectCommPrepStart(sel.getNonDefaultCommClause(0), n2) + or + not exists(sel.getNonDefaultCommClause(0)) and n2.isIn(sel) + ) + or + exists(Go::CommClause cc, int i | cc = sel.getNonDefaultCommClause(i) | + selectCommPrepStep(cc, n1, n2) + or + selectCommPrepEnd(cc, n1) and + ( + selectCommPrepStart(sel.getNonDefaultCommClause(i + 1), n2) + or + not exists(sel.getNonDefaultCommClause(i + 1)) and n2.isIn(sel) + ) + ) + or + n1.isIn(sel) and + exists(Go::CommClause cc | sel.getACommClause() = cc | n2.isBefore(cc)) + or + exists(Go::CommClause cc | sel.getACommClause() = cc | + n1.isBefore(cc) and + ( + n2.isIn(cc.getComm().(Go::RecvStmt).getExpr()) + or + n2.isIn(cc.getComm().(Go::SendStmt)) + or + not exists(cc.getComm()) and commClauseBodyStart(sel, cc, n2) + ) + or + exists(Go::RecvStmt recv | selectRecvStmtStep(sel, cc, recv, n1, n2)) + or + n1.isAfter(cc.getComm().(Go::SendStmt)) and commClauseBodyStart(sel, cc, n2) + or + exists(int j | n1.isAfter(cc.getStmt(j)) and n2.isBefore(cc.getStmt(j + 1))) + or + exists(int last | + last = max(int j | exists(cc.getStmt(j))) and + n1.isAfter(cc.getStmt(last)) and + n2.isAfter(sel) + ) + ) + ) + } + + private predicate deferStmt(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::DeferStmt s | + n1.isBefore(s) and n2.isBefore(s.getCall()) + or + n1.isAfter(s.getCall()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + private predicate goStmtStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::GoStmt s | + n1.isBefore(s) and n2.isBefore(s.getCall()) + or + n1.isAfter(s.getCall()) and n2.isIn(s) + or + n1.isIn(s) and n2.isAfter(s) + ) + } + + /** + * Function definition prologue and epilogue: + * - Prologue: Before(body) → arg:-1 → param-init:-1 → arg:0 → param-init:0 → ... + * when a receiver exists; otherwise it starts at arg:0. Then + * result-zero-init:0 → result-init:0 → ... → first statement + * - Epilogue: return → result-read:0 → result-read:1 → ... → After(body) + * + * `After(body)` goes to `NormalExit(fd)` via the shared library's built-in step. + */ + private predicate hasFuncDefPrologue(Go::FuncDef fd) { + exists(fd.getParameter(_)) or exists(fd.getResultVar(_)) + } + + private predicate funcDefBodyStart(Go::FuncDef fd, PreControlFlowNode n) { + n.isBefore(getRankedChild(fd.getBody(), 1)) + or + not exists(getRankedChild(fd.getBody(), _)) and n.isAfter(fd.getBody()) + } + + private predicate funcDefBodyStep(Go::FuncDef fd, PreControlFlowNode n1, PreControlFlowNode n2) { + not hasFuncDefPrologue(fd) and + n1.isBefore(fd.getBody()) and + funcDefBodyStart(fd, n2) + or + exists(int i | + n1.isAfter(getRankedChild(fd.getBody(), i)) and + n2.isBefore(getRankedChild(fd.getBody(), i + 1)) + ) + or + exists(Ast::AstNode lastChild | lastChild = getLastRankedChild(fd.getBody()) | + n1.isAfter(lastChild) and n2.isAfter(fd.getBody()) + ) + } + + private predicate funcDefStep(PreControlFlowNode n1, PreControlFlowNode n2) { + exists(Go::FuncDef fd | exists(fd.getBody()) | + // Before(body) → first prologue node, or first body statement if no prologue + n1.isBefore(fd.getBody()) and + ( + // Has receiver: start with arg:-1 + exists(fd.getParameter(-1)) and n2.isAdditional(fd.getBody(), "arg:-1") + or + // Has ordinary parameters: start with arg:0 + not exists(fd.getParameter(-1)) and + exists(fd.getParameter(0)) and + n2.isAdditional(fd.getBody(), "arg:0") + or + // No parameters, has result vars: start with result-zero-init:0 + not exists(fd.getParameter(_)) and + exists(fd.getResultVar(0)) and + n2.isAdditional(fd.getBody(), "result-zero-init:0") + or + // No parameters and no result vars: go directly to Before(body) + not exists(fd.getParameter(_)) and + not exists(fd.getResultVar(_)) and + funcDefBodyStart(fd, n2) + ) + or + // arg:i → param-init:i (for each parameter) + exists(int i | exists(fd.getParameter(i)) | + n1.isAdditional(fd.getBody(), "arg:" + i.toString()) and + n2.isAdditional(fd.getBody(), "param-init:" + i.toString()) + ) + or + // param-init:i → next: arg:(i+1), or result-zero-init:0, or Before(body) + exists(int i | exists(fd.getParameter(i)) | + n1.isAdditional(fd.getBody(), "param-init:" + i.toString()) and + ( + // Next parameter exists + exists(fd.getParameter(i + 1)) and + n2.isAdditional(fd.getBody(), "arg:" + (i + 1).toString()) + or + // No next parameter, has result vars: go to result-zero-init:0 + not exists(fd.getParameter(i + 1)) and + exists(fd.getResultVar(0)) and + n2.isAdditional(fd.getBody(), "result-zero-init:0") + or + // No next parameter and no result vars: go to Before(body) + not exists(fd.getParameter(i + 1)) and + not exists(fd.getResultVar(_)) and + funcDefBodyStart(fd, n2) + ) + ) + or + // result-zero-init:j → result-init:j (for each result variable) + exists(int j | exists(fd.getResultVar(j)) | + n1.isAdditional(fd.getBody(), "result-zero-init:" + j.toString()) and + n2.isAdditional(fd.getBody(), "result-init:" + j.toString()) + ) + or + // result-init:j → next: result-zero-init:(j+1), or Before(body) + exists(int j | exists(fd.getResultVar(j)) | + n1.isAdditional(fd.getBody(), "result-init:" + j.toString()) and + ( + // Next result var exists + exists(fd.getResultVar(j + 1)) and + n2.isAdditional(fd.getBody(), "result-zero-init:" + (j + 1).toString()) + or + // No next result var: go to Before(body) + not exists(fd.getResultVar(j + 1)) and + funcDefBodyStart(fd, n2) + ) + ) + or + funcDefBodyStep(fd, n1, n2) + or + // result-read:j → result-read:(j+1) or After(body) + exists(int j | exists(fd.getResultVar(j)) | + n1.isAdditional(fd.getBody(), "result-read:" + j.toString()) and + ( + exists(fd.getResultVar(j + 1)) and + n2.isAdditional(fd.getBody(), "result-read:" + (j + 1).toString()) + or + not exists(fd.getResultVar(j + 1)) and n2.isAfter(fd.getBody()) + ) + ) + ) + } + } +} diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index a4c730041082..2181a1d9f296 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -7,52 +7,67 @@ * 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 { + /** + * Holds if `e` is in a boolean conditional context, meaning its evaluation + * is split across branch successors rather than producing a single value. + * + * This mirrors the conditions under which the shared CFG library creates + * `TAfterValueNode`s instead of a single `TAfterNode` (see + * `inConditionalContext` in `shared/controlflow/.../ControlFlowGraph.qll`), + * restricted to the Go-relevant cases that affect `NotExpr` and + * `LogicalBinaryExpr`. + */ + private predicate isInBooleanCondContext(Expr e) { + e = any(IfStmt s).getCond() + or + e = any(ForStmt s).getCond() + or + exists(ExpressionSwitchStmt ess | + not exists(ess.getExpr()) and e = ess.getACase().(CaseClause).getExpr(_) + ) + or + e = any(LogicalBinaryExpr be | isInBooleanCondContext(be)).getAnOperand() + or + e = any(NotExpr ne | isInBooleanCondContext(ne)).getOperand() + or + e = any(ParenExpr pe | isInBooleanCondContext(pe)).getExpr() + } + /** * An IR instruction. */ class Instruction extends ControlFlow::Node { Instruction() { - this instanceof MkExprNode or - this instanceof MkLiteralElementInitNode or - this instanceof MkImplicitLiteralElementIndex or - this instanceof MkAssignNode or - this instanceof MkCompoundAssignRhsNode or - this instanceof MkExtractNode or - this instanceof MkZeroInitNode or - this instanceof MkFuncDeclNode or - this instanceof MkDeferNode or - this instanceof MkGoNode or - this instanceof MkConditionGuardNode or - this instanceof MkIncDecNode or - this instanceof MkIncDecRhs or - this instanceof MkImplicitOne or - this instanceof MkReturnNode or - this instanceof MkResultWriteNode or - this instanceof MkResultReadNode or - this instanceof MkSelectNode or - this instanceof MkSendNode or - this instanceof MkParameterInit or - this instanceof MkArgumentNode or - this instanceof MkResultInit or - this instanceof MkNextNode or - this instanceof MkImplicitTrue or - this instanceof MkCaseCheckNode or - this instanceof MkTypeSwitchImplicitVariable or - this instanceof MkImplicitLowerSliceBound or - this instanceof MkImplicitUpperSliceBound or - this instanceof MkImplicitMaxSliceBound or - this instanceof MkImplicitDeref or - this instanceof MkImplicitFieldSelection + this.isIn(_) + or + this.isAdditional(_, _) + or + this.isAfterTrue(_) + or + this.isAfterFalse(_) + or + // `NotExpr` and `LogicalBinaryExpr` are not in `postOrInOrder`, so they + // have no `isIn` node. When such an expression is not in a conditional + // context (so it has a single combined after-node rather than per-branch + // value-after-nodes), use that after-node as the value-producing + // instruction. In conditional contexts the value is already split + // across branches and the `ConditionGuardInstruction` for each branch + // captures the outcome, so no separate value instruction is needed. + exists(Expr e | + (e instanceof NotExpr or e instanceof LogicalBinaryExpr) and + not isInBooleanCondContext(e) and + this.isAfter(e) + ) } /** Holds if this instruction reads the value of variable or constant `v`. */ @@ -120,78 +135,98 @@ 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 EvalImplicitUpperSliceBoundInstruction and result = "implicit upper bound" or - this instanceof MkImplicitUpperSliceBound and result = "implicit upper bound" + this instanceof EvalImplicitMaxSliceBoundInstruction and result = "implicit maximum" or - this instanceof MkImplicitMaxSliceBound and result = "implicit maximum" + this instanceof EvalImplicitDerefInstruction and result = "implicit dereference" or - this instanceof MkImplicitDeref and result = "implicit dereference" + this instanceof ImplicitFieldReadInstruction and result = "implicit field selection" + } + } + + /** A condition guard instruction, representing a known boolean outcome for a condition. */ + private class ConditionGuardInstruction extends Instruction { + ConditionGuardInstruction() { + this.isAfterTrue(_) or - this instanceof MkImplicitFieldSelection and result = "implicit field selection" + this.isAfterFalse(_) } } /** * An IR instruction representing the evaluation of an expression. */ - class EvalInstruction extends Instruction, MkExprNode { + class EvalInstruction extends Instruction { Expr e; - EvalInstruction() { this = MkExprNode(e) } + EvalInstruction() { + this.isIn(e) + or + // `NotExpr` and `LogicalBinaryExpr` are not in `postOrInOrder`, so they + // don't have an `isIn` node. Only use the after-node when the + // expression is not in a conditional context; otherwise the value is + // split across `TAfterValueNode`s per branch and should not be exposed + // as a single value-producing instruction. + (e instanceof NotExpr or e instanceof LogicalBinaryExpr) and + not isInBooleanCondContext(e) and + this.isAfter(e) + } /** Gets the expression underlying this instruction. */ Expr getExpr() { result = e } @@ -217,10 +252,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 +267,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 +288,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 +307,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 +320,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 +331,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 +356,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 +369,51 @@ module IR { } /** - * An IR instruction for an implicit field read as part of reading a - * promoted field. - * - * If the field that is being implicitly read has a pointer type then this - * instruction represents an implicit dereference of it. + * An IR instruction for an implicit field read as part of reading a promoted field. */ - class ImplicitFieldReadInstruction extends FieldReadInstruction, MkImplicitFieldSelection { - ImplicitFieldReadInstruction() { this = MkImplicitFieldSelection(e, index, field) } + class ImplicitFieldReadInstruction extends Instruction { + SelectorExpr sel; + int idx; + Field fld; + + ImplicitFieldReadInstruction() { + this.isAdditional(sel, "implicit-field:" + idx.toString()) and + GoCfg::implicitFieldSelection(sel, idx, fld) + } - override predicate reads(ValueEntity v) { v = field } + /** Gets the `SelectorExpr` for which this is an implicit field read. */ + SelectorExpr getSelectorExpr() { result = sel } - override Type getResultType() { result = lookThroughPointerType(field.getType()) } + /** Gets the index of this implicit field read. */ + int getIndex() { result = idx } - override ControlFlow::Root getRoot() { result.isRootOf(e) } + /** Gets the field being read. */ + Field getField() { result = fld } - override string toString() { result = "implicit read of field " + field.toString() } + /** Gets the instruction computing the base value on which the field is read. */ + Instruction getBaseInstruction() { + exists(ImplicitFieldReadInstruction fri | + fri.getSelectorExpr() = sel and fri.getIndex() = pragma[only_bind_into](idx + 1) + | + result = fri + ) + or + not exists(ImplicitFieldReadInstruction fri | + fri.getSelectorExpr() = sel and fri.getIndex() = pragma[only_bind_into](idx + 1) + ) and + ( + result = implicitDerefInstruction(sel.getBase()) + or + not exists(implicitDerefInstruction(sel.getBase())) and + result = evalExprInstruction(sel.getBase()) + ) + } - override Location getLocation() { result = e.getBase().getLocation() } + override predicate reads(ValueEntity v) { v = fld } + + override Type getResultType() { result = lookThroughPointerType(fld.getType()) } + + override ControlFlow::Root getRoot() { result.isRootOf(sel) } } /** @@ -463,13 +514,14 @@ module IR { /** * An IR instruction that initializes a component of a composite literal. */ - class InitLiteralComponentInstruction extends WriteInstruction, MkLiteralElementInitNode { + class InitLiteralComponentInstruction extends WriteInstruction { CompositeLit lit; - int i; + int litIdx; Expr elt; InitLiteralComponentInstruction() { - this = MkLiteralElementInitNode(elt) and elt = lit.getElement(i) + this.isAdditional(elt, "lit-init") and + elt = lit.getElement(litIdx) } /** Gets the instruction allocating the composite literal. */ @@ -481,10 +533,6 @@ module IR { } override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override string toString() { result = "init of " + elt } - - override Location getLocation() { result = elt.getLocation() } } /** @@ -498,7 +546,7 @@ module IR { string getFieldName() { if elt instanceof KeyValueExpr then result = elt.(KeyValueExpr).getKey().(Ident).getName() - else pragma[only_bind_out](lit.getStructType()).hasOwnField(i, result, _, _) + else pragma[only_bind_out](lit.getStructType()).hasOwnField(litIdx, result, _, _) } /** Gets the initialized field. */ @@ -527,34 +575,26 @@ module IR { Instruction getIndex() { result = evalExprInstruction(elt.(KeyValueExpr).getKey()) or - result = MkImplicitLiteralElementIndex(elt) + result.(ImplicitLiteralElementIndexInstruction).isAdditional(elt, "lit-index") } } - /** - * An IR instruction that initializes an element of an array literal. - */ + /** An IR instruction that initializes an element of an array literal. */ class InitLiteralArrayElementInstruction extends InitLiteralElementInstruction { override ArrayType literalType; } - /** - * An IR instruction that initializes an element of a slice literal. - */ + /** An IR instruction that initializes an element of a slice literal. */ class InitLiteralSliceElementInstruction extends InitLiteralElementInstruction { override SliceType literalType; } - /** - * An IR instruction that initializes an element of a map literal. - */ + /** An IR instruction that initializes an element of a map literal. */ class InitLiteralMapElementInstruction extends InitLiteralElementInstruction { override MapType literalType; } - /** - * An IR instruction that writes to a field. - */ + /** An IR instruction that writes to a field. */ class FieldWriteInstruction extends WriteInstruction { override FieldTarget lhs; @@ -565,43 +605,35 @@ module IR { Field getField() { result = lhs.getField() } override predicate writesField(Instruction base, Field f, Instruction rhs) { - this.getBase() = base and - this.getField() = f and - this.getRhs() = rhs + this.getBase() = base and this.getField() = f and this.getRhs() = rhs } } - /** - * An IR instruction that writes to an element of an array, slice, or map. - */ + /** An IR instruction that writes to an element of an array, slice, or map. */ class ElementWriteInstruction extends WriteInstruction { override ElementTarget lhs; - /** Gets the instruction computing the base value on which the field is written. */ + /** Gets the instruction computing the base value on which the element is written. */ Instruction getBase() { result = lhs.getBase() } /** Gets the instruction computing the element index being written. */ Instruction getIndex() { result = lhs.getIndex() } override predicate writesElement(Instruction base, Instruction index) { - this.getBase() = base and - this.getIndex() = index + this.getBase() = base and this.getIndex() = index } } - /** Holds if `lit` does not specify any explicit keys. */ private predicate noExplicitKeys(CompositeLit lit) { not lit.getAnElement() instanceof KeyValueExpr } - /** Gets the index of the `i`th element in (array or slice) literal `lit`. */ private int getElementIndex(CompositeLit lit, int i) { ( lit.getType().getUnderlyingType() instanceof ArrayType or lit.getType().getUnderlyingType() instanceof SliceType ) and exists(Expr elt | elt = lit.getElement(i) | - // short-circuit computation for literals without any explicit keys noExplicitKeys(lit) and result = i or result = elt.(KeyValueExpr).getKey().getIntValue() @@ -618,10 +650,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 +670,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 +700,63 @@ module IR { spec.getNumName() = spec.getNumInit() and result = evalExprInstruction(spec.getInit(i)) or - result = MkZeroInitNode(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) + result = + implicitInitInstruction(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) ) or - result = MkCompoundAssignRhsNode(assgn) + result.(EvalCompoundAssignRhsInstruction).isAdditional(assgn, "compound-rhs") or - result = MkExtractNode(assgn, i) + result.(ExtractTupleElementInstruction).isAdditional(assgn, "extract:" + i.toString()) } override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = "assignment to " + this.getLhs() } - - override Location getLocation() { result = this.getLhs().getLocation() } } - /** An instruction computing the value of the right-hand side of a compound assignment. */ - class EvalCompoundAssignRhsInstruction extends Instruction, MkCompoundAssignRhsNode { + /** + * An instruction that computes the (implicit) right-hand side of a compound assignment. + */ + class EvalCompoundAssignRhsInstruction extends Instruction { CompoundAssignStmt assgn; - EvalCompoundAssignRhsInstruction() { this = MkCompoundAssignRhsNode(assgn) } + EvalCompoundAssignRhsInstruction() { this.isAdditional(assgn, "compound-rhs") } - /** Gets the underlying assignment of this instruction. */ + /** Gets the corresponding compound assignment statement. */ CompoundAssignStmt getAssignment() { result = assgn } override Type getResultType() { result = assgn.getRhs().getType() } override ControlFlow::Root getRoot() { result.isRootOf(assgn) } - - override string toString() { result = assgn.toString() } - - override Location getLocation() { result = assgn.getLocation() } } - /** - * An instruction selecting one of multiple values returned by a function, or either the key - * or the value of the iterator in a range loop, or the result or success value from a type - * assertion. - */ - class ExtractTupleElementInstruction extends Instruction, MkExtractNode { + /** An instruction extracting a component of a tuple value. */ + class ExtractTupleElementInstruction extends Instruction { AstNode s; int i; - ExtractTupleElementInstruction() { this = MkExtractNode(s, i) } + ExtractTupleElementInstruction() { + this.isAdditional(s, "extract:" + i.toString()) and + ( + exists(s.(Assignment).getLhs(i)) + or + exists(s.(ValueSpec).getNameExpr(i)) + or + s instanceof RangeStmt and i in [0, 1] + or + exists(s.(ReturnStmt).getEnclosingFunction().getType().(SignatureType).getResultType(i)) + or + exists( + s.(CallExpr) + .getArgument(0) + .stripParens() + .(CallExpr) + .getType() + .(TupleType) + .getComponentType(i) + ) + ) + } - /** Gets the instruction computing the tuple value from which one value is extracted. */ + /** Gets the instruction computing the tuple value from which the element is extracted. */ Instruction getBase() { exists(Expr baseExpr | baseExpr = s.(Assignment).getRhs() or @@ -716,14 +765,14 @@ module IR { result = evalExprInstruction(baseExpr) ) or - result = MkNextNode(s) + result.(GetNextEntryInstruction).isAdditional(s, "next") or result = evalExprInstruction(s.(ReturnStmt).getExpr()) or result = evalExprInstruction(s.(CallExpr).getArgument(0).stripParens()) } - /** Holds if this extracts the `idx`th value of the result of `base`. */ + /** Holds if this instruction extracts element `idx` from the tuple `base`. */ predicate extractsElement(Instruction base, int idx) { base = this.getBase() and idx = i } override Type getResultType() { @@ -738,51 +787,46 @@ module IR { rangeType.(PointerType).getBaseType().getUnderlyingType().(ArrayType).getElementType() or baseType = rangeType.(SliceType).getElementType() | - i = 0 and - result instanceof IntType + i = 0 and result instanceof IntType or - i = 1 and - result = baseType + i = 1 and result = baseType ) or rangeType instanceof StringType and ( - i = 0 and - result instanceof IntType + i = 0 and result instanceof IntType or result = Builtin::rune().getType() ) or exists(MapType map | map = rangeType | - i = 0 and - result = map.getKeyType() + i = 0 and result = map.getKeyType() or - i = 1 and - result = map.getValueType() + i = 1 and result = map.getValueType() ) or - i = 0 and - result = rangeType.(RecvChanType).getElementType() + i = 0 and result = rangeType.(RecvChanType).getElementType() or - i = 0 and - result = rangeType.(SendRecvChanType).getElementType() + i = 0 and result = rangeType.(SendRecvChanType).getElementType() ) } override ControlFlow::Root getRoot() { result.isRootOf(s) } - - override string toString() { result = s + "[" + i + "]" } - - override Location getLocation() { result = s.getLocation() } } /** - * An instruction that computes the zero value for a variable or constant. + * An instruction that computes the zero value to which a variable without an initializer + * expression is initialized. */ - class EvalImplicitInitInstruction extends Instruction, MkZeroInitNode { + class EvalImplicitInitInstruction extends Instruction { ValueEntity v; + int idx; + ValueSpec spec; - EvalImplicitInitInstruction() { this = MkZeroInitNode(v) } + EvalImplicitInitInstruction() { + this.isAdditional(spec, "zero-init:" + idx.toString()) and + spec.getNameExpr(idx) = v.getDeclaration() + } override Type getResultType() { result = v.getType() } @@ -814,82 +858,56 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "zero value for " + v } - - override Location getLocation() { result = v.getDeclaration().getLocation() } } - /** - * An instruction that corresponds to the declaration of a function. - */ - class DeclareFunctionInstruction extends Instruction, MkFuncDeclNode { + /** An instruction that declares a function. */ + class DeclareFunctionInstruction extends Instruction { FuncDecl fd; - DeclareFunctionInstruction() { this = MkFuncDeclNode(fd) } + DeclareFunctionInstruction() { this.isIn(fd) } override Type getResultType() { result = fd.getType() } - - override string toString() { result = fd.toString() } - - override Location getLocation() { result = fd.getLocation() } } - /** - * An instruction that corresponds to a `defer` statement. - */ - class DeferInstruction extends Instruction, MkDeferNode { + /** An instruction that corresponds to a `defer` statement. */ + class DeferInstruction extends Instruction { DeferStmt defer; - DeferInstruction() { this = MkDeferNode(defer) } + DeferInstruction() { this.isIn(defer) } override ControlFlow::Root getRoot() { result.isRootOf(defer) } - - override string toString() { result = defer.toString() } - - override Location getLocation() { result = defer.getLocation() } } - /** - * An instruction that corresponds to a `go` statement. - */ - class GoInstruction extends Instruction, MkGoNode { + /** An instruction that corresponds to a `go` statement. */ + class GoInstruction extends Instruction { GoStmt go; - GoInstruction() { this = MkGoNode(go) } + GoInstruction() { this.isIn(go) } override ControlFlow::Root getRoot() { result.isRootOf(go) } - - override string toString() { result = go.toString() } - - override Location getLocation() { result = go.getLocation() } } - /** - * An instruction that corresponds to an increment or decrement statement. - */ - class IncDecInstruction extends WriteInstruction, MkIncDecNode { + /** An instruction that corresponds to an increment or decrement statement. */ + class IncDecInstruction extends WriteInstruction { IncDecStmt ids; - IncDecInstruction() { this = MkIncDecNode(ids) } + IncDecInstruction() { this.isIn(ids) } - override Instruction getRhs() { result = MkIncDecRhs(ids) } + override Instruction getRhs() { + result.(EvalIncDecRhsInstruction).isAdditional(ids, "incdec-rhs") + } override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = ids.toString() } - - override Location getLocation() { result = ids.getLocation() } } /** * An instruction that computes the (implicit) right-hand side of an increment or * decrement statement. */ - class EvalIncDecRhsInstruction extends Instruction, MkIncDecRhs { + class EvalIncDecRhsInstruction extends Instruction { IncDecStmt ids; - EvalIncDecRhsInstruction() { this = MkIncDecRhs(ids) } + EvalIncDecRhsInstruction() { this.isAdditional(ids, "incdec-rhs") } /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } @@ -897,19 +915,13 @@ module IR { override Type getResultType() { result = ids.getOperand().getType() } override ControlFlow::Root getRoot() { result.isRootOf(ids) } - - override string toString() { result = "rhs of " + ids } - - override Location getLocation() { result = ids.getLocation() } } - /** - * An instruction computing the implicit operand `1` in an increment or decrement statement. - */ - class EvalImplicitOneInstruction extends Instruction, MkImplicitOne { + /** An instruction computing the implicit operand `1` in an increment or decrement statement. */ + class EvalImplicitOneInstruction extends Instruction { IncDecStmt ids; - EvalImplicitOneInstruction() { this = MkImplicitOne(ids) } + EvalImplicitOneInstruction() { this.isAdditional(ids, "implicit-one") } /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } @@ -925,25 +937,23 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "1" } - - override Location getLocation() { result = ids.getLocation() } } - /** - * An instruction corresponding to a return from a function. - */ - class ReturnInstruction extends Instruction, MkReturnNode { + /** An instruction corresponding to a return from a function. */ + class ReturnInstruction extends Instruction { ReturnStmt ret; - ReturnInstruction() { this = MkReturnNode(ret) } + ReturnInstruction() { this.isIn(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 } + predicate returnsMultipleResults() { + exists(ExtractTupleElementInstruction ext | ext.isAdditional(ret, _)) + or + ret.getNumExpr() > 1 + } /** Gets the instruction whose result is the (unique) result returned by this statement. */ Instruction getResult() { @@ -953,36 +963,34 @@ module IR { /** Gets the instruction whose result is the `i`th result returned by this statement. */ Instruction getResult(int i) { - result = MkExtractNode(ret, i) + result.isAdditional(ret, _) and + result.(ExtractTupleElementInstruction).extractsElement(_, i) or - not exists(MkExtractNode(ret, _)) and + not exists(ExtractTupleElementInstruction ext | ext.isAdditional(ret, _)) and result = evalExprInstruction(ret.getExpr(i)) } override ControlFlow::Root getRoot() { result.isRootOf(ret) } - - override string toString() { result = ret.toString() } - - override Location getLocation() { result = ret.getLocation() } } /** * An instruction that represents the implicit assignment to a result variable * performed by a return statement. */ - class WriteResultInstruction extends WriteInstruction, MkResultWriteNode { + class WriteResultInstruction extends WriteInstruction { ResultVariable var; - int i; - ReturnInstruction ret; + int idx; + ReturnStmt retStmt; WriteResultInstruction() { - exists(ReturnStmt retstmt | - this = MkResultWriteNode(var, i, retstmt) and - ret = MkReturnNode(retstmt) - ) + this.isAdditional(retStmt, "result-write:" + idx.toString()) and + var = retStmt.getEnclosingFunction().getResultVar(idx) and + exists(retStmt.getAnExpr()) } - override Instruction getRhs() { result = ret.getResult(i) } + private ReturnInstruction getReturnInstruction() { result.getReturnStmt() = retStmt } + + override Instruction getRhs() { result = this.getReturnInstruction().getResult(idx) } /** Gets the result variable being assigned. */ ResultVariable getResultVariable() { result = var } @@ -990,120 +998,132 @@ module IR { override Type getResultType() { result = var.getType() } override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit write of " + var } - - override Location getLocation() { result = ret.getResult(i).getLocation() } } /** * An instruction that reads the final value of a result variable upon returning * from a function. */ - class ReadResultInstruction extends Instruction, MkResultReadNode { + class ReadResultInstruction extends Instruction { ResultVariable var; + int idx; + FuncDef fd; - ReadResultInstruction() { this = MkResultReadNode(var) } + ReadResultInstruction() { + this.isAdditional(fd.getBody(), "result-read:" + idx.toString()) and + var = fd.getResultVar(idx) + } override predicate reads(ValueEntity v) { v = var } override Type getResultType() { result = var.getType() } override ControlFlow::Root getRoot() { var = result.(FuncDef).getAResultVar() } - - override string toString() { result = "implicit read of " + var } - - override Location getLocation() { result = var.getDeclaration().getLocation() } - } - - /** - * An instruction corresponding to a `select` statement. - */ - class SelectInstruction extends Instruction, MkSelectNode { - SelectStmt sel; - - SelectInstruction() { this = MkSelectNode(sel) } - - override ControlFlow::Root getRoot() { result.isRootOf(sel) } - - override string toString() { result = sel.toString() } - - override Location getLocation() { result = sel.getLocation() } } - /** - * An instruction corresponding to a send statement. - */ - class SendInstruction extends Instruction, MkSendNode { + /** An instruction corresponding to a send statement. */ + class SendInstruction extends Instruction { SendStmt send; - SendInstruction() { this = MkSendNode(send) } + SendInstruction() { this.isAdditional(send, "send") } override ControlFlow::Root getRoot() { result.isRootOf(send) } - - override string toString() { result = send.toString() } - - override Location getLocation() { result = send.getLocation() } } - /** - * An instruction initializing a parameter to the corresponding argument. - */ - class InitParameterInstruction extends WriteInstruction, MkParameterInit { + /** An instruction initializing a parameter to the corresponding argument. */ + class InitParameterInstruction extends WriteInstruction { Parameter parm; + int idx; + FuncDef fd; - InitParameterInstruction() { this = MkParameterInit(parm) } + InitParameterInstruction() { + this.isAdditional(fd.getBody(), "param-init:" + idx.toString()) and + parm = fd.getParameter(idx) + } - override Instruction getRhs() { result = MkArgumentNode(parm) } + override Instruction getRhs() { + result.(ReadArgumentInstruction).isAdditional(fd.getBody(), "arg:" + idx.toString()) + } override ControlFlow::Root getRoot() { result = parm.getFunction() } - - override string toString() { result = "initialization of " + parm } - - override Location getLocation() { result = parm.getDeclaration().getLocation() } } - /** - * An instruction reading the value of a function argument. - */ - class ReadArgumentInstruction extends Instruction, MkArgumentNode { + /** An instruction reading the value of a function argument. */ + class ReadArgumentInstruction extends Instruction { Parameter parm; + int idx; + FuncDef fd; - ReadArgumentInstruction() { this = MkArgumentNode(parm) } + ReadArgumentInstruction() { + this.isAdditional(fd.getBody(), "arg:" + idx.toString()) and + parm = fd.getParameter(idx) + } override Type getResultType() { result = parm.getType() } override ControlFlow::Root getRoot() { result = parm.getFunction() } + } + + /** An instruction initializing a result variable to its zero value. */ + class InitResultInstruction extends WriteInstruction { + ResultVariable res; + int idx; + FuncDef fd; + + InitResultInstruction() { + this.isAdditional(fd.getBody(), "result-init:" + idx.toString()) and + res = fd.getResultVar(idx) + } - override string toString() { result = "argument corresponding to " + parm } + override Instruction getRhs() { + result + .(ResultZeroInitInstruction) + .isAdditional(fd.getBody(), "result-zero-init:" + idx.toString()) + } - override Location getLocation() { result = parm.getDeclaration().getLocation() } + override ControlFlow::Root getRoot() { result = res.getFunction() } } - /** - * An instruction initializing a result variable to its zero value. - */ - class InitResultInstruction extends WriteInstruction, MkResultInit { + private class ResultZeroInitInstruction extends Instruction { ResultVariable res; + int idx; + FuncDef fd; + + ResultZeroInitInstruction() { + this.isAdditional(fd.getBody(), "result-zero-init:" + idx.toString()) and + res = fd.getResultVar(idx) + } - InitResultInstruction() { this = MkResultInit(res) } + override Type getResultType() { result = res.getType() } - override Instruction getRhs() { result = MkZeroInitNode(res) } + override ControlFlow::Root getRoot() { result.isRootOf(fd) } - override ControlFlow::Root getRoot() { result = res.getFunction() } + 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 toString() { result = "initialization of " + res } + override string getStringValue() { + res.getType().getUnderlyingType() instanceof StringType and result = "" + } - override Location getLocation() { result = res.getDeclaration().getLocation() } + override boolean getBoolValue() { + res.getType().getUnderlyingType() instanceof BoolType and result = false + } + + override predicate isConst() { any() } + + override predicate isPlatformIndependentConstant() { any() } } - /** - * An instruction that gets the next key-value pair in a range loop. - */ - class GetNextEntryInstruction extends Instruction, MkNextNode { + /** An instruction that gets the next key-value pair in a range loop. */ + class GetNextEntryInstruction extends Instruction { RangeStmt rs; - GetNextEntryInstruction() { this = MkNextNode(rs) } + GetNextEntryInstruction() { this.isAdditional(rs, "next") } /** * Gets the instruction computing the value whose key-value pairs this instruction reads. @@ -1111,19 +1131,15 @@ module IR { Instruction getDomain() { result = evalExprInstruction(rs.getDomain()) } override ControlFlow::Root getRoot() { result.isRootOf(rs) } - - override string toString() { result = "next key-value pair in range" } - - override Location getLocation() { result = rs.getDomain().getLocation() } } /** * An instruction computing the implicit `true` value in an expression-less `switch` statement. */ - class EvalImplicitTrueInstruction extends Instruction, MkImplicitTrue { - Stmt stmt; + class EvalImplicitTrueInstruction extends Instruction { + ExpressionSwitchStmt stmt; - EvalImplicitTrueInstruction() { this = MkImplicitTrue(stmt) } + EvalImplicitTrueInstruction() { this.isAdditional(stmt, "implicit-true") } override Type getResultType() { result instanceof BoolType } @@ -1136,91 +1152,46 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "true" } - - override Location getLocation() { result = stmt.getLocation() } } /** * An instruction corresponding to the implicit comparison or type check performed by an * expression in a `case` clause. - * - * For example, consider this `switch` statement: - * - * ```go - * switch x { - * case 2, y+1: - * ... - * } - * ``` - * - * The expressions `2` and `y+1` are implicitly compared to `x`. These comparisons are - * represented by case instructions. */ - class CaseInstruction extends Instruction, MkCaseCheckNode { + class CaseInstruction extends Instruction { CaseClause cc; int i; - CaseInstruction() { this = MkCaseCheckNode(cc, i) } + CaseInstruction() { + this.isAdditional(cc, "case-check:" + i.toString()) and + exists(cc.getExpr(i)) + } override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "case " + cc.getExpr(i) } - - override Location getLocation() { result = cc.getExpr(i).getLocation() } } /** - * An instruction corresponding to the implicit declaration of the variable - * `lv` in case clause `cc` and its assignment of the value `switchExpr` from - * the guard. This only occurs in case clauses in a type switch statement - * which declares a variable in its guard. - * - * For example, consider this type switch statement: - * - * ```go - * switch y := x.(type) { - * case Type1: - * f(y) - * ... - * } - * ``` - * - * The `y` inside the case clause is actually a local variable with type - * `Type1` that is implicitly declared at the top of the case clause. In - * default clauses and case clauses which list more than one type, the type - * of the implicitly declared variable is the type of `switchExpr`. + * An instruction corresponding to the implicit declaration and assignment of a variable + * in a type switch case clause. */ - class TypeSwitchImplicitVariableInstruction extends Instruction, MkTypeSwitchImplicitVariable { + class TypeSwitchImplicitVariableInstruction extends Instruction { CaseClause cc; - LocalVariable lv; - Expr switchExpr; - TypeSwitchImplicitVariableInstruction() { - this = MkTypeSwitchImplicitVariable(cc, lv, switchExpr) - } + TypeSwitchImplicitVariableInstruction() { this.isAdditional(cc, "type-switch-var") } override predicate writes(ValueEntity v, Instruction rhs) { - v = lv and - rhs = evalExprInstruction(switchExpr) + v = cc.getImplicitlyDeclaredVariable() and + exists(TypeSwitchStmt ts | cc = ts.getACase() | rhs = evalExprInstruction(ts.getExpr())) } override ControlFlow::Root getRoot() { result.isRootOf(cc) } - - override string toString() { result = "implicit type switch variable declaration" } - - override Location getLocation() { result = cc.getLocation() } } - /** - * An instruction computing the implicit lower slice bound of zero in a slice expression without - * an explicit lower bound. - */ - class EvalImplicitLowerSliceBoundInstruction extends Instruction, MkImplicitLowerSliceBound { + /** An instruction computing the implicit lower bound of a slice expression. */ + class EvalImplicitLowerSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitLowerSliceBoundInstruction() { this = MkImplicitLowerSliceBound(slice) } + EvalImplicitLowerSliceBoundInstruction() { this.isAdditional(slice, "implicit-low") } override Type getResultType() { result instanceof IntType } @@ -1233,56 +1204,38 @@ module IR { override predicate isConst() { any() } override predicate isPlatformIndependentConstant() { any() } - - override string toString() { result = "0" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction computing the implicit upper slice bound in a slice expression without an - * explicit upper bound. - */ - class EvalImplicitUpperSliceBoundInstruction extends Instruction, MkImplicitUpperSliceBound { + /** An instruction computing the implicit upper bound of a slice expression. */ + class EvalImplicitUpperSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitUpperSliceBoundInstruction() { this = MkImplicitUpperSliceBound(slice) } + EvalImplicitUpperSliceBoundInstruction() { this.isAdditional(slice, "implicit-high") } override ControlFlow::Root getRoot() { result.isRootOf(slice) } override Type getResultType() { result instanceof IntType } - - override string toString() { result = "len" } - - override Location getLocation() { result = slice.getLocation() } } - /** - * An instruction computing the implicit maximum slice bound in a slice expression without an - * explicit maximum bound. - */ - class EvalImplicitMaxSliceBoundInstruction extends Instruction, MkImplicitMaxSliceBound { + /** An instruction computing the implicit maximum bound of a slice expression. */ + class EvalImplicitMaxSliceBoundInstruction extends Instruction { SliceExpr slice; - EvalImplicitMaxSliceBoundInstruction() { this = MkImplicitMaxSliceBound(slice) } + EvalImplicitMaxSliceBoundInstruction() { this.isAdditional(slice, "implicit-max") } override ControlFlow::Root getRoot() { result.isRootOf(slice) } override Type getResultType() { result instanceof IntType } - - override string toString() { result = "cap" } - - override Location getLocation() { result = slice.getLocation() } } /** - * An instruction implicitly dereferencing the base in a field or method reference through a - * pointer, or the base in an element or slice reference through a pointer. + * An instruction computing the implicit dereference of a pointer used as the base of a field + * or method access, element access, or slice expression. */ - class EvalImplicitDerefInstruction extends Instruction, MkImplicitDeref { + class EvalImplicitDerefInstruction extends Instruction { Expr e; - EvalImplicitDerefInstruction() { this = MkImplicitDeref(e) } + EvalImplicitDerefInstruction() { this.isAdditional(e, "implicit-deref") } /** Gets the operand that is being dereferenced. */ Expr getOperand() { result = e } @@ -1292,12 +1245,43 @@ module IR { } override ControlFlow::Root getRoot() { result.isRootOf(e) } - - override string toString() { result = "implicit dereference" } - - override Location getLocation() { result = e.getLocation() } } + /** A representation of the target of a write instruction. */ + newtype TWriteTarget = + /** A left-hand side of an assignment. */ + MkLhs(ControlFlow::Node write, Expr lhs) { + exists(AstNode assgn, int i | write.isAdditional(assgn, "assign:" + i.toString()) | + lhs = assgn.(Assignment).getLhs(i).stripParens() + or + lhs = assgn.(ValueSpec).getNameExpr(i) + or + exists(RangeStmt rs | rs = assgn | + i = 0 and lhs = rs.getKey().stripParens() + or + i = 1 and lhs = rs.getValue().stripParens() + ) + ) + or + exists(IncDecStmt ids | write.isIn(ids) | lhs = ids.getOperand().stripParens()) + or + exists(FuncDef fd, int idx | + write.isAdditional(fd.getBody(), "param-init:" + idx.toString()) and + lhs = fd.getParameter(idx).getDeclaration() + ) + or + exists(FuncDef fd, int idx | + write.isAdditional(fd.getBody(), "result-init:" + idx.toString()) and + lhs = fd.getResultVar(idx).getDeclaration() + ) + } or + /** A composite literal element target. */ + MkLiteralElementTarget(ControlFlow::Node write) { + write.isAdditional(any(CompositeLit lit).getAnElement(), "lit-init") + } or + /** A result variable write target. */ + MkResultWriteTarget(WriteResultInstruction w) + /** A representation of the target of a write instruction. */ class WriteTarget extends TWriteTarget { ControlFlow::Node w; @@ -1330,10 +1314,6 @@ module IR { * DEPRECATED: Use `getLocation()` instead. * * Holds if this element is at the specified location. - * The location spans column `startcolumn` of line `startline` to - * column `endcolumn` of line `endline` in file `filepath`. - * For more information, see - * [Locations](https://codeql.github.com/docs/writing-codeql-queries/providing-locations-in-codeql-queries/). */ deprecated predicate hasLocationInfo( string filepath, int startline, int startcolumn, int endline, int endcolumn @@ -1395,10 +1375,6 @@ module IR { /** Gets the constant this refers to, if any. */ Constant getConstant() { this.refersTo(result) } - - override string toString() { result = this.getName() } - - override Location getLocation() { result = loc.getLocation() } } /** A reference to a field, used as the target of a write. */ @@ -1416,7 +1392,7 @@ module IR { result = w.(InitLiteralStructFieldInstruction).getBase() } - /** Get the type of the base of this field access, that is, the type that contains the field. */ + /** Gets the type of the base of this field access, that is, the type that contains the field. */ Type getBaseType() { result = this.getBase().getResultType() } override predicate refersTo(ValueEntity e) { @@ -1429,24 +1405,10 @@ module IR { /** Gets the field this refers to, if it can be determined. */ Field getField() { this.refersTo(result) } - - override string toString() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | - result = "field " + sel.getSelector().getName() - ) - or - result = "field " + w.(InitLiteralStructFieldInstruction).getFieldName() - } - - override Location getLocation() { - exists(SelectorExpr sel | this = MkLhs(_, sel) | result = sel.getLocation()) - or - result = w.(InitLiteralStructFieldInstruction).getLocation() - } } /** - * A reference to an element of an array, slice or map, used as the target of a write. + * A reference to an element of an array, slice, or map, used as the target of a write. */ class ElementTarget extends WriteTarget { ElementTarget() { @@ -1468,14 +1430,6 @@ module IR { or result = w.(InitLiteralElementInstruction).getIndex() } - - override string toString() { result = "element" } - - override Location getLocation() { - exists(IndexExpr idx | this = MkLhs(_, idx) | result = idx.getLocation()) - or - result = w.(InitLiteralElementInstruction).getLocation() - } } /** @@ -1495,66 +1449,75 @@ module IR { result = evalExprInstruction(base) ) } - - override string toString() { result = lhs.toString() } - - override Location getLocation() { result = lhs.getLocation() } } /** * Gets the (final) instruction computing the value of `e`. - * - * Note that some expressions (such as type expressions or labels) have no corresponding - * instruction, so this predicate is undefined for them. - * - * Short-circuiting expressions that are purely used for control flow (meaning that their - * value is not stored in a variable or used to compute the value of a non-shortcircuiting - * expression) do not have a final instruction either. */ Instruction evalExprInstruction(Expr e) { - result = MkExprNode(e) or + result.(EvalInstruction).getExpr() = e + or result = evalExprInstruction(e.(ParenExpr).getExpr()) } /** * Gets the instruction corresponding to the initialization of `r`. */ - InitParameterInstruction initRecvInstruction(ReceiverVariable r) { result = MkParameterInit(r) } + InitParameterInstruction initRecvInstruction(ReceiverVariable r) { + exists(FuncDef fd, int i | + fd.getParameter(i) = r and result.isAdditional(fd.getBody(), "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.getBody(), "param-init:" + i.toString()) + ) + } /** * Gets the instruction corresponding to the `i`th assignment happening at * `assgn` (0-based). */ - AssignInstruction assignInstruction(Assignment assgn, int i) { result = MkAssignNode(assgn, i) } + AssignInstruction assignInstruction(Assignment assgn, int i) { + result.isAdditional(assgn, "assign:" + i.toString()) and + exists(assgn.getLhs(i)) + } /** * Gets the instruction corresponding to the `i`th initialization happening * at `spec` (0-based). */ - AssignInstruction initInstruction(ValueSpec spec, int i) { result = MkAssignNode(spec, i) } + AssignInstruction initInstruction(ValueSpec spec, int i) { + result.isAdditional(spec, "assign:" + i.toString()) and + exists(spec.getNameExpr(i)) + } /** * Gets the instruction corresponding to the assignment of the key variable * of range statement `rs`. */ - AssignInstruction assignKeyInstruction(RangeStmt rs) { result = MkAssignNode(rs, 0) } + AssignInstruction assignKeyInstruction(RangeStmt rs) { result.isAdditional(rs, "assign:0") } /** * Gets the instruction corresponding to the assignment of the value variable * of range statement `rs`. */ - AssignInstruction assignValueInstruction(RangeStmt rs) { result = MkAssignNode(rs, 1) } + AssignInstruction assignValueInstruction(RangeStmt rs) { result.isAdditional(rs, "assign:1") } /** * Gets the instruction corresponding to the implicit initialization of `v` * to its zero value. */ - EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { result = MkZeroInitNode(v) } + EvalImplicitInitInstruction implicitInitInstruction(ValueEntity v) { + exists(ValueSpec spec, int i | + spec.getNameExpr(i) = v.getDeclaration() and + result.isAdditional(spec, "zero-init:" + i.toString()) + ) + } /** * Gets the instruction corresponding to the extraction of the `idx`th element @@ -1568,28 +1531,30 @@ module IR { * Gets the instruction corresponding to the implicit lower bound of slice `e`, if any. */ EvalImplicitLowerSliceBoundInstruction implicitLowerSliceBoundInstruction(SliceExpr e) { - result = MkImplicitLowerSliceBound(e) + result.isAdditional(e, "implicit-low") } /** * Gets the instruction corresponding to the implicit upper bound of slice `e`, if any. */ EvalImplicitUpperSliceBoundInstruction implicitUpperSliceBoundInstruction(SliceExpr e) { - result = MkImplicitUpperSliceBound(e) + result.isAdditional(e, "implicit-high") } /** * Gets the instruction corresponding to the implicit maximum bound of slice `e`, if any. */ EvalImplicitMaxSliceBoundInstruction implicitMaxSliceBoundInstruction(SliceExpr e) { - result = MkImplicitMaxSliceBound(e) + result.isAdditional(e, "implicit-max") } /** * Gets the implicit dereference instruction for `e`, where `e` is a pointer used as the base * in a field/method access, element access, or slice expression. */ - EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { result = MkImplicitDeref(e) } + EvalImplicitDerefInstruction implicitDerefInstruction(Expr e) { + result.isAdditional(e, "implicit-deref") + } /** Gets the base of `insn`, if `insn` is an implicit field read. */ Instruction lookThroughImplicitFieldRead(Instruction insn) { diff --git a/go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll b/go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll index 3547e70b858a..541d52e6da35 100644 --- a/go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll +++ b/go/ql/lib/semmle/go/dataflow/GlobalValueNumbering.qll @@ -200,7 +200,7 @@ private ControlFlow::Node mostRecentSideEffect(ControlFlow::Node entry, ControlF cached private ControlFlow::Node mostRecentSideEffectUnique(ControlFlow::Node node) { - result = unique( | | mostRecentSideEffect(_, node)) + result = unique( | | mostRecentSideEffect(getControlFlowEntry(node), node)) } /** Used to represent the "global value number" of an expression. */ diff --git a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll index 9648335a6dde..8a66103ba5e9 100644 --- a/go/ql/lib/semmle/go/dataflow/SsaImpl.qll +++ b/go/ql/lib/semmle/go/dataflow/SsaImpl.qll @@ -89,7 +89,7 @@ private module Internal { /** Holds if the `i`th node of `bb` in function `f` is an entry node. */ private predicate entryNode(FuncDef f, ReachableBasicBlock bb, int i) { f = bb.getScope() and - bb.getNode(i).isEntryNode() + bb.getNode(i).(ControlFlow::Node).isEntryNode() } /** diff --git a/go/ql/lib/semmle/go/frameworks/Glog.qll b/go/ql/lib/semmle/go/frameworks/Glog.qll index 9715cc910733..eb7210fd36d8 100644 --- a/go/ql/lib/semmle/go/frameworks/Glog.qll +++ b/go/ql/lib/semmle/go/frameworks/Glog.qll @@ -59,7 +59,7 @@ module Glog { /** Holds if this function takes a format string. */ predicate formatter() { format = "f" } - override predicate mayReturnNormally() { level != "Fatal" and level != "Exit" } + override predicate mustNotReturnNormally() { level = "Fatal" or level = "Exit" } } private class StringFormatter extends StringOps::Formatting::Range instanceof GlogFunction { diff --git a/go/ql/lib/semmle/go/frameworks/Logrus.qll b/go/ql/lib/semmle/go/frameworks/Logrus.qll index 069764318d57..e124fd477293 100644 --- a/go/ql/lib/semmle/go/frameworks/Logrus.qll +++ b/go/ql/lib/semmle/go/frameworks/Logrus.qll @@ -29,8 +29,8 @@ module Logrus { ) } - override predicate mayReturnNormally() { - not exists(string level, string suffix | level = ["Fatal", "Panic"] | + override predicate mustNotReturnNormally() { + exists(string level, string suffix | level = ["Fatal", "Panic"] | this.getName() = level + suffix ) } diff --git a/go/ql/lib/semmle/go/frameworks/Revel.qll b/go/ql/lib/semmle/go/frameworks/Revel.qll index c6250c2f8a51..085db1d2b0a8 100644 --- a/go/ql/lib/semmle/go/frameworks/Revel.qll +++ b/go/ql/lib/semmle/go/frameworks/Revel.qll @@ -154,7 +154,7 @@ module Revel { private IR::EvalInstruction skipImplicitFieldReads(IR::Instruction insn) { result = insn or - result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBase()) + result = skipImplicitFieldReads(insn.(IR::ImplicitFieldReadInstruction).getBaseInstruction()) } /** A call to `Controller.Render`. */ diff --git a/go/ql/lib/semmle/go/frameworks/Zap.qll b/go/ql/lib/semmle/go/frameworks/Zap.qll index cf0abcd9336e..95947d22116e 100644 --- a/go/ql/lib/semmle/go/frameworks/Zap.qll +++ b/go/ql/lib/semmle/go/frameworks/Zap.qll @@ -54,7 +54,7 @@ module Zap { this.hasQualifiedName(packagePath(), "SugaredLogger", "Fatal" + getSuffix()) } - override predicate mayReturnNormally() { none() } + override predicate mustNotReturnNormally() { any() } } /** A Zap logging function which always panics. */ diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll index 1ff1a4b320fa..fece3cbab9ae 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/Log.qll @@ -44,7 +44,7 @@ module Log { ) } - override predicate mayReturnNormally() { none() } + override predicate mustNotReturnNormally() { any() } } /** A log function which must panic. */ diff --git a/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll b/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll index 0a633de08c82..6a3550e1cbd4 100644 --- a/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll +++ b/go/ql/lib/semmle/go/frameworks/stdlib/Os.qll @@ -12,7 +12,7 @@ module Os { private class Exit extends Function { Exit() { this.hasQualifiedName("os", "Exit") } - override predicate mayReturnNormally() { none() } + override predicate mustNotReturnNormally() { any() } } // These models are not implemented using Models-as-Data because they represent reverse flow. diff --git a/go/ql/test/experimental/CWE-369/DivideByZero.expected b/go/ql/test/experimental/CWE-369/DivideByZero.expected index 01211d9d107a..7f206a30c8a4 100644 --- a/go/ql/test/experimental/CWE-369/DivideByZero.expected +++ b/go/ql/test/experimental/CWE-369/DivideByZero.expected @@ -8,23 +8,23 @@ edges | DivideByZero.go:10:12:10:16 | selection of URL | DivideByZero.go:10:12:10:24 | call to Query | provenance | Src:MaD:1 MaD:2 | | DivideByZero.go:10:12:10:24 | call to Query | DivideByZero.go:11:27:11:32 | param1 | provenance | | -| DivideByZero.go:11:2:11:33 | ... := ...[0] | DivideByZero.go:12:16:12:20 | value | provenance | | -| DivideByZero.go:11:27:11:32 | param1 | DivideByZero.go:11:2:11:33 | ... := ...[0] | provenance | Config | +| DivideByZero.go:11:2:11:33 | extract:0 ... := ... | DivideByZero.go:12:16:12:20 | value | provenance | | +| DivideByZero.go:11:27:11:32 | param1 | DivideByZero.go:11:2:11:33 | extract:0 ... := ... | provenance | Config | | DivideByZero.go:17:12:17:16 | selection of URL | DivideByZero.go:17:12:17:24 | call to Query | provenance | Src:MaD:1 MaD:2 | | DivideByZero.go:17:12:17:24 | call to Query | DivideByZero.go:18:11:18:24 | type conversion | provenance | | | DivideByZero.go:18:11:18:24 | type conversion | DivideByZero.go:19:16:19:20 | value | provenance | | | DivideByZero.go:24:12:24:16 | selection of URL | DivideByZero.go:24:12:24:24 | call to Query | provenance | Src:MaD:1 MaD:2 | | DivideByZero.go:24:12:24:24 | call to Query | DivideByZero.go:25:31:25:36 | param1 | provenance | | -| DivideByZero.go:25:2:25:45 | ... := ...[0] | DivideByZero.go:26:16:26:20 | value | provenance | | -| DivideByZero.go:25:31:25:36 | param1 | DivideByZero.go:25:2:25:45 | ... := ...[0] | provenance | Config | +| DivideByZero.go:25:2:25:45 | extract:0 ... := ... | DivideByZero.go:26:16:26:20 | value | provenance | | +| DivideByZero.go:25:31:25:36 | param1 | DivideByZero.go:25:2:25:45 | extract:0 ... := ... | provenance | Config | | DivideByZero.go:31:12:31:16 | selection of URL | DivideByZero.go:31:12:31:24 | call to Query | provenance | Src:MaD:1 MaD:2 | | DivideByZero.go:31:12:31:24 | call to Query | DivideByZero.go:32:33:32:38 | param1 | provenance | | -| DivideByZero.go:32:2:32:43 | ... := ...[0] | DivideByZero.go:33:16:33:20 | value | provenance | | -| DivideByZero.go:32:33:32:38 | param1 | DivideByZero.go:32:2:32:43 | ... := ...[0] | provenance | Config | +| DivideByZero.go:32:2:32:43 | extract:0 ... := ... | DivideByZero.go:33:16:33:20 | value | provenance | | +| DivideByZero.go:32:33:32:38 | param1 | DivideByZero.go:32:2:32:43 | extract:0 ... := ... | provenance | Config | | DivideByZero.go:38:12:38:16 | selection of URL | DivideByZero.go:38:12:38:24 | call to Query | provenance | Src:MaD:1 MaD:2 | | DivideByZero.go:38:12:38:24 | call to Query | DivideByZero.go:39:32:39:37 | param1 | provenance | | -| DivideByZero.go:39:2:39:46 | ... := ...[0] | DivideByZero.go:40:16:40:20 | value | provenance | | -| DivideByZero.go:39:32:39:37 | param1 | DivideByZero.go:39:2:39:46 | ... := ...[0] | provenance | Config | +| DivideByZero.go:39:2:39:46 | extract:0 ... := ... | DivideByZero.go:40:16:40:20 | value | provenance | | +| DivideByZero.go:39:32:39:37 | param1 | DivideByZero.go:39:2:39:46 | extract:0 ... := ... | provenance | Config | | DivideByZero.go:54:12:54:16 | selection of URL | DivideByZero.go:54:12:54:24 | call to Query | provenance | Src:MaD:1 MaD:2 | | DivideByZero.go:54:12:54:24 | call to Query | DivideByZero.go:55:11:55:24 | type conversion | provenance | | | DivideByZero.go:55:11:55:24 | type conversion | DivideByZero.go:57:17:57:21 | value | provenance | | @@ -34,7 +34,7 @@ models nodes | DivideByZero.go:10:12:10:16 | selection of URL | semmle.label | selection of URL | | DivideByZero.go:10:12:10:24 | call to Query | semmle.label | call to Query | -| DivideByZero.go:11:2:11:33 | ... := ...[0] | semmle.label | ... := ...[0] | +| DivideByZero.go:11:2:11:33 | extract:0 ... := ... | semmle.label | extract:0 ... := ... | | DivideByZero.go:11:27:11:32 | param1 | semmle.label | param1 | | DivideByZero.go:12:16:12:20 | value | semmle.label | value | | DivideByZero.go:17:12:17:16 | selection of URL | semmle.label | selection of URL | @@ -43,17 +43,17 @@ nodes | DivideByZero.go:19:16:19:20 | value | semmle.label | value | | DivideByZero.go:24:12:24:16 | selection of URL | semmle.label | selection of URL | | DivideByZero.go:24:12:24:24 | call to Query | semmle.label | call to Query | -| DivideByZero.go:25:2:25:45 | ... := ...[0] | semmle.label | ... := ...[0] | +| DivideByZero.go:25:2:25:45 | extract:0 ... := ... | semmle.label | extract:0 ... := ... | | DivideByZero.go:25:31:25:36 | param1 | semmle.label | param1 | | DivideByZero.go:26:16:26:20 | value | semmle.label | value | | DivideByZero.go:31:12:31:16 | selection of URL | semmle.label | selection of URL | | DivideByZero.go:31:12:31:24 | call to Query | semmle.label | call to Query | -| DivideByZero.go:32:2:32:43 | ... := ...[0] | semmle.label | ... := ...[0] | +| DivideByZero.go:32:2:32:43 | extract:0 ... := ... | semmle.label | extract:0 ... := ... | | DivideByZero.go:32:33:32:38 | param1 | semmle.label | param1 | | DivideByZero.go:33:16:33:20 | value | semmle.label | value | | DivideByZero.go:38:12:38:16 | selection of URL | semmle.label | selection of URL | | DivideByZero.go:38:12:38:24 | call to Query | semmle.label | call to Query | -| DivideByZero.go:39:2:39:46 | ... := ...[0] | semmle.label | ... := ...[0] | +| DivideByZero.go:39:2:39:46 | extract:0 ... := ... | semmle.label | extract:0 ... := ... | | DivideByZero.go:39:32:39:37 | param1 | semmle.label | param1 | | DivideByZero.go:40:16:40:20 | value | semmle.label | value | | DivideByZero.go:54:12:54:16 | selection of URL | semmle.label | selection of URL | diff --git a/go/ql/test/experimental/CWE-74/DsnInjectionLocal.expected b/go/ql/test/experimental/CWE-74/DsnInjectionLocal.expected index 4b26395c8e7c..1a938af237c2 100644 --- a/go/ql/test/experimental/CWE-74/DsnInjectionLocal.expected +++ b/go/ql/test/experimental/CWE-74/DsnInjectionLocal.expected @@ -8,14 +8,14 @@ edges | Dsn.go:28:102:28:109 | index expression | Dsn.go:28:11:28:110 | []type{args} [array] | provenance | | | Dsn.go:28:102:28:109 | index expression | Dsn.go:28:11:28:110 | call to Sprintf | provenance | FunctionModel | | Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | Dsn.go:67:102:67:104 | cfg [pointer] | provenance | | -| Dsn.go:63:9:63:11 | implicit dereference [postupdate] | Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | provenance | | -| Dsn.go:63:9:63:11 | implicit dereference [postupdate] | Dsn.go:67:102:67:108 | selection of dsn | provenance | | +| Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | provenance | | +| Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | Dsn.go:67:102:67:108 | selection of dsn | provenance | | | Dsn.go:63:19:63:25 | selection of Args | Dsn.go:63:19:63:29 | slice expression | provenance | Src:MaD:1 | -| Dsn.go:63:19:63:29 | slice expression | Dsn.go:63:9:63:11 | implicit dereference [postupdate] | provenance | FunctionModel | +| Dsn.go:63:19:63:29 | slice expression | Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | provenance | FunctionModel | | Dsn.go:67:11:67:109 | []type{args} [array] | Dsn.go:67:11:67:109 | call to Sprintf | provenance | MaD:2 | | Dsn.go:67:11:67:109 | call to Sprintf | Dsn.go:68:29:68:33 | dbDSN | provenance | | -| Dsn.go:67:102:67:104 | cfg [pointer] | Dsn.go:67:102:67:104 | implicit dereference | provenance | | -| Dsn.go:67:102:67:104 | implicit dereference | Dsn.go:67:102:67:108 | selection of dsn | provenance | | +| Dsn.go:67:102:67:104 | cfg [pointer] | Dsn.go:67:102:67:104 | implicit-deref cfg | provenance | | +| Dsn.go:67:102:67:104 | implicit-deref cfg | Dsn.go:67:102:67:108 | selection of dsn | provenance | | | Dsn.go:67:102:67:108 | selection of dsn | Dsn.go:67:11:67:109 | []type{args} [array] | provenance | | | Dsn.go:67:102:67:108 | selection of dsn | Dsn.go:67:11:67:109 | call to Sprintf | provenance | FunctionModel | models @@ -28,13 +28,13 @@ nodes | Dsn.go:28:102:28:109 | index expression | semmle.label | index expression | | Dsn.go:29:29:29:33 | dbDSN | semmle.label | dbDSN | | Dsn.go:63:9:63:11 | cfg [postupdate] [pointer] | semmle.label | cfg [postupdate] [pointer] | -| Dsn.go:63:9:63:11 | implicit dereference [postupdate] | semmle.label | implicit dereference [postupdate] | +| Dsn.go:63:9:63:11 | implicit-deref cfg [postupdate] | semmle.label | implicit-deref cfg [postupdate] | | Dsn.go:63:19:63:25 | selection of Args | semmle.label | selection of Args | | Dsn.go:63:19:63:29 | slice expression | semmle.label | slice expression | | Dsn.go:67:11:67:109 | []type{args} [array] | semmle.label | []type{args} [array] | | Dsn.go:67:11:67:109 | call to Sprintf | semmle.label | call to Sprintf | | Dsn.go:67:102:67:104 | cfg [pointer] | semmle.label | cfg [pointer] | -| Dsn.go:67:102:67:104 | implicit dereference | semmle.label | implicit dereference | +| Dsn.go:67:102:67:104 | implicit-deref cfg | semmle.label | implicit-deref cfg | | Dsn.go:67:102:67:108 | selection of dsn | semmle.label | selection of dsn | | Dsn.go:68:29:68:33 | dbDSN | semmle.label | dbDSN | subpaths diff --git a/go/ql/test/library-tests/semmle/go/IR/test.expected b/go/ql/test/library-tests/semmle/go/IR/test.expected index c42cdaa9932f..af7aa675e8e2 100644 --- a/go/ql/test/library-tests/semmle/go/IR/test.expected +++ b/go/ql/test/library-tests/semmle/go/IR/test.expected @@ -1,9 +1,9 @@ -| test.go:9:2:9:16 | ... := ...[0] | test.go:9:13:9:16 | <-... | 0 | file://:0:0:0:0 | bool | -| test.go:9:2:9:16 | ... := ...[1] | test.go:9:13:9:16 | <-... | 1 | file://:0:0:0:0 | bool | -| test.go:15:2:15:20 | ... := ...[0] | test.go:15:13:15:20 | index expression | 0 | file://:0:0:0:0 | string | -| test.go:15:2:15:20 | ... := ...[1] | test.go:15:13:15:20 | index expression | 1 | file://:0:0:0:0 | bool | -| test.go:21:2:21:22 | ... := ...[0] | test.go:21:13:21:22 | type assertion | 0 | file://:0:0:0:0 | string | -| test.go:21:2:21:22 | ... := ...[1] | test.go:21:13:21:22 | type assertion | 1 | file://:0:0:0:0 | bool | +| test.go:9:2:9:16 | extract:0 ... := ... | test.go:9:13:9:16 | <-... | 0 | file://:0:0:0:0 | bool | +| test.go:9:2:9:16 | extract:1 ... := ... | test.go:9:13:9:16 | <-... | 1 | file://:0:0:0:0 | bool | +| test.go:15:2:15:20 | extract:0 ... := ... | test.go:15:13:15:20 | index expression | 0 | file://:0:0:0:0 | string | +| test.go:15:2:15:20 | extract:1 ... := ... | test.go:15:13:15:20 | index expression | 1 | file://:0:0:0:0 | bool | +| test.go:21:2:21:22 | extract:0 ... := ... | test.go:21:13:21:22 | type assertion | 0 | file://:0:0:0:0 | string | +| test.go:21:2:21:22 | extract:1 ... := ... | test.go:21:13:21:22 | type assertion | 1 | file://:0:0:0:0 | bool | | test.go:29:2:29:7 | call to f[0] | test.go:29:4:29:6 | call to g | 0 | file://:0:0:0:0 | int | | test.go:29:2:29:7 | call to f[1] | test.go:29:4:29:6 | call to g | 1 | file://:0:0:0:0 | int | | test.go:33:2:33:7 | call to f[0] | test.go:33:4:33:6 | call to v | 0 | file://:0:0:0:0 | int | diff --git a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go index bdb57aae2e1b..56677fff99b8 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go +++ b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/logrus.go @@ -13,7 +13,7 @@ func logSomething(entry *logrus.Entry) { entry.Traceln(text) // $ logger=text } -func logrusCalls() { +func logrusCalls(selector int) { err := errors.New("Error") var fields logrus.Fields = nil var fn logrus.LogFunction = nil @@ -27,11 +27,15 @@ func logrusCalls() { tmp = logrus.WithFields(fields) // $ logger=fields logSomething(tmp) - logrus.Error(text) // $ logger=text - logrus.Fatalf(fmt, text) // $ logger=fmt logger=text - logrus.Panicln(text) // $ logger=text - logrus.Infof(fmt, text) // $ logger=fmt logger=text - logrus.FatalFn(fn) // $ logger=fn + logrus.Error(text) // $ logger=text + logrus.Infof(fmt, text) // $ logger=fmt logger=text + if selector == 0 { + logrus.Fatalf(fmt, text) // $ logger=fmt logger=text + } else if selector == 1 { + logrus.Panicln(text) // $ logger=text + } else if selector == 2 { + logrus.FatalFn(fn) // $ logger=fn + } // components corresponding to the format specifier "%T" are not considered vulnerable logrus.Infof("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v diff --git a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/main.go b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/main.go index 688c59bc2eac..217d7029a30d 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/main.go +++ b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/main.go @@ -7,5 +7,5 @@ var v []byte func main() { glogTest(len(v)) - stdlib() + stdlib(len(v)) } diff --git a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/stdlib.go b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/stdlib.go index 6fbf3c43fd35..e77e83a2ac5b 100644 --- a/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/stdlib.go +++ b/go/ql/test/library-tests/semmle/go/concepts/LoggerCall/stdlib.go @@ -4,37 +4,69 @@ import ( "log" ) -func stdlib() { +func stdlib(selector int) { var logger log.Logger logger.SetPrefix("prefix: ") - logger.Fatal(text) // $ logger=text - logger.Fatalf(fmt, text) // $ logger=fmt logger=text - logger.Fatalln(text) // $ logger=text - logger.Panic(text) // $ logger=text - logger.Panicf(fmt, text) // $ logger=fmt logger=text - logger.Panicln(text) // $ logger=text - logger.Print(text) // $ logger=text - logger.Printf(fmt, text) // $ logger=fmt logger=text - logger.Println(text) // $ logger=text + switch selector { + case 0: + logger.Fatal(text) // $ logger=text + case 1: + logger.Fatalf(fmt, text) // $ logger=fmt logger=text + case 2: + logger.Fatalln(text) // $ logger=text + case 3: + logger.Panic(text) // $ logger=text + case 4: + logger.Panicf(fmt, text) // $ logger=fmt logger=text + case 5: + logger.Panicln(text) // $ logger=text + case 6: + logger.Print(text) // $ logger=text + case 7: + logger.Printf(fmt, text) // $ logger=fmt logger=text + case 8: + logger.Println(text) // $ logger=text + } // components corresponding to the format specifier "%T" are not considered vulnerable - logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v - logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v - logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + switch selector { + case 9: + logger.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + case 10: + logger.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + case 11: + logger.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + } log.SetPrefix("prefix: ") - log.Fatal(text) // $ logger=text - log.Fatalf(fmt, text) // $ logger=fmt logger=text - log.Fatalln(text) // $ logger=text - log.Panic(text) // $ logger=text - log.Panicf(fmt, text) // $ logger=fmt logger=text - log.Panicln(text) // $ logger=text - log.Print(text) // $ logger=text - log.Printf(fmt, text) // $ logger=fmt logger=text - log.Println(text) // $ logger=text + switch selector { + case 12: + log.Fatal(text) // $ logger=text + case 13: + log.Fatalf(fmt, text) // $ logger=fmt logger=text + case 14: + log.Fatalln(text) // $ logger=text + case 15: + log.Panic(text) // $ logger=text + case 16: + log.Panicf(fmt, text) // $ logger=fmt logger=text + case 17: + log.Panicln(text) // $ logger=text + case 18: + log.Print(text) // $ logger=text + case 19: + log.Printf(fmt, text) // $ logger=fmt logger=text + case 20: + log.Println(text) // $ logger=text + } // components corresponding to the format specifier "%T" are not considered vulnerable - log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v - log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v - log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + switch selector { + case 21: + log.Fatalf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + case 22: + log.Panicf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + case 23: + log.Printf("%s: found type %T", text, v) // $ logger="%s: found type %T" logger=text type-logger=v + } } diff --git a/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected b/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected index f3d27b4bf388..151395101432 100644 --- a/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected +++ b/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/ControlFlowNode_getASuccessor.expected @@ -5,40 +5,40 @@ | DuplicateSwitchCase.go:3:17:3:19 | argument corresponding to msg | DuplicateSwitchCase.go:3:17:3:19 | initialization of msg | | DuplicateSwitchCase.go:3:17:3:19 | initialization of msg | DuplicateSwitchCase.go:4:2:11:2 | true | | DuplicateSwitchCase.go:4:2:11:2 | true | DuplicateSwitchCase.go:5:7:5:9 | msg | -| DuplicateSwitchCase.go:5:7:5:9 | msg | DuplicateSwitchCase.go:5:14:5:20 | "start" | -| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | case ...==... | +| DuplicateSwitchCase.go:5:7:5:9 | After msg | DuplicateSwitchCase.go:5:14:5:20 | Before "start" | +| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | After ...==... [false] | | DuplicateSwitchCase.go:5:7:5:20 | ...==... is false | DuplicateSwitchCase.go:7:7:7:9 | msg | | DuplicateSwitchCase.go:5:7:5:20 | ...==... is true | DuplicateSwitchCase.go:6:3:6:7 | start | -| DuplicateSwitchCase.go:5:7:5:20 | case ...==... | DuplicateSwitchCase.go:5:7:5:20 | ...==... is false | +| DuplicateSwitchCase.go:5:7:5:20 | ...==... | DuplicateSwitchCase.go:5:7:5:20 | After ...==... [true] | | DuplicateSwitchCase.go:5:7:5:20 | case ...==... | DuplicateSwitchCase.go:5:7:5:20 | ...==... is true | -| DuplicateSwitchCase.go:5:14:5:20 | "start" | DuplicateSwitchCase.go:5:7:5:20 | ...==... | -| DuplicateSwitchCase.go:6:3:6:7 | start | DuplicateSwitchCase.go:6:3:6:9 | call to start | -| DuplicateSwitchCase.go:6:3:6:9 | call to start | DuplicateSwitchCase.go:3:1:12:1 | exit | -| DuplicateSwitchCase.go:7:7:7:9 | msg | DuplicateSwitchCase.go:7:14:7:20 | "start" | -| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | case ...==... | +| DuplicateSwitchCase.go:5:14:5:20 | After "start" | DuplicateSwitchCase.go:5:7:5:20 | ...==... | +| DuplicateSwitchCase.go:6:3:6:7 | After start | DuplicateSwitchCase.go:6:3:6:9 | call to start | +| DuplicateSwitchCase.go:6:3:6:9 | call to start | DuplicateSwitchCase.go:3:1:12:1 | Exceptional Exit | +| DuplicateSwitchCase.go:7:7:7:9 | After msg | DuplicateSwitchCase.go:7:14:7:20 | Before "start" | +| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | After ...==... [false] | | DuplicateSwitchCase.go:7:7:7:20 | ...==... is false | DuplicateSwitchCase.go:10:3:10:7 | panic | | DuplicateSwitchCase.go:7:7:7:20 | ...==... is true | DuplicateSwitchCase.go:8:3:8:6 | stop | -| DuplicateSwitchCase.go:7:7:7:20 | case ...==... | DuplicateSwitchCase.go:7:7:7:20 | ...==... is false | +| DuplicateSwitchCase.go:7:7:7:20 | ...==... | DuplicateSwitchCase.go:7:7:7:20 | After ...==... [true] | | DuplicateSwitchCase.go:7:7:7:20 | case ...==... | DuplicateSwitchCase.go:7:7:7:20 | ...==... is true | -| DuplicateSwitchCase.go:7:14:7:20 | "start" | DuplicateSwitchCase.go:7:7:7:20 | ...==... | -| DuplicateSwitchCase.go:8:3:8:6 | stop | DuplicateSwitchCase.go:8:3:8:8 | call to stop | -| DuplicateSwitchCase.go:8:3:8:8 | call to stop | DuplicateSwitchCase.go:3:1:12:1 | exit | -| DuplicateSwitchCase.go:10:3:10:7 | panic | DuplicateSwitchCase.go:10:9:10:33 | "Message not understood." | -| DuplicateSwitchCase.go:10:3:10:34 | call to panic | DuplicateSwitchCase.go:3:1:12:1 | exit | -| DuplicateSwitchCase.go:10:9:10:33 | "Message not understood." | DuplicateSwitchCase.go:10:3:10:34 | call to panic | -| DuplicateSwitchCase.go:14:1:14:15 | entry | DuplicateSwitchCase.go:14:14:14:15 | skip | +| DuplicateSwitchCase.go:7:14:7:20 | After "start" | DuplicateSwitchCase.go:7:7:7:20 | ...==... | +| DuplicateSwitchCase.go:8:3:8:6 | After stop | DuplicateSwitchCase.go:8:3:8:8 | call to stop | +| DuplicateSwitchCase.go:8:3:8:8 | call to stop | DuplicateSwitchCase.go:3:1:12:1 | Exceptional Exit | +| DuplicateSwitchCase.go:10:3:10:7 | After panic | DuplicateSwitchCase.go:10:9:10:33 | Before "Message not understood." | +| DuplicateSwitchCase.go:10:3:10:34 | call to panic | DuplicateSwitchCase.go:3:1:12:1 | Exceptional Exit | +| DuplicateSwitchCase.go:10:9:10:33 | After "Message not understood." | DuplicateSwitchCase.go:10:3:10:34 | call to panic | +| DuplicateSwitchCase.go:14:1:14:15 | Entry | DuplicateSwitchCase.go:14:14:14:15 | block statement | | DuplicateSwitchCase.go:14:1:14:15 | function declaration | DuplicateSwitchCase.go:16:6:16:9 | skip | | DuplicateSwitchCase.go:14:6:14:10 | skip | DuplicateSwitchCase.go:14:1:14:15 | function declaration | -| DuplicateSwitchCase.go:14:14:14:15 | skip | DuplicateSwitchCase.go:14:1:14:15 | exit | -| DuplicateSwitchCase.go:16:1:16:14 | entry | DuplicateSwitchCase.go:16:13:16:14 | skip | -| DuplicateSwitchCase.go:16:1:16:14 | function declaration | DuplicateSwitchCase.go:0:0:0:0 | exit | +| DuplicateSwitchCase.go:14:14:14:15 | block statement | DuplicateSwitchCase.go:14:1:14:15 | Normal Exit | +| DuplicateSwitchCase.go:16:1:16:14 | Entry | DuplicateSwitchCase.go:16:13:16:14 | block statement | +| DuplicateSwitchCase.go:16:1:16:14 | After function declaration | DuplicateSwitchCase.go:0:0:0:0 | After DuplicateSwitchCase.go | | DuplicateSwitchCase.go:16:6:16:9 | skip | DuplicateSwitchCase.go:16:1:16:14 | function declaration | -| DuplicateSwitchCase.go:16:13:16:14 | skip | DuplicateSwitchCase.go:16:1:16:14 | exit | -| equalitytests.go:0:0:0:0 | entry | equalitytests.go:3:1:5:1 | skip | -| equalitytests.go:3:1:5:1 | skip | equalitytests.go:7:1:9:1 | skip | +| DuplicateSwitchCase.go:16:13:16:14 | block statement | DuplicateSwitchCase.go:16:1:16:14 | Normal Exit | +| equalitytests.go:0:0:0:0 | equalitytests.go | equalitytests.go:3:1:5:1 | type declaration | +| equalitytests.go:3:1:5:1 | After type declaration | equalitytests.go:7:1:9:1 | type declaration | | equalitytests.go:7:1:9:1 | skip | equalitytests.go:11:6:11:18 | skip | | equalitytests.go:11:1:13:1 | entry | equalitytests.go:11:20:11:21 | argument corresponding to i1 | -| equalitytests.go:11:1:13:1 | function declaration | equalitytests.go:0:0:0:0 | exit | +| equalitytests.go:11:1:13:1 | After function declaration | equalitytests.go:0:0:0:0 | After equalitytests.go | | equalitytests.go:11:6:11:18 | skip | equalitytests.go:11:1:13:1 | function declaration | | equalitytests.go:11:20:11:21 | argument corresponding to i1 | equalitytests.go:11:20:11:21 | initialization of i1 | | equalitytests.go:11:20:11:21 | initialization of i1 | equalitytests.go:11:28:11:29 | argument corresponding to i2 | @@ -64,47 +64,47 @@ | equalitytests.go:11:184:11:185 | initialization of a3 | equalitytests.go:11:195:11:196 | argument corresponding to a4 | | equalitytests.go:11:195:11:196 | argument corresponding to a4 | equalitytests.go:11:195:11:196 | initialization of a4 | | equalitytests.go:11:195:11:196 | initialization of a4 | equalitytests.go:12:9:12:10 | i1 | -| equalitytests.go:12:2:12:76 | return statement | equalitytests.go:11:1:13:1 | exit | -| equalitytests.go:12:9:12:10 | i1 | equalitytests.go:12:15:12:16 | i2 | -| equalitytests.go:12:9:12:16 | ...==... | equalitytests.go:12:9:12:16 | ...==... is false | -| equalitytests.go:12:9:12:16 | ...==... | equalitytests.go:12:9:12:16 | ...==... is true | -| equalitytests.go:12:9:12:16 | ...==... is false | equalitytests.go:12:9:12:28 | ...&&... is false | +| equalitytests.go:12:2:12:76 | return statement | equalitytests.go:11:1:13:1 | Normal Exit | +| equalitytests.go:12:9:12:10 | After i1 | equalitytests.go:12:15:12:16 | Before i2 | +| equalitytests.go:12:9:12:16 | ...==... | equalitytests.go:12:9:12:16 | After ...==... [false] | +| equalitytests.go:12:9:12:16 | ...==... | equalitytests.go:12:9:12:16 | After ...==... [true] | +| equalitytests.go:12:9:12:16 | After ...==... [false] | equalitytests.go:12:9:12:28 | After ...&&... [false] | | equalitytests.go:12:9:12:16 | ...==... is true | equalitytests.go:12:21:12:22 | e1 | -| equalitytests.go:12:9:12:28 | ...&&... is false | equalitytests.go:12:9:12:40 | ...&&... is false | +| equalitytests.go:12:9:12:28 | After ...&&... [false] | equalitytests.go:12:9:12:40 | After ...&&... [false] | | equalitytests.go:12:9:12:28 | ...&&... is true | equalitytests.go:12:33:12:34 | s1 | -| equalitytests.go:12:9:12:40 | ...&&... is false | equalitytests.go:12:9:12:52 | ...&&... is false | +| equalitytests.go:12:9:12:40 | After ...&&... [false] | equalitytests.go:12:9:12:52 | After ...&&... [false] | | equalitytests.go:12:9:12:40 | ...&&... is true | equalitytests.go:12:45:12:46 | s3 | -| equalitytests.go:12:9:12:52 | ...&&... is false | equalitytests.go:12:9:12:64 | ...&&... is false | +| equalitytests.go:12:9:12:52 | After ...&&... [false] | equalitytests.go:12:9:12:64 | After ...&&... [false] | | equalitytests.go:12:9:12:52 | ...&&... is true | equalitytests.go:12:57:12:58 | a1 | -| equalitytests.go:12:9:12:64 | ...&&... is false | equalitytests.go:12:9:12:76 | ...&&... | +| equalitytests.go:12:9:12:64 | After ...&&... [false] | equalitytests.go:12:9:12:76 | After ...&&... | | equalitytests.go:12:9:12:64 | ...&&... is true | equalitytests.go:12:69:12:70 | a3 | -| equalitytests.go:12:9:12:76 | ...&&... | equalitytests.go:12:2:12:76 | return statement | -| equalitytests.go:12:15:12:16 | i2 | equalitytests.go:12:9:12:16 | ...==... | -| equalitytests.go:12:21:12:22 | e1 | equalitytests.go:12:27:12:28 | e2 | +| equalitytests.go:12:9:12:76 | After ...&&... | equalitytests.go:12:2:12:76 | return statement | +| equalitytests.go:12:15:12:16 | After i2 | equalitytests.go:12:9:12:16 | ...==... | +| equalitytests.go:12:21:12:22 | After e1 | equalitytests.go:12:27:12:28 | Before e2 | | equalitytests.go:12:21:12:28 | ...==... | equalitytests.go:11:1:13:1 | exit | -| equalitytests.go:12:21:12:28 | ...==... | equalitytests.go:12:9:12:28 | ...&&... is false | -| equalitytests.go:12:21:12:28 | ...==... | equalitytests.go:12:9:12:28 | ...&&... is true | -| equalitytests.go:12:27:12:28 | e2 | equalitytests.go:12:21:12:28 | ...==... | -| equalitytests.go:12:33:12:34 | s1 | equalitytests.go:12:39:12:40 | s2 | +| equalitytests.go:12:21:12:28 | After ...==... [false] | equalitytests.go:12:9:12:28 | After ...&&... [false] | +| equalitytests.go:12:21:12:28 | After ...==... [true] | equalitytests.go:12:9:12:28 | After ...&&... [true] | +| equalitytests.go:12:27:12:28 | After e2 | equalitytests.go:12:21:12:28 | ...==... | +| equalitytests.go:12:33:12:34 | After s1 | equalitytests.go:12:39:12:40 | Before s2 | | equalitytests.go:12:33:12:40 | ...==... | equalitytests.go:11:1:13:1 | exit | -| equalitytests.go:12:33:12:40 | ...==... | equalitytests.go:12:9:12:40 | ...&&... is false | -| equalitytests.go:12:33:12:40 | ...==... | equalitytests.go:12:9:12:40 | ...&&... is true | -| equalitytests.go:12:39:12:40 | s2 | equalitytests.go:12:33:12:40 | ...==... | -| equalitytests.go:12:45:12:46 | s3 | equalitytests.go:12:51:12:52 | s4 | +| equalitytests.go:12:33:12:40 | After ...==... [false] | equalitytests.go:12:9:12:40 | After ...&&... [false] | +| equalitytests.go:12:33:12:40 | After ...==... [true] | equalitytests.go:12:9:12:40 | After ...&&... [true] | +| equalitytests.go:12:39:12:40 | After s2 | equalitytests.go:12:33:12:40 | ...==... | +| equalitytests.go:12:45:12:46 | After s3 | equalitytests.go:12:51:12:52 | Before s4 | | equalitytests.go:12:45:12:52 | ...==... | equalitytests.go:11:1:13:1 | exit | -| equalitytests.go:12:45:12:52 | ...==... | equalitytests.go:12:9:12:52 | ...&&... is false | -| equalitytests.go:12:45:12:52 | ...==... | equalitytests.go:12:9:12:52 | ...&&... is true | -| equalitytests.go:12:51:12:52 | s4 | equalitytests.go:12:45:12:52 | ...==... | -| equalitytests.go:12:57:12:58 | a1 | equalitytests.go:12:63:12:64 | a2 | +| equalitytests.go:12:45:12:52 | After ...==... [false] | equalitytests.go:12:9:12:52 | After ...&&... [false] | +| equalitytests.go:12:45:12:52 | After ...==... [true] | equalitytests.go:12:9:12:52 | After ...&&... [true] | +| equalitytests.go:12:51:12:52 | After s4 | equalitytests.go:12:45:12:52 | ...==... | +| equalitytests.go:12:57:12:58 | After a1 | equalitytests.go:12:63:12:64 | Before a2 | | equalitytests.go:12:57:12:64 | ...==... | equalitytests.go:11:1:13:1 | exit | -| equalitytests.go:12:57:12:64 | ...==... | equalitytests.go:12:9:12:64 | ...&&... is false | -| equalitytests.go:12:57:12:64 | ...==... | equalitytests.go:12:9:12:64 | ...&&... is true | -| equalitytests.go:12:63:12:64 | a2 | equalitytests.go:12:57:12:64 | ...==... | -| equalitytests.go:12:69:12:70 | a3 | equalitytests.go:12:75:12:76 | a4 | +| equalitytests.go:12:57:12:64 | After ...==... [false] | equalitytests.go:12:9:12:64 | After ...&&... [false] | +| equalitytests.go:12:57:12:64 | After ...==... [true] | equalitytests.go:12:9:12:64 | After ...&&... [true] | +| equalitytests.go:12:63:12:64 | After a2 | equalitytests.go:12:57:12:64 | ...==... | +| equalitytests.go:12:69:12:70 | After a3 | equalitytests.go:12:75:12:76 | Before a4 | | equalitytests.go:12:69:12:76 | ...==... | equalitytests.go:11:1:13:1 | exit | -| equalitytests.go:12:69:12:76 | ...==... | equalitytests.go:12:9:12:76 | ...&&... | -| equalitytests.go:12:75:12:76 | a4 | equalitytests.go:12:69:12:76 | ...==... | -| exprs.go:0:0:0:0 | entry | exprs.go:3:1:3:29 | skip | +| equalitytests.go:12:69:12:76 | After ...==... | equalitytests.go:12:9:12:76 | After ...&&... | +| equalitytests.go:12:75:12:76 | After a4 | equalitytests.go:12:69:12:76 | ...==... | +| exprs.go:0:0:0:0 | exprs.go | exprs.go:3:1:3:29 | type declaration | | exprs.go:3:1:3:29 | skip | exprs.go:5:6:5:9 | skip | | exprs.go:5:1:26:1 | entry | exprs.go:6:6:6:6 | skip | | exprs.go:5:1:26:1 | function declaration | exprs.go:28:6:28:10 | skip | @@ -113,21 +113,21 @@ | exprs.go:6:6:6:6 | skip | exprs.go:6:9:6:9 | skip | | exprs.go:6:9:6:9 | assignment to j | exprs.go:7:6:7:6 | skip | | exprs.go:6:9:6:9 | skip | exprs.go:6:13:6:13 | 0 | -| exprs.go:6:13:6:13 | 0 | exprs.go:6:16:6:26 | ...+... | +| exprs.go:6:13:6:13 | After 0 | exprs.go:6:16:6:26 | Before ...+... | | exprs.go:6:16:6:26 | ...+... | exprs.go:6:6:6:6 | assignment to i | | exprs.go:7:6:7:6 | assignment to k | exprs.go:8:2:8:2 | skip | | exprs.go:7:6:7:6 | skip | exprs.go:7:10:7:10 | i | | exprs.go:7:10:7:10 | i | exprs.go:7:14:7:14 | 2 | | exprs.go:7:10:7:16 | ...+... | exprs.go:7:6:7:6 | assignment to k | -| exprs.go:7:14:7:14 | 2 | exprs.go:7:16:7:16 | j | -| exprs.go:7:14:7:16 | ...*... | exprs.go:7:10:7:16 | ...+... | -| exprs.go:7:16:7:16 | j | exprs.go:7:14:7:16 | ...*... | +| exprs.go:7:14:7:14 | After 2 | exprs.go:7:16:7:16 | Before j | +| exprs.go:7:14:7:16 | After ...*... | exprs.go:7:10:7:16 | ...+... | +| exprs.go:7:16:7:16 | After j | exprs.go:7:14:7:16 | ...*... | | exprs.go:8:2:8:2 | assignment to s | exprs.go:9:2:9:3 | skip | | exprs.go:8:2:8:2 | skip | exprs.go:8:7:8:12 | "k = " | | exprs.go:8:7:8:12 | "k = " | exprs.go:8:23:8:23 | k | | exprs.go:8:7:8:24 | ...+... | exprs.go:8:2:8:2 | assignment to s | -| exprs.go:8:16:8:24 | type conversion | exprs.go:8:7:8:24 | ...+... | -| exprs.go:8:23:8:23 | k | exprs.go:8:16:8:24 | type conversion | +| exprs.go:8:16:8:24 | After type conversion | exprs.go:8:7:8:24 | ...+... | +| exprs.go:8:23:8:23 | After k | exprs.go:8:16:8:24 | type conversion | | exprs.go:9:2:9:3 | assignment to fn | exprs.go:10:2:10:8 | skip | | exprs.go:9:2:9:3 | skip | exprs.go:9:8:9:61 | function literal | | exprs.go:9:8:9:61 | entry | exprs.go:9:13:9:13 | argument corresponding to a | @@ -138,142 +138,142 @@ | exprs.go:9:16:9:16 | initialization of b | exprs.go:9:23:9:23 | argument corresponding to z | | exprs.go:9:23:9:23 | argument corresponding to z | exprs.go:9:23:9:23 | initialization of z | | exprs.go:9:23:9:23 | initialization of z | exprs.go:9:48:9:48 | a | -| exprs.go:9:41:9:59 | return statement | exprs.go:9:8:9:61 | exit | -| exprs.go:9:48:9:48 | a | exprs.go:9:50:9:50 | b | +| exprs.go:9:41:9:59 | return statement | exprs.go:9:8:9:61 | Normal Exit | +| exprs.go:9:48:9:48 | After a | exprs.go:9:50:9:50 | Before b | | exprs.go:9:48:9:50 | ...*... | exprs.go:9:58:9:58 | z | -| exprs.go:9:48:9:59 | ...<... | exprs.go:9:41:9:59 | return statement | -| exprs.go:9:50:9:50 | b | exprs.go:9:48:9:50 | ...*... | -| exprs.go:9:54:9:59 | type conversion | exprs.go:9:48:9:59 | ...<... | -| exprs.go:9:58:9:58 | z | exprs.go:9:54:9:59 | type conversion | +| exprs.go:9:48:9:59 | After ...<... | exprs.go:9:41:9:59 | return statement | +| exprs.go:9:50:9:50 | After b | exprs.go:9:48:9:50 | ...*... | +| exprs.go:9:54:9:59 | After type conversion | exprs.go:9:48:9:59 | ...<... | +| exprs.go:9:58:9:58 | After z | exprs.go:9:54:9:59 | type conversion | | exprs.go:10:2:10:8 | assignment to struct1 | exprs.go:11:2:11:8 | skip | | exprs.go:10:2:10:8 | skip | exprs.go:10:13:10:32 | struct literal | | exprs.go:10:13:10:32 | struct literal | exprs.go:10:2:10:8 | assignment to struct1 | | exprs.go:11:2:11:8 | assignment to struct2 | exprs.go:15:2:15:8 | skip | | exprs.go:11:2:11:8 | skip | exprs.go:11:13:14:21 | struct literal | -| exprs.go:11:13:14:21 | struct literal | exprs.go:14:4:14:4 | k | +| exprs.go:11:13:14:21 | struct literal | exprs.go:14:4:14:4 | Before k | | exprs.go:14:4:14:4 | init of k | exprs.go:14:7:14:8 | fn | -| exprs.go:14:4:14:4 | k | exprs.go:14:4:14:4 | init of k | -| exprs.go:14:7:14:8 | fn | exprs.go:14:10:14:10 | i | -| exprs.go:14:7:14:20 | call to fn | exprs.go:5:1:26:1 | exit | -| exprs.go:14:7:14:20 | call to fn | exprs.go:14:7:14:20 | init of call to fn | +| exprs.go:14:4:14:4 | After k | exprs.go:14:4:14:4 | lit-init k | +| exprs.go:14:7:14:8 | After fn | exprs.go:14:10:14:10 | Before i | +| exprs.go:14:7:14:20 | call to fn | exprs.go:5:1:26:1 | Exceptional Exit | +| exprs.go:14:7:14:20 | call to fn | exprs.go:14:7:14:20 | After call to fn | | exprs.go:14:7:14:20 | init of call to fn | exprs.go:11:2:11:8 | assignment to struct2 | -| exprs.go:14:10:14:10 | i | exprs.go:14:13:14:13 | j | -| exprs.go:14:13:14:13 | j | exprs.go:14:16:14:19 | .../... | -| exprs.go:14:16:14:19 | .../... | exprs.go:14:7:14:20 | call to fn | +| exprs.go:14:10:14:10 | After i | exprs.go:14:13:14:13 | Before j | +| exprs.go:14:13:14:13 | After j | exprs.go:14:16:14:19 | Before .../... | +| exprs.go:14:16:14:19 | After .../... | exprs.go:14:7:14:20 | call to fn | | exprs.go:15:2:15:8 | assignment to struct3 | exprs.go:16:2:16:5 | skip | | exprs.go:15:2:15:8 | skip | exprs.go:15:13:15:58 | struct literal | | exprs.go:15:13:15:58 | struct literal | exprs.go:15:35:15:41 | struct1 | | exprs.go:15:32:15:43 | init of key-value pair | exprs.go:15:49:15:55 | struct2 | -| exprs.go:15:35:15:41 | struct1 | exprs.go:15:35:15:43 | selection of x | -| exprs.go:15:35:15:43 | selection of x | exprs.go:15:32:15:43 | init of key-value pair | +| exprs.go:15:35:15:41 | After struct1 | exprs.go:15:35:15:43 | selection of x | +| exprs.go:15:35:15:43 | After selection of x | exprs.go:15:32:15:43 | key-value pair | | exprs.go:15:46:15:57 | init of key-value pair | exprs.go:15:2:15:8 | assignment to struct3 | -| exprs.go:15:49:15:55 | struct2 | exprs.go:15:49:15:57 | selection of x | -| exprs.go:15:49:15:57 | selection of x | exprs.go:15:46:15:57 | init of key-value pair | +| exprs.go:15:49:15:55 | After struct2 | exprs.go:15:49:15:57 | selection of x | +| exprs.go:15:49:15:57 | After selection of x | exprs.go:15:46:15:57 | key-value pair | | exprs.go:16:2:16:5 | assignment to arr1 | exprs.go:17:2:17:5 | skip | | exprs.go:16:2:16:5 | skip | exprs.go:16:10:16:26 | array literal | -| exprs.go:16:10:16:26 | array literal | exprs.go:16:17:16:25 | element index | -| exprs.go:16:17:16:23 | struct3 | exprs.go:16:17:16:25 | selection of x | -| exprs.go:16:17:16:25 | element index | exprs.go:16:17:16:23 | struct3 | +| exprs.go:16:10:16:26 | array literal | exprs.go:16:17:16:25 | Before selection of x | +| exprs.go:16:17:16:23 | After struct3 | exprs.go:16:17:16:25 | selection of x | +| exprs.go:16:17:16:25 | Before selection of x | exprs.go:16:17:16:23 | Before struct3 | | exprs.go:16:17:16:25 | init of selection of x | exprs.go:16:2:16:5 | assignment to arr1 | -| exprs.go:16:17:16:25 | selection of x | exprs.go:16:17:16:25 | init of selection of x | +| exprs.go:16:17:16:25 | lit-index selection of x | exprs.go:16:17:16:25 | lit-init selection of x | | exprs.go:17:2:17:5 | assignment to arr2 | exprs.go:18:2:18:4 | skip | | exprs.go:17:2:17:5 | skip | exprs.go:17:10:17:40 | array literal | -| exprs.go:17:10:17:40 | array literal | exprs.go:17:19:17:27 | element index | -| exprs.go:17:19:17:25 | struct3 | exprs.go:17:19:17:27 | selection of x | -| exprs.go:17:19:17:27 | element index | exprs.go:17:19:17:25 | struct3 | +| exprs.go:17:10:17:40 | array literal | exprs.go:17:19:17:27 | Before selection of x | +| exprs.go:17:19:17:25 | After struct3 | exprs.go:17:19:17:27 | selection of x | +| exprs.go:17:19:17:27 | Before selection of x | exprs.go:17:19:17:25 | Before struct3 | | exprs.go:17:19:17:27 | init of selection of x | exprs.go:17:30:17:30 | 2 | -| exprs.go:17:19:17:27 | selection of x | exprs.go:17:19:17:27 | init of selection of x | +| exprs.go:17:19:17:27 | lit-index selection of x | exprs.go:17:19:17:27 | lit-init selection of x | | exprs.go:17:30:17:30 | 2 | exprs.go:17:33:17:36 | arr1 | | exprs.go:17:30:17:39 | init of key-value pair | exprs.go:17:2:17:5 | assignment to arr2 | -| exprs.go:17:33:17:36 | arr1 | exprs.go:17:38:17:38 | 0 | -| exprs.go:17:33:17:39 | index expression | exprs.go:5:1:26:1 | exit | -| exprs.go:17:33:17:39 | index expression | exprs.go:17:30:17:39 | init of key-value pair | -| exprs.go:17:38:17:38 | 0 | exprs.go:17:33:17:39 | index expression | +| exprs.go:17:33:17:36 | After arr1 | exprs.go:17:38:17:38 | Before 0 | +| exprs.go:17:33:17:39 | index expression | exprs.go:5:1:26:1 | Exceptional Exit | +| exprs.go:17:33:17:39 | After index expression | exprs.go:17:30:17:39 | key-value pair | +| exprs.go:17:38:17:38 | After 0 | exprs.go:17:33:17:39 | index expression | | exprs.go:18:2:18:4 | assignment to slc | exprs.go:19:2:19:3 | skip | | exprs.go:18:2:18:4 | skip | exprs.go:18:9:18:22 | slice literal | -| exprs.go:18:9:18:22 | slice literal | exprs.go:18:18:18:18 | element index | -| exprs.go:18:18:18:18 | element index | exprs.go:18:18:18:18 | s | -| exprs.go:18:18:18:18 | init of s | exprs.go:18:21:18:21 | element index | -| exprs.go:18:18:18:18 | s | exprs.go:18:18:18:18 | init of s | -| exprs.go:18:21:18:21 | element index | exprs.go:18:21:18:21 | s | +| exprs.go:18:9:18:22 | slice literal | exprs.go:18:18:18:18 | Before s | +| exprs.go:18:18:18:18 | After s | exprs.go:18:18:18:18 | lit-index s | +| exprs.go:18:18:18:18 | lit-init s | exprs.go:18:21:18:21 | Before s | +| exprs.go:18:18:18:18 | s | exprs.go:18:18:18:18 | After s | +| exprs.go:18:21:18:21 | After s | exprs.go:18:21:18:21 | lit-index s | | exprs.go:18:21:18:21 | init of s | exprs.go:18:2:18:4 | assignment to slc | -| exprs.go:18:21:18:21 | s | exprs.go:18:21:18:21 | init of s | +| exprs.go:18:21:18:21 | Before s | exprs.go:18:21:18:21 | s | | exprs.go:19:2:19:3 | assignment to mp | exprs.go:20:2:20:5 | skip | | exprs.go:19:2:19:3 | skip | exprs.go:19:8:19:38 | map literal | | exprs.go:19:8:19:38 | map literal | exprs.go:19:23:19:25 | slc | -| exprs.go:19:23:19:25 | slc | exprs.go:19:27:19:27 | 0 | -| exprs.go:19:23:19:28 | index expression | exprs.go:5:1:26:1 | exit | +| exprs.go:19:23:19:25 | After slc | exprs.go:19:27:19:27 | Before 0 | +| exprs.go:19:23:19:28 | index expression | exprs.go:5:1:26:1 | Exceptional Exit | | exprs.go:19:23:19:28 | index expression | exprs.go:19:31:19:34 | arr2 | | exprs.go:19:23:19:37 | init of key-value pair | exprs.go:19:2:19:3 | assignment to mp | -| exprs.go:19:27:19:27 | 0 | exprs.go:19:23:19:28 | index expression | -| exprs.go:19:31:19:34 | arr2 | exprs.go:19:36:19:36 | 1 | -| exprs.go:19:31:19:37 | index expression | exprs.go:5:1:26:1 | exit | -| exprs.go:19:31:19:37 | index expression | exprs.go:19:23:19:37 | init of key-value pair | -| exprs.go:19:36:19:36 | 1 | exprs.go:19:31:19:37 | index expression | +| exprs.go:19:27:19:27 | After 0 | exprs.go:19:23:19:28 | index expression | +| exprs.go:19:31:19:34 | After arr2 | exprs.go:19:36:19:36 | Before 1 | +| exprs.go:19:31:19:37 | index expression | exprs.go:5:1:26:1 | Exceptional Exit | +| exprs.go:19:31:19:37 | After index expression | exprs.go:19:23:19:37 | key-value pair | +| exprs.go:19:36:19:36 | After 1 | exprs.go:19:31:19:37 | index expression | | exprs.go:20:2:20:5 | assignment to slc2 | exprs.go:21:2:21:5 | skip | | exprs.go:20:2:20:5 | skip | exprs.go:20:10:20:12 | slc | -| exprs.go:20:10:20:12 | slc | exprs.go:20:14:20:14 | 1 | +| exprs.go:20:10:20:12 | After slc | exprs.go:20:14:20:14 | Before 1 | | exprs.go:20:10:20:19 | slice expression | exprs.go:5:1:26:1 | exit | | exprs.go:20:10:20:19 | slice expression | exprs.go:20:2:20:5 | assignment to slc2 | -| exprs.go:20:14:20:14 | 1 | exprs.go:20:16:20:16 | 2 | -| exprs.go:20:16:20:16 | 2 | exprs.go:20:18:20:18 | 3 | -| exprs.go:20:18:20:18 | 3 | exprs.go:20:10:20:19 | slice expression | +| exprs.go:20:14:20:14 | After 1 | exprs.go:20:16:20:16 | Before 2 | +| exprs.go:20:16:20:16 | After 2 | exprs.go:20:18:20:18 | Before 3 | +| exprs.go:20:18:20:18 | After 3 | exprs.go:20:10:20:19 | slice expression | | exprs.go:21:2:21:5 | assignment to slc3 | exprs.go:22:2:22:5 | skip | | exprs.go:21:2:21:5 | skip | exprs.go:21:10:21:13 | slc2 | -| exprs.go:21:10:21:13 | slc2 | exprs.go:21:10:21:19 | 0 | -| exprs.go:21:10:21:19 | 0 | exprs.go:21:16:21:16 | 2 | +| exprs.go:21:10:21:13 | After slc2 | exprs.go:21:10:21:19 | implicit-low slice expression | +| exprs.go:21:10:21:19 | implicit-low slice expression | exprs.go:21:16:21:16 | Before 2 | | exprs.go:21:10:21:19 | slice expression | exprs.go:5:1:26:1 | exit | | exprs.go:21:10:21:19 | slice expression | exprs.go:21:2:21:5 | assignment to slc3 | -| exprs.go:21:16:21:16 | 2 | exprs.go:21:18:21:18 | 3 | -| exprs.go:21:18:21:18 | 3 | exprs.go:21:10:21:19 | slice expression | +| exprs.go:21:16:21:16 | After 2 | exprs.go:21:18:21:18 | Before 3 | +| exprs.go:21:18:21:18 | After 3 | exprs.go:21:10:21:19 | slice expression | | exprs.go:22:2:22:5 | assignment to slc4 | exprs.go:23:2:23:5 | skip | | exprs.go:22:2:22:5 | skip | exprs.go:22:10:22:13 | slc3 | -| exprs.go:22:10:22:13 | slc3 | exprs.go:22:15:22:15 | 0 | -| exprs.go:22:10:22:18 | cap | exprs.go:22:10:22:18 | slice expression | +| exprs.go:22:10:22:13 | After slc3 | exprs.go:22:15:22:15 | Before 0 | +| exprs.go:22:10:22:18 | implicit-max slice expression | exprs.go:22:10:22:18 | slice expression | | exprs.go:22:10:22:18 | slice expression | exprs.go:5:1:26:1 | exit | | exprs.go:22:10:22:18 | slice expression | exprs.go:22:2:22:5 | assignment to slc4 | -| exprs.go:22:15:22:15 | 0 | exprs.go:22:17:22:17 | 2 | -| exprs.go:22:17:22:17 | 2 | exprs.go:22:10:22:18 | cap | +| exprs.go:22:15:22:15 | After 0 | exprs.go:22:17:22:17 | Before 2 | +| exprs.go:22:17:22:17 | After 2 | exprs.go:22:10:22:18 | implicit-max slice expression | | exprs.go:23:2:23:5 | assignment to slc5 | exprs.go:24:2:24:5 | skip | | exprs.go:23:2:23:5 | skip | exprs.go:23:10:23:13 | slc4 | -| exprs.go:23:10:23:13 | slc4 | exprs.go:23:15:23:15 | 0 | -| exprs.go:23:10:23:17 | cap | exprs.go:23:10:23:17 | slice expression | -| exprs.go:23:10:23:17 | len | exprs.go:23:10:23:17 | cap | +| exprs.go:23:10:23:13 | After slc4 | exprs.go:23:15:23:15 | Before 0 | +| exprs.go:23:10:23:17 | implicit-high slice expression | exprs.go:23:10:23:17 | implicit-max slice expression | +| exprs.go:23:10:23:17 | implicit-max slice expression | exprs.go:23:10:23:17 | slice expression | | exprs.go:23:10:23:17 | slice expression | exprs.go:5:1:26:1 | exit | | exprs.go:23:10:23:17 | slice expression | exprs.go:23:2:23:5 | assignment to slc5 | -| exprs.go:23:15:23:15 | 0 | exprs.go:23:10:23:17 | len | +| exprs.go:23:15:23:15 | After 0 | exprs.go:23:10:23:17 | implicit-high slice expression | | exprs.go:24:2:24:5 | assignment to slc6 | exprs.go:25:9:25:34 | struct literal | | exprs.go:24:2:24:5 | skip | exprs.go:24:10:24:13 | slc5 | -| exprs.go:24:10:24:13 | slc5 | exprs.go:24:10:24:17 | 0 | -| exprs.go:24:10:24:17 | 0 | exprs.go:24:16:24:16 | 2 | -| exprs.go:24:10:24:17 | cap | exprs.go:24:10:24:17 | slice expression | +| exprs.go:24:10:24:13 | After slc5 | exprs.go:24:10:24:17 | implicit-low slice expression | +| exprs.go:24:10:24:17 | implicit-low slice expression | exprs.go:24:16:24:16 | Before 2 | +| exprs.go:24:10:24:17 | implicit-max slice expression | exprs.go:24:10:24:17 | slice expression | | exprs.go:24:10:24:17 | slice expression | exprs.go:5:1:26:1 | exit | | exprs.go:24:10:24:17 | slice expression | exprs.go:24:2:24:5 | assignment to slc6 | -| exprs.go:24:16:24:16 | 2 | exprs.go:24:10:24:17 | cap | -| exprs.go:25:2:25:34 | return statement | exprs.go:5:1:26:1 | exit | +| exprs.go:24:16:24:16 | After 2 | exprs.go:24:10:24:17 | implicit-max slice expression | +| exprs.go:25:2:25:34 | return statement | exprs.go:5:1:26:1 | Normal Exit | | exprs.go:25:9:25:34 | struct literal | exprs.go:25:15:25:16 | mp | -| exprs.go:25:15:25:16 | mp | exprs.go:25:18:25:18 | s | -| exprs.go:25:15:25:19 | index expression | exprs.go:5:1:26:1 | exit | -| exprs.go:25:15:25:19 | index expression | exprs.go:25:15:25:19 | init of index expression | +| exprs.go:25:15:25:16 | After mp | exprs.go:25:18:25:18 | Before s | +| exprs.go:25:15:25:19 | index expression | exprs.go:5:1:26:1 | Exceptional Exit | +| exprs.go:25:15:25:19 | index expression | exprs.go:25:15:25:19 | After index expression | | exprs.go:25:15:25:19 | init of index expression | exprs.go:25:22:25:24 | len | -| exprs.go:25:18:25:18 | s | exprs.go:25:15:25:19 | index expression | +| exprs.go:25:18:25:18 | After s | exprs.go:25:15:25:19 | index expression | | exprs.go:25:22:25:24 | len | exprs.go:25:26:25:29 | slc6 | -| exprs.go:25:22:25:33 | call to len | exprs.go:25:22:25:33 | init of call to len | +| exprs.go:25:22:25:33 | After call to len | exprs.go:25:22:25:33 | lit-init call to len | | exprs.go:25:22:25:33 | init of call to len | exprs.go:25:2:25:34 | return statement | -| exprs.go:25:26:25:29 | slc6 | exprs.go:25:31:25:31 | 0 | -| exprs.go:25:26:25:32 | index expression | exprs.go:5:1:26:1 | exit | -| exprs.go:25:26:25:32 | index expression | exprs.go:25:22:25:33 | call to len | -| exprs.go:25:31:25:31 | 0 | exprs.go:25:26:25:32 | index expression | +| exprs.go:25:26:25:29 | After slc6 | exprs.go:25:31:25:31 | Before 0 | +| exprs.go:25:26:25:32 | index expression | exprs.go:5:1:26:1 | Exceptional Exit | +| exprs.go:25:26:25:32 | After index expression | exprs.go:25:22:25:33 | call to len | +| exprs.go:25:31:25:31 | After 0 | exprs.go:25:26:25:32 | index expression | | exprs.go:28:1:30:1 | entry | exprs.go:28:12:28:14 | argument corresponding to arg | | exprs.go:28:1:30:1 | function declaration | exprs.go:32:6:32:10 | skip | | exprs.go:28:6:28:10 | skip | exprs.go:28:1:30:1 | function declaration | | exprs.go:28:12:28:14 | argument corresponding to arg | exprs.go:28:12:28:14 | initialization of arg | | exprs.go:28:12:28:14 | initialization of arg | exprs.go:29:9:29:11 | arg | -| exprs.go:29:2:29:21 | return statement | exprs.go:28:1:30:1 | exit | -| exprs.go:29:9:29:11 | arg | exprs.go:29:9:29:19 | type assertion | -| exprs.go:29:9:29:19 | type assertion | exprs.go:28:1:30:1 | exit | -| exprs.go:29:9:29:19 | type assertion | exprs.go:29:9:29:21 | selection of x | -| exprs.go:29:9:29:21 | selection of x | exprs.go:29:2:29:21 | return statement | +| exprs.go:29:2:29:21 | return statement | exprs.go:28:1:30:1 | Normal Exit | +| exprs.go:29:9:29:11 | After arg | exprs.go:29:9:29:19 | type assertion | +| exprs.go:29:9:29:19 | type assertion | exprs.go:28:1:30:1 | Exceptional Exit | +| exprs.go:29:9:29:19 | After type assertion | exprs.go:29:9:29:21 | selection of x | +| exprs.go:29:9:29:21 | After selection of x | exprs.go:29:2:29:21 | return statement | | exprs.go:32:1:37:1 | entry | exprs.go:32:12:32:14 | argument corresponding to arg | | exprs.go:32:1:37:1 | function declaration | exprs.go:39:6:39:10 | skip | | exprs.go:32:6:32:10 | skip | exprs.go:32:1:37:1 | function declaration | @@ -285,17 +285,17 @@ | exprs.go:33:5:33:24 | ... := ...[1] | exprs.go:33:8:33:9 | assignment to ok | | exprs.go:33:8:33:9 | assignment to ok | exprs.go:33:27:33:28 | ok | | exprs.go:33:8:33:9 | skip | exprs.go:33:14:33:16 | arg | -| exprs.go:33:14:33:16 | arg | exprs.go:33:14:33:24 | type assertion | -| exprs.go:33:14:33:24 | type assertion | exprs.go:33:5:33:24 | ... := ...[0] | -| exprs.go:33:27:33:28 | ok | exprs.go:33:27:33:28 | ok is false | -| exprs.go:33:27:33:28 | ok | exprs.go:33:27:33:28 | ok is true | +| exprs.go:33:14:33:16 | After arg | exprs.go:33:14:33:24 | type assertion | +| exprs.go:33:14:33:24 | After type assertion | exprs.go:33:5:33:24 | extract:0 ... := ... | +| exprs.go:33:27:33:28 | Before ok | exprs.go:33:27:33:28 | ok | +| exprs.go:33:27:33:28 | ok | exprs.go:33:27:33:28 | After ok [false] | | exprs.go:33:27:33:28 | ok is false | exprs.go:36:9:36:10 | -... | | exprs.go:33:27:33:28 | ok is true | exprs.go:34:10:34:10 | p | -| exprs.go:34:3:34:12 | return statement | exprs.go:32:1:37:1 | exit | -| exprs.go:34:10:34:10 | p | exprs.go:34:10:34:12 | selection of x | -| exprs.go:34:10:34:12 | selection of x | exprs.go:34:3:34:12 | return statement | -| exprs.go:36:2:36:10 | return statement | exprs.go:32:1:37:1 | exit | -| exprs.go:36:9:36:10 | -... | exprs.go:36:2:36:10 | return statement | +| exprs.go:34:3:34:12 | return statement | exprs.go:32:1:37:1 | Normal Exit | +| exprs.go:34:10:34:10 | After p | exprs.go:34:10:34:12 | selection of x | +| exprs.go:34:10:34:12 | After selection of x | exprs.go:34:3:34:12 | return statement | +| exprs.go:36:2:36:10 | return statement | exprs.go:32:1:37:1 | Normal Exit | +| exprs.go:36:9:36:10 | After -... | exprs.go:36:2:36:10 | return statement | | exprs.go:39:1:47:1 | entry | exprs.go:39:12:39:14 | argument corresponding to arg | | exprs.go:39:1:47:1 | function declaration | exprs.go:49:6:49:8 | skip | | exprs.go:39:6:39:10 | skip | exprs.go:39:1:47:1 | function declaration | @@ -313,17 +313,17 @@ | exprs.go:42:2:42:20 | ... = ...[1] | exprs.go:42:5:42:6 | assignment to ok | | exprs.go:42:5:42:6 | assignment to ok | exprs.go:43:5:43:6 | ok | | exprs.go:42:5:42:6 | skip | exprs.go:42:10:42:12 | arg | -| exprs.go:42:10:42:12 | arg | exprs.go:42:10:42:20 | type assertion | -| exprs.go:42:10:42:20 | type assertion | exprs.go:42:2:42:20 | ... = ...[0] | -| exprs.go:43:5:43:6 | ok | exprs.go:43:5:43:6 | ok is false | -| exprs.go:43:5:43:6 | ok | exprs.go:43:5:43:6 | ok is true | +| exprs.go:42:10:42:12 | After arg | exprs.go:42:10:42:20 | type assertion | +| exprs.go:42:10:42:20 | After type assertion | exprs.go:42:2:42:20 | extract:0 ... = ... | +| exprs.go:43:5:43:6 | Before ok | exprs.go:43:5:43:6 | ok | +| exprs.go:43:5:43:6 | ok | exprs.go:43:5:43:6 | After ok [false] | | exprs.go:43:5:43:6 | ok is false | exprs.go:46:9:46:10 | -... | | exprs.go:43:5:43:6 | ok is true | exprs.go:44:10:44:10 | p | -| exprs.go:44:3:44:12 | return statement | exprs.go:39:1:47:1 | exit | -| exprs.go:44:10:44:10 | p | exprs.go:44:10:44:12 | selection of x | -| exprs.go:44:10:44:12 | selection of x | exprs.go:44:3:44:12 | return statement | -| exprs.go:46:2:46:10 | return statement | exprs.go:39:1:47:1 | exit | -| exprs.go:46:9:46:10 | -... | exprs.go:46:2:46:10 | return statement | +| exprs.go:44:3:44:12 | return statement | exprs.go:39:1:47:1 | Normal Exit | +| exprs.go:44:10:44:10 | After p | exprs.go:44:10:44:12 | selection of x | +| exprs.go:44:10:44:12 | After selection of x | exprs.go:44:3:44:12 | return statement | +| exprs.go:46:2:46:10 | return statement | exprs.go:39:1:47:1 | Normal Exit | +| exprs.go:46:9:46:10 | After -... | exprs.go:46:2:46:10 | return statement | | exprs.go:49:1:54:1 | entry | exprs.go:49:10:49:11 | argument corresponding to xs | | exprs.go:49:1:54:1 | function declaration | exprs.go:56:6:56:9 | skip | | exprs.go:49:6:49:8 | skip | exprs.go:49:1:54:1 | function declaration | @@ -336,65 +336,65 @@ | exprs.go:50:6:50:6 | skip | exprs.go:50:11:50:11 | 0 | | exprs.go:50:11:50:11 | 0 | exprs.go:50:6:50:6 | assignment to i | | exprs.go:50:14:50:14 | i | exprs.go:50:18:50:20 | len | -| exprs.go:50:14:50:24 | ...<... | exprs.go:50:14:50:24 | ...<... is false | -| exprs.go:50:14:50:24 | ...<... | exprs.go:50:14:50:24 | ...<... is true | +| exprs.go:50:14:50:24 | ...<... | exprs.go:50:14:50:24 | After ...<... [false] | +| exprs.go:50:14:50:24 | ...<... | exprs.go:50:14:50:24 | After ...<... [true] | | exprs.go:50:14:50:24 | ...<... is false | exprs.go:53:2:53:7 | return statement | | exprs.go:50:14:50:24 | ...<... is true | exprs.go:51:3:51:5 | res | -| exprs.go:50:18:50:20 | len | exprs.go:50:22:50:23 | xs | -| exprs.go:50:18:50:24 | call to len | exprs.go:50:14:50:24 | ...<... | -| exprs.go:50:22:50:23 | xs | exprs.go:50:18:50:24 | call to len | -| exprs.go:50:27:50:27 | i | exprs.go:50:27:50:29 | 1 | -| exprs.go:50:27:50:29 | 1 | exprs.go:50:27:50:29 | rhs of increment statement | +| exprs.go:50:18:50:20 | After len | exprs.go:50:22:50:23 | Before xs | +| exprs.go:50:18:50:24 | After call to len | exprs.go:50:14:50:24 | ...<... | +| exprs.go:50:22:50:23 | After xs | exprs.go:50:18:50:24 | call to len | +| exprs.go:50:27:50:27 | After i | exprs.go:50:27:50:29 | implicit-one increment statement | +| exprs.go:50:27:50:29 | implicit-one increment statement | exprs.go:50:27:50:29 | incdec-rhs increment statement | | exprs.go:50:27:50:29 | increment statement | exprs.go:50:14:50:14 | i | -| exprs.go:50:27:50:29 | rhs of increment statement | exprs.go:50:27:50:29 | increment statement | +| exprs.go:50:27:50:29 | incdec-rhs increment statement | exprs.go:50:27:50:29 | increment statement | | exprs.go:51:3:51:5 | assignment to res | exprs.go:50:27:50:27 | i | | exprs.go:51:3:51:5 | res | exprs.go:51:10:51:11 | xs | -| exprs.go:51:3:51:14 | ... += ... | exprs.go:51:3:51:5 | assignment to res | -| exprs.go:51:10:51:11 | xs | exprs.go:51:13:51:13 | i | -| exprs.go:51:10:51:14 | index expression | exprs.go:49:1:54:1 | exit | -| exprs.go:51:10:51:14 | index expression | exprs.go:51:3:51:14 | ... += ... | -| exprs.go:51:13:51:13 | i | exprs.go:51:10:51:14 | index expression | +| exprs.go:51:3:51:14 | ... += ... | exprs.go:51:3:51:5 | Before res | +| exprs.go:51:10:51:11 | After xs | exprs.go:51:13:51:13 | Before i | +| exprs.go:51:10:51:14 | index expression | exprs.go:49:1:54:1 | Exceptional Exit | +| exprs.go:51:10:51:14 | After index expression | exprs.go:51:3:51:14 | compound-rhs ... += ... | +| exprs.go:51:13:51:13 | After i | exprs.go:51:10:51:14 | index expression | | exprs.go:53:2:53:7 | return statement | exprs.go:49:21:49:23 | implicit read of res | | exprs.go:56:1:58:1 | entry | exprs.go:56:11:56:12 | argument corresponding to xs | | exprs.go:56:1:58:1 | function declaration | exprs.go:60:6:60:9 | skip | | exprs.go:56:6:56:9 | skip | exprs.go:56:1:58:1 | function declaration | | exprs.go:56:11:56:12 | argument corresponding to xs | exprs.go:56:11:56:12 | initialization of xs | | exprs.go:56:11:56:12 | initialization of xs | exprs.go:57:9:57:11 | sum | -| exprs.go:57:2:57:15 | return statement | exprs.go:56:1:58:1 | exit | -| exprs.go:57:9:57:11 | sum | exprs.go:57:13:57:14 | xs | -| exprs.go:57:9:57:15 | call to sum | exprs.go:56:1:58:1 | exit | -| exprs.go:57:9:57:15 | call to sum | exprs.go:57:2:57:15 | return statement | -| exprs.go:57:13:57:14 | xs | exprs.go:57:9:57:15 | call to sum | +| exprs.go:57:2:57:15 | return statement | exprs.go:56:1:58:1 | Normal Exit | +| exprs.go:57:9:57:11 | After sum | exprs.go:57:13:57:14 | Before xs | +| exprs.go:57:9:57:15 | call to sum | exprs.go:56:1:58:1 | Exceptional Exit | +| exprs.go:57:9:57:15 | After call to sum | exprs.go:57:2:57:15 | return statement | +| exprs.go:57:13:57:14 | After xs | exprs.go:57:9:57:15 | call to sum | | exprs.go:60:1:62:1 | entry | exprs.go:61:9:61:22 | slice literal | | exprs.go:60:1:62:1 | function declaration | exprs.go:64:5:64:5 | skip | | exprs.go:60:6:60:9 | skip | exprs.go:60:1:62:1 | function declaration | -| exprs.go:61:2:61:22 | return statement | exprs.go:60:1:62:1 | exit | -| exprs.go:61:9:61:22 | slice literal | exprs.go:61:15:61:15 | element index | -| exprs.go:61:15:61:15 | 1 | exprs.go:61:15:61:15 | init of 1 | -| exprs.go:61:15:61:15 | element index | exprs.go:61:15:61:15 | 1 | -| exprs.go:61:15:61:15 | init of 1 | exprs.go:61:18:61:18 | element index | -| exprs.go:61:18:61:18 | 2 | exprs.go:61:18:61:18 | init of 2 | -| exprs.go:61:18:61:18 | element index | exprs.go:61:18:61:18 | 2 | -| exprs.go:61:18:61:18 | init of 2 | exprs.go:61:21:61:21 | element index | -| exprs.go:61:21:61:21 | 3 | exprs.go:61:21:61:21 | init of 3 | -| exprs.go:61:21:61:21 | element index | exprs.go:61:21:61:21 | 3 | +| exprs.go:61:2:61:22 | return statement | exprs.go:60:1:62:1 | Normal Exit | +| exprs.go:61:9:61:22 | slice literal | exprs.go:61:15:61:15 | Before 1 | +| exprs.go:61:15:61:15 | 1 | exprs.go:61:15:61:15 | After 1 | +| exprs.go:61:15:61:15 | After 1 | exprs.go:61:15:61:15 | lit-index 1 | +| exprs.go:61:15:61:15 | lit-init 1 | exprs.go:61:18:61:18 | Before 2 | +| exprs.go:61:18:61:18 | 2 | exprs.go:61:18:61:18 | After 2 | +| exprs.go:61:18:61:18 | After 2 | exprs.go:61:18:61:18 | lit-index 2 | +| exprs.go:61:18:61:18 | lit-init 2 | exprs.go:61:21:61:21 | Before 3 | +| exprs.go:61:21:61:21 | 3 | exprs.go:61:21:61:21 | After 3 | +| exprs.go:61:21:61:21 | After 3 | exprs.go:61:21:61:21 | lit-index 3 | | exprs.go:61:21:61:21 | init of 3 | exprs.go:61:2:61:22 | return statement | | exprs.go:64:5:64:5 | assignment to s | exprs.go:65:5:65:6 | skip | | exprs.go:64:5:64:5 | skip | exprs.go:64:9:64:11 | sum | | exprs.go:64:9:64:11 | sum | exprs.go:64:13:64:16 | ints | -| exprs.go:64:9:64:19 | call to sum | exprs.go:0:0:0:0 | exit | +| exprs.go:64:9:64:19 | call to sum | exprs.go:0:0:0:0 | Exceptional Exit | | exprs.go:64:9:64:19 | call to sum | exprs.go:64:5:64:5 | assignment to s | -| exprs.go:64:13:64:16 | ints | exprs.go:64:13:64:18 | call to ints | -| exprs.go:64:13:64:18 | call to ints | exprs.go:0:0:0:0 | exit | -| exprs.go:64:13:64:18 | call to ints | exprs.go:64:9:64:19 | call to sum | +| exprs.go:64:13:64:16 | After ints | exprs.go:64:13:64:18 | call to ints | +| exprs.go:64:13:64:18 | call to ints | exprs.go:0:0:0:0 | Exceptional Exit | +| exprs.go:64:13:64:18 | After call to ints | exprs.go:64:9:64:19 | call to sum | | exprs.go:65:5:65:6 | assignment to s2 | exprs.go:67:6:67:8 | skip | | exprs.go:65:5:65:6 | skip | exprs.go:65:10:65:13 | sum2 | | exprs.go:65:10:65:13 | sum2 | exprs.go:65:15:65:18 | ints | -| exprs.go:65:10:65:24 | call to sum2 | exprs.go:0:0:0:0 | exit | +| exprs.go:65:10:65:24 | call to sum2 | exprs.go:0:0:0:0 | Exceptional Exit | | exprs.go:65:10:65:24 | call to sum2 | exprs.go:65:5:65:6 | assignment to s2 | -| exprs.go:65:15:65:18 | ints | exprs.go:65:15:65:20 | call to ints | -| exprs.go:65:15:65:20 | call to ints | exprs.go:0:0:0:0 | exit | -| exprs.go:65:15:65:20 | call to ints | exprs.go:65:10:65:24 | call to sum2 | +| exprs.go:65:15:65:18 | After ints | exprs.go:65:15:65:20 | call to ints | +| exprs.go:65:15:65:20 | call to ints | exprs.go:0:0:0:0 | Exceptional Exit | +| exprs.go:65:15:65:20 | After call to ints | exprs.go:65:10:65:24 | call to sum2 | | exprs.go:67:1:69:1 | entry | exprs.go:67:10:67:10 | argument corresponding to x | | exprs.go:67:1:69:1 | function declaration | exprs.go:71:6:71:8 | skip | | exprs.go:67:6:67:8 | skip | exprs.go:67:1:69:1 | function declaration | @@ -402,26 +402,26 @@ | exprs.go:67:10:67:10 | initialization of x | exprs.go:67:13:67:13 | argument corresponding to y | | exprs.go:67:13:67:13 | argument corresponding to y | exprs.go:67:13:67:13 | initialization of y | | exprs.go:67:13:67:13 | initialization of y | exprs.go:68:9:68:9 | x | -| exprs.go:68:2:68:13 | return statement | exprs.go:67:1:69:1 | exit | -| exprs.go:68:9:68:9 | x | exprs.go:68:13:68:13 | y | -| exprs.go:68:9:68:13 | ...+... | exprs.go:68:2:68:13 | return statement | -| exprs.go:68:13:68:13 | y | exprs.go:68:9:68:13 | ...+... | +| exprs.go:68:2:68:13 | return statement | exprs.go:67:1:69:1 | Normal Exit | +| exprs.go:68:9:68:9 | After x | exprs.go:68:13:68:13 | Before y | +| exprs.go:68:9:68:13 | After ...+... | exprs.go:68:2:68:13 | return statement | +| exprs.go:68:13:68:13 | After y | exprs.go:68:9:68:13 | ...+... | | exprs.go:71:1:73:1 | entry | exprs.go:72:9:72:9 | 1 | | exprs.go:71:1:73:1 | function declaration | exprs.go:75:5:75:6 | skip | | exprs.go:71:6:71:8 | skip | exprs.go:71:1:73:1 | function declaration | -| exprs.go:72:2:72:12 | return statement | exprs.go:71:1:73:1 | exit | -| exprs.go:72:9:72:9 | 1 | exprs.go:72:12:72:12 | 2 | -| exprs.go:72:12:72:12 | 2 | exprs.go:72:2:72:12 | return statement | +| exprs.go:72:2:72:12 | return statement | exprs.go:71:1:73:1 | Normal Exit | +| exprs.go:72:9:72:9 | After 1 | exprs.go:72:12:72:12 | Before 2 | +| exprs.go:72:12:72:12 | After 2 | exprs.go:72:2:72:12 | return statement | | exprs.go:75:5:75:6 | assignment to s3 | exprs.go:77:6:77:10 | skip | | exprs.go:75:5:75:6 | skip | exprs.go:75:10:75:12 | add | | exprs.go:75:10:75:12 | add | exprs.go:75:14:75:16 | gen | -| exprs.go:75:10:75:19 | call to add | exprs.go:0:0:0:0 | exit | +| exprs.go:75:10:75:19 | call to add | exprs.go:0:0:0:0 | Exceptional Exit | | exprs.go:75:10:75:19 | call to add | exprs.go:75:5:75:6 | assignment to s3 | -| exprs.go:75:10:75:19 | call to add[0] | exprs.go:75:10:75:19 | call to add[1] | +| exprs.go:75:10:75:19 | call to add | exprs.go:75:10:75:19 | After call to add | | exprs.go:75:10:75:19 | call to add[1] | exprs.go:75:10:75:19 | call to add | -| exprs.go:75:14:75:16 | gen | exprs.go:75:14:75:18 | call to gen | -| exprs.go:75:14:75:18 | call to gen | exprs.go:0:0:0:0 | exit | -| exprs.go:75:14:75:18 | call to gen | exprs.go:75:10:75:19 | call to add[0] | +| exprs.go:75:14:75:16 | After gen | exprs.go:75:14:75:18 | call to gen | +| exprs.go:75:14:75:18 | call to gen | exprs.go:0:0:0:0 | Exceptional Exit | +| exprs.go:75:14:75:18 | After call to gen | exprs.go:75:10:75:19 | call to add | | exprs.go:77:1:79:1 | entry | exprs.go:77:12:77:12 | argument corresponding to x | | exprs.go:77:1:79:1 | function declaration | exprs.go:81:6:81:16 | skip | | exprs.go:77:6:77:10 | skip | exprs.go:77:1:79:1 | function declaration | @@ -431,19 +431,19 @@ | exprs.go:77:15:77:15 | initialization of y | exprs.go:77:18:77:18 | argument corresponding to z | | exprs.go:77:18:77:18 | argument corresponding to z | exprs.go:77:18:77:18 | initialization of z | | exprs.go:77:18:77:18 | initialization of z | exprs.go:78:11:78:11 | x | -| exprs.go:78:2:78:22 | return statement | exprs.go:77:1:79:1 | exit | +| exprs.go:78:2:78:22 | return statement | exprs.go:77:1:79:1 | Normal Exit | | exprs.go:78:9:78:17 | !... | exprs.go:78:9:78:17 | !... is false | | exprs.go:78:9:78:17 | !... | exprs.go:78:9:78:17 | !... is true | -| exprs.go:78:9:78:17 | !... is false | exprs.go:78:22:78:22 | z | -| exprs.go:78:9:78:17 | !... is true | exprs.go:78:9:78:22 | ...\|\|... | -| exprs.go:78:9:78:22 | ...\|\|... | exprs.go:78:2:78:22 | return statement | +| exprs.go:78:9:78:17 | After !... [false] | exprs.go:78:22:78:22 | Before z | +| exprs.go:78:9:78:17 | After !... [true] | exprs.go:78:9:78:22 | After ...\|\|... | +| exprs.go:78:9:78:22 | After ...\|\|... | exprs.go:78:2:78:22 | return statement | | exprs.go:78:11:78:11 | x | exprs.go:78:11:78:11 | x is false | | exprs.go:78:11:78:11 | x | exprs.go:78:11:78:11 | x is true | | exprs.go:78:11:78:11 | x is false | exprs.go:78:11:78:16 | ...&&... | | exprs.go:78:11:78:11 | x is true | exprs.go:78:16:78:16 | y | | exprs.go:78:11:78:16 | ...&&... | exprs.go:78:9:78:17 | !... | | exprs.go:78:16:78:16 | y | exprs.go:78:11:78:16 | ...&&... | -| exprs.go:78:22:78:22 | z | exprs.go:78:9:78:22 | ...\|\|... | +| exprs.go:78:22:78:22 | After z | exprs.go:78:9:78:22 | After ...\|\|... | | exprs.go:81:1:87:1 | entry | exprs.go:81:18:81:19 | argument corresponding to ch | | exprs.go:81:1:87:1 | function declaration | exprs.go:89:7:89:9 | skip | | exprs.go:81:6:81:16 | skip | exprs.go:81:1:87:1 | function declaration | @@ -455,28 +455,28 @@ | exprs.go:82:2:82:16 | ... := ...[1] | exprs.go:82:7:82:8 | assignment to ok | | exprs.go:82:7:82:8 | assignment to ok | exprs.go:83:5:83:6 | ok | | exprs.go:82:7:82:8 | skip | exprs.go:82:15:82:16 | ch | -| exprs.go:82:13:82:16 | <-... | exprs.go:82:2:82:16 | ... := ...[0] | -| exprs.go:82:15:82:16 | ch | exprs.go:82:13:82:16 | <-... | -| exprs.go:83:5:83:6 | ok | exprs.go:83:5:83:6 | ok is false | -| exprs.go:83:5:83:6 | ok | exprs.go:83:5:83:6 | ok is true | +| exprs.go:82:13:82:16 | After <-... | exprs.go:82:2:82:16 | extract:0 ... := ... | +| exprs.go:82:15:82:16 | After ch | exprs.go:82:13:82:16 | <-... | +| exprs.go:83:5:83:6 | Before ok | exprs.go:83:5:83:6 | ok | +| exprs.go:83:5:83:6 | ok | exprs.go:83:5:83:6 | After ok [false] | | exprs.go:83:5:83:6 | ok is false | exprs.go:86:2:86:6 | panic | | exprs.go:83:5:83:6 | ok is true | exprs.go:84:10:84:12 | val | -| exprs.go:84:3:84:12 | return statement | exprs.go:81:1:87:1 | exit | -| exprs.go:84:10:84:12 | val | exprs.go:84:3:84:12 | return statement | -| exprs.go:86:2:86:6 | panic | exprs.go:86:8:86:17 | "No value" | -| exprs.go:86:2:86:18 | call to panic | exprs.go:81:1:87:1 | exit | -| exprs.go:86:8:86:17 | "No value" | exprs.go:86:2:86:18 | call to panic | +| exprs.go:84:3:84:12 | return statement | exprs.go:81:1:87:1 | Normal Exit | +| exprs.go:84:10:84:12 | After val | exprs.go:84:3:84:12 | return statement | +| exprs.go:86:2:86:6 | After panic | exprs.go:86:8:86:17 | Before "No value" | +| exprs.go:86:2:86:18 | call to panic | exprs.go:81:1:87:1 | Exceptional Exit | +| exprs.go:86:8:86:17 | After "No value" | exprs.go:86:2:86:18 | call to panic | | exprs.go:89:7:89:9 | assignment to one | exprs.go:91:5:91:5 | skip | | exprs.go:89:7:89:9 | skip | exprs.go:89:13:89:13 | 1 | | exprs.go:89:13:89:13 | 1 | exprs.go:89:7:89:9 | assignment to one | | exprs.go:91:5:91:5 | assignment to a | exprs.go:93:6:93:11 | skip | | exprs.go:91:5:91:5 | skip | exprs.go:91:9:91:25 | slice literal | | exprs.go:91:9:91:25 | slice literal | exprs.go:91:15:91:21 | ...+... | -| exprs.go:91:15:91:21 | ...+... | exprs.go:91:24:91:24 | 2 | +| exprs.go:91:15:91:21 | After ...+... | exprs.go:91:24:91:24 | Before 2 | | exprs.go:91:15:91:24 | init of key-value pair | exprs.go:91:5:91:5 | assignment to a | -| exprs.go:91:24:91:24 | 2 | exprs.go:91:15:91:24 | init of key-value pair | +| exprs.go:91:24:91:24 | After 2 | exprs.go:91:15:91:24 | key-value pair | | exprs.go:93:1:95:1 | entry | exprs.go:93:13:93:13 | argument corresponding to x | -| exprs.go:93:1:95:1 | function declaration | exprs.go:0:0:0:0 | exit | +| exprs.go:93:1:95:1 | After function declaration | exprs.go:0:0:0:0 | After exprs.go | | exprs.go:93:6:93:11 | skip | exprs.go:93:1:95:1 | function declaration | | exprs.go:93:13:93:13 | argument corresponding to x | exprs.go:93:13:93:13 | initialization of x | | exprs.go:93:13:93:13 | initialization of x | exprs.go:93:16:93:16 | argument corresponding to y | @@ -484,28 +484,28 @@ | exprs.go:93:16:93:16 | initialization of y | exprs.go:93:19:93:19 | argument corresponding to z | | exprs.go:93:19:93:19 | argument corresponding to z | exprs.go:93:19:93:19 | initialization of z | | exprs.go:93:19:93:19 | initialization of z | exprs.go:94:10:94:10 | x | -| exprs.go:94:2:94:21 | return statement | exprs.go:93:1:95:1 | exit | -| exprs.go:94:9:94:16 | (...) is false | exprs.go:94:21:94:21 | z | -| exprs.go:94:9:94:16 | (...) is true | exprs.go:94:9:94:21 | ...\|\|... | -| exprs.go:94:9:94:21 | ...\|\|... | exprs.go:94:2:94:21 | return statement | +| exprs.go:94:2:94:21 | return statement | exprs.go:93:1:95:1 | Normal Exit | +| exprs.go:94:9:94:16 | After (...) [false] | exprs.go:94:21:94:21 | Before z | +| exprs.go:94:9:94:16 | After (...) [true] | exprs.go:94:9:94:21 | After ...\|\|... | +| exprs.go:94:9:94:21 | After ...\|\|... | exprs.go:94:2:94:21 | return statement | | exprs.go:94:10:94:10 | x | exprs.go:94:10:94:10 | x is false | | exprs.go:94:10:94:10 | x | exprs.go:94:10:94:10 | x is true | | exprs.go:94:10:94:10 | x is false | exprs.go:94:9:94:16 | (...) is false | | exprs.go:94:10:94:10 | x is true | exprs.go:94:15:94:15 | y | | exprs.go:94:15:94:15 | y | exprs.go:94:9:94:16 | (...) is false | | exprs.go:94:15:94:15 | y | exprs.go:94:9:94:16 | (...) is true | -| exprs.go:94:21:94:21 | z | exprs.go:94:9:94:21 | ...\|\|... | -| generic.go:0:0:0:0 | entry | generic.go:3:1:5:1 | skip | +| exprs.go:94:21:94:21 | After z | exprs.go:94:9:94:21 | After ...\|\|... | +| generic.go:0:0:0:0 | generic.go | generic.go:3:1:5:1 | type declaration | | generic.go:3:1:5:1 | skip | generic.go:7:28:7:35 | skip | | generic.go:7:1:7:55 | entry | generic.go:7:7:7:7 | argument corresponding to g | -| generic.go:7:1:7:55 | function declaration | generic.go:9:1:12:1 | skip | +| generic.go:7:1:7:55 | After function declaration | generic.go:9:1:12:1 | type declaration | | generic.go:7:7:7:7 | argument corresponding to g | generic.go:7:7:7:7 | initialization of g | | generic.go:7:7:7:7 | initialization of g | generic.go:7:37:7:37 | argument corresponding to u | | generic.go:7:28:7:35 | skip | generic.go:7:1:7:55 | function declaration | | generic.go:7:37:7:37 | argument corresponding to u | generic.go:7:37:7:37 | initialization of u | | generic.go:7:37:7:37 | initialization of u | generic.go:7:53:7:53 | u | -| generic.go:7:46:7:53 | return statement | generic.go:7:1:7:55 | exit | -| generic.go:7:53:7:53 | u | generic.go:7:46:7:53 | return statement | +| generic.go:7:46:7:53 | return statement | generic.go:7:1:7:55 | Normal Exit | +| generic.go:7:53:7:53 | After u | generic.go:7:46:7:53 | return statement | | generic.go:9:1:12:1 | skip | generic.go:14:31:14:39 | skip | | generic.go:14:1:14:59 | entry | generic.go:14:7:14:7 | argument corresponding to g | | generic.go:14:1:14:59 | function declaration | generic.go:16:6:16:21 | skip | @@ -514,15 +514,15 @@ | generic.go:14:31:14:39 | skip | generic.go:14:1:14:59 | function declaration | | generic.go:14:41:14:41 | argument corresponding to u | generic.go:14:41:14:41 | initialization of u | | generic.go:14:41:14:41 | initialization of u | generic.go:14:57:14:57 | u | -| generic.go:14:50:14:57 | return statement | generic.go:14:1:14:59 | exit | -| generic.go:14:57:14:57 | u | generic.go:14:50:14:57 | return statement | +| generic.go:14:50:14:57 | return statement | generic.go:14:1:14:59 | Normal Exit | +| generic.go:14:57:14:57 | After u | generic.go:14:50:14:57 | return statement | | generic.go:16:1:18:1 | entry | generic.go:16:30:16:30 | argument corresponding to t | | generic.go:16:1:18:1 | function declaration | generic.go:20:6:20:21 | skip | | generic.go:16:6:16:21 | skip | generic.go:16:1:18:1 | function declaration | | generic.go:16:30:16:30 | argument corresponding to t | generic.go:16:30:16:30 | initialization of t | | generic.go:16:30:16:30 | initialization of t | generic.go:17:9:17:9 | t | -| generic.go:17:2:17:9 | return statement | generic.go:16:1:18:1 | exit | -| generic.go:17:9:17:9 | t | generic.go:17:2:17:9 | return statement | +| generic.go:17:2:17:9 | return statement | generic.go:16:1:18:1 | Normal Exit | +| generic.go:17:9:17:9 | After t | generic.go:17:2:17:9 | return statement | | generic.go:20:1:22:1 | entry | generic.go:20:33:20:33 | argument corresponding to s | | generic.go:20:1:22:1 | function declaration | generic.go:24:6:24:12 | skip | | generic.go:20:6:20:21 | skip | generic.go:20:1:22:1 | function declaration | @@ -530,50 +530,50 @@ | generic.go:20:33:20:33 | initialization of s | generic.go:20:38:20:38 | argument corresponding to t | | generic.go:20:38:20:38 | argument corresponding to t | generic.go:20:38:20:38 | initialization of t | | generic.go:20:38:20:38 | initialization of t | generic.go:21:9:21:9 | s | -| generic.go:21:2:21:12 | return statement | generic.go:20:1:22:1 | exit | -| generic.go:21:9:21:9 | s | generic.go:21:12:21:12 | t | -| generic.go:21:12:21:12 | t | generic.go:21:2:21:12 | return statement | +| generic.go:21:2:21:12 | return statement | generic.go:20:1:22:1 | Normal Exit | +| generic.go:21:9:21:9 | After s | generic.go:21:12:21:12 | Before t | +| generic.go:21:12:21:12 | After t | generic.go:21:2:21:12 | return statement | | generic.go:24:1:35:1 | entry | generic.go:25:2:25:4 | skip | -| generic.go:24:1:35:1 | function declaration | generic.go:0:0:0:0 | exit | +| generic.go:24:1:35:1 | After function declaration | generic.go:0:0:0:0 | After generic.go | | generic.go:24:6:24:12 | skip | generic.go:24:1:35:1 | function declaration | | generic.go:25:2:25:4 | assignment to gs1 | generic.go:26:2:26:2 | skip | | generic.go:25:2:25:4 | skip | generic.go:25:9:25:35 | struct literal | -| generic.go:25:9:25:35 | struct literal | generic.go:25:32:25:34 | "x" | -| generic.go:25:32:25:34 | "x" | generic.go:25:32:25:34 | init of "x" | +| generic.go:25:9:25:35 | struct literal | generic.go:25:32:25:34 | Before "x" | +| generic.go:25:32:25:34 | "x" | generic.go:25:32:25:34 | After "x" | | generic.go:25:32:25:34 | init of "x" | generic.go:25:2:25:4 | assignment to gs1 | | generic.go:26:2:26:2 | assignment to a | generic.go:27:2:27:4 | skip | | generic.go:26:2:26:2 | skip | generic.go:26:7:26:9 | gs1 | -| generic.go:26:7:26:9 | gs1 | generic.go:26:7:26:18 | selection of Identity | -| generic.go:26:7:26:18 | selection of Identity | generic.go:26:20:26:26 | "hello" | -| generic.go:26:7:26:27 | call to Identity | generic.go:24:1:35:1 | exit | +| generic.go:26:7:26:9 | After gs1 | generic.go:26:7:26:18 | selection of Identity | +| generic.go:26:7:26:18 | After selection of Identity | generic.go:26:20:26:26 | Before "hello" | +| generic.go:26:7:26:27 | call to Identity | generic.go:24:1:35:1 | Exceptional Exit | | generic.go:26:7:26:27 | call to Identity | generic.go:26:2:26:2 | assignment to a | -| generic.go:26:20:26:26 | "hello" | generic.go:26:7:26:27 | call to Identity | +| generic.go:26:20:26:26 | After "hello" | generic.go:26:7:26:27 | call to Identity | | generic.go:27:2:27:4 | assignment to gs2 | generic.go:28:2:28:2 | skip | | generic.go:27:2:27:4 | skip | generic.go:27:9:27:48 | struct literal | -| generic.go:27:9:27:48 | struct literal | generic.go:27:40:27:42 | "y" | -| generic.go:27:40:27:42 | "y" | generic.go:27:40:27:42 | init of "y" | -| generic.go:27:40:27:42 | init of "y" | generic.go:27:45:27:47 | "z" | -| generic.go:27:45:27:47 | "z" | generic.go:27:45:27:47 | init of "z" | +| generic.go:27:9:27:48 | struct literal | generic.go:27:40:27:42 | Before "y" | +| generic.go:27:40:27:42 | "y" | generic.go:27:40:27:42 | After "y" | +| generic.go:27:40:27:42 | lit-init "y" | generic.go:27:45:27:47 | Before "z" | +| generic.go:27:45:27:47 | "z" | generic.go:27:45:27:47 | After "z" | | generic.go:27:45:27:47 | init of "z" | generic.go:27:2:27:4 | assignment to gs2 | | generic.go:28:2:28:2 | assignment to b | generic.go:29:2:29:2 | skip | | generic.go:28:2:28:2 | skip | generic.go:28:7:28:9 | gs2 | -| generic.go:28:7:28:9 | gs2 | generic.go:28:7:28:19 | selection of Identity1 | -| generic.go:28:7:28:19 | selection of Identity1 | generic.go:28:21:28:21 | a | -| generic.go:28:7:28:22 | call to Identity1 | generic.go:24:1:35:1 | exit | +| generic.go:28:7:28:9 | After gs2 | generic.go:28:7:28:19 | selection of Identity1 | +| generic.go:28:7:28:19 | After selection of Identity1 | generic.go:28:21:28:21 | Before a | +| generic.go:28:7:28:22 | call to Identity1 | generic.go:24:1:35:1 | Exceptional Exit | | generic.go:28:7:28:22 | call to Identity1 | generic.go:28:2:28:2 | assignment to b | -| generic.go:28:21:28:21 | a | generic.go:28:7:28:22 | call to Identity1 | +| generic.go:28:21:28:21 | After a | generic.go:28:7:28:22 | call to Identity1 | | generic.go:29:2:29:2 | assignment to c | generic.go:30:2:30:2 | skip | | generic.go:29:2:29:2 | skip | generic.go:29:7:29:22 | genericIdentity1 | | generic.go:29:7:29:22 | genericIdentity1 | generic.go:29:32:29:32 | b | -| generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:24:1:35:1 | exit | +| generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:24:1:35:1 | Exceptional Exit | | generic.go:29:7:29:33 | call to genericIdentity1 | generic.go:29:2:29:2 | assignment to c | -| generic.go:29:32:29:32 | b | generic.go:29:7:29:33 | call to genericIdentity1 | +| generic.go:29:32:29:32 | After b | generic.go:29:7:29:33 | call to genericIdentity1 | | generic.go:30:2:30:2 | assignment to d | generic.go:31:2:31:2 | skip | | generic.go:30:2:30:2 | skip | generic.go:30:7:30:22 | genericIdentity1 | -| generic.go:30:7:30:22 | genericIdentity1 | generic.go:30:24:30:24 | c | -| generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:24:1:35:1 | exit | +| generic.go:30:7:30:22 | After genericIdentity1 | generic.go:30:24:30:24 | Before c | +| generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:24:1:35:1 | Exceptional Exit | | generic.go:30:7:30:25 | call to genericIdentity1 | generic.go:30:2:30:2 | assignment to d | -| generic.go:30:24:30:24 | c | generic.go:30:7:30:25 | call to genericIdentity1 | +| generic.go:30:24:30:24 | After c | generic.go:30:7:30:25 | call to genericIdentity1 | | generic.go:31:2:31:2 | assignment to e | generic.go:31:2:31:53 | ... := ...[1] | | generic.go:31:2:31:2 | skip | generic.go:31:5:31:5 | skip | | generic.go:31:2:31:53 | ... := ...[0] | generic.go:31:2:31:2 | assignment to e | @@ -581,47 +581,47 @@ | generic.go:31:5:31:5 | assignment to f | generic.go:32:2:32:2 | skip | | generic.go:31:5:31:5 | skip | generic.go:31:10:31:25 | genericIdentity2 | | generic.go:31:10:31:25 | genericIdentity2 | generic.go:31:43:31:43 | d | -| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:24:1:35:1 | exit | -| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:31:2:31:53 | ... := ...[0] | -| generic.go:31:43:31:43 | d | generic.go:31:46:31:52 | "hello" | -| generic.go:31:46:31:52 | "hello" | generic.go:31:10:31:53 | call to genericIdentity2 | +| generic.go:31:10:31:53 | call to genericIdentity2 | generic.go:24:1:35:1 | Exceptional Exit | +| generic.go:31:10:31:53 | After call to genericIdentity2 | generic.go:31:2:31:53 | extract:0 ... := ... | +| generic.go:31:43:31:43 | After d | generic.go:31:46:31:52 | Before "hello" | +| generic.go:31:46:31:52 | After "hello" | generic.go:31:10:31:53 | call to genericIdentity2 | | generic.go:32:2:32:2 | assignment to g | generic.go:32:2:32:31 | ... := ...[1] | | generic.go:32:2:32:2 | skip | generic.go:32:5:32:5 | skip | | generic.go:32:2:32:31 | ... := ...[0] | generic.go:32:2:32:2 | assignment to g | | generic.go:32:2:32:31 | ... := ...[1] | generic.go:32:5:32:5 | assignment to h | | generic.go:32:5:32:5 | assignment to h | generic.go:33:2:33:2 | skip | | generic.go:32:5:32:5 | skip | generic.go:32:10:32:25 | genericIdentity2 | -| generic.go:32:10:32:25 | genericIdentity2 | generic.go:32:27:32:27 | e | -| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:24:1:35:1 | exit | -| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:32:2:32:31 | ... := ...[0] | -| generic.go:32:27:32:27 | e | generic.go:32:30:32:30 | f | -| generic.go:32:30:32:30 | f | generic.go:32:10:32:31 | call to genericIdentity2 | +| generic.go:32:10:32:25 | After genericIdentity2 | generic.go:32:27:32:27 | Before e | +| generic.go:32:10:32:31 | call to genericIdentity2 | generic.go:24:1:35:1 | Exceptional Exit | +| generic.go:32:10:32:31 | After call to genericIdentity2 | generic.go:32:2:32:31 | extract:0 ... := ... | +| generic.go:32:27:32:27 | After e | generic.go:32:30:32:30 | Before f | +| generic.go:32:30:32:30 | After f | generic.go:32:10:32:31 | call to genericIdentity2 | | generic.go:33:2:33:2 | skip | generic.go:33:6:33:6 | g | | generic.go:33:6:33:6 | g | generic.go:34:2:34:2 | skip | | generic.go:34:2:34:2 | skip | generic.go:34:6:34:6 | h | | generic.go:34:6:34:6 | h | generic.go:24:1:35:1 | exit | -| hello.go:0:0:0:0 | entry | hello.go:3:1:3:12 | skip | +| hello.go:0:0:0:0 | hello.go | hello.go:3:1:3:12 | import declaration | | hello.go:3:1:3:12 | skip | hello.go:5:7:5:13 | skip | | hello.go:5:7:5:13 | assignment to message | hello.go:7:6:7:13 | skip | | hello.go:5:7:5:13 | skip | hello.go:5:17:5:31 | "Hello, world!" | | hello.go:5:17:5:31 | "Hello, world!" | hello.go:5:7:5:13 | assignment to message | | hello.go:7:1:9:1 | entry | hello.go:8:2:8:12 | selection of Println | -| hello.go:7:1:9:1 | function declaration | hello.go:0:0:0:0 | exit | +| hello.go:7:1:9:1 | After function declaration | hello.go:0:0:0:0 | After hello.go | | hello.go:7:6:7:13 | skip | hello.go:7:1:9:1 | function declaration | -| hello.go:8:2:8:12 | selection of Println | hello.go:8:14:8:20 | message | -| hello.go:8:2:8:21 | call to Println | hello.go:7:1:9:1 | exit | -| hello.go:8:14:8:20 | message | hello.go:8:2:8:21 | call to Println | -| main.go:0:0:0:0 | entry | main.go:3:1:6:1 | skip | +| hello.go:8:2:8:12 | After selection of Println | hello.go:8:14:8:20 | Before message | +| hello.go:8:2:8:21 | call to Println | hello.go:7:1:9:1 | Exceptional Exit | +| hello.go:8:14:8:20 | After message | hello.go:8:2:8:21 | call to Println | +| main.go:0:0:0:0 | main.go | main.go:3:1:6:1 | import declaration | | main.go:3:1:6:1 | skip | main.go:8:6:8:9 | skip | | main.go:8:1:10:1 | entry | main.go:9:9:9:20 | selection of Float64 | | main.go:8:1:10:1 | function declaration | main.go:12:6:12:9 | skip | | main.go:8:6:8:9 | skip | main.go:8:1:10:1 | function declaration | -| main.go:9:2:9:29 | return statement | main.go:8:1:10:1 | exit | -| main.go:9:9:9:20 | selection of Float64 | main.go:9:9:9:22 | call to Float64 | -| main.go:9:9:9:22 | call to Float64 | main.go:8:1:10:1 | exit | -| main.go:9:9:9:22 | call to Float64 | main.go:9:27:9:29 | 0.5 | -| main.go:9:9:9:29 | ...>=... | main.go:9:2:9:29 | return statement | -| main.go:9:27:9:29 | 0.5 | main.go:9:9:9:29 | ...>=... | +| main.go:9:2:9:29 | return statement | main.go:8:1:10:1 | Normal Exit | +| main.go:9:9:9:20 | After selection of Float64 | main.go:9:9:9:22 | call to Float64 | +| main.go:9:9:9:22 | call to Float64 | main.go:8:1:10:1 | Exceptional Exit | +| main.go:9:9:9:22 | After call to Float64 | main.go:9:27:9:29 | Before 0.5 | +| main.go:9:9:9:29 | After ...>=... | main.go:9:2:9:29 | return statement | +| main.go:9:27:9:29 | After 0.5 | main.go:9:9:9:29 | ...>=... | | main.go:12:1:24:1 | entry | main.go:13:6:13:6 | skip | | main.go:12:1:24:1 | function declaration | main.go:26:6:26:8 | skip | | main.go:12:6:12:9 | skip | main.go:12:1:24:1 | function declaration | @@ -631,39 +631,39 @@ | main.go:14:2:14:2 | assignment to y | main.go:15:2:15:10 | selection of Print | | main.go:14:2:14:2 | skip | main.go:14:7:14:8 | 23 | | main.go:14:7:14:8 | 23 | main.go:14:2:14:2 | assignment to y | -| main.go:15:2:15:10 | selection of Print | main.go:15:12:15:12 | x | -| main.go:15:2:15:16 | call to Print | main.go:12:1:24:1 | exit | +| main.go:15:2:15:10 | After selection of Print | main.go:15:12:15:12 | Before x | +| main.go:15:2:15:16 | call to Print | main.go:12:1:24:1 | Exceptional Exit | | main.go:15:2:15:16 | call to Print | main.go:16:5:16:8 | cond | -| main.go:15:12:15:12 | x | main.go:15:15:15:15 | y | -| main.go:15:15:15:15 | y | main.go:15:2:15:16 | call to Print | -| main.go:16:5:16:8 | cond | main.go:16:5:16:10 | call to cond | -| main.go:16:5:16:10 | call to cond | main.go:12:1:24:1 | exit | -| main.go:16:5:16:10 | call to cond | main.go:16:5:16:10 | call to cond is false | -| main.go:16:5:16:10 | call to cond | main.go:16:5:16:10 | call to cond is true | +| main.go:15:12:15:12 | After x | main.go:15:15:15:15 | Before y | +| main.go:15:15:15:15 | After y | main.go:15:2:15:16 | call to Print | +| main.go:16:5:16:8 | After cond | main.go:16:5:16:10 | call to cond | +| main.go:16:5:16:10 | call to cond | main.go:12:1:24:1 | Exceptional Exit | +| main.go:16:5:16:10 | call to cond | main.go:16:5:16:10 | After call to cond [false] | +| main.go:16:5:16:10 | call to cond | main.go:16:5:16:10 | After call to cond [true] | | main.go:16:5:16:10 | call to cond is false | main.go:19:2:19:10 | selection of Print | | main.go:16:5:16:10 | call to cond is true | main.go:17:3:17:3 | y | | main.go:17:3:17:3 | assignment to y | main.go:19:2:19:10 | selection of Print | -| main.go:17:3:17:3 | y | main.go:17:8:17:9 | 19 | -| main.go:17:3:17:9 | ... += ... | main.go:17:3:17:3 | assignment to y | -| main.go:17:8:17:9 | 19 | main.go:17:3:17:9 | ... += ... | -| main.go:19:2:19:10 | selection of Print | main.go:19:12:19:12 | x | -| main.go:19:2:19:16 | call to Print | main.go:12:1:24:1 | exit | +| main.go:17:3:17:3 | After y | main.go:17:8:17:9 | Before 19 | +| main.go:17:3:17:9 | ... += ... | main.go:17:3:17:3 | Before y | +| main.go:17:8:17:9 | After 19 | main.go:17:3:17:9 | compound-rhs ... += ... | +| main.go:19:2:19:10 | After selection of Print | main.go:19:12:19:12 | Before x | +| main.go:19:2:19:16 | call to Print | main.go:12:1:24:1 | Exceptional Exit | | main.go:19:2:19:16 | call to Print | main.go:20:5:20:8 | cond | -| main.go:19:12:19:12 | x | main.go:19:15:19:15 | y | -| main.go:19:15:19:15 | y | main.go:19:2:19:16 | call to Print | -| main.go:20:5:20:8 | cond | main.go:20:5:20:10 | call to cond | -| main.go:20:5:20:10 | call to cond | main.go:12:1:24:1 | exit | -| main.go:20:5:20:10 | call to cond | main.go:20:5:20:10 | call to cond is false | -| main.go:20:5:20:10 | call to cond | main.go:20:5:20:10 | call to cond is true | +| main.go:19:12:19:12 | After x | main.go:19:15:19:15 | Before y | +| main.go:19:15:19:15 | After y | main.go:19:2:19:16 | call to Print | +| main.go:20:5:20:8 | After cond | main.go:20:5:20:10 | call to cond | +| main.go:20:5:20:10 | call to cond | main.go:12:1:24:1 | Exceptional Exit | +| main.go:20:5:20:10 | call to cond | main.go:20:5:20:10 | After call to cond [false] | +| main.go:20:5:20:10 | call to cond | main.go:20:5:20:10 | After call to cond [true] | | main.go:20:5:20:10 | call to cond is false | main.go:23:2:23:10 | selection of Print | | main.go:20:5:20:10 | call to cond is true | main.go:21:3:21:3 | skip | | main.go:21:3:21:3 | assignment to x | main.go:23:2:23:10 | selection of Print | | main.go:21:3:21:3 | skip | main.go:21:7:21:7 | y | | main.go:21:7:21:7 | y | main.go:21:3:21:3 | assignment to x | -| main.go:23:2:23:10 | selection of Print | main.go:23:12:23:12 | x | -| main.go:23:2:23:16 | call to Print | main.go:12:1:24:1 | exit | -| main.go:23:12:23:12 | x | main.go:23:15:23:15 | y | -| main.go:23:15:23:15 | y | main.go:23:2:23:16 | call to Print | +| main.go:23:2:23:10 | After selection of Print | main.go:23:12:23:12 | Before x | +| main.go:23:2:23:16 | call to Print | main.go:12:1:24:1 | Exceptional Exit | +| main.go:23:12:23:12 | After x | main.go:23:15:23:15 | Before y | +| main.go:23:15:23:15 | After y | main.go:23:2:23:16 | call to Print | | main.go:26:1:32:1 | entry | main.go:26:10:26:10 | argument corresponding to x | | main.go:26:1:32:1 | function declaration | main.go:34:6:34:9 | skip | | main.go:26:6:26:8 | skip | main.go:26:1:32:1 | function declaration | @@ -673,23 +673,23 @@ | main.go:27:2:27:2 | skip | main.go:27:5:27:5 | skip | | main.go:27:5:27:5 | assignment to b | main.go:28:5:28:8 | cond | | main.go:27:5:27:5 | skip | main.go:27:10:27:10 | x | -| main.go:27:10:27:10 | x | main.go:27:13:27:13 | 0 | +| main.go:27:10:27:10 | After x | main.go:27:13:27:13 | Before 0 | | main.go:27:13:27:13 | 0 | main.go:27:2:27:2 | assignment to a | -| main.go:28:5:28:8 | cond | main.go:28:5:28:10 | call to cond | -| main.go:28:5:28:10 | call to cond | main.go:26:1:32:1 | exit | -| main.go:28:5:28:10 | call to cond | main.go:28:5:28:10 | call to cond is false | -| main.go:28:5:28:10 | call to cond | main.go:28:5:28:10 | call to cond is true | +| main.go:28:5:28:8 | After cond | main.go:28:5:28:10 | call to cond | +| main.go:28:5:28:10 | call to cond | main.go:26:1:32:1 | Exceptional Exit | +| main.go:28:5:28:10 | call to cond | main.go:28:5:28:10 | After call to cond [false] | +| main.go:28:5:28:10 | call to cond | main.go:28:5:28:10 | After call to cond [true] | | main.go:28:5:28:10 | call to cond is false | main.go:31:9:31:9 | a | | main.go:28:5:28:10 | call to cond is true | main.go:29:3:29:3 | skip | | main.go:29:3:29:3 | assignment to a | main.go:29:6:29:6 | assignment to b | | main.go:29:3:29:3 | skip | main.go:29:6:29:6 | skip | | main.go:29:6:29:6 | assignment to b | main.go:31:9:31:9 | a | | main.go:29:6:29:6 | skip | main.go:29:10:29:10 | b | -| main.go:29:10:29:10 | b | main.go:29:13:29:13 | a | +| main.go:29:10:29:10 | After b | main.go:29:13:29:13 | Before a | | main.go:29:13:29:13 | a | main.go:29:3:29:3 | assignment to a | -| main.go:31:2:31:12 | return statement | main.go:26:1:32:1 | exit | -| main.go:31:9:31:9 | a | main.go:31:12:31:12 | b | -| main.go:31:12:31:12 | b | main.go:31:2:31:12 | return statement | +| main.go:31:2:31:12 | return statement | main.go:26:1:32:1 | Normal Exit | +| main.go:31:9:31:9 | After a | main.go:31:12:31:12 | Before b | +| main.go:31:12:31:12 | After b | main.go:31:2:31:12 | return statement | | main.go:34:1:36:1 | entry | main.go:34:11:34:11 | argument corresponding to x | | main.go:34:1:36:1 | function declaration | main.go:38:6:38:8 | skip | | main.go:34:6:34:9 | skip | main.go:34:1:36:1 | function declaration | @@ -697,10 +697,10 @@ | main.go:34:11:34:11 | initialization of x | main.go:35:3:35:3 | x | | main.go:35:2:35:3 | assignment to star expression | main.go:34:1:36:1 | exit | | main.go:35:2:35:3 | star expression | main.go:34:1:36:1 | exit | -| main.go:35:2:35:3 | star expression | main.go:35:8:35:9 | 19 | -| main.go:35:2:35:9 | ... += ... | main.go:35:2:35:3 | assignment to star expression | -| main.go:35:3:35:3 | x | main.go:35:2:35:3 | star expression | -| main.go:35:8:35:9 | 19 | main.go:35:2:35:9 | ... += ... | +| main.go:35:2:35:3 | After star expression | main.go:35:8:35:9 | Before 19 | +| main.go:35:2:35:9 | ... += ... | main.go:35:2:35:3 | Before star expression | +| main.go:35:3:35:3 | After x | main.go:35:2:35:3 | star expression | +| main.go:35:8:35:9 | After 19 | main.go:35:2:35:9 | compound-rhs ... += ... | | main.go:38:1:45:1 | entry | main.go:39:2:39:2 | skip | | main.go:38:1:45:1 | function declaration | main.go:47:6:47:8 | skip | | main.go:38:6:38:8 | skip | main.go:38:1:45:1 | function declaration | @@ -710,20 +710,20 @@ | main.go:40:2:40:4 | assignment to ptr | main.go:41:5:41:8 | cond | | main.go:40:2:40:4 | skip | main.go:40:10:40:10 | x | | main.go:40:9:40:10 | &... | main.go:40:2:40:4 | assignment to ptr | -| main.go:40:10:40:10 | x | main.go:40:9:40:10 | &... | -| main.go:41:5:41:8 | cond | main.go:41:5:41:10 | call to cond | -| main.go:41:5:41:10 | call to cond | main.go:38:1:45:1 | exit | -| main.go:41:5:41:10 | call to cond | main.go:41:5:41:10 | call to cond is false | -| main.go:41:5:41:10 | call to cond | main.go:41:5:41:10 | call to cond is true | +| main.go:40:10:40:10 | After x | main.go:40:9:40:10 | &... | +| main.go:41:5:41:8 | After cond | main.go:41:5:41:10 | call to cond | +| main.go:41:5:41:10 | call to cond | main.go:38:1:45:1 | Exceptional Exit | +| main.go:41:5:41:10 | call to cond | main.go:41:5:41:10 | After call to cond [false] | +| main.go:41:5:41:10 | call to cond | main.go:41:5:41:10 | After call to cond [true] | | main.go:41:5:41:10 | call to cond is false | main.go:44:2:44:10 | selection of Print | | main.go:41:5:41:10 | call to cond is true | main.go:42:3:42:6 | bump | -| main.go:42:3:42:6 | bump | main.go:42:8:42:10 | ptr | -| main.go:42:3:42:11 | call to bump | main.go:38:1:45:1 | exit | +| main.go:42:3:42:6 | After bump | main.go:42:8:42:10 | Before ptr | +| main.go:42:3:42:11 | call to bump | main.go:38:1:45:1 | Exceptional Exit | | main.go:42:3:42:11 | call to bump | main.go:44:2:44:10 | selection of Print | -| main.go:42:8:42:10 | ptr | main.go:42:3:42:11 | call to bump | -| main.go:44:2:44:10 | selection of Print | main.go:44:12:44:12 | x | -| main.go:44:2:44:13 | call to Print | main.go:38:1:45:1 | exit | -| main.go:44:12:44:12 | x | main.go:44:2:44:13 | call to Print | +| main.go:42:8:42:10 | After ptr | main.go:42:3:42:11 | call to bump | +| main.go:44:2:44:10 | After selection of Print | main.go:44:12:44:12 | Before x | +| main.go:44:2:44:13 | call to Print | main.go:38:1:45:1 | Exceptional Exit | +| main.go:44:12:44:12 | After x | main.go:44:2:44:13 | call to Print | | main.go:47:1:50:1 | entry | main.go:47:13:47:18 | zero value for result | | main.go:47:1:50:1 | function declaration | main.go:52:6:52:9 | skip | | main.go:47:6:47:8 | skip | main.go:47:1:50:1 | function declaration | @@ -747,66 +747,66 @@ | main.go:57:6:57:6 | assignment to x | main.go:58:6:58:9 | cond | | main.go:57:6:57:6 | skip | main.go:57:6:57:6 | zero value for x | | main.go:57:6:57:6 | zero value for x | main.go:57:6:57:6 | assignment to x | -| main.go:58:6:58:9 | cond | main.go:58:6:58:11 | call to cond | -| main.go:58:6:58:11 | call to cond | main.go:56:1:80:1 | exit | -| main.go:58:6:58:11 | call to cond | main.go:58:6:58:11 | call to cond is false | -| main.go:58:6:58:11 | call to cond | main.go:58:6:58:11 | call to cond is true | +| main.go:58:6:58:9 | After cond | main.go:58:6:58:11 | call to cond | +| main.go:58:6:58:11 | call to cond | main.go:56:1:80:1 | Exceptional Exit | +| main.go:58:6:58:11 | call to cond | main.go:58:6:58:11 | After call to cond [false] | +| main.go:58:6:58:11 | call to cond | main.go:58:6:58:11 | After call to cond [true] | | main.go:58:6:58:11 | call to cond is false | main.go:61:2:61:10 | selection of Print | | main.go:58:6:58:11 | call to cond is true | main.go:59:3:59:3 | skip | | main.go:59:3:59:3 | assignment to x | main.go:58:6:58:9 | cond | | main.go:59:3:59:3 | skip | main.go:59:7:59:7 | 2 | | main.go:59:7:59:7 | 2 | main.go:59:3:59:3 | assignment to x | -| main.go:61:2:61:10 | selection of Print | main.go:61:12:61:12 | x | -| main.go:61:2:61:13 | call to Print | main.go:56:1:80:1 | exit | +| main.go:61:2:61:10 | After selection of Print | main.go:61:12:61:12 | Before x | +| main.go:61:2:61:13 | call to Print | main.go:56:1:80:1 | Exceptional Exit | | main.go:61:2:61:13 | call to Print | main.go:63:2:63:2 | skip | -| main.go:61:12:61:12 | x | main.go:61:2:61:13 | call to Print | +| main.go:61:12:61:12 | After x | main.go:61:2:61:13 | call to Print | | main.go:63:2:63:2 | assignment to y | main.go:64:6:64:6 | skip | | main.go:63:2:63:2 | skip | main.go:63:7:63:7 | 1 | | main.go:63:7:63:7 | 1 | main.go:63:2:63:2 | assignment to y | | main.go:64:6:64:6 | assignment to i | main.go:65:6:65:9 | cond | | main.go:64:6:64:6 | skip | main.go:64:11:64:11 | 0 | | main.go:64:11:64:11 | 0 | main.go:64:6:64:6 | assignment to i | -| main.go:64:16:64:16 | i | main.go:64:16:64:18 | 1 | -| main.go:64:16:64:18 | 1 | main.go:64:16:64:18 | rhs of increment statement | +| main.go:64:16:64:16 | After i | main.go:64:16:64:18 | implicit-one increment statement | +| main.go:64:16:64:18 | implicit-one increment statement | main.go:64:16:64:18 | incdec-rhs increment statement | | main.go:64:16:64:18 | increment statement | main.go:65:6:65:9 | cond | -| main.go:64:16:64:18 | rhs of increment statement | main.go:64:16:64:18 | increment statement | -| main.go:65:6:65:9 | cond | main.go:65:6:65:11 | call to cond | -| main.go:65:6:65:11 | call to cond | main.go:56:1:80:1 | exit | -| main.go:65:6:65:11 | call to cond | main.go:65:6:65:11 | call to cond is false | -| main.go:65:6:65:11 | call to cond | main.go:65:6:65:11 | call to cond is true | +| main.go:64:16:64:18 | incdec-rhs increment statement | main.go:64:16:64:18 | increment statement | +| main.go:65:6:65:9 | After cond | main.go:65:6:65:11 | call to cond | +| main.go:65:6:65:11 | call to cond | main.go:56:1:80:1 | Exceptional Exit | +| main.go:65:6:65:11 | call to cond | main.go:65:6:65:11 | After call to cond [false] | +| main.go:65:6:65:11 | call to cond | main.go:65:6:65:11 | After call to cond [true] | | main.go:65:6:65:11 | call to cond is false | main.go:68:3:68:3 | skip | | main.go:65:6:65:11 | call to cond is true | main.go:66:4:66:8 | skip | | main.go:66:4:66:8 | skip | main.go:70:2:70:10 | selection of Print | | main.go:68:3:68:3 | assignment to y | main.go:64:16:64:16 | i | | main.go:68:3:68:3 | skip | main.go:68:7:68:7 | 2 | | main.go:68:7:68:7 | 2 | main.go:68:3:68:3 | assignment to y | -| main.go:70:2:70:10 | selection of Print | main.go:70:12:70:12 | y | -| main.go:70:2:70:13 | call to Print | main.go:56:1:80:1 | exit | +| main.go:70:2:70:10 | After selection of Print | main.go:70:12:70:12 | Before y | +| main.go:70:2:70:13 | call to Print | main.go:56:1:80:1 | Exceptional Exit | | main.go:70:2:70:13 | call to Print | main.go:72:2:72:2 | skip | -| main.go:70:12:70:12 | y | main.go:70:2:70:13 | call to Print | +| main.go:70:12:70:12 | After y | main.go:70:2:70:13 | call to Print | | main.go:72:2:72:2 | assignment to z | main.go:73:6:73:6 | skip | | main.go:72:2:72:2 | skip | main.go:72:7:72:7 | 1 | | main.go:72:7:72:7 | 1 | main.go:72:2:72:2 | assignment to z | | main.go:73:6:73:6 | assignment to i | main.go:74:3:74:3 | skip | | main.go:73:6:73:6 | skip | main.go:73:11:73:11 | 0 | | main.go:73:11:73:11 | 0 | main.go:73:6:73:6 | assignment to i | -| main.go:73:16:73:16 | i | main.go:73:16:73:18 | 1 | -| main.go:73:16:73:18 | 1 | main.go:73:16:73:18 | rhs of increment statement | +| main.go:73:16:73:16 | After i | main.go:73:16:73:18 | implicit-one increment statement | +| main.go:73:16:73:18 | implicit-one increment statement | main.go:73:16:73:18 | incdec-rhs increment statement | | main.go:73:16:73:18 | increment statement | main.go:74:3:74:3 | skip | -| main.go:73:16:73:18 | rhs of increment statement | main.go:73:16:73:18 | increment statement | +| main.go:73:16:73:18 | incdec-rhs increment statement | main.go:73:16:73:18 | increment statement | | main.go:74:3:74:3 | assignment to z | main.go:75:6:75:9 | cond | | main.go:74:3:74:3 | skip | main.go:74:7:74:7 | 2 | | main.go:74:7:74:7 | 2 | main.go:74:3:74:3 | assignment to z | -| main.go:75:6:75:9 | cond | main.go:75:6:75:11 | call to cond | -| main.go:75:6:75:11 | call to cond | main.go:56:1:80:1 | exit | -| main.go:75:6:75:11 | call to cond | main.go:75:6:75:11 | call to cond is false | -| main.go:75:6:75:11 | call to cond | main.go:75:6:75:11 | call to cond is true | +| main.go:75:6:75:9 | After cond | main.go:75:6:75:11 | call to cond | +| main.go:75:6:75:11 | call to cond | main.go:56:1:80:1 | Exceptional Exit | +| main.go:75:6:75:11 | call to cond | main.go:75:6:75:11 | After call to cond [false] | +| main.go:75:6:75:11 | call to cond | main.go:75:6:75:11 | After call to cond [true] | | main.go:75:6:75:11 | call to cond is false | main.go:73:16:73:16 | i | | main.go:75:6:75:11 | call to cond is true | main.go:76:4:76:8 | skip | | main.go:76:4:76:8 | skip | main.go:79:2:79:10 | selection of Print | -| main.go:79:2:79:10 | selection of Print | main.go:79:12:79:12 | z | -| main.go:79:2:79:13 | call to Print | main.go:56:1:80:1 | exit | -| main.go:79:12:79:12 | z | main.go:79:2:79:13 | call to Print | +| main.go:79:2:79:10 | After selection of Print | main.go:79:12:79:12 | Before z | +| main.go:79:2:79:13 | call to Print | main.go:56:1:80:1 | Exceptional Exit | +| main.go:79:12:79:12 | After z | main.go:79:2:79:13 | call to Print | | main.go:82:1:86:1 | entry | main.go:82:18:82:18 | zero value for a | | main.go:82:1:86:1 | function declaration | main.go:88:6:88:23 | skip | | main.go:82:6:82:13 | skip | main.go:82:1:86:1 | function declaration | @@ -823,13 +823,13 @@ | main.go:84:2:84:2 | skip | main.go:84:5:84:5 | skip | | main.go:84:5:84:5 | assignment to a | main.go:85:2:85:7 | return statement | | main.go:84:5:84:5 | skip | main.go:84:9:84:9 | x | -| main.go:84:9:84:9 | x | main.go:84:11:84:12 | 19 | -| main.go:84:9:84:12 | ...+... | main.go:84:15:84:15 | x | -| main.go:84:11:84:12 | 19 | main.go:84:9:84:12 | ...+... | +| main.go:84:9:84:9 | After x | main.go:84:11:84:12 | Before 19 | +| main.go:84:9:84:12 | After ...+... | main.go:84:15:84:15 | Before x | +| main.go:84:11:84:12 | After 19 | main.go:84:9:84:12 | ...+... | | main.go:84:15:84:15 | x | main.go:84:2:84:2 | assignment to x | | main.go:85:2:85:7 | return statement | main.go:82:18:82:18 | implicit read of a | | main.go:88:1:96:1 | entry | main.go:88:25:88:25 | argument corresponding to x | -| main.go:88:1:96:1 | function declaration | main.go:0:0:0:0 | exit | +| main.go:88:1:96:1 | After function declaration | main.go:0:0:0:0 | After main.go | | main.go:88:6:88:23 | skip | main.go:88:1:96:1 | function declaration | | main.go:88:25:88:25 | argument corresponding to x | main.go:88:25:88:25 | initialization of x | | main.go:88:25:88:25 | initialization of x | main.go:89:2:89:2 | skip | @@ -837,104 +837,104 @@ | main.go:89:2:89:2 | skip | main.go:89:5:89:5 | skip | | main.go:89:5:89:5 | assignment to b | main.go:90:5:90:8 | cond | | main.go:89:5:89:5 | skip | main.go:89:10:89:10 | x | -| main.go:89:10:89:10 | x | main.go:89:13:89:13 | 0 | +| main.go:89:10:89:10 | After x | main.go:89:13:89:13 | Before 0 | | main.go:89:13:89:13 | 0 | main.go:89:2:89:2 | assignment to a | -| main.go:90:5:90:8 | cond | main.go:90:5:90:10 | call to cond | -| main.go:90:5:90:10 | call to cond | main.go:88:1:96:1 | exit | -| main.go:90:5:90:10 | call to cond | main.go:90:5:90:10 | call to cond is false | -| main.go:90:5:90:10 | call to cond | main.go:90:5:90:10 | call to cond is true | +| main.go:90:5:90:8 | After cond | main.go:90:5:90:10 | call to cond | +| main.go:90:5:90:10 | call to cond | main.go:88:1:96:1 | Exceptional Exit | +| main.go:90:5:90:10 | call to cond | main.go:90:5:90:10 | After call to cond [false] | +| main.go:90:5:90:10 | call to cond | main.go:90:5:90:10 | After call to cond [true] | | main.go:90:5:90:10 | call to cond is false | main.go:93:3:93:3 | skip | | main.go:90:5:90:10 | call to cond is true | main.go:91:3:91:3 | skip | | main.go:91:3:91:3 | assignment to a | main.go:95:9:95:9 | a | | main.go:91:3:91:3 | skip | main.go:91:6:91:6 | skip | | main.go:91:6:91:6 | skip | main.go:91:10:91:10 | b | -| main.go:91:10:91:10 | b | main.go:91:13:91:13 | a | +| main.go:91:10:91:10 | After b | main.go:91:13:91:13 | Before a | | main.go:91:13:91:13 | a | main.go:91:3:91:3 | assignment to a | | main.go:93:3:93:3 | skip | main.go:93:6:93:6 | skip | | main.go:93:6:93:6 | assignment to b | main.go:95:9:95:9 | a | | main.go:93:6:93:6 | skip | main.go:93:10:93:10 | b | -| main.go:93:10:93:10 | b | main.go:93:13:93:13 | a | +| main.go:93:10:93:10 | After b | main.go:93:13:93:13 | Before a | | main.go:93:13:93:13 | a | main.go:93:6:93:6 | assignment to b | -| main.go:95:2:95:12 | return statement | main.go:88:1:96:1 | exit | -| main.go:95:9:95:9 | a | main.go:95:12:95:12 | b | -| main.go:95:12:95:12 | b | main.go:95:2:95:12 | return statement | -| noretfunctions.go:0:0:0:0 | entry | noretfunctions.go:3:1:6:1 | skip | +| main.go:95:2:95:12 | return statement | main.go:88:1:96:1 | Normal Exit | +| main.go:95:9:95:9 | After a | main.go:95:12:95:12 | Before b | +| main.go:95:12:95:12 | After b | main.go:95:2:95:12 | return statement | +| noretfunctions.go:0:0:0:0 | noretfunctions.go | noretfunctions.go:3:1:6:1 | import declaration | | noretfunctions.go:3:1:6:1 | skip | noretfunctions.go:8:6:8:12 | skip | | noretfunctions.go:8:1:10:1 | entry | noretfunctions.go:9:2:9:8 | selection of Exit | | noretfunctions.go:8:1:10:1 | function declaration | noretfunctions.go:12:6:12:11 | skip | | noretfunctions.go:8:6:8:12 | skip | noretfunctions.go:8:1:10:1 | function declaration | -| noretfunctions.go:9:2:9:8 | selection of Exit | noretfunctions.go:9:10:9:10 | 1 | -| noretfunctions.go:9:2:9:11 | call to Exit | noretfunctions.go:8:1:10:1 | exit | -| noretfunctions.go:9:10:9:10 | 1 | noretfunctions.go:9:2:9:11 | call to Exit | +| noretfunctions.go:9:2:9:8 | After selection of Exit | noretfunctions.go:9:10:9:10 | Before 1 | +| noretfunctions.go:9:2:9:11 | call to Exit | noretfunctions.go:8:1:10:1 | Exceptional Exit | +| noretfunctions.go:9:10:9:10 | After 1 | noretfunctions.go:9:2:9:11 | call to Exit | | noretfunctions.go:12:1:16:1 | entry | noretfunctions.go:12:13:12:13 | argument corresponding to x | | noretfunctions.go:12:1:16:1 | function declaration | noretfunctions.go:18:6:18:12 | skip | | noretfunctions.go:12:6:12:11 | skip | noretfunctions.go:12:1:16:1 | function declaration | | noretfunctions.go:12:13:12:13 | argument corresponding to x | noretfunctions.go:12:13:12:13 | initialization of x | | noretfunctions.go:12:13:12:13 | initialization of x | noretfunctions.go:13:5:13:5 | x | -| noretfunctions.go:13:5:13:5 | x | noretfunctions.go:13:10:13:10 | 0 | -| noretfunctions.go:13:5:13:10 | ...!=... | noretfunctions.go:13:5:13:10 | ...!=... is false | -| noretfunctions.go:13:5:13:10 | ...!=... | noretfunctions.go:13:5:13:10 | ...!=... is true | +| noretfunctions.go:13:5:13:5 | After x | noretfunctions.go:13:10:13:10 | Before 0 | +| noretfunctions.go:13:5:13:10 | ...!=... | noretfunctions.go:13:5:13:10 | After ...!=... [false] | +| noretfunctions.go:13:5:13:10 | ...!=... | noretfunctions.go:13:5:13:10 | After ...!=... [true] | | noretfunctions.go:13:5:13:10 | ...!=... is false | noretfunctions.go:12:1:16:1 | exit | | noretfunctions.go:13:5:13:10 | ...!=... is true | noretfunctions.go:14:3:14:9 | selection of Exit | -| noretfunctions.go:13:10:13:10 | 0 | noretfunctions.go:13:5:13:10 | ...!=... | -| noretfunctions.go:14:3:14:9 | selection of Exit | noretfunctions.go:14:11:14:11 | x | -| noretfunctions.go:14:3:14:12 | call to Exit | noretfunctions.go:12:1:16:1 | exit | -| noretfunctions.go:14:11:14:11 | x | noretfunctions.go:14:3:14:12 | call to Exit | -| noretfunctions.go:18:1:18:17 | entry | noretfunctions.go:18:16:18:17 | skip | +| noretfunctions.go:13:10:13:10 | After 0 | noretfunctions.go:13:5:13:10 | ...!=... | +| noretfunctions.go:14:3:14:9 | After selection of Exit | noretfunctions.go:14:11:14:11 | Before x | +| noretfunctions.go:14:3:14:12 | call to Exit | noretfunctions.go:12:1:16:1 | Exceptional Exit | +| noretfunctions.go:14:11:14:11 | After x | noretfunctions.go:14:3:14:12 | call to Exit | +| noretfunctions.go:18:1:18:17 | Entry | noretfunctions.go:18:16:18:17 | block statement | | noretfunctions.go:18:1:18:17 | function declaration | noretfunctions.go:20:6:20:22 | skip | | noretfunctions.go:18:6:18:12 | skip | noretfunctions.go:18:1:18:17 | function declaration | -| noretfunctions.go:18:16:18:17 | skip | noretfunctions.go:18:1:18:17 | exit | +| noretfunctions.go:18:16:18:17 | block statement | noretfunctions.go:18:1:18:17 | Normal Exit | | noretfunctions.go:20:1:22:1 | entry | noretfunctions.go:21:2:21:10 | selection of Fatal | | noretfunctions.go:20:1:22:1 | function declaration | noretfunctions.go:24:6:24:23 | skip | | noretfunctions.go:20:6:20:22 | skip | noretfunctions.go:20:1:22:1 | function declaration | -| noretfunctions.go:21:2:21:10 | selection of Fatal | noretfunctions.go:21:12:21:18 | "Oh no" | -| noretfunctions.go:21:2:21:19 | call to Fatal | noretfunctions.go:20:1:22:1 | exit | -| noretfunctions.go:21:12:21:18 | "Oh no" | noretfunctions.go:21:2:21:19 | call to Fatal | +| noretfunctions.go:21:2:21:10 | After selection of Fatal | noretfunctions.go:21:12:21:18 | Before "Oh no" | +| noretfunctions.go:21:2:21:19 | call to Fatal | noretfunctions.go:20:1:22:1 | Exceptional Exit | +| noretfunctions.go:21:12:21:18 | After "Oh no" | noretfunctions.go:21:2:21:19 | call to Fatal | | noretfunctions.go:24:1:26:1 | entry | noretfunctions.go:25:2:25:11 | selection of Fatalf | -| noretfunctions.go:24:1:26:1 | function declaration | noretfunctions.go:0:0:0:0 | exit | +| noretfunctions.go:24:1:26:1 | After function declaration | noretfunctions.go:0:0:0:0 | After noretfunctions.go | | noretfunctions.go:24:6:24:23 | skip | noretfunctions.go:24:1:26:1 | function declaration | -| noretfunctions.go:25:2:25:11 | selection of Fatalf | noretfunctions.go:25:13:25:30 | "It's as I feared" | -| noretfunctions.go:25:2:25:31 | call to Fatalf | noretfunctions.go:24:1:26:1 | exit | -| noretfunctions.go:25:13:25:30 | "It's as I feared" | noretfunctions.go:25:2:25:31 | call to Fatalf | +| noretfunctions.go:25:2:25:11 | After selection of Fatalf | noretfunctions.go:25:13:25:30 | Before "It's as I feared" | +| noretfunctions.go:25:2:25:31 | call to Fatalf | noretfunctions.go:24:1:26:1 | Exceptional Exit | +| noretfunctions.go:25:13:25:30 | After "It's as I feared" | noretfunctions.go:25:2:25:31 | call to Fatalf | | stmts2.go:0:0:0:0 | entry | stmts2.go:3:6:3:11 | skip | | stmts2.go:3:1:7:1 | entry | stmts2.go:4:2:4:2 | skip | | stmts2.go:3:1:7:1 | function declaration | stmts2.go:9:6:9:11 | skip | | stmts2.go:3:6:3:11 | skip | stmts2.go:3:1:7:1 | function declaration | | stmts2.go:4:2:4:2 | skip | stmts2.go:4:6:4:10 | test7 | -| stmts2.go:4:6:4:10 | test7 | stmts2.go:4:12:4:12 | 0 | -| stmts2.go:4:6:4:13 | call to test7 | stmts2.go:3:1:7:1 | exit | +| stmts2.go:4:6:4:10 | After test7 | stmts2.go:4:12:4:12 | Before 0 | +| stmts2.go:4:6:4:13 | call to test7 | stmts2.go:3:1:7:1 | Exceptional Exit | | stmts2.go:4:6:4:13 | call to test7 | stmts2.go:5:6:5:6 | skip | -| stmts2.go:4:12:4:12 | 0 | stmts2.go:4:6:4:13 | call to test7 | +| stmts2.go:4:12:4:12 | After 0 | stmts2.go:4:6:4:13 | call to test7 | | stmts2.go:5:6:5:6 | skip | stmts2.go:5:10:5:14 | test7 | -| stmts2.go:5:10:5:14 | test7 | stmts2.go:5:16:5:16 | 1 | -| stmts2.go:5:10:5:17 | call to test7 | stmts2.go:3:1:7:1 | exit | +| stmts2.go:5:10:5:14 | After test7 | stmts2.go:5:16:5:16 | Before 1 | +| stmts2.go:5:10:5:17 | call to test7 | stmts2.go:3:1:7:1 | Exceptional Exit | | stmts2.go:5:10:5:17 | call to test7 | stmts2.go:6:9:6:9 | 2 | -| stmts2.go:5:16:5:16 | 1 | stmts2.go:5:10:5:17 | call to test7 | -| stmts2.go:6:2:6:9 | return statement | stmts2.go:3:1:7:1 | exit | -| stmts2.go:6:9:6:9 | 2 | stmts2.go:6:2:6:9 | return statement | +| stmts2.go:5:16:5:16 | After 1 | stmts2.go:5:10:5:17 | call to test7 | +| stmts2.go:6:2:6:9 | return statement | stmts2.go:3:1:7:1 | Normal Exit | +| stmts2.go:6:9:6:9 | After 2 | stmts2.go:6:2:6:9 | return statement | | stmts2.go:9:1:13:1 | entry | stmts2.go:10:2:10:2 | skip | | stmts2.go:9:1:13:1 | function declaration | stmts2.go:15:6:15:11 | skip | | stmts2.go:9:6:9:11 | skip | stmts2.go:9:1:13:1 | function declaration | | stmts2.go:10:2:10:2 | skip | stmts2.go:10:5:10:5 | skip | -| stmts2.go:10:2:10:14 | ... := ...[0] | stmts2.go:10:2:10:14 | ... := ...[1] | +| stmts2.go:10:2:10:14 | assign:1 ... := ... | stmts2.go:10:2:10:14 | After ... := ... | | stmts2.go:10:2:10:14 | ... := ...[1] | stmts2.go:10:5:10:5 | assignment to x | | stmts2.go:10:5:10:5 | assignment to x | stmts2.go:11:6:11:6 | skip | | stmts2.go:10:5:10:5 | skip | stmts2.go:10:10:10:12 | gen | -| stmts2.go:10:10:10:12 | gen | stmts2.go:10:10:10:14 | call to gen | -| stmts2.go:10:10:10:14 | call to gen | stmts2.go:9:1:13:1 | exit | -| stmts2.go:10:10:10:14 | call to gen | stmts2.go:10:2:10:14 | ... := ...[0] | +| stmts2.go:10:10:10:12 | After gen | stmts2.go:10:10:10:14 | call to gen | +| stmts2.go:10:10:10:14 | call to gen | stmts2.go:9:1:13:1 | Exceptional Exit | +| stmts2.go:10:10:10:14 | After call to gen | stmts2.go:10:2:10:14 | extract:0 ... := ... | | stmts2.go:11:6:11:6 | skip | stmts2.go:11:9:11:9 | skip | -| stmts2.go:11:6:11:17 | value declaration specifier[0] | stmts2.go:11:6:11:17 | value declaration specifier[1] | +| stmts2.go:11:6:11:17 | assign:1 value declaration specifier | stmts2.go:11:6:11:17 | After value declaration specifier | | stmts2.go:11:6:11:17 | value declaration specifier[1] | stmts2.go:11:9:11:9 | assignment to y | | stmts2.go:11:9:11:9 | assignment to y | stmts2.go:12:9:12:9 | x | | stmts2.go:11:9:11:9 | skip | stmts2.go:11:13:11:15 | gen | -| stmts2.go:11:13:11:15 | gen | stmts2.go:11:13:11:17 | call to gen | -| stmts2.go:11:13:11:17 | call to gen | stmts2.go:9:1:13:1 | exit | -| stmts2.go:11:13:11:17 | call to gen | stmts2.go:11:6:11:17 | value declaration specifier[0] | -| stmts2.go:12:2:12:13 | return statement | stmts2.go:9:1:13:1 | exit | -| stmts2.go:12:9:12:9 | x | stmts2.go:12:13:12:13 | y | -| stmts2.go:12:9:12:13 | ...+... | stmts2.go:12:2:12:13 | return statement | -| stmts2.go:12:13:12:13 | y | stmts2.go:12:9:12:13 | ...+... | +| stmts2.go:11:13:11:15 | After gen | stmts2.go:11:13:11:17 | call to gen | +| stmts2.go:11:13:11:17 | call to gen | stmts2.go:9:1:13:1 | Exceptional Exit | +| stmts2.go:11:13:11:17 | After call to gen | stmts2.go:11:6:11:17 | extract:0 value declaration specifier | +| stmts2.go:12:2:12:13 | return statement | stmts2.go:9:1:13:1 | Normal Exit | +| stmts2.go:12:9:12:9 | After x | stmts2.go:12:13:12:13 | Before y | +| stmts2.go:12:9:12:13 | After ...+... | stmts2.go:12:2:12:13 | return statement | +| stmts2.go:12:13:12:13 | After y | stmts2.go:12:9:12:13 | ...+... | | stmts2.go:15:1:28:1 | entry | stmts2.go:15:13:15:14 | argument corresponding to ch | | stmts2.go:15:1:28:1 | function declaration | stmts2.go:30:6:30:12 | skip | | stmts2.go:15:6:15:11 | skip | stmts2.go:15:1:28:1 | function declaration | @@ -946,64 +946,64 @@ | stmts2.go:16:2:26:2 | select statement | stmts2.go:25:14:25:17 | <-... | | stmts2.go:17:2:17:15 | skip | stmts2.go:27:9:27:9 | 1 | | stmts2.go:17:7:17:7 | skip | stmts2.go:17:2:17:15 | skip | -| stmts2.go:17:11:17:14 | <-... | stmts2.go:17:7:17:7 | skip | -| stmts2.go:17:13:17:14 | ch | stmts2.go:18:17:18:18 | ch | +| stmts2.go:17:11:17:14 | <-... | stmts2.go:17:7:17:7 | Before _ | +| stmts2.go:17:13:17:14 | After ch | stmts2.go:18:17:18:18 | Before ch | | stmts2.go:18:7:18:7 | assignment to x | stmts2.go:18:7:18:18 | ... := ...[1] | -| stmts2.go:18:7:18:7 | skip | stmts2.go:18:10:18:10 | skip | +| stmts2.go:18:7:18:7 | After x | stmts2.go:18:10:18:10 | Before _ | | stmts2.go:18:7:18:18 | ... := ...[0] | stmts2.go:18:7:18:7 | assignment to x | | stmts2.go:18:7:18:18 | ... := ...[1] | stmts2.go:19:10:19:10 | x | -| stmts2.go:18:10:18:10 | skip | stmts2.go:18:7:18:18 | ... := ...[0] | -| stmts2.go:18:15:18:18 | <-... | stmts2.go:18:7:18:7 | skip | -| stmts2.go:18:17:18:18 | ch | stmts2.go:20:17:20:18 | ch | -| stmts2.go:19:3:19:10 | return statement | stmts2.go:15:1:28:1 | exit | -| stmts2.go:19:10:19:10 | x | stmts2.go:19:3:19:10 | return statement | -| stmts2.go:20:7:20:7 | skip | stmts2.go:20:10:20:10 | skip | -| stmts2.go:20:7:20:18 | ... := ...[0] | stmts2.go:20:7:20:18 | ... := ...[1] | +| stmts2.go:18:10:18:10 | After _ | stmts2.go:18:7:18:18 | extract:0 ... := ... | +| stmts2.go:18:15:18:18 | <-... | stmts2.go:18:7:18:7 | Before x | +| stmts2.go:18:17:18:18 | After ch | stmts2.go:20:17:20:18 | Before ch | +| stmts2.go:19:3:19:10 | return statement | stmts2.go:15:1:28:1 | Normal Exit | +| stmts2.go:19:10:19:10 | After x | stmts2.go:19:3:19:10 | return statement | +| stmts2.go:20:7:20:7 | After _ | stmts2.go:20:10:20:10 | Before y | +| stmts2.go:20:7:20:18 | extract:0 ... := ... | stmts2.go:20:7:20:18 | extract:1 ... := ... | | stmts2.go:20:7:20:18 | ... := ...[1] | stmts2.go:20:10:20:10 | assignment to y | | stmts2.go:20:10:20:10 | assignment to y | stmts2.go:21:6:21:6 | y | -| stmts2.go:20:10:20:10 | skip | stmts2.go:20:7:20:18 | ... := ...[0] | -| stmts2.go:20:15:20:18 | <-... | stmts2.go:20:7:20:7 | skip | -| stmts2.go:20:17:20:18 | ch | stmts2.go:25:16:25:17 | ch | -| stmts2.go:21:6:21:6 | y | stmts2.go:21:6:21:6 | y is false | -| stmts2.go:21:6:21:6 | y | stmts2.go:21:6:21:6 | y is true | +| stmts2.go:20:10:20:10 | After y | stmts2.go:20:7:20:18 | extract:0 ... := ... | +| stmts2.go:20:15:20:18 | <-... | stmts2.go:20:7:20:7 | Before _ | +| stmts2.go:20:17:20:18 | After ch | stmts2.go:25:16:25:17 | Before ch | +| stmts2.go:21:6:21:6 | Before y | stmts2.go:21:6:21:6 | y | +| stmts2.go:21:6:21:6 | y | stmts2.go:21:6:21:6 | After y [false] | | stmts2.go:21:6:21:6 | y is false | stmts2.go:24:10:24:10 | 0 | | stmts2.go:21:6:21:6 | y is true | stmts2.go:22:4:22:8 | skip | | stmts2.go:22:4:22:8 | skip | stmts2.go:27:9:27:9 | 1 | -| stmts2.go:24:3:24:10 | return statement | stmts2.go:15:1:28:1 | exit | -| stmts2.go:24:10:24:10 | 0 | stmts2.go:24:3:24:10 | return statement | +| stmts2.go:24:3:24:10 | return statement | stmts2.go:15:1:28:1 | Normal Exit | +| stmts2.go:24:10:24:10 | After 0 | stmts2.go:24:3:24:10 | return statement | | stmts2.go:25:2:25:18 | skip | stmts2.go:27:9:27:9 | 1 | -| stmts2.go:25:7:25:7 | skip | stmts2.go:25:10:25:10 | skip | -| stmts2.go:25:7:25:17 | ... = ...[0] | stmts2.go:25:7:25:17 | ... = ...[1] | +| stmts2.go:25:7:25:7 | After _ | stmts2.go:25:10:25:10 | Before _ | +| stmts2.go:25:7:25:17 | extract:0 ... = ... | stmts2.go:25:7:25:17 | extract:1 ... = ... | | stmts2.go:25:7:25:17 | ... = ...[1] | stmts2.go:25:2:25:18 | skip | -| stmts2.go:25:10:25:10 | skip | stmts2.go:25:7:25:17 | ... = ...[0] | -| stmts2.go:25:14:25:17 | <-... | stmts2.go:25:7:25:7 | skip | -| stmts2.go:25:16:25:17 | ch | stmts2.go:16:2:26:2 | select statement | -| stmts2.go:27:2:27:9 | return statement | stmts2.go:15:1:28:1 | exit | -| stmts2.go:27:9:27:9 | 1 | stmts2.go:27:2:27:9 | return statement | +| stmts2.go:25:10:25:10 | After _ | stmts2.go:25:7:25:17 | extract:0 ... = ... | +| stmts2.go:25:14:25:17 | <-... | stmts2.go:25:7:25:7 | Before _ | +| stmts2.go:25:16:25:17 | After ch | stmts2.go:16:2:26:2 | select statement | +| stmts2.go:27:2:27:9 | return statement | stmts2.go:15:1:28:1 | Normal Exit | +| stmts2.go:27:9:27:9 | After 1 | stmts2.go:27:2:27:9 | return statement | | stmts2.go:30:1:34:1 | entry | stmts2.go:31:2:31:2 | skip | -| stmts2.go:30:1:34:1 | function declaration | stmts2.go:0:0:0:0 | exit | +| stmts2.go:30:1:34:1 | After function declaration | stmts2.go:0:0:0:0 | After stmts2.go | | stmts2.go:30:6:30:12 | skip | stmts2.go:30:1:34:1 | function declaration | | stmts2.go:31:2:31:2 | assignment to x | stmts2.go:31:2:31:14 | ... := ...[1] | | stmts2.go:31:2:31:2 | skip | stmts2.go:31:5:31:5 | skip | | stmts2.go:31:2:31:14 | ... := ...[0] | stmts2.go:31:2:31:2 | assignment to x | | stmts2.go:31:2:31:14 | ... := ...[1] | stmts2.go:32:6:32:6 | skip | | stmts2.go:31:5:31:5 | skip | stmts2.go:31:10:31:12 | gen | -| stmts2.go:31:10:31:12 | gen | stmts2.go:31:10:31:14 | call to gen | -| stmts2.go:31:10:31:14 | call to gen | stmts2.go:30:1:34:1 | exit | -| stmts2.go:31:10:31:14 | call to gen | stmts2.go:31:2:31:14 | ... := ...[0] | +| stmts2.go:31:10:31:12 | After gen | stmts2.go:31:10:31:14 | call to gen | +| stmts2.go:31:10:31:14 | call to gen | stmts2.go:30:1:34:1 | Exceptional Exit | +| stmts2.go:31:10:31:14 | After call to gen | stmts2.go:31:2:31:14 | extract:0 ... := ... | | stmts2.go:32:6:32:6 | assignment to y | stmts2.go:32:6:32:17 | value declaration specifier[1] | | stmts2.go:32:6:32:6 | skip | stmts2.go:32:9:32:9 | skip | | stmts2.go:32:6:32:17 | value declaration specifier[0] | stmts2.go:32:6:32:6 | assignment to y | | stmts2.go:32:6:32:17 | value declaration specifier[1] | stmts2.go:33:9:33:9 | x | | stmts2.go:32:9:32:9 | skip | stmts2.go:32:13:32:15 | gen | -| stmts2.go:32:13:32:15 | gen | stmts2.go:32:13:32:17 | call to gen | -| stmts2.go:32:13:32:17 | call to gen | stmts2.go:30:1:34:1 | exit | -| stmts2.go:32:13:32:17 | call to gen | stmts2.go:32:6:32:17 | value declaration specifier[0] | -| stmts2.go:33:2:33:13 | return statement | stmts2.go:30:1:34:1 | exit | -| stmts2.go:33:9:33:9 | x | stmts2.go:33:13:33:13 | y | -| stmts2.go:33:9:33:13 | ...+... | stmts2.go:33:2:33:13 | return statement | -| stmts2.go:33:13:33:13 | y | stmts2.go:33:9:33:13 | ...+... | -| stmts3.go:0:0:0:0 | entry | stmts3.go:3:1:3:13 | skip | +| stmts2.go:32:13:32:15 | After gen | stmts2.go:32:13:32:17 | call to gen | +| stmts2.go:32:13:32:17 | call to gen | stmts2.go:30:1:34:1 | Exceptional Exit | +| stmts2.go:32:13:32:17 | After call to gen | stmts2.go:32:6:32:17 | extract:0 value declaration specifier | +| stmts2.go:33:2:33:13 | return statement | stmts2.go:30:1:34:1 | Normal Exit | +| stmts2.go:33:9:33:9 | After x | stmts2.go:33:13:33:13 | Before y | +| stmts2.go:33:9:33:13 | After ...+... | stmts2.go:33:2:33:13 | return statement | +| stmts2.go:33:13:33:13 | After y | stmts2.go:33:9:33:13 | ...+... | +| stmts3.go:0:0:0:0 | stmts3.go | stmts3.go:3:1:3:13 | import declaration | | stmts3.go:3:1:3:13 | skip | stmts3.go:5:6:5:11 | skip | | stmts3.go:5:1:12:1 | entry | stmts3.go:7:3:7:5 | skip | | stmts3.go:5:1:12:1 | function declaration | stmts3.go:14:6:14:11 | skip | @@ -1011,14 +1011,14 @@ | stmts3.go:7:3:7:5 | assignment to red | stmts3.go:8:3:8:7 | skip | | stmts3.go:7:3:7:5 | skip | stmts3.go:7:9:7:12 | iota | | stmts3.go:7:9:7:12 | iota | stmts3.go:7:3:7:5 | assignment to red | -| stmts3.go:8:3:8:7 | assignment to green | stmts3.go:9:3:9:6 | skip | -| stmts3.go:8:3:8:7 | skip | stmts3.go:8:3:8:7 | zero value for green | -| stmts3.go:8:3:8:7 | zero value for green | stmts3.go:8:3:8:7 | assignment to green | +| stmts3.go:8:3:8:7 | After value declaration specifier | stmts3.go:9:3:9:6 | value declaration specifier | +| stmts3.go:8:3:8:7 | assign:0 value declaration specifier | stmts3.go:8:3:8:7 | After value declaration specifier | +| stmts3.go:8:3:8:7 | value declaration specifier | stmts3.go:8:3:8:7 | zero-init:0 value declaration specifier | | stmts3.go:9:3:9:6 | assignment to blue | stmts3.go:11:9:11:26 | ...-... | -| stmts3.go:9:3:9:6 | skip | stmts3.go:9:3:9:6 | zero value for blue | -| stmts3.go:9:3:9:6 | zero value for blue | stmts3.go:9:3:9:6 | assignment to blue | -| stmts3.go:11:2:11:26 | return statement | stmts3.go:5:1:12:1 | exit | -| stmts3.go:11:9:11:26 | ...-... | stmts3.go:11:2:11:26 | return statement | +| stmts3.go:9:3:9:6 | assign:0 value declaration specifier | stmts3.go:9:3:9:6 | After value declaration specifier | +| stmts3.go:9:3:9:6 | value declaration specifier | stmts3.go:9:3:9:6 | zero-init:0 value declaration specifier | +| stmts3.go:11:2:11:26 | return statement | stmts3.go:5:1:12:1 | Normal Exit | +| stmts3.go:11:9:11:26 | After ...-... | stmts3.go:11:2:11:26 | return statement | | stmts3.go:14:1:16:1 | entry | stmts3.go:14:13:14:13 | argument corresponding to x | | stmts3.go:14:1:16:1 | function declaration | stmts3.go:18:6:18:11 | skip | | stmts3.go:14:6:14:11 | skip | stmts3.go:14:1:16:1 | function declaration | @@ -1030,19 +1030,19 @@ | stmts3.go:15:3:15:3 | x | stmts3.go:15:2:15:3 | skip | | stmts3.go:15:7:15:8 | 42 | stmts3.go:15:2:15:3 | assignment to star expression | | stmts3.go:18:1:20:1 | entry | stmts3.go:19:2:19:11 | skip | -| stmts3.go:18:1:20:1 | function declaration | stmts3.go:0:0:0:0 | exit | +| stmts3.go:18:1:20:1 | After function declaration | stmts3.go:0:0:0:0 | After stmts3.go | | stmts3.go:18:6:18:11 | skip | stmts3.go:18:1:20:1 | function declaration | | stmts3.go:19:2:19:11 | assignment to Usage | stmts3.go:18:1:20:1 | exit | -| stmts3.go:19:2:19:11 | skip | stmts3.go:19:15:19:23 | function literal | -| stmts3.go:19:15:19:23 | entry | stmts3.go:19:22:19:23 | skip | +| stmts3.go:19:2:19:11 | After selection of Usage | stmts3.go:19:15:19:23 | Before function literal | +| stmts3.go:19:15:19:23 | Entry | stmts3.go:19:22:19:23 | block statement | | stmts3.go:19:15:19:23 | function literal | stmts3.go:19:2:19:11 | assignment to Usage | -| stmts3.go:19:22:19:23 | skip | stmts3.go:19:15:19:23 | exit | +| stmts3.go:19:22:19:23 | block statement | stmts3.go:19:15:19:23 | Normal Exit | | stmts4.go:0:0:0:0 | entry | stmts4.go:3:5:3:5 | skip | | stmts4.go:3:5:3:5 | skip | stmts4.go:3:5:3:5 | zero value for _ | | stmts4.go:3:5:3:5 | zero value for _ | stmts4.go:5:6:5:11 | skip | -| stmts4.go:5:1:5:26 | function declaration | stmts4.go:0:0:0:0 | exit | +| stmts4.go:5:1:5:26 | After function declaration | stmts4.go:0:0:0:0 | After stmts4.go | | stmts4.go:5:6:5:11 | skip | stmts4.go:5:1:5:26 | function declaration | -| stmts5.go:0:0:0:0 | entry | stmts5.go:3:1:5:1 | skip | +| stmts5.go:0:0:0:0 | stmts5.go | stmts5.go:3:1:5:1 | type declaration | | stmts5.go:3:1:5:1 | skip | stmts5.go:7:17:7:20 | skip | | stmts5.go:7:1:9:1 | entry | stmts5.go:7:7:7:8 | argument corresponding to me | | stmts5.go:7:1:9:1 | function declaration | stmts5.go:11:14:11:16 | skip | @@ -1051,47 +1051,47 @@ | stmts5.go:7:17:7:20 | skip | stmts5.go:7:1:9:1 | function declaration | | stmts5.go:7:22:7:26 | argument corresponding to other | stmts5.go:7:22:7:26 | initialization of other | | stmts5.go:7:22:7:26 | initialization of other | stmts5.go:8:2:8:3 | me | -| stmts5.go:8:2:8:3 | me | stmts5.go:8:2:8:7 | selection of val | +| stmts5.go:8:2:8:3 | After me | stmts5.go:8:2:8:7 | selection of val | | stmts5.go:8:2:8:7 | assignment to field val | stmts5.go:7:1:9:1 | exit | -| stmts5.go:8:2:8:7 | selection of val | stmts5.go:8:12:8:16 | other | -| stmts5.go:8:2:8:16 | ... += ... | stmts5.go:8:2:8:7 | assignment to field val | -| stmts5.go:8:12:8:16 | other | stmts5.go:8:2:8:16 | ... += ... | -| stmts5.go:11:1:12:1 | entry | stmts5.go:11:20:12:1 | skip | -| stmts5.go:11:1:12:1 | function declaration | stmts5.go:0:0:0:0 | exit | +| stmts5.go:8:2:8:7 | After selection of val | stmts5.go:8:12:8:16 | Before other | +| stmts5.go:8:2:8:16 | ... += ... | stmts5.go:8:2:8:7 | Before selection of val | +| stmts5.go:8:12:8:16 | After other | stmts5.go:8:2:8:16 | compound-rhs ... += ... | +| stmts5.go:11:1:12:1 | Entry | stmts5.go:11:20:12:1 | block statement | +| stmts5.go:11:1:12:1 | After function declaration | stmts5.go:0:0:0:0 | After stmts5.go | | stmts5.go:11:14:11:16 | skip | stmts5.go:11:1:12:1 | function declaration | -| stmts5.go:11:20:12:1 | skip | stmts5.go:11:1:12:1 | exit | +| stmts5.go:11:20:12:1 | block statement | stmts5.go:11:1:12:1 | Normal Exit | | stmts6.go:0:0:0:0 | entry | stmts6.go:3:6:3:11 | skip | | stmts6.go:3:1:8:1 | entry | stmts6.go:4:5:4:8 | true | -| stmts6.go:3:1:8:1 | function declaration | stmts6.go:0:0:0:0 | exit | +| stmts6.go:3:1:8:1 | After function declaration | stmts6.go:0:0:0:0 | After stmts6.go | | stmts6.go:3:6:3:11 | skip | stmts6.go:3:1:8:1 | function declaration | -| stmts6.go:4:5:4:8 | true | stmts6.go:4:5:4:8 | true is true | +| stmts6.go:4:5:4:8 | Before true | stmts6.go:4:5:4:8 | true | | stmts6.go:4:5:4:8 | true is false | stmts6.go:7:9:7:10 | 23 | | stmts6.go:4:5:4:8 | true is true | stmts6.go:5:10:5:11 | 42 | -| stmts6.go:5:3:5:11 | return statement | stmts6.go:3:1:8:1 | exit | -| stmts6.go:5:10:5:11 | 42 | stmts6.go:5:3:5:11 | return statement | +| stmts6.go:5:3:5:11 | return statement | stmts6.go:3:1:8:1 | Normal Exit | +| stmts6.go:5:10:5:11 | After 42 | stmts6.go:5:3:5:11 | return statement | | stmts6.go:7:9:7:10 | 23 | stmts6.go:7:2:7:10 | return statement | -| stmts7.go:0:0:0:0 | entry | stmts7.go:3:1:3:12 | skip | +| stmts7.go:0:0:0:0 | stmts7.go | stmts7.go:3:1:3:12 | import declaration | | stmts7.go:3:1:3:12 | skip | stmts7.go:5:6:5:17 | skip | | stmts7.go:5:1:8:1 | entry | stmts7.go:6:2:6:5 | skip | | stmts7.go:5:1:8:1 | function declaration | stmts7.go:10:6:10:15 | skip | | stmts7.go:5:6:5:17 | skip | stmts7.go:5:1:8:1 | function declaration | | stmts7.go:6:2:6:5 | assignment to blah | stmts7.go:7:2:7:12 | selection of Println | | stmts7.go:6:2:6:5 | skip | stmts7.go:6:10:6:16 | recover | -| stmts7.go:6:10:6:16 | recover | stmts7.go:6:10:6:18 | call to recover | +| stmts7.go:6:10:6:16 | After recover | stmts7.go:6:10:6:18 | call to recover | | stmts7.go:6:10:6:18 | call to recover | stmts7.go:6:2:6:5 | assignment to blah | -| stmts7.go:7:2:7:12 | selection of Println | stmts7.go:7:14:7:26 | "recovered: " | -| stmts7.go:7:2:7:33 | call to Println | stmts7.go:5:1:8:1 | exit | -| stmts7.go:7:14:7:26 | "recovered: " | stmts7.go:7:29:7:32 | blah | -| stmts7.go:7:29:7:32 | blah | stmts7.go:7:2:7:33 | call to Println | +| stmts7.go:7:2:7:12 | After selection of Println | stmts7.go:7:14:7:26 | Before "recovered: " | +| stmts7.go:7:2:7:33 | call to Println | stmts7.go:5:1:8:1 | Exceptional Exit | +| stmts7.go:7:14:7:26 | After "recovered: " | stmts7.go:7:29:7:32 | Before blah | +| stmts7.go:7:29:7:32 | After blah | stmts7.go:7:2:7:33 | call to Println | | stmts7.go:10:1:13:1 | entry | stmts7.go:11:8:11:19 | recoverPanic | -| stmts7.go:10:1:13:1 | function declaration | stmts7.go:15:1:17:1 | skip | +| stmts7.go:10:1:13:1 | After function declaration | stmts7.go:15:1:17:1 | type declaration | | stmts7.go:10:6:10:15 | skip | stmts7.go:10:1:13:1 | function declaration | | stmts7.go:11:2:11:21 | defer statement | stmts7.go:12:2:12:6 | panic | | stmts7.go:11:8:11:19 | recoverPanic | stmts7.go:11:2:11:21 | defer statement | -| stmts7.go:11:8:11:21 | call to recoverPanic | stmts7.go:10:1:13:1 | exit | -| stmts7.go:12:2:12:6 | panic | stmts7.go:12:8:12:9 | "" | +| stmts7.go:11:8:11:21 | call to recoverPanic | stmts7.go:10:1:13:1 | Exceptional Exit | +| stmts7.go:12:2:12:6 | After panic | stmts7.go:12:8:12:9 | Before "" | | stmts7.go:12:2:12:10 | call to panic | stmts7.go:11:8:11:21 | call to recoverPanic | -| stmts7.go:12:8:12:9 | "" | stmts7.go:12:2:12:10 | call to panic | +| stmts7.go:12:8:12:9 | After "" | stmts7.go:12:2:12:10 | call to panic | | stmts7.go:15:1:17:1 | skip | stmts7.go:19:26:19:28 | skip | | stmts7.go:19:1:21:1 | entry | stmts7.go:19:7:19:13 | argument corresponding to methods | | stmts7.go:19:1:21:1 | function declaration | stmts7.go:23:6:23:14 | skip | @@ -1099,32 +1099,32 @@ | stmts7.go:19:7:19:13 | initialization of methods | stmts7.go:20:2:20:8 | methods | | stmts7.go:19:26:19:28 | skip | stmts7.go:19:1:21:1 | function declaration | | stmts7.go:20:2:20:8 | implicit dereference | stmts7.go:19:1:21:1 | exit | -| stmts7.go:20:2:20:8 | implicit dereference | stmts7.go:20:2:20:11 | selection of fn | -| stmts7.go:20:2:20:8 | methods | stmts7.go:20:2:20:8 | implicit dereference | -| stmts7.go:20:2:20:11 | selection of fn | stmts7.go:20:2:20:13 | call to fn | -| stmts7.go:20:2:20:13 | call to fn | stmts7.go:19:1:21:1 | exit | +| stmts7.go:20:2:20:8 | implicit-deref methods | stmts7.go:20:2:20:11 | selection of fn | +| stmts7.go:20:2:20:8 | methods | stmts7.go:20:2:20:8 | After methods | +| stmts7.go:20:2:20:11 | After selection of fn | stmts7.go:20:2:20:13 | call to fn | +| stmts7.go:20:2:20:13 | call to fn | stmts7.go:19:1:21:1 | Exceptional Exit | | stmts7.go:23:1:28:1 | entry | stmts7.go:23:16:23:23 | argument corresponding to callback | -| stmts7.go:23:1:28:1 | function declaration | stmts7.go:0:0:0:0 | exit | +| stmts7.go:23:1:28:1 | After function declaration | stmts7.go:0:0:0:0 | After stmts7.go | | stmts7.go:23:6:23:14 | skip | stmts7.go:23:1:28:1 | function declaration | | stmts7.go:23:16:23:23 | argument corresponding to callback | stmts7.go:23:16:23:23 | initialization of callback | | stmts7.go:23:16:23:23 | initialization of callback | stmts7.go:24:8:24:15 | callback | | stmts7.go:24:2:24:20 | defer statement | stmts7.go:25:10:25:17 | callback | -| stmts7.go:24:8:24:15 | callback | stmts7.go:24:8:24:18 | selection of fn | +| stmts7.go:24:8:24:15 | After callback | stmts7.go:24:8:24:18 | selection of fn | | stmts7.go:24:8:24:18 | selection of fn | stmts7.go:24:2:24:20 | defer statement | -| stmts7.go:24:8:24:20 | call to fn | stmts7.go:23:1:28:1 | exit | +| stmts7.go:24:8:24:20 | call to fn | stmts7.go:23:1:28:1 | Exceptional Exit | | stmts7.go:25:2:25:23 | defer statement | stmts7.go:26:2:26:12 | selection of Println | | stmts7.go:25:8:25:18 | implicit dereference | stmts7.go:24:8:24:20 | call to fn | -| stmts7.go:25:8:25:18 | implicit dereference | stmts7.go:25:8:25:21 | selection of fn | +| stmts7.go:25:8:25:18 | implicit-deref (...) | stmts7.go:25:8:25:21 | selection of fn | | stmts7.go:25:8:25:21 | selection of fn | stmts7.go:25:2:25:23 | defer statement | | stmts7.go:25:8:25:23 | call to fn | stmts7.go:24:8:24:20 | call to fn | | stmts7.go:25:9:25:17 | &... | stmts7.go:25:8:25:18 | implicit dereference | | stmts7.go:25:10:25:17 | callback | stmts7.go:25:9:25:17 | &... | -| stmts7.go:26:2:26:12 | selection of Println | stmts7.go:26:14:26:30 | "print something" | +| stmts7.go:26:2:26:12 | After selection of Println | stmts7.go:26:14:26:30 | Before "print something" | | stmts7.go:26:2:26:31 | call to Println | stmts7.go:25:8:25:23 | call to fn | | stmts7.go:26:2:26:31 | call to Println | stmts7.go:27:9:27:13 | false | -| stmts7.go:26:14:26:30 | "print something" | stmts7.go:26:2:26:31 | call to Println | +| stmts7.go:26:14:26:30 | After "print something" | stmts7.go:26:2:26:31 | call to Println | | stmts7.go:27:2:27:13 | return statement | stmts7.go:25:8:25:23 | call to fn | -| stmts7.go:27:9:27:13 | false | stmts7.go:27:2:27:13 | return statement | +| stmts7.go:27:9:27:13 | After false | stmts7.go:27:2:27:13 | return statement | | stmts8.go:0:0:0:0 | entry | stmts8.go:3:6:3:11 | skip | | stmts8.go:3:1:7:1 | entry | stmts8.go:3:13:3:13 | argument corresponding to x | | stmts8.go:3:1:7:1 | function declaration | stmts8.go:9:6:9:12 | skip | @@ -1133,31 +1133,31 @@ | stmts8.go:3:13:3:13 | initialization of x | stmts8.go:4:2:4:2 | skip | | stmts8.go:4:2:4:2 | assignment to y | stmts8.go:5:2:5:2 | skip | | stmts8.go:4:2:4:2 | skip | stmts8.go:4:7:4:7 | x | -| stmts8.go:4:7:4:7 | x | stmts8.go:4:12:4:12 | 5 | +| stmts8.go:4:7:4:7 | After x | stmts8.go:4:12:4:12 | Before 5 | | stmts8.go:4:7:4:12 | ...>>... | stmts8.go:4:2:4:2 | assignment to y | -| stmts8.go:4:12:4:12 | 5 | stmts8.go:4:7:4:12 | ...>>... | +| stmts8.go:4:12:4:12 | After 5 | stmts8.go:4:7:4:12 | ...>>... | | stmts8.go:5:2:5:2 | assignment to z | stmts8.go:6:9:6:9 | z | | stmts8.go:5:2:5:2 | skip | stmts8.go:5:7:5:7 | x | -| stmts8.go:5:7:5:7 | x | stmts8.go:5:12:5:12 | 1 | +| stmts8.go:5:7:5:7 | After x | stmts8.go:5:12:5:12 | Before 1 | | stmts8.go:5:7:5:13 | ...%... | stmts8.go:5:2:5:2 | assignment to z | -| stmts8.go:5:12:5:12 | 1 | stmts8.go:5:7:5:13 | ...%... | -| stmts8.go:6:2:6:17 | return statement | stmts8.go:3:1:7:1 | exit | +| stmts8.go:5:12:5:12 | After 1 | stmts8.go:5:7:5:13 | ...%... | +| stmts8.go:6:2:6:17 | return statement | stmts8.go:3:1:7:1 | Normal Exit | | stmts8.go:6:9:6:9 | z | stmts8.go:6:12:6:12 | y | -| stmts8.go:6:12:6:12 | y | stmts8.go:6:16:6:17 | 13 | -| stmts8.go:6:12:6:17 | ...%... | stmts8.go:6:2:6:17 | return statement | -| stmts8.go:6:16:6:17 | 13 | stmts8.go:6:12:6:17 | ...%... | +| stmts8.go:6:12:6:12 | After y | stmts8.go:6:16:6:17 | Before 13 | +| stmts8.go:6:12:6:17 | After ...%... | stmts8.go:6:2:6:17 | return statement | +| stmts8.go:6:16:6:17 | After 13 | stmts8.go:6:12:6:17 | ...%... | | stmts8.go:9:1:14:1 | entry | stmts8.go:10:5:10:9 | linux | -| stmts8.go:9:1:14:1 | function declaration | stmts8.go:0:0:0:0 | exit | +| stmts8.go:9:1:14:1 | After function declaration | stmts8.go:0:0:0:0 | After stmts8.go | | stmts8.go:9:6:9:12 | skip | stmts8.go:9:1:14:1 | function declaration | -| stmts8.go:10:5:10:9 | linux | stmts8.go:10:5:10:9 | linux is false | -| stmts8.go:10:5:10:9 | linux | stmts8.go:10:5:10:9 | linux is true | +| stmts8.go:10:5:10:9 | Before linux | stmts8.go:10:5:10:9 | linux | +| stmts8.go:10:5:10:9 | linux | stmts8.go:10:5:10:9 | After linux [false] | | stmts8.go:10:5:10:9 | linux is false | stmts8.go:13:9:13:13 | false | | stmts8.go:10:5:10:9 | linux is true | stmts8.go:11:10:11:13 | true | -| stmts8.go:11:3:11:13 | return statement | stmts8.go:9:1:14:1 | exit | -| stmts8.go:11:10:11:13 | true | stmts8.go:11:3:11:13 | return statement | -| stmts8.go:13:2:13:13 | return statement | stmts8.go:9:1:14:1 | exit | -| stmts8.go:13:9:13:13 | false | stmts8.go:13:2:13:13 | return statement | -| stmts.go:0:0:0:0 | entry | stmts.go:3:1:3:12 | skip | +| stmts8.go:11:3:11:13 | return statement | stmts8.go:9:1:14:1 | Normal Exit | +| stmts8.go:11:10:11:13 | After true | stmts8.go:11:3:11:13 | return statement | +| stmts8.go:13:2:13:13 | return statement | stmts8.go:9:1:14:1 | Normal Exit | +| stmts8.go:13:9:13:13 | After false | stmts8.go:13:2:13:13 | return statement | +| stmts.go:0:0:0:0 | stmts.go | stmts.go:3:1:3:12 | import declaration | | stmts.go:3:1:3:12 | skip | stmts.go:10:6:10:10 | skip | | stmts.go:10:1:43:1 | entry | stmts.go:10:12:10:12 | argument corresponding to b | | stmts.go:10:1:43:1 | function declaration | stmts.go:46:6:46:10 | skip | @@ -1168,62 +1168,62 @@ | stmts.go:12:6:12:7 | !... | stmts.go:12:6:12:7 | !... is true | | stmts.go:12:6:12:7 | !... is false | stmts.go:15:3:16:3 | skip | | stmts.go:12:6:12:7 | !... is true | stmts.go:13:4:13:13 | skip | -| stmts.go:12:7:12:7 | b | stmts.go:12:6:12:7 | !... | +| stmts.go:12:7:12:7 | After b [false] | stmts.go:12:6:12:7 | After !... [true] | | stmts.go:13:4:13:13 | skip | stmts.go:23:6:23:9 | true | -| stmts.go:15:3:16:3 | skip | stmts.go:17:3:17:3 | skip | +| stmts.go:15:3:16:3 | block statement | stmts.go:17:3:17:3 | empty statement | | stmts.go:17:3:17:3 | skip | stmts.go:20:2:20:12 | selection of Println | -| stmts.go:20:2:20:12 | selection of Println | stmts.go:20:14:20:17 | "Hi" | -| stmts.go:20:2:20:18 | call to Println | stmts.go:10:1:43:1 | exit | +| stmts.go:20:2:20:12 | After selection of Println | stmts.go:20:14:20:17 | Before "Hi" | +| stmts.go:20:2:20:18 | call to Println | stmts.go:10:1:43:1 | Exceptional Exit | | stmts.go:20:2:20:18 | call to Println | stmts.go:23:6:23:9 | true | -| stmts.go:20:14:20:17 | "Hi" | stmts.go:20:2:20:18 | call to Println | -| stmts.go:23:6:23:9 | true | stmts.go:23:6:23:9 | true is true | +| stmts.go:20:14:20:17 | After "Hi" | stmts.go:20:2:20:18 | call to Println | +| stmts.go:23:6:23:9 | Before true | stmts.go:23:6:23:9 | true | | stmts.go:23:6:23:9 | true is false | stmts.go:39:2:39:2 | skip | | stmts.go:23:6:23:9 | true is true | stmts.go:24:7:24:7 | skip | | stmts.go:24:7:24:7 | assignment to i | stmts.go:24:15:24:15 | i | | stmts.go:24:7:24:7 | skip | stmts.go:24:12:24:12 | 0 | | stmts.go:24:12:24:12 | 0 | stmts.go:24:7:24:7 | assignment to i | -| stmts.go:24:15:24:15 | i | stmts.go:24:19:24:20 | 10 | -| stmts.go:24:15:24:20 | ...<... | stmts.go:24:15:24:20 | ...<... is false | -| stmts.go:24:15:24:20 | ...<... | stmts.go:24:15:24:20 | ...<... is true | +| stmts.go:24:15:24:15 | After i | stmts.go:24:19:24:20 | Before 10 | +| stmts.go:24:15:24:20 | ...<... | stmts.go:24:15:24:20 | After ...<... [false] | +| stmts.go:24:15:24:20 | ...<... | stmts.go:24:15:24:20 | After ...<... [true] | | stmts.go:24:15:24:20 | ...<... is false | stmts.go:23:6:23:9 | true | | stmts.go:24:15:24:20 | ...<... is true | stmts.go:25:7:25:7 | skip | -| stmts.go:24:19:24:20 | 10 | stmts.go:24:15:24:20 | ...<... | -| stmts.go:24:23:24:23 | i | stmts.go:24:23:24:25 | 1 | -| stmts.go:24:23:24:25 | 1 | stmts.go:24:23:24:25 | rhs of increment statement | +| stmts.go:24:19:24:20 | After 10 | stmts.go:24:15:24:20 | ...<... | +| stmts.go:24:23:24:23 | After i | stmts.go:24:23:24:25 | implicit-one increment statement | +| stmts.go:24:23:24:25 | implicit-one increment statement | stmts.go:24:23:24:25 | incdec-rhs increment statement | | stmts.go:24:23:24:25 | increment statement | stmts.go:24:15:24:15 | i | -| stmts.go:24:23:24:25 | rhs of increment statement | stmts.go:24:23:24:25 | increment statement | +| stmts.go:24:23:24:25 | incdec-rhs increment statement | stmts.go:24:23:24:25 | increment statement | | stmts.go:25:7:25:7 | assignment to j | stmts.go:25:19:25:19 | j | | stmts.go:25:7:25:7 | skip | stmts.go:25:12:25:12 | i | -| stmts.go:25:12:25:12 | i | stmts.go:25:16:25:16 | 1 | +| stmts.go:25:12:25:12 | After i | stmts.go:25:16:25:16 | Before 1 | | stmts.go:25:12:25:16 | ...-... | stmts.go:25:7:25:7 | assignment to j | -| stmts.go:25:16:25:16 | 1 | stmts.go:25:12:25:16 | ...-... | -| stmts.go:25:19:25:19 | j | stmts.go:25:23:25:23 | 5 | -| stmts.go:25:19:25:23 | ...>... | stmts.go:25:19:25:23 | ...>... is false | -| stmts.go:25:19:25:23 | ...>... | stmts.go:25:19:25:23 | ...>... is true | +| stmts.go:25:16:25:16 | After 1 | stmts.go:25:12:25:16 | ...-... | +| stmts.go:25:19:25:19 | After j | stmts.go:25:23:25:23 | Before 5 | +| stmts.go:25:19:25:23 | ...>... | stmts.go:25:19:25:23 | After ...>... [false] | +| stmts.go:25:19:25:23 | ...>... | stmts.go:25:19:25:23 | After ...>... [true] | | stmts.go:25:19:25:23 | ...>... is false | stmts.go:27:14:27:14 | i | | stmts.go:25:19:25:23 | ...>... is true | stmts.go:26:5:26:15 | skip | -| stmts.go:25:23:25:23 | 5 | stmts.go:25:19:25:23 | ...>... | +| stmts.go:25:23:25:23 | After 5 | stmts.go:25:19:25:23 | ...>... | | stmts.go:26:5:26:15 | skip | stmts.go:39:2:39:2 | skip | -| stmts.go:27:14:27:14 | i | stmts.go:27:18:27:18 | 3 | -| stmts.go:27:14:27:18 | ...<... | stmts.go:27:14:27:18 | ...<... is false | -| stmts.go:27:14:27:18 | ...<... | stmts.go:27:14:27:18 | ...<... is true | +| stmts.go:27:14:27:14 | After i | stmts.go:27:18:27:18 | Before 3 | +| stmts.go:27:14:27:18 | ...<... | stmts.go:27:14:27:18 | After ...<... [false] | +| stmts.go:27:14:27:18 | ...<... | stmts.go:27:14:27:18 | After ...<... [true] | | stmts.go:27:14:27:18 | ...<... is false | stmts.go:29:14:29:14 | i | | stmts.go:27:14:27:18 | ...<... is true | stmts.go:28:5:28:9 | skip | -| stmts.go:27:18:27:18 | 3 | stmts.go:27:14:27:18 | ...<... | +| stmts.go:27:18:27:18 | After 3 | stmts.go:27:14:27:18 | ...<... | | stmts.go:28:5:28:9 | skip | stmts.go:23:6:23:9 | true | -| stmts.go:29:14:29:14 | i | stmts.go:29:19:29:19 | 9 | -| stmts.go:29:14:29:19 | ...!=... | stmts.go:29:14:29:19 | ...!=... is false | -| stmts.go:29:14:29:19 | ...!=... | stmts.go:29:14:29:19 | ...!=... is true | +| stmts.go:29:14:29:14 | After i | stmts.go:29:19:29:19 | Before 9 | +| stmts.go:29:14:29:19 | ...!=... | stmts.go:29:14:29:19 | After ...!=... [false] | +| stmts.go:29:14:29:19 | ...!=... | stmts.go:29:14:29:19 | After ...!=... [true] | | stmts.go:29:14:29:19 | ...!=... is false | stmts.go:31:14:31:14 | i | | stmts.go:29:14:29:19 | ...!=... is true | stmts.go:30:5:30:18 | skip | -| stmts.go:29:19:29:19 | 9 | stmts.go:29:14:29:19 | ...!=... | +| stmts.go:29:19:29:19 | After 9 | stmts.go:29:14:29:19 | ...!=... | | stmts.go:30:5:30:18 | skip | stmts.go:23:6:23:9 | true | -| stmts.go:31:14:31:14 | i | stmts.go:31:19:31:19 | 4 | -| stmts.go:31:14:31:19 | ...>=... | stmts.go:31:14:31:19 | ...>=... is false | -| stmts.go:31:14:31:19 | ...>=... | stmts.go:31:14:31:19 | ...>=... is true | +| stmts.go:31:14:31:14 | After i | stmts.go:31:19:31:19 | Before 4 | +| stmts.go:31:14:31:19 | ...>=... | stmts.go:31:14:31:19 | After ...>=... [false] | +| stmts.go:31:14:31:19 | ...>=... | stmts.go:31:14:31:19 | After ...>=... [true] | | stmts.go:31:14:31:19 | ...>=... is false | stmts.go:34:5:34:12 | skip | | stmts.go:31:14:31:19 | ...>=... is true | stmts.go:32:5:32:14 | skip | -| stmts.go:31:19:31:19 | 4 | stmts.go:31:14:31:19 | ...>=... | +| stmts.go:31:19:31:19 | After 4 | stmts.go:31:14:31:19 | ...>=... | | stmts.go:32:5:32:14 | skip | stmts.go:23:6:23:9 | true | | stmts.go:34:5:34:12 | skip | stmts.go:24:23:24:23 | i | | stmts.go:39:2:39:2 | assignment to k | stmts.go:41:3:41:12 | skip | @@ -1252,67 +1252,67 @@ | stmts.go:50:2:59:2 | select statement | stmts.go:57:3:57:13 | selection of Println | | stmts.go:50:2:59:2 | select statement | stmts.go:58:7:58:15 | send statement | | stmts.go:51:7:51:11 | <-... | stmts.go:52:3:52:13 | selection of Println | -| stmts.go:51:9:51:11 | ch1 | stmts.go:53:19:53:21 | ch2 | -| stmts.go:52:3:52:13 | selection of Println | stmts.go:52:15:52:30 | "Heard from ch1" | -| stmts.go:52:3:52:31 | call to Println | stmts.go:46:1:62:1 | exit | +| stmts.go:51:9:51:11 | After ch1 | stmts.go:53:19:53:21 | Before ch2 | +| stmts.go:52:3:52:13 | After selection of Println | stmts.go:52:15:52:30 | Before "Heard from ch1" | +| stmts.go:52:3:52:31 | call to Println | stmts.go:46:1:62:1 | Exceptional Exit | | stmts.go:52:3:52:31 | call to Println | stmts.go:61:2:61:10 | select statement | -| stmts.go:52:15:52:30 | "Heard from ch1" | stmts.go:52:3:52:31 | call to Println | -| stmts.go:53:7:53:7 | a | stmts.go:53:9:53:9 | 0 | +| stmts.go:52:15:52:30 | After "Heard from ch1" | stmts.go:52:3:52:31 | call to Println | +| stmts.go:53:7:53:7 | After a | stmts.go:53:9:53:9 | Before 0 | | stmts.go:53:7:53:10 | assignment to element | stmts.go:53:7:53:21 | ... = ...[1] | -| stmts.go:53:7:53:10 | skip | stmts.go:46:1:62:1 | exit | -| stmts.go:53:7:53:10 | skip | stmts.go:53:13:53:13 | skip | +| stmts.go:53:7:53:10 | index expression | stmts.go:46:1:62:1 | Exceptional Exit | +| stmts.go:53:7:53:10 | After index expression | stmts.go:53:13:53:13 | Before w | | stmts.go:53:7:53:21 | ... = ...[0] | stmts.go:53:7:53:10 | assignment to element | | stmts.go:53:7:53:21 | ... = ...[1] | stmts.go:53:13:53:13 | assignment to w | -| stmts.go:53:9:53:9 | 0 | stmts.go:53:7:53:10 | skip | +| stmts.go:53:9:53:9 | After 0 | stmts.go:53:7:53:10 | index expression | | stmts.go:53:13:53:13 | assignment to w | stmts.go:54:3:54:13 | selection of Println | -| stmts.go:53:13:53:13 | skip | stmts.go:53:7:53:21 | ... = ...[0] | +| stmts.go:53:13:53:13 | After w | stmts.go:53:7:53:21 | extract:0 ... = ... | | stmts.go:53:17:53:21 | <-... | stmts.go:53:7:53:7 | a | -| stmts.go:53:19:53:21 | ch2 | stmts.go:58:7:58:9 | ch1 | -| stmts.go:54:3:54:13 | selection of Println | stmts.go:54:15:54:15 | a | -| stmts.go:54:3:54:16 | call to Println | stmts.go:46:1:62:1 | exit | +| stmts.go:53:19:53:21 | After ch2 | stmts.go:58:7:58:9 | Before ch1 | +| stmts.go:54:3:54:13 | After selection of Println | stmts.go:54:15:54:15 | Before a | +| stmts.go:54:3:54:16 | call to Println | stmts.go:46:1:62:1 | Exceptional Exit | | stmts.go:54:3:54:16 | call to Println | stmts.go:55:3:55:13 | selection of Println | -| stmts.go:54:15:54:15 | a | stmts.go:54:3:54:16 | call to Println | -| stmts.go:55:3:55:13 | selection of Println | stmts.go:55:15:55:15 | w | -| stmts.go:55:3:55:16 | call to Println | stmts.go:46:1:62:1 | exit | +| stmts.go:54:15:54:15 | After a | stmts.go:54:3:54:16 | call to Println | +| stmts.go:55:3:55:13 | After selection of Println | stmts.go:55:15:55:15 | Before w | +| stmts.go:55:3:55:16 | call to Println | stmts.go:46:1:62:1 | Exceptional Exit | | stmts.go:55:3:55:16 | call to Println | stmts.go:61:2:61:10 | select statement | -| stmts.go:55:15:55:15 | w | stmts.go:55:3:55:16 | call to Println | -| stmts.go:57:3:57:13 | selection of Println | stmts.go:57:3:57:15 | call to Println | -| stmts.go:57:3:57:15 | call to Println | stmts.go:46:1:62:1 | exit | +| stmts.go:55:15:55:15 | After w | stmts.go:55:3:55:16 | call to Println | +| stmts.go:57:3:57:13 | After selection of Println | stmts.go:57:3:57:15 | call to Println | +| stmts.go:57:3:57:15 | call to Println | stmts.go:46:1:62:1 | Exceptional Exit | | stmts.go:57:3:57:15 | call to Println | stmts.go:61:2:61:10 | select statement | | stmts.go:58:2:58:16 | skip | stmts.go:61:2:61:10 | select statement | -| stmts.go:58:7:58:9 | ch1 | stmts.go:58:14:58:15 | 42 | +| stmts.go:58:7:58:9 | After ch1 | stmts.go:58:14:58:15 | Before 42 | | stmts.go:58:7:58:15 | send statement | stmts.go:46:1:62:1 | exit | | stmts.go:58:7:58:15 | send statement | stmts.go:58:2:58:16 | skip | -| stmts.go:58:14:58:15 | 42 | stmts.go:50:2:59:2 | select statement | +| stmts.go:58:14:58:15 | After 42 | stmts.go:50:2:59:2 | select statement | | stmts.go:65:1:72:1 | entry | stmts.go:65:12:65:12 | argument corresponding to x | | stmts.go:65:1:72:1 | function declaration | stmts.go:75:6:75:10 | skip | | stmts.go:65:6:65:10 | skip | stmts.go:65:1:72:1 | function declaration | | stmts.go:65:12:65:12 | argument corresponding to x | stmts.go:65:12:65:12 | initialization of x | | stmts.go:65:12:65:12 | initialization of x | stmts.go:66:5:66:5 | x | -| stmts.go:66:5:66:5 | x | stmts.go:66:9:66:9 | 0 | -| stmts.go:66:5:66:9 | ...>... | stmts.go:66:5:66:9 | ...>... is false | -| stmts.go:66:5:66:9 | ...>... | stmts.go:66:5:66:9 | ...>... is true | +| stmts.go:66:5:66:5 | After x | stmts.go:66:9:66:9 | Before 0 | +| stmts.go:66:5:66:9 | ...>... | stmts.go:66:5:66:9 | After ...>... [false] | +| stmts.go:66:5:66:9 | ...>... | stmts.go:66:5:66:9 | After ...>... [true] | | stmts.go:66:5:66:9 | ...>... is false | stmts.go:69:9:69:34 | function literal | | stmts.go:66:5:66:9 | ...>... is true | stmts.go:67:9:67:33 | function literal | -| stmts.go:66:9:66:9 | 0 | stmts.go:66:5:66:9 | ...>... | +| stmts.go:66:9:66:9 | After 0 | stmts.go:66:5:66:9 | ...>... | | stmts.go:67:3:67:35 | defer statement | stmts.go:71:9:71:10 | 42 | | stmts.go:67:9:67:33 | entry | stmts.go:67:18:67:28 | selection of Println | | stmts.go:67:9:67:33 | function literal | stmts.go:67:3:67:35 | defer statement | -| stmts.go:67:9:67:35 | function call | stmts.go:65:1:72:1 | exit | -| stmts.go:67:18:67:28 | selection of Println | stmts.go:67:30:67:30 | x | -| stmts.go:67:18:67:31 | call to Println | stmts.go:67:9:67:33 | exit | -| stmts.go:67:30:67:30 | x | stmts.go:67:18:67:31 | call to Println | +| stmts.go:67:9:67:35 | function call | stmts.go:65:1:72:1 | Exceptional Exit | +| stmts.go:67:18:67:28 | After selection of Println | stmts.go:67:30:67:30 | Before x | +| stmts.go:67:18:67:31 | call to Println | stmts.go:67:9:67:33 | Exceptional Exit | +| stmts.go:67:30:67:30 | After x | stmts.go:67:18:67:31 | call to Println | | stmts.go:69:3:69:36 | defer statement | stmts.go:71:9:71:10 | 42 | | stmts.go:69:9:69:34 | entry | stmts.go:69:18:69:28 | selection of Println | | stmts.go:69:9:69:34 | function literal | stmts.go:69:3:69:36 | defer statement | -| stmts.go:69:9:69:36 | function call | stmts.go:65:1:72:1 | exit | +| stmts.go:69:9:69:36 | function call | stmts.go:65:1:72:1 | Exceptional Exit | | stmts.go:69:18:69:28 | selection of Println | stmts.go:69:31:69:31 | x | -| stmts.go:69:18:69:32 | call to Println | stmts.go:69:9:69:34 | exit | -| stmts.go:69:30:69:31 | -... | stmts.go:69:18:69:32 | call to Println | -| stmts.go:69:31:69:31 | x | stmts.go:69:30:69:31 | -... | +| stmts.go:69:18:69:32 | call to Println | stmts.go:69:9:69:34 | Exceptional Exit | +| stmts.go:69:30:69:31 | After -... | stmts.go:69:18:69:32 | call to Println | +| stmts.go:69:31:69:31 | After x | stmts.go:69:30:69:31 | -... | | stmts.go:71:2:71:10 | return statement | stmts.go:67:9:67:35 | function call | | stmts.go:71:2:71:10 | return statement | stmts.go:69:9:69:36 | function call | -| stmts.go:71:9:71:10 | 42 | stmts.go:71:2:71:10 | return statement | +| stmts.go:71:9:71:10 | After 42 | stmts.go:71:2:71:10 | return statement | | stmts.go:75:1:109:1 | entry | stmts.go:75:12:75:12 | argument corresponding to x | | stmts.go:75:1:109:1 | function declaration | stmts.go:112:6:112:10 | skip | | stmts.go:75:6:75:10 | skip | stmts.go:75:1:109:1 | function declaration | @@ -1322,49 +1322,49 @@ | stmts.go:79:9:79:9 | assignment to y | stmts.go:79:17:79:17 | y | | stmts.go:79:9:79:9 | skip | stmts.go:79:14:79:14 | x | | stmts.go:79:14:79:14 | x | stmts.go:79:9:79:9 | assignment to y | -| stmts.go:79:17:79:17 | y | stmts.go:79:21:79:22 | 19 | +| stmts.go:79:17:79:17 | After y | stmts.go:79:21:79:22 | Before 19 | | stmts.go:79:17:79:22 | ...-... | stmts.go:81:3:81:7 | test5 | -| stmts.go:79:21:79:22 | 19 | stmts.go:79:17:79:22 | ...-... | -| stmts.go:81:3:81:7 | test5 | stmts.go:81:9:81:13 | false | -| stmts.go:81:3:81:14 | call to test5 | stmts.go:75:1:109:1 | exit | -| stmts.go:81:9:81:13 | false | stmts.go:81:3:81:14 | call to test5 | +| stmts.go:79:21:79:22 | After 19 | stmts.go:79:17:79:22 | ...-... | +| stmts.go:81:3:81:7 | After test5 | stmts.go:81:9:81:13 | Before false | +| stmts.go:81:3:81:14 | call to test5 | stmts.go:75:1:109:1 | Exceptional Exit | +| stmts.go:81:9:81:13 | After false | stmts.go:81:3:81:14 | call to test5 | | stmts.go:84:9:84:9 | x | stmts.go:85:7:85:7 | 1 | | stmts.go:84:9:84:9 | x | stmts.go:90:9:90:9 | x | | stmts.go:85:2:85:8 | skip | stmts.go:90:9:90:9 | x | -| stmts.go:85:7:85:7 | 1 | stmts.go:85:7:85:7 | case 1 | +| stmts.go:85:7:85:7 | 1 | stmts.go:85:7:85:7 | After 1 | | stmts.go:85:7:85:7 | case 1 | stmts.go:85:2:85:8 | skip | | stmts.go:85:7:85:7 | case 1 | stmts.go:86:7:86:7 | 2 | -| stmts.go:86:7:86:7 | 2 | stmts.go:86:7:86:7 | case 2 | -| stmts.go:86:7:86:7 | case 2 | stmts.go:86:10:86:10 | 3 | +| stmts.go:86:7:86:7 | 2 | stmts.go:86:7:86:7 | After 2 | +| stmts.go:86:7:86:7 | After 2 | stmts.go:86:10:86:10 | Before 3 | | stmts.go:86:7:86:7 | case 2 | stmts.go:87:3:87:7 | test5 | -| stmts.go:86:10:86:10 | 3 | stmts.go:86:10:86:10 | case 3 | +| stmts.go:86:10:86:10 | 3 | stmts.go:86:10:86:10 | After 3 | | stmts.go:86:10:86:10 | case 3 | stmts.go:87:3:87:7 | test5 | | stmts.go:86:10:86:10 | case 3 | stmts.go:90:9:90:9 | x | -| stmts.go:87:3:87:7 | test5 | stmts.go:87:9:87:12 | true | -| stmts.go:87:9:87:12 | true | stmts.go:87:3:87:13 | call to test5 | +| stmts.go:87:3:87:7 | After test5 | stmts.go:87:9:87:12 | Before true | +| stmts.go:87:9:87:12 | After true | stmts.go:87:3:87:13 | call to test5 | | stmts.go:90:9:90:9 | x | stmts.go:91:7:91:7 | 1 | | stmts.go:90:9:90:9 | x | stmts.go:98:9:98:9 | x | -| stmts.go:91:7:91:7 | 1 | stmts.go:91:7:91:7 | case 1 | +| stmts.go:91:7:91:7 | 1 | stmts.go:91:7:91:7 | After 1 | | stmts.go:91:7:91:7 | case 1 | stmts.go:92:3:92:7 | test5 | | stmts.go:91:7:91:7 | case 1 | stmts.go:94:7:94:11 | ...-... | -| stmts.go:92:3:92:7 | test5 | stmts.go:92:9:92:13 | false | -| stmts.go:92:9:92:13 | false | stmts.go:92:3:92:14 | call to test5 | +| stmts.go:92:3:92:7 | After test5 | stmts.go:92:9:92:13 | Before false | +| stmts.go:92:9:92:13 | After false | stmts.go:92:3:92:14 | call to test5 | | stmts.go:93:3:93:13 | skip | stmts.go:95:3:95:7 | test5 | -| stmts.go:94:7:94:11 | ...-... | stmts.go:94:7:94:11 | case ...-... | +| stmts.go:94:7:94:11 | ...-... | stmts.go:94:7:94:11 | After ...-... | | stmts.go:94:7:94:11 | case ...-... | stmts.go:95:3:95:7 | test5 | | stmts.go:94:7:94:11 | case ...-... | stmts.go:98:9:98:9 | x | -| stmts.go:95:3:95:7 | test5 | stmts.go:95:9:95:12 | true | -| stmts.go:95:9:95:12 | true | stmts.go:95:3:95:13 | call to test5 | +| stmts.go:95:3:95:7 | After test5 | stmts.go:95:9:95:12 | Before true | +| stmts.go:95:9:95:12 | After true | stmts.go:95:3:95:13 | call to test5 | | stmts.go:98:9:98:9 | x | stmts.go:100:7:100:7 | 2 | | stmts.go:99:2:99:9 | skip | stmts.go:104:2:108:2 | true | -| stmts.go:100:7:100:7 | 2 | stmts.go:100:7:100:7 | case 2 | -| stmts.go:100:7:100:7 | case 2 | stmts.go:99:2:99:9 | skip | +| stmts.go:100:7:100:7 | 2 | stmts.go:100:7:100:7 | After 2 | +| stmts.go:100:7:100:7 | After 2 | stmts.go:99:2:99:9 | case clause | | stmts.go:100:7:100:7 | case 2 | stmts.go:101:3:101:7 | test5 | -| stmts.go:101:3:101:7 | test5 | stmts.go:101:9:101:12 | true | -| stmts.go:101:9:101:12 | true | stmts.go:101:3:101:13 | call to test5 | +| stmts.go:101:3:101:7 | After test5 | stmts.go:101:9:101:12 | Before true | +| stmts.go:101:9:101:12 | After true | stmts.go:101:3:101:13 | call to test5 | | stmts.go:104:2:108:2 | true | stmts.go:107:7:107:10 | true | -| stmts.go:107:7:107:10 | case true | stmts.go:107:7:107:10 | true is false | -| stmts.go:107:7:107:10 | case true | stmts.go:107:7:107:10 | true is true | +| stmts.go:107:7:107:10 | Before true | stmts.go:107:7:107:10 | true | +| stmts.go:107:7:107:10 | true | stmts.go:107:7:107:10 | After true [true] | | stmts.go:107:7:107:10 | true | stmts.go:107:7:107:10 | case true | | stmts.go:107:7:107:10 | true is false | stmts.go:106:3:106:7 | skip | | stmts.go:107:7:107:10 | true is true | stmts.go:107:2:107:11 | skip | @@ -1375,58 +1375,58 @@ | stmts.go:112:12:112:12 | initialization of x | stmts.go:113:9:113:9 | skip | | stmts.go:113:9:113:9 | assignment to y | stmts.go:114:7:114:11 | case error | | stmts.go:113:9:113:9 | skip | stmts.go:113:14:113:14 | x | -| stmts.go:113:14:113:14 | x | stmts.go:113:14:113:21 | type assertion | +| stmts.go:113:14:113:14 | After x | stmts.go:113:14:113:21 | type assertion | | stmts.go:113:14:113:21 | type assertion | stmts.go:113:9:113:9 | assignment to y | | stmts.go:114:2:115:16 | implicit type switch variable declaration | stmts.go:115:3:115:13 | selection of Println | | stmts.go:114:7:114:11 | case error | stmts.go:114:2:115:16 | implicit type switch variable declaration | -| stmts.go:114:7:114:11 | case error | stmts.go:114:14:114:19 | case string | +| stmts.go:114:7:114:11 | error | stmts.go:114:14:114:19 | string | | stmts.go:114:14:114:19 | case string | stmts.go:114:2:115:16 | implicit type switch variable declaration | | stmts.go:114:14:114:19 | case string | stmts.go:116:7:116:13 | case float32 | -| stmts.go:115:3:115:13 | selection of Println | stmts.go:115:15:115:15 | y | -| stmts.go:115:3:115:16 | call to Println | stmts.go:112:1:137:1 | exit | +| stmts.go:115:3:115:13 | After selection of Println | stmts.go:115:15:115:15 | Before y | +| stmts.go:115:3:115:16 | call to Println | stmts.go:112:1:137:1 | Exceptional Exit | | stmts.go:115:3:115:16 | call to Println | stmts.go:123:9:123:9 | skip | -| stmts.go:115:15:115:15 | y | stmts.go:115:3:115:16 | call to Println | +| stmts.go:115:15:115:15 | After y | stmts.go:115:3:115:16 | call to Println | | stmts.go:116:2:118:14 | implicit type switch variable declaration | stmts.go:117:3:117:7 | test5 | | stmts.go:116:7:116:13 | case float32 | stmts.go:116:2:118:14 | implicit type switch variable declaration | -| stmts.go:116:7:116:13 | case float32 | stmts.go:119:2:120:7 | implicit type switch variable declaration | -| stmts.go:117:3:117:7 | test5 | stmts.go:117:9:117:12 | true | -| stmts.go:117:3:117:13 | call to test5 | stmts.go:112:1:137:1 | exit | -| stmts.go:117:9:117:12 | true | stmts.go:117:3:117:13 | call to test5 | -| stmts.go:118:3:118:7 | test5 | stmts.go:118:9:118:13 | false | -| stmts.go:118:9:118:13 | false | stmts.go:118:3:118:14 | call to test5 | +| stmts.go:116:7:116:13 | float32 | stmts.go:119:2:120:7 | case clause | +| stmts.go:117:3:117:7 | After test5 | stmts.go:117:9:117:12 | Before true | +| stmts.go:117:3:117:13 | call to test5 | stmts.go:112:1:137:1 | Exceptional Exit | +| stmts.go:117:9:117:12 | After true | stmts.go:117:3:117:13 | call to test5 | +| stmts.go:118:3:118:7 | After test5 | stmts.go:118:9:118:13 | Before false | +| stmts.go:118:9:118:13 | After false | stmts.go:118:3:118:14 | call to test5 | | stmts.go:119:2:120:7 | implicit type switch variable declaration | stmts.go:120:3:120:3 | skip | | stmts.go:120:3:120:3 | skip | stmts.go:120:7:120:7 | y | | stmts.go:120:7:120:7 | y | stmts.go:123:9:123:9 | skip | | stmts.go:123:9:123:9 | assignment to y | stmts.go:123:17:123:17 | y | | stmts.go:123:9:123:9 | skip | stmts.go:123:14:123:14 | x | | stmts.go:123:14:123:14 | x | stmts.go:123:9:123:9 | assignment to y | -| stmts.go:123:17:123:17 | y | stmts.go:123:17:123:24 | type assertion | +| stmts.go:123:17:123:17 | After y | stmts.go:123:17:123:24 | type assertion | | stmts.go:123:17:123:24 | type assertion | stmts.go:124:7:124:11 | case error | -| stmts.go:124:7:124:11 | case error | stmts.go:124:14:124:19 | case string | +| stmts.go:124:7:124:11 | error | stmts.go:124:14:124:19 | string | | stmts.go:124:7:124:11 | case error | stmts.go:125:3:125:13 | selection of Println | | stmts.go:124:14:124:19 | case string | stmts.go:125:3:125:13 | selection of Println | | stmts.go:124:14:124:19 | case string | stmts.go:126:7:126:13 | case float32 | -| stmts.go:125:3:125:13 | selection of Println | stmts.go:125:15:125:15 | y | -| stmts.go:125:3:125:16 | call to Println | stmts.go:112:1:137:1 | exit | +| stmts.go:125:3:125:13 | After selection of Println | stmts.go:125:15:125:15 | Before y | +| stmts.go:125:3:125:16 | call to Println | stmts.go:112:1:137:1 | Exceptional Exit | | stmts.go:125:3:125:16 | call to Println | stmts.go:133:9:133:9 | skip | -| stmts.go:125:15:125:15 | y | stmts.go:125:3:125:16 | call to Println | +| stmts.go:125:15:125:15 | After y | stmts.go:125:3:125:16 | call to Println | | stmts.go:126:7:126:13 | case float32 | stmts.go:127:3:127:7 | test5 | | stmts.go:126:7:126:13 | case float32 | stmts.go:130:3:130:3 | skip | -| stmts.go:127:3:127:7 | test5 | stmts.go:127:9:127:12 | true | -| stmts.go:127:3:127:13 | call to test5 | stmts.go:112:1:137:1 | exit | -| stmts.go:127:9:127:12 | true | stmts.go:127:3:127:13 | call to test5 | -| stmts.go:128:3:128:7 | test5 | stmts.go:128:9:128:13 | false | -| stmts.go:128:9:128:13 | false | stmts.go:128:3:128:14 | call to test5 | +| stmts.go:127:3:127:7 | After test5 | stmts.go:127:9:127:12 | Before true | +| stmts.go:127:3:127:13 | call to test5 | stmts.go:112:1:137:1 | Exceptional Exit | +| stmts.go:127:9:127:12 | After true | stmts.go:127:3:127:13 | call to test5 | +| stmts.go:128:3:128:7 | After test5 | stmts.go:128:9:128:13 | Before false | +| stmts.go:128:9:128:13 | After false | stmts.go:128:3:128:14 | call to test5 | | stmts.go:130:3:130:3 | skip | stmts.go:130:7:130:7 | y | | stmts.go:130:7:130:7 | y | stmts.go:133:9:133:9 | skip | | stmts.go:133:9:133:9 | assignment to y | stmts.go:133:17:133:17 | y | | stmts.go:133:9:133:9 | skip | stmts.go:133:14:133:14 | x | | stmts.go:133:14:133:14 | x | stmts.go:133:9:133:9 | assignment to y | -| stmts.go:133:17:133:17 | y | stmts.go:133:17:133:24 | type assertion | +| stmts.go:133:17:133:17 | After y | stmts.go:133:17:133:24 | type assertion | | stmts.go:133:17:133:24 | type assertion | stmts.go:135:3:135:7 | test5 | -| stmts.go:135:3:135:7 | test5 | stmts.go:135:9:135:13 | false | -| stmts.go:135:3:135:14 | call to test5 | stmts.go:112:1:137:1 | exit | -| stmts.go:135:9:135:13 | false | stmts.go:135:3:135:14 | call to test5 | +| stmts.go:135:3:135:7 | After test5 | stmts.go:135:9:135:13 | Before false | +| stmts.go:135:3:135:14 | call to test5 | stmts.go:112:1:137:1 | Exceptional Exit | +| stmts.go:135:9:135:13 | After false | stmts.go:135:3:135:14 | call to test5 | | stmts.go:140:1:142:1 | entry | stmts.go:140:13:140:13 | argument corresponding to f | | stmts.go:140:1:142:1 | function declaration | stmts.go:145:6:145:11 | skip | | stmts.go:140:6:140:11 | skip | stmts.go:140:1:142:1 | function declaration | @@ -1435,7 +1435,7 @@ | stmts.go:141:2:141:7 | go statement | stmts.go:140:1:142:1 | exit | | stmts.go:141:5:141:5 | f | stmts.go:141:2:141:7 | go statement | | stmts.go:145:1:159:1 | entry | stmts.go:145:13:145:14 | argument corresponding to xs | -| stmts.go:145:1:159:1 | function declaration | stmts.go:0:0:0:0 | exit | +| stmts.go:145:1:159:1 | After function declaration | stmts.go:0:0:0:0 | After stmts.go | | stmts.go:145:6:145:11 | skip | stmts.go:145:1:159:1 | function declaration | | stmts.go:145:13:145:14 | argument corresponding to xs | stmts.go:145:13:145:14 | initialization of xs | | stmts.go:145:13:145:14 | initialization of xs | stmts.go:146:17:146:18 | xs | @@ -1444,19 +1444,19 @@ | stmts.go:146:6:146:6 | skip | stmts.go:146:2:151:2 | range statement[0] | | stmts.go:146:17:146:18 | next key-value pair in range | stmts.go:146:6:146:6 | skip | | stmts.go:146:17:146:18 | next key-value pair in range | stmts.go:153:20:153:21 | xs | -| stmts.go:146:17:146:18 | xs | stmts.go:146:17:146:18 | next key-value pair in range | -| stmts.go:147:6:147:6 | x | stmts.go:147:10:147:10 | 5 | -| stmts.go:147:6:147:10 | ...>... | stmts.go:147:6:147:10 | ...>... is false | -| stmts.go:147:6:147:10 | ...>... | stmts.go:147:6:147:10 | ...>... is true | +| stmts.go:146:17:146:18 | Before xs | stmts.go:146:17:146:18 | xs | +| stmts.go:147:6:147:6 | After x | stmts.go:147:10:147:10 | Before 5 | +| stmts.go:147:6:147:10 | ...>... | stmts.go:147:6:147:10 | After ...>... [false] | +| stmts.go:147:6:147:10 | ...>... | stmts.go:147:6:147:10 | After ...>... [true] | | stmts.go:147:6:147:10 | ...>... is false | stmts.go:150:3:150:11 | selection of Print | | stmts.go:147:6:147:10 | ...>... is true | stmts.go:148:4:148:11 | skip | -| stmts.go:147:10:147:10 | 5 | stmts.go:147:6:147:10 | ...>... | +| stmts.go:147:10:147:10 | After 5 | stmts.go:147:6:147:10 | ...>... | | stmts.go:148:4:148:11 | skip | stmts.go:146:17:146:18 | next key-value pair in range | -| stmts.go:150:3:150:11 | selection of Print | stmts.go:150:13:150:13 | x | -| stmts.go:150:3:150:14 | call to Print | stmts.go:145:1:159:1 | exit | +| stmts.go:150:3:150:11 | After selection of Print | stmts.go:150:13:150:13 | Before x | +| stmts.go:150:3:150:14 | call to Print | stmts.go:145:1:159:1 | Exceptional Exit | | stmts.go:150:3:150:14 | call to Print | stmts.go:146:17:146:18 | next key-value pair in range | -| stmts.go:150:13:150:13 | x | stmts.go:150:3:150:14 | call to Print | -| stmts.go:153:2:155:2 | range statement[0] | stmts.go:153:2:155:2 | range statement[1] | +| stmts.go:150:13:150:13 | After x | stmts.go:150:3:150:14 | call to Print | +| stmts.go:153:2:155:2 | [LoopHeader] range statement | stmts.go:153:2:155:2 | After range statement | | stmts.go:153:2:155:2 | range statement[1] | stmts.go:153:6:153:6 | assignment to i | | stmts.go:153:6:153:6 | assignment to i | stmts.go:153:9:153:9 | assignment to v | | stmts.go:153:6:153:6 | skip | stmts.go:153:9:153:9 | skip | @@ -1464,15 +1464,15 @@ | stmts.go:153:9:153:9 | skip | stmts.go:153:2:155:2 | range statement[0] | | stmts.go:153:20:153:21 | next key-value pair in range | stmts.go:153:6:153:6 | skip | | stmts.go:153:20:153:21 | next key-value pair in range | stmts.go:157:12:157:13 | xs | -| stmts.go:153:20:153:21 | xs | stmts.go:153:20:153:21 | next key-value pair in range | -| stmts.go:154:3:154:11 | selection of Print | stmts.go:154:13:154:13 | i | -| stmts.go:154:3:154:17 | call to Print | stmts.go:145:1:159:1 | exit | +| stmts.go:153:20:153:21 | Before xs | stmts.go:153:20:153:21 | xs | +| stmts.go:154:3:154:11 | After selection of Print | stmts.go:154:13:154:13 | Before i | +| stmts.go:154:3:154:17 | call to Print | stmts.go:145:1:159:1 | Exceptional Exit | | stmts.go:154:3:154:17 | call to Print | stmts.go:153:20:153:21 | next key-value pair in range | -| stmts.go:154:13:154:13 | i | stmts.go:154:16:154:16 | v | -| stmts.go:154:16:154:16 | v | stmts.go:154:3:154:17 | call to Print | +| stmts.go:154:13:154:13 | After i | stmts.go:154:16:154:16 | Before v | +| stmts.go:154:16:154:16 | After v | stmts.go:154:3:154:17 | call to Print | | stmts.go:157:12:157:13 | next key-value pair in range | stmts.go:145:1:159:1 | exit | | stmts.go:157:12:157:13 | next key-value pair in range | stmts.go:157:15:158:2 | skip | -| stmts.go:157:12:157:13 | xs | stmts.go:157:12:157:13 | next key-value pair in range | +| stmts.go:157:12:157:13 | Before xs | stmts.go:157:12:157:13 | xs | | stmts.go:157:15:158:2 | skip | stmts.go:157:12:157:13 | next key-value pair in range | | tst.go:0:0:0:0 | entry | tst.go:3:6:3:10 | skip | | tst.go:3:1:12:1 | entry | tst.go:3:12:3:12 | argument corresponding to x | @@ -1482,29 +1482,29 @@ | tst.go:3:12:3:12 | initialization of x | tst.go:4:2:11:2 | true | | tst.go:4:2:11:2 | true | tst.go:5:7:5:7 | x | | tst.go:5:2:5:13 | skip | tst.go:3:1:12:1 | exit | -| tst.go:5:7:5:7 | x | tst.go:5:11:5:12 | 23 | -| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | case ...<... | +| tst.go:5:7:5:7 | After x | tst.go:5:11:5:12 | Before 23 | +| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | After ...<... [false] | | tst.go:5:7:5:12 | ...<... is false | tst.go:7:7:7:7 | x | | tst.go:5:7:5:12 | ...<... is true | tst.go:5:2:5:13 | skip | -| tst.go:5:7:5:12 | case ...<... | tst.go:5:7:5:12 | ...<... is false | +| tst.go:5:7:5:12 | ...<... | tst.go:5:7:5:12 | After ...<... [true] | | tst.go:5:7:5:12 | case ...<... | tst.go:5:7:5:12 | ...<... is true | -| tst.go:5:11:5:12 | 23 | tst.go:5:7:5:12 | ...<... | +| tst.go:5:11:5:12 | After 23 | tst.go:5:7:5:12 | ...<... | | tst.go:7:2:7:13 | skip | tst.go:3:1:12:1 | exit | -| tst.go:7:7:7:7 | x | tst.go:7:11:7:12 | 42 | -| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | case ...<... | +| tst.go:7:7:7:7 | After x | tst.go:7:11:7:12 | Before 42 | +| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | After ...<... [false] | | tst.go:7:7:7:12 | ...<... is false | tst.go:9:7:9:7 | x | | tst.go:7:7:7:12 | ...<... is true | tst.go:7:2:7:13 | skip | -| tst.go:7:7:7:12 | case ...<... | tst.go:7:7:7:12 | ...<... is false | +| tst.go:7:7:7:12 | ...<... | tst.go:7:7:7:12 | After ...<... [true] | | tst.go:7:7:7:12 | case ...<... | tst.go:7:7:7:12 | ...<... is true | -| tst.go:7:11:7:12 | 42 | tst.go:7:7:7:12 | ...<... | +| tst.go:7:11:7:12 | After 42 | tst.go:7:7:7:12 | ...<... | | tst.go:9:2:9:13 | skip | tst.go:3:1:12:1 | exit | -| tst.go:9:7:9:7 | x | tst.go:9:11:9:12 | 23 | -| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | case ...<... | +| tst.go:9:7:9:7 | After x | tst.go:9:11:9:12 | Before 23 | +| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | After ...<... [false] | | tst.go:9:7:9:12 | ...<... is false | tst.go:3:1:12:1 | exit | | tst.go:9:7:9:12 | ...<... is true | tst.go:9:2:9:13 | skip | -| tst.go:9:7:9:12 | case ...<... | tst.go:9:7:9:12 | ...<... is false | +| tst.go:9:7:9:12 | ...<... | tst.go:9:7:9:12 | After ...<... [true] | | tst.go:9:7:9:12 | case ...<... | tst.go:9:7:9:12 | ...<... is true | -| tst.go:9:11:9:12 | 23 | tst.go:9:7:9:12 | ...<... | +| tst.go:9:11:9:12 | After 23 | tst.go:9:7:9:12 | ...<... | | tst.go:14:1:21:1 | entry | tst.go:14:13:14:17 | argument corresponding to value | | tst.go:14:1:21:1 | function declaration | tst.go:23:6:23:11 | skip | | tst.go:14:6:14:11 | skip | tst.go:14:1:21:1 | function declaration | @@ -1512,27 +1512,27 @@ | tst.go:14:13:14:17 | initialization of value | tst.go:15:2:20:2 | true | | tst.go:15:2:20:2 | true | tst.go:16:7:16:11 | value | | tst.go:16:2:16:34 | skip | tst.go:14:1:21:1 | exit | -| tst.go:16:7:16:11 | value | tst.go:16:15:16:33 | ...*... | -| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | case ...<... | +| tst.go:16:7:16:11 | After value | tst.go:16:15:16:33 | Before ...*... | +| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | After ...<... [false] | | tst.go:16:7:16:33 | ...<... is false | tst.go:18:7:18:11 | value | | tst.go:16:7:16:33 | ...<... is true | tst.go:16:2:16:34 | skip | -| tst.go:16:7:16:33 | case ...<... | tst.go:16:7:16:33 | ...<... is false | +| tst.go:16:7:16:33 | ...<... | tst.go:16:7:16:33 | After ...<... [true] | | tst.go:16:7:16:33 | case ...<... | tst.go:16:7:16:33 | ...<... is true | -| tst.go:16:15:16:33 | ...*... | tst.go:16:7:16:33 | ...<... | +| tst.go:16:15:16:33 | After ...*... | tst.go:16:7:16:33 | ...<... | | tst.go:18:2:18:39 | skip | tst.go:14:1:21:1 | exit | -| tst.go:18:7:18:11 | value | tst.go:18:15:18:38 | ...*... | -| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | case ...<... | +| tst.go:18:7:18:11 | After value | tst.go:18:15:18:38 | Before ...*... | +| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | After ...<... [false] | | tst.go:18:7:18:38 | ...<... is false | tst.go:14:1:21:1 | exit | | tst.go:18:7:18:38 | ...<... is true | tst.go:18:2:18:39 | skip | -| tst.go:18:7:18:38 | case ...<... | tst.go:18:7:18:38 | ...<... is false | +| tst.go:18:7:18:38 | ...<... | tst.go:18:7:18:38 | After ...<... [true] | | tst.go:18:7:18:38 | case ...<... | tst.go:18:7:18:38 | ...<... is true | -| tst.go:18:15:18:38 | ...*... | tst.go:18:7:18:38 | ...<... | +| tst.go:18:15:18:38 | After ...*... | tst.go:18:7:18:38 | ...<... | | tst.go:23:1:26:1 | entry | tst.go:24:2:25:2 | true | | tst.go:23:1:26:1 | function declaration | tst.go:28:6:28:11 | skip | | tst.go:23:6:23:11 | skip | tst.go:23:1:26:1 | function declaration | | tst.go:24:2:25:2 | true | tst.go:23:1:26:1 | exit | | tst.go:28:1:32:1 | entry | tst.go:29:2:31:2 | true | -| tst.go:28:1:32:1 | function declaration | tst.go:0:0:0:0 | exit | +| tst.go:28:1:32:1 | After function declaration | tst.go:0:0:0:0 | After tst.go | | tst.go:28:6:28:11 | skip | tst.go:28:1:32:1 | function declaration | -| tst.go:29:2:31:2 | true | tst.go:30:2:30:9 | skip | +| tst.go:29:2:31:2 | expression-switch statement | tst.go:30:2:30:9 | case clause | | tst.go:30:2:30:9 | skip | tst.go:28:1:32:1 | exit | diff --git a/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/NoretFunctions.expected b/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/NoretFunctions.expected index 4e466b74504b..1ec4ba194a00 100644 --- a/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/NoretFunctions.expected +++ b/go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph/NoretFunctions.expected @@ -18,4 +18,3 @@ | stmts7.go:10:6:10:15 | canRecover | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.canRecover | | stmts.go:10:6:10:10 | test5 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test5 | | stmts.go:46:6:46:10 | test6 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test6 | -| stmts.go:112:6:112:10 | test9 | github.com/github/codeql-go/ql/test/library-tests/semmle/go/controlflow/ControlFlowGraph.test9 | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected index f5768d49d1b5..af684f7e0f24 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/srcs.expected @@ -4,9 +4,9 @@ invalidModelRow | test.go:40:8:40:15 | call to Src2 | qltest | | test.go:40:8:40:15 | call to Src2 | qltest-w-subtypes | | test.go:41:8:41:16 | call to Src2 | qltest-w-subtypes | -| test.go:42:2:42:21 | ... = ...[0] | qltest | -| test.go:42:2:42:21 | ... = ...[1] | qltest-w-subtypes | -| test.go:43:2:43:22 | ... = ...[1] | qltest-w-subtypes | +| test.go:42:2:42:21 | extract:0 ... = ... | qltest | +| test.go:42:2:42:21 | extract:1 ... = ... | qltest-w-subtypes | +| test.go:43:2:43:22 | extract:1 ... = ... | qltest-w-subtypes | | test.go:44:11:44:13 | arg [postupdate] | qltest-arg | | test.go:59:9:59:16 | call to Src1 | qltest | | test.go:102:46:102:53 | call to Src1 | qltest | @@ -22,4 +22,4 @@ invalidModelRow | test.go:187:24:187:31 | call to Src1 | qltest | | test.go:191:24:191:31 | call to Src1 | qltest | | test.go:209:10:209:28 | selection of SourceVariable | qltest | -| test.go:216:15:216:17 | definition of src | qltest | +| test.go:216:37:218:1 | definition of src | qltest | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/steps.expected b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/steps.expected index eb52daa42537..83861e4d9f8d 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/steps.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ExternalValueFlow/steps.expected @@ -1,14 +1,14 @@ invalidModelRow #select | test.go:17:23:17:25 | arg | test.go:17:10:17:26 | call to StepArgRes | -| test.go:18:27:18:29 | arg | test.go:18:2:18:30 | ... = ...[1] | +| test.go:18:27:18:29 | arg | test.go:18:2:18:30 | extract:1 ... = ... | | test.go:19:15:19:17 | arg | test.go:19:20:19:23 | arg1 [postupdate] | | test.go:21:16:21:18 | arg | test.go:21:2:21:2 | t [postupdate] | | test.go:22:10:22:10 | t | test.go:22:10:22:24 | call to StepQualRes | | test.go:23:2:23:2 | t | test.go:23:16:23:18 | arg [postupdate] | | test.go:24:32:24:34 | arg | test.go:24:10:24:35 | call to StepArgResNoQual | | test.go:61:25:61:27 | src | test.go:61:12:61:28 | call to StepArgRes | -| test.go:64:29:64:31 | src | test.go:64:2:64:32 | ... := ...[1] | +| test.go:64:29:64:31 | src | test.go:64:2:64:32 | extract:1 ... := ... | | test.go:68:15:68:17 | src | test.go:68:20:68:25 | taint3 [postupdate] | | test.go:76:21:76:23 | src | test.go:76:2:76:7 | taint4 [postupdate] | | test.go:79:13:79:25 | type assertion | test.go:79:12:79:40 | call to StepQualRes | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/GlobalValueNumbering/GlobalValueNumber.expected b/go/ql/test/library-tests/semmle/go/dataflow/GlobalValueNumbering/GlobalValueNumber.expected index 93b3593ec94e..afcc55c1aeff 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/GlobalValueNumbering/GlobalValueNumber.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/GlobalValueNumbering/GlobalValueNumber.expected @@ -1,4 +1,4 @@ -| main.go:6:2:6:5 | 1 | main.go:14:7:14:7 | 1 | +| main.go:6:2:6:5 | implicit-one increment statement | main.go:14:7:14:7 | 1 | | main.go:10:2:10:2 | definition of x | main.go:10:7:10:7 | 0 | | main.go:10:7:10:7 | 0 | main.go:10:7:10:7 | 0 | | main.go:11:6:11:6 | definition of y | main.go:10:7:10:7 | 0 | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsElement.expected b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsElement.expected index 640c0dec2676..df6a0b049b60 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsElement.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsElement.expected @@ -1,3 +1,3 @@ | tst.go:19:10:19:14 | index expression | tst.go:19:10:19:11 | xs | tst.go:19:13:19:13 | 1 | -| tst.go:20:10:20:14 | index expression | tst.go:20:10:20:11 | implicit dereference | tst.go:20:13:20:13 | 1 | +| tst.go:20:10:20:14 | index expression | tst.go:20:10:20:11 | implicit-deref ps | tst.go:20:13:20:13 | 1 | | tst.go:20:10:20:14 | index expression | tst.go:20:10:20:11 | ps | tst.go:20:13:20:13 | 1 | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsField.expected b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsField.expected index 683117030c76..3dbbdc221e8d 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsField.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsField.expected @@ -1,4 +1,4 @@ -| tst.go:8:8:8:10 | selection of f | tst.go:8:8:8:8 | implicit dereference | tst.go:4:2:4:2 | f | +| tst.go:8:8:8:10 | selection of f | tst.go:8:8:8:8 | implicit-deref t | tst.go:4:2:4:2 | f | | tst.go:8:8:8:10 | selection of f | tst.go:8:8:8:8 | t | tst.go:4:2:4:2 | f | | tst.go:13:9:13:11 | selection of f | tst.go:13:9:13:9 | t | tst.go:4:2:4:2 | f | | tst.go:17:8:17:10 | selection of f | tst.go:17:8:17:8 | x | tst.go:4:2:4:2 | f | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsMethod.expected b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsMethod.expected index 1909e3257452..0a948af2cd23 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsMethod.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/readsMethod.expected @@ -1,3 +1,3 @@ -| tst.go:9:9:9:13 | selection of get | tst.go:9:9:9:9 | implicit dereference | tst.go:12:12:12:14 | get | +| tst.go:9:9:9:13 | selection of get | tst.go:9:9:9:9 | implicit-deref t | tst.go:12:12:12:14 | get | | tst.go:9:9:9:13 | selection of get | tst.go:9:9:9:9 | t | tst.go:12:12:12:14 | get | | tst.go:18:2:18:7 | selection of bump | tst.go:18:2:18:2 | x | tst.go:7:13:7:16 | bump | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesElement.expected b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesElement.expected index 44792aa3d299..23cfd9b6acab 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesElement.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesElement.expected @@ -1,3 +1,3 @@ -| tst.go:19:2:19:6 | assignment to element | tst.go:19:2:19:3 | xs [postupdate] | tst.go:19:5:19:5 | 0 | tst.go:19:10:19:14 | index expression | -| tst.go:20:2:20:6 | assignment to element | tst.go:20:2:20:3 | implicit dereference [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression | -| tst.go:20:2:20:6 | assignment to element | tst.go:20:2:20:3 | ps [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression | +| tst.go:19:2:19:14 | assign:0 ... = ... | tst.go:19:2:19:3 | xs [postupdate] | tst.go:19:5:19:5 | 0 | tst.go:19:10:19:14 | index expression | +| tst.go:20:2:20:14 | assign:0 ... = ... | tst.go:20:2:20:3 | implicit-deref ps [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression | +| tst.go:20:2:20:14 | assign:0 ... = ... | tst.go:20:2:20:3 | ps [postupdate] | tst.go:20:5:20:5 | 0 | tst.go:20:10:20:14 | index expression | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesField.expected b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesField.expected index 7862b2d61b3d..414a82acfc95 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesField.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/ReadsAndWrites/writesField.expected @@ -1,3 +1,3 @@ -| tst.go:8:2:8:4 | assignment to field f | tst.go:8:2:8:2 | implicit dereference [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... | -| tst.go:8:2:8:4 | assignment to field f | tst.go:8:2:8:2 | t [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... | -| tst.go:17:2:17:4 | assignment to field f | tst.go:17:2:17:2 | x [postupdate] | tst.go:4:2:4:2 | f | tst.go:17:8:17:14 | ...+... | +| tst.go:8:2:8:14 | assign:0 ... = ... | tst.go:8:2:8:2 | implicit-deref t [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... | +| tst.go:8:2:8:14 | assign:0 ... = ... | tst.go:8:2:8:2 | t [postupdate] | tst.go:4:2:4:2 | f | tst.go:8:8:8:14 | ...+... | +| tst.go:17:2:17:14 | assign:0 ... = ... | tst.go:17:2:17:2 | x [postupdate] | tst.go:4:2:4:2 | f | tst.go:17:8:17:14 | ...+... | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Echo/ReflectedXss.expected b/go/ql/test/library-tests/semmle/go/frameworks/Echo/ReflectedXss.expected index 4e885d284d48..4df73eb04ddb 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Echo/ReflectedXss.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Echo/ReflectedXss.expected @@ -5,11 +5,11 @@ | test.go:34:16:34:20 | param | test.go:33:11:33:27 | call to QueryParams | test.go:34:16:34:20 | param | Cross-site scripting vulnerability due to $@. | test.go:33:11:33:27 | call to QueryParams | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:40:16:40:19 | qstr | test.go:39:10:39:26 | call to QueryString | test.go:40:16:40:19 | qstr | Cross-site scripting vulnerability due to $@. | test.go:39:10:39:26 | call to QueryString | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:46:16:46:18 | val | test.go:45:9:45:34 | call to FormValue | test.go:46:16:46:18 | val | Cross-site scripting vulnerability due to $@. | test.go:45:9:45:34 | call to FormValue | user-provided value | test.go:0:0:0:0 | test.go | | -| test.go:52:16:52:37 | index expression | test.go:51:2:51:30 | ... := ...[0] | test.go:52:16:52:37 | index expression | Cross-site scripting vulnerability due to $@. | test.go:51:2:51:30 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | | -| test.go:61:20:61:25 | buffer | test.go:57:2:57:46 | ... := ...[0] | test.go:61:20:61:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:57:2:57:46 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | | -| test.go:67:16:67:41 | index expression | test.go:66:2:66:31 | ... := ...[0] | test.go:67:16:67:41 | index expression | Cross-site scripting vulnerability due to $@. | test.go:66:2:66:31 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | | -| test.go:77:20:77:25 | buffer | test.go:72:2:72:31 | ... := ...[0] | test.go:77:20:77:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:72:2:72:31 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | | -| test.go:83:16:83:24 | selection of Value | test.go:82:2:82:32 | ... := ...[0] | test.go:83:16:83:24 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:82:2:82:32 | ... := ...[0] | user-provided value | test.go:0:0:0:0 | test.go | | +| test.go:52:16:52:37 | index expression | test.go:51:2:51:30 | extract:0 ... := ... | test.go:52:16:52:37 | index expression | Cross-site scripting vulnerability due to $@. | test.go:51:2:51:30 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | | +| test.go:61:20:61:25 | buffer | test.go:57:2:57:46 | extract:0 ... := ... | test.go:61:20:61:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:57:2:57:46 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | | +| test.go:67:16:67:41 | index expression | test.go:66:2:66:31 | extract:0 ... := ... | test.go:67:16:67:41 | index expression | Cross-site scripting vulnerability due to $@. | test.go:66:2:66:31 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | | +| test.go:77:20:77:25 | buffer | test.go:72:2:72:31 | extract:0 ... := ... | test.go:77:20:77:25 | buffer | Cross-site scripting vulnerability due to $@. | test.go:72:2:72:31 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | | +| test.go:83:16:83:24 | selection of Value | test.go:82:2:82:32 | extract:0 ... := ... | test.go:83:16:83:24 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:82:2:82:32 | extract:0 ... := ... | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:89:16:89:31 | selection of Value | test.go:88:13:88:25 | call to Cookies | test.go:89:16:89:31 | selection of Value | Cross-site scripting vulnerability due to $@. | test.go:88:13:88:25 | call to Cookies | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:100:16:100:21 | selection of s | test.go:99:11:99:15 | &... [postupdate] | test.go:100:16:100:21 | selection of s | Cross-site scripting vulnerability due to $@. | test.go:99:11:99:15 | &... [postupdate] | user-provided value | test.go:0:0:0:0 | test.go | | | test.go:114:16:114:42 | type assertion | test.go:113:21:113:42 | call to Param | test.go:114:16:114:42 | type assertion | Cross-site scripting vulnerability due to $@. | test.go:113:21:113:42 | call to Param | user-provided value | test.go:0:0:0:0 | test.go | | @@ -25,23 +25,23 @@ edges | test.go:33:11:33:27 | call to QueryParams | test.go:34:16:34:20 | param | provenance | Src:MaD:11 | | test.go:39:10:39:26 | call to QueryString | test.go:40:16:40:19 | qstr | provenance | Src:MaD:12 | | test.go:45:9:45:34 | call to FormValue | test.go:46:16:46:18 | val | provenance | Src:MaD:6 | -| test.go:51:2:51:30 | ... := ...[0] | test.go:52:16:52:37 | index expression | provenance | Src:MaD:5 | -| test.go:57:2:57:46 | ... := ...[0] | test.go:58:13:58:22 | fileHeader | provenance | Src:MaD:4 | -| test.go:58:2:58:29 | ... := ...[0] | test.go:60:2:60:5 | file | provenance | | -| test.go:58:13:58:22 | fileHeader | test.go:58:2:58:29 | ... := ...[0] | provenance | MaD:17 | +| test.go:51:2:51:30 | extract:0 ... := ... | test.go:52:16:52:37 | index expression | provenance | Src:MaD:5 | +| test.go:57:2:57:46 | extract:0 ... := ... | test.go:58:13:58:22 | fileHeader | provenance | Src:MaD:4 | +| test.go:58:2:58:29 | extract:0 ... := ... | test.go:60:2:60:5 | file | provenance | | +| test.go:58:13:58:22 | fileHeader | test.go:58:2:58:29 | extract:0 ... := ... | provenance | MaD:17 | | test.go:60:2:60:5 | file | test.go:60:12:60:17 | buffer [postupdate] | provenance | MaD:15 | | test.go:60:2:60:5 | file | test.go:60:12:60:17 | buffer [postupdate] | provenance | MaD:16 | | test.go:60:2:60:5 | file | test.go:60:12:60:17 | buffer [postupdate] | provenance | MaD:18 | | test.go:60:12:60:17 | buffer [postupdate] | test.go:61:20:61:25 | buffer | provenance | | -| test.go:66:2:66:31 | ... := ...[0] | test.go:67:16:67:41 | index expression | provenance | Src:MaD:7 | -| test.go:72:2:72:31 | ... := ...[0] | test.go:74:13:74:22 | fileHeader | provenance | Src:MaD:7 | -| test.go:74:2:74:29 | ... := ...[0] | test.go:76:2:76:5 | file | provenance | | -| test.go:74:13:74:22 | fileHeader | test.go:74:2:74:29 | ... := ...[0] | provenance | MaD:17 | +| test.go:66:2:66:31 | extract:0 ... := ... | test.go:67:16:67:41 | index expression | provenance | Src:MaD:7 | +| test.go:72:2:72:31 | extract:0 ... := ... | test.go:74:13:74:22 | fileHeader | provenance | Src:MaD:7 | +| test.go:74:2:74:29 | extract:0 ... := ... | test.go:76:2:76:5 | file | provenance | | +| test.go:74:13:74:22 | fileHeader | test.go:74:2:74:29 | extract:0 ... := ... | provenance | MaD:17 | | test.go:76:2:76:5 | file | test.go:76:12:76:17 | buffer [postupdate] | provenance | MaD:15 | | test.go:76:2:76:5 | file | test.go:76:12:76:17 | buffer [postupdate] | provenance | MaD:16 | | test.go:76:2:76:5 | file | test.go:76:12:76:17 | buffer [postupdate] | provenance | MaD:18 | | test.go:76:12:76:17 | buffer [postupdate] | test.go:77:20:77:25 | buffer | provenance | | -| test.go:82:2:82:32 | ... := ...[0] | test.go:83:16:83:24 | selection of Value | provenance | Src:MaD:2 | +| test.go:82:2:82:32 | extract:0 ... := ... | test.go:83:16:83:24 | selection of Value | provenance | Src:MaD:2 | | test.go:88:13:88:25 | call to Cookies | test.go:89:16:89:31 | selection of Value | provenance | Src:MaD:3 | | test.go:99:11:99:15 | &... [postupdate] | test.go:100:16:100:21 | selection of s | provenance | Src:MaD:1 | | test.go:113:2:113:4 | ctx [postupdate] | test.go:114:16:114:18 | ctx | provenance | | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Gin/Gin.expected b/go/ql/test/library-tests/semmle/go/frameworks/Gin/Gin.expected index 071bf34cd7e0..46239a3c3394 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Gin/Gin.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Gin/Gin.expected @@ -7,18 +7,18 @@ | Gin.go:58:10:58:25 | call to Param | | Gin.go:62:10:62:34 | call to GetStringSlice | | Gin.go:66:10:66:29 | call to GetString | -| Gin.go:70:3:70:28 | ... := ...[0] | +| Gin.go:70:3:70:28 | extract:0 ... := ... | | Gin.go:74:10:74:23 | call to ClientIP | | Gin.go:78:10:78:26 | call to ContentType | -| Gin.go:82:3:82:29 | ... := ...[0] | -| Gin.go:86:3:86:36 | ... := ...[0] | -| Gin.go:90:3:90:31 | ... := ...[0] | -| Gin.go:94:3:94:39 | ... := ...[0] | -| Gin.go:98:3:98:34 | ... := ...[0] | +| Gin.go:82:3:82:29 | extract:0 ... := ... | +| Gin.go:86:3:86:36 | extract:0 ... := ... | +| Gin.go:90:3:90:31 | extract:0 ... := ... | +| Gin.go:94:3:94:39 | extract:0 ... := ... | +| Gin.go:98:3:98:34 | extract:0 ... := ... | | Gin.go:102:10:102:52 | call to DefaultPostForm | | Gin.go:106:10:106:49 | call to DefaultQuery | -| Gin.go:110:3:110:37 | ... := ...[0] | -| Gin.go:114:3:114:34 | ... := ...[0] | +| Gin.go:110:3:110:37 | extract:0 ... := ... | +| Gin.go:114:3:114:34 | extract:0 ... := ... | | Gin.go:118:10:118:32 | call to GetStringMap | | Gin.go:122:10:122:38 | call to GetStringMapString | | Gin.go:126:10:126:43 | call to GetStringMapStringSlice | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Gorestful/gorestful.expected b/go/ql/test/library-tests/semmle/go/frameworks/Gorestful/gorestful.expected index 0af67462f7c2..e375fe75be8b 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Gorestful/gorestful.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Gorestful/gorestful.expected @@ -6,11 +6,11 @@ models | 5 | Source: github.com/emicklei/go-restful; Request; true; ReadEntity; ; ; Argument[0]; remote; manual | edges | gorestful.go:15:15:15:44 | call to QueryParameters | gorestful.go:15:15:15:47 | index expression | provenance | Src:MaD:4 Sink:MaD:1 | -| gorestful.go:17:2:17:39 | ... := ...[0] | gorestful.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 | +| gorestful.go:17:2:17:39 | extract:0 ... := ... | gorestful.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 | | gorestful.go:21:15:21:38 | call to PathParameters | gorestful.go:21:15:21:45 | index expression | provenance | Src:MaD:3 Sink:MaD:1 | | gorestful.go:23:21:23:24 | &... [postupdate] | gorestful.go:24:15:24:21 | selection of cmd | provenance | Src:MaD:5 Sink:MaD:1 | | gorestful_v2.go:15:15:15:44 | call to QueryParameters | gorestful_v2.go:15:15:15:47 | index expression | provenance | Src:MaD:4 Sink:MaD:1 | -| gorestful_v2.go:17:2:17:39 | ... := ...[0] | gorestful_v2.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 | +| gorestful_v2.go:17:2:17:39 | extract:0 ... := ... | gorestful_v2.go:18:15:18:17 | val | provenance | Src:MaD:2 Sink:MaD:1 | | gorestful_v2.go:21:15:21:38 | call to PathParameters | gorestful_v2.go:21:15:21:45 | index expression | provenance | Src:MaD:3 Sink:MaD:1 | | gorestful_v2.go:23:21:23:24 | &... [postupdate] | gorestful_v2.go:24:15:24:21 | selection of cmd | provenance | Src:MaD:5 Sink:MaD:1 | nodes @@ -41,14 +41,14 @@ invalidModelRow #select | gorestful.go:15:15:15:47 | index expression | gorestful.go:15:15:15:44 | call to QueryParameters | gorestful.go:15:15:15:47 | index expression | This command depends on $@. | gorestful.go:15:15:15:44 | call to QueryParameters | a user-provided value | | gorestful.go:16:15:16:43 | call to QueryParameter | gorestful.go:16:15:16:43 | call to QueryParameter | gorestful.go:16:15:16:43 | call to QueryParameter | This command depends on $@. | gorestful.go:16:15:16:43 | call to QueryParameter | a user-provided value | -| gorestful.go:18:15:18:17 | val | gorestful.go:17:2:17:39 | ... := ...[0] | gorestful.go:18:15:18:17 | val | This command depends on $@. | gorestful.go:17:2:17:39 | ... := ...[0] | a user-provided value | +| gorestful.go:18:15:18:17 | val | gorestful.go:17:2:17:39 | extract:0 ... := ... | gorestful.go:18:15:18:17 | val | This command depends on $@. | gorestful.go:17:2:17:39 | extract:0 ... := ... | a user-provided value | | gorestful.go:19:15:19:44 | call to HeaderParameter | gorestful.go:19:15:19:44 | call to HeaderParameter | gorestful.go:19:15:19:44 | call to HeaderParameter | This command depends on $@. | gorestful.go:19:15:19:44 | call to HeaderParameter | a user-provided value | | gorestful.go:20:15:20:42 | call to PathParameter | gorestful.go:20:15:20:42 | call to PathParameter | gorestful.go:20:15:20:42 | call to PathParameter | This command depends on $@. | gorestful.go:20:15:20:42 | call to PathParameter | a user-provided value | | gorestful.go:21:15:21:45 | index expression | gorestful.go:21:15:21:38 | call to PathParameters | gorestful.go:21:15:21:45 | index expression | This command depends on $@. | gorestful.go:21:15:21:38 | call to PathParameters | a user-provided value | | gorestful.go:24:15:24:21 | selection of cmd | gorestful.go:23:21:23:24 | &... [postupdate] | gorestful.go:24:15:24:21 | selection of cmd | This command depends on $@. | gorestful.go:23:21:23:24 | &... [postupdate] | a user-provided value | | gorestful_v2.go:15:15:15:47 | index expression | gorestful_v2.go:15:15:15:44 | call to QueryParameters | gorestful_v2.go:15:15:15:47 | index expression | This command depends on $@. | gorestful_v2.go:15:15:15:44 | call to QueryParameters | a user-provided value | | gorestful_v2.go:16:15:16:43 | call to QueryParameter | gorestful_v2.go:16:15:16:43 | call to QueryParameter | gorestful_v2.go:16:15:16:43 | call to QueryParameter | This command depends on $@. | gorestful_v2.go:16:15:16:43 | call to QueryParameter | a user-provided value | -| gorestful_v2.go:18:15:18:17 | val | gorestful_v2.go:17:2:17:39 | ... := ...[0] | gorestful_v2.go:18:15:18:17 | val | This command depends on $@. | gorestful_v2.go:17:2:17:39 | ... := ...[0] | a user-provided value | +| gorestful_v2.go:18:15:18:17 | val | gorestful_v2.go:17:2:17:39 | extract:0 ... := ... | gorestful_v2.go:18:15:18:17 | val | This command depends on $@. | gorestful_v2.go:17:2:17:39 | extract:0 ... := ... | a user-provided value | | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | This command depends on $@. | gorestful_v2.go:19:15:19:44 | call to HeaderParameter | a user-provided value | | gorestful_v2.go:20:15:20:42 | call to PathParameter | gorestful_v2.go:20:15:20:42 | call to PathParameter | gorestful_v2.go:20:15:20:42 | call to PathParameter | This command depends on $@. | gorestful_v2.go:20:15:20:42 | call to PathParameter | a user-provided value | | gorestful_v2.go:21:15:21:45 | index expression | gorestful_v2.go:21:15:21:38 | call to PathParameters | gorestful_v2.go:21:15:21:45 | index expression | This command depends on $@. | gorestful_v2.go:21:15:21:38 | call to PathParameters | a user-provided value | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Revel/OpenRedirect.expected b/go/ql/test/library-tests/semmle/go/frameworks/Revel/OpenRedirect.expected index d3f52f4f9c67..453d61a3e383 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Revel/OpenRedirect.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Revel/OpenRedirect.expected @@ -1,11 +1,11 @@ #select | EndToEnd.go:94:20:94:49 | call to Get | EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:49 | call to Get | This path to an untrusted URL redirection depends on a $@. | EndToEnd.go:94:20:94:27 | selection of Params | user-provided value | edges -| EndToEnd.go:94:20:94:27 | implicit dereference | EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | provenance | Config | -| EndToEnd.go:94:20:94:27 | implicit dereference | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Config | -| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:27 | implicit dereference | provenance | Src:MaD:2 Config | +| EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | provenance | Config | +| EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Config | +| EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | provenance | Src:MaD:2 Config | | EndToEnd.go:94:20:94:27 | selection of Params | EndToEnd.go:94:20:94:32 | selection of Form | provenance | Src:MaD:2 Config | -| EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | EndToEnd.go:94:20:94:27 | implicit dereference | provenance | Config | +| EndToEnd.go:94:20:94:27 | selection of Params [postupdate] | EndToEnd.go:94:20:94:27 | implicit-deref selection of Params | provenance | Config | | EndToEnd.go:94:20:94:32 | selection of Form | EndToEnd.go:94:20:94:49 | call to Get | provenance | Config Sink:MaD:1 | models | 1 | Sink: group:revel; Controller; true; Redirect; ; ; Argument[0]; url-redirection; manual | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected b/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected index cfbbc771f777..98381fa1ed1c 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/TaintSteps/TaintStep.expected @@ -1,12 +1,12 @@ invalidModelRow #select -| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | ... := ...[0] | -| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | ... := ...[1] | -| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | ... := ...[0] | -| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | ... := ...[1] | -| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[0] | -| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | ... := ...[1] | -| crypto.go:11:42:11:51 | ciphertext | crypto.go:11:2:11:57 | ... := ...[0] | +| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | extract:0 ... := ... | +| crypto.go:9:14:9:31 | call to NewCipher | crypto.go:9:2:9:31 | extract:1 ... := ... | +| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | extract:0 ... := ... | +| crypto.go:10:15:10:34 | call to NewGCM | crypto.go:10:2:10:34 | extract:1 ... := ... | +| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | extract:0 ... := ... | +| crypto.go:11:18:11:57 | call to Open | crypto.go:11:2:11:57 | extract:1 ... := ... | +| crypto.go:11:42:11:51 | ciphertext | crypto.go:11:2:11:57 | extract:0 ... := ... | | io.go:14:31:14:43 | "some string" | io.go:14:13:14:44 | call to NewReader | | io.go:16:23:16:27 | &... | io.go:16:24:16:27 | buf1 [postupdate] | | io.go:16:23:16:27 | &... [postupdate] | io.go:16:24:16:27 | buf1 [postupdate] | @@ -31,9 +31,9 @@ invalidModelRow | io.go:33:20:33:23 | buf1 | io.go:33:19:33:23 | &... | | io.go:33:20:33:23 | buf1 [postupdate] | io.go:33:19:33:23 | &... | | io.go:35:16:35:21 | reader | io.go:35:12:35:13 | w2 [postupdate] | -| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | ... := ...[0] | -| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | ... := ...[1] | -| io.go:40:14:40:14 | w [postupdate] | io.go:39:3:39:19 | ... := ...[0] | +| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | extract:0 ... := ... | +| io.go:39:11:39:19 | call to Pipe | io.go:39:3:39:19 | extract:1 ... := ... | +| io.go:40:14:40:14 | w [postupdate] | io.go:39:3:39:19 | extract:0 ... := ... | | io.go:40:17:40:31 | "some string\\n" | io.go:40:14:40:14 | w [postupdate] | | io.go:43:16:43:16 | r | io.go:43:3:43:5 | buf [postupdate] | | io.go:44:13:44:15 | buf | io.go:44:13:44:24 | call to String | @@ -74,35 +74,35 @@ invalidModelRow | io.go:101:26:101:38 | "some string" | io.go:101:8:101:39 | call to NewReader | | io.go:102:3:102:3 | r | io.go:102:13:102:21 | selection of Stdout [postupdate] | | io.go:108:30:108:42 | "some string" | io.go:108:12:108:43 | call to NewReader | -| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | ... := ...[0] | -| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | ... := ...[1] | -| io.go:109:27:109:32 | reader | io.go:109:2:109:33 | ... := ...[0] | +| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | extract:0 ... := ... | +| io.go:109:12:109:33 | call to ReadAll | io.go:109:2:109:33 | extract:1 ... := ... | +| io.go:109:27:109:32 | reader | io.go:109:2:109:33 | extract:0 ... := ... | | io.go:110:18:110:20 | buf | io.go:110:2:110:10 | selection of Stdout [postupdate] | -| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | ... := ...[0] | -| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | ... := ...[1] | -| main.go:11:25:11:25 | v | main.go:11:2:11:26 | ... := ...[0] | -| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | ... := ...[0] | -| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | ... := ...[1] | -| main.go:13:33:13:33 | v | main.go:13:2:13:52 | ... := ...[0] | -| main.go:13:36:13:45 | "/*JSON*/" | main.go:13:2:13:52 | ... := ...[0] | -| main.go:13:48:13:51 | " " | main.go:13:2:13:52 | ... := ...[0] | +| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | extract:0 ... := ... | +| main.go:11:12:11:26 | call to Marshal | main.go:11:2:11:26 | extract:1 ... := ... | +| main.go:11:25:11:25 | v | main.go:11:2:11:26 | extract:0 ... := ... | +| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | extract:0 ... := ... | +| main.go:13:14:13:52 | call to MarshalIndent | main.go:13:2:13:52 | extract:1 ... := ... | +| main.go:13:33:13:33 | v | main.go:13:2:13:52 | extract:0 ... := ... | +| main.go:13:36:13:45 | "/*JSON*/" | main.go:13:2:13:52 | extract:0 ... := ... | +| main.go:13:48:13:51 | " " | main.go:13:2:13:52 | extract:0 ... := ... | | main.go:14:25:14:25 | b | main.go:14:9:14:41 | slice literal | | main.go:14:28:14:30 | err | main.go:14:9:14:41 | slice literal | | main.go:14:33:14:34 | b2 | main.go:14:9:14:41 | slice literal | | main.go:14:37:14:40 | err2 | main.go:14:9:14:41 | slice literal | -| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | ... := ...[0] | -| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | ... := ...[1] | -| main.go:19:35:19:41 | encoded | main.go:19:2:19:42 | ... := ...[0] | +| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | extract:0 ... := ... | +| main.go:19:18:19:42 | call to DecodeString | main.go:19:2:19:42 | extract:1 ... := ... | +| main.go:19:35:19:41 | encoded | main.go:19:2:19:42 | extract:0 ... := ... | | main.go:23:25:23:31 | decoded | main.go:23:9:23:48 | slice literal | | main.go:23:34:23:36 | err | main.go:23:9:23:48 | slice literal | | main.go:23:39:23:47 | reEncoded | main.go:23:9:23:48 | slice literal | -| main.go:28:2:28:4 | implicit dereference | main.go:28:2:28:4 | req [postupdate] | -| main.go:28:2:28:4 | implicit dereference | main.go:28:2:28:9 | selection of Body | -| main.go:28:2:28:4 | req | main.go:28:2:28:4 | implicit dereference | -| main.go:28:2:28:4 | req [postupdate] | main.go:28:2:28:4 | implicit dereference | +| main.go:28:2:28:4 | implicit-deref req | main.go:28:2:28:4 | req [postupdate] | +| main.go:28:2:28:4 | implicit-deref req | main.go:28:2:28:9 | selection of Body | +| main.go:28:2:28:4 | req | main.go:28:2:28:4 | implicit-deref req | +| main.go:28:2:28:4 | req [postupdate] | main.go:28:2:28:4 | implicit-deref req | | main.go:28:2:28:9 | selection of Body | main.go:28:16:28:16 | b [postupdate] | -| main.go:34:2:34:4 | implicit dereference | main.go:34:2:34:4 | req [postupdate] | -| main.go:34:2:34:4 | implicit dereference | main.go:34:2:34:9 | selection of Body | -| main.go:34:2:34:4 | req | main.go:34:2:34:4 | implicit dereference | -| main.go:34:2:34:4 | req [postupdate] | main.go:34:2:34:4 | implicit dereference | +| main.go:34:2:34:4 | implicit-deref req | main.go:34:2:34:4 | req [postupdate] | +| main.go:34:2:34:4 | implicit-deref req | main.go:34:2:34:9 | selection of Body | +| main.go:34:2:34:4 | req | main.go:34:2:34:4 | implicit-deref req | +| main.go:34:2:34:4 | req [postupdate] | main.go:34:2:34:4 | implicit-deref req | | main.go:34:2:34:9 | selection of Body | main.go:34:16:34:16 | b [postupdate] | diff --git a/go/ql/test/library-tests/semmle/go/frameworks/Twirp/RequestForgery.expected b/go/ql/test/library-tests/semmle/go/frameworks/Twirp/RequestForgery.expected index 7b1fa1a31215..df1f5ec39b80 100644 --- a/go/ql/test/library-tests/semmle/go/frameworks/Twirp/RequestForgery.expected +++ b/go/ql/test/library-tests/semmle/go/frameworks/Twirp/RequestForgery.expected @@ -4,8 +4,8 @@ edges | client/main.go:16:35:16:78 | &... | server/main.go:19:56:19:61 | definition of params | provenance | | | client/main.go:16:35:16:78 | &... [postupdate] | client/main.go:16:35:16:78 | &... | provenance | | -| rpc/notes/service.twirp.go:538:2:538:33 | ... := ...[0] | rpc/notes/service.twirp.go:544:27:544:29 | buf | provenance | | -| rpc/notes/service.twirp.go:538:25:538:32 | selection of Body | rpc/notes/service.twirp.go:538:2:538:33 | ... := ...[0] | provenance | Src:MaD:1 MaD:3 | +| rpc/notes/service.twirp.go:538:2:538:33 | extract:0 ... := ... | rpc/notes/service.twirp.go:544:27:544:29 | buf | provenance | | +| rpc/notes/service.twirp.go:538:25:538:32 | selection of Body | rpc/notes/service.twirp.go:538:2:538:33 | extract:0 ... := ... | provenance | Src:MaD:1 MaD:3 | | rpc/notes/service.twirp.go:544:27:544:29 | buf | rpc/notes/service.twirp.go:544:32:544:41 | reqContent [postupdate] | provenance | MaD:2 | | rpc/notes/service.twirp.go:544:32:544:41 | reqContent [postupdate] | rpc/notes/service.twirp.go:574:2:577:2 | capture variable reqContent | provenance | | | rpc/notes/service.twirp.go:574:2:577:2 | capture variable reqContent | rpc/notes/service.twirp.go:576:35:576:44 | reqContent | provenance | | diff --git a/go/ql/test/query-tests/InconsistentCode/UnhandledCloseWritableHandle/UnhandledCloseWritableHandle.expected b/go/ql/test/query-tests/InconsistentCode/UnhandledCloseWritableHandle/UnhandledCloseWritableHandle.expected index 41034c557961..5b6df847e0b6 100644 --- a/go/ql/test/query-tests/InconsistentCode/UnhandledCloseWritableHandle/UnhandledCloseWritableHandle.expected +++ b/go/ql/test/query-tests/InconsistentCode/UnhandledCloseWritableHandle/UnhandledCloseWritableHandle.expected @@ -1,30 +1,30 @@ #select -| tests.go:10:8:10:8 | f | tests.go:32:5:32:78 | ... := ...[0] | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile | -| tests.go:10:8:10:8 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile | -| tests.go:15:3:15:3 | f | tests.go:32:5:32:78 | ... := ...[0] | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile | -| tests.go:15:3:15:3 | f | tests.go:46:5:46:76 | ... := ...[0] | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile | -| tests.go:57:3:57:3 | f | tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:55:15:55:78 | call to OpenFile | call to OpenFile | -| tests.go:69:3:69:3 | f | tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:67:15:67:76 | call to OpenFile | call to OpenFile | -| tests.go:111:9:111:9 | f | tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:109:15:109:78 | call to OpenFile | call to OpenFile | -| tests.go:130:3:130:3 | f | tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:126:15:126:78 | call to OpenFile | call to OpenFile | -| tests.go:151:8:151:8 | f | tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:147:12:147:74 | call to OpenFile | call to OpenFile | +| tests.go:10:8:10:8 | f | tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile | +| tests.go:10:8:10:8 | f | tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:10:8:10:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile | +| tests.go:15:3:15:3 | f | tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:32:15:32:78 | call to OpenFile | call to OpenFile | +| tests.go:15:3:15:3 | f | tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:15:3:15:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:46:15:46:76 | call to OpenFile | call to OpenFile | +| tests.go:57:3:57:3 | f | tests.go:55:5:55:78 | extract:0 ... := ... | tests.go:57:3:57:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:55:15:55:78 | call to OpenFile | call to OpenFile | +| tests.go:69:3:69:3 | f | tests.go:67:5:67:76 | extract:0 ... := ... | tests.go:69:3:69:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:67:15:67:76 | call to OpenFile | call to OpenFile | +| tests.go:111:9:111:9 | f | tests.go:109:5:109:78 | extract:0 ... := ... | tests.go:111:9:111:9 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:109:15:109:78 | call to OpenFile | call to OpenFile | +| tests.go:130:3:130:3 | f | tests.go:126:5:126:78 | extract:0 ... := ... | tests.go:130:3:130:3 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:126:15:126:78 | call to OpenFile | call to OpenFile | +| tests.go:151:8:151:8 | f | tests.go:147:2:147:74 | extract:0 ... := ... | tests.go:151:8:151:8 | f | File handle may be writable as a result of data flow from a $@ and closing it may result in data loss upon failure, which is not handled explicitly. | tests.go:147:12:147:74 | call to OpenFile | call to OpenFile | edges | tests.go:9:24:9:24 | definition of f | tests.go:10:8:10:8 | f | provenance | | | tests.go:13:32:13:32 | definition of f | tests.go:14:13:16:2 | capture variable f | provenance | | | tests.go:14:13:16:2 | capture variable f | tests.go:15:3:15:3 | f | provenance | | -| tests.go:32:5:32:78 | ... := ...[0] | tests.go:33:21:33:21 | f | provenance | Src:MaD:1 | -| tests.go:32:5:32:78 | ... := ...[0] | tests.go:34:29:34:29 | f | provenance | Src:MaD:1 | +| tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:33:21:33:21 | f | provenance | Src:MaD:1 | +| tests.go:32:5:32:78 | extract:0 ... := ... | tests.go:34:29:34:29 | f | provenance | Src:MaD:1 | | tests.go:33:21:33:21 | f | tests.go:9:24:9:24 | definition of f | provenance | | | tests.go:34:29:34:29 | f | tests.go:13:32:13:32 | definition of f | provenance | | -| tests.go:46:5:46:76 | ... := ...[0] | tests.go:47:21:47:21 | f | provenance | Src:MaD:1 | -| tests.go:46:5:46:76 | ... := ...[0] | tests.go:48:29:48:29 | f | provenance | Src:MaD:1 | +| tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:47:21:47:21 | f | provenance | Src:MaD:1 | +| tests.go:46:5:46:76 | extract:0 ... := ... | tests.go:48:29:48:29 | f | provenance | Src:MaD:1 | | tests.go:47:21:47:21 | f | tests.go:9:24:9:24 | definition of f | provenance | | | tests.go:48:29:48:29 | f | tests.go:13:32:13:32 | definition of f | provenance | | -| tests.go:55:5:55:78 | ... := ...[0] | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 | -| tests.go:67:5:67:76 | ... := ...[0] | tests.go:69:3:69:3 | f | provenance | Src:MaD:1 | -| tests.go:109:5:109:78 | ... := ...[0] | tests.go:111:9:111:9 | f | provenance | Src:MaD:1 | -| tests.go:126:5:126:78 | ... := ...[0] | tests.go:130:3:130:3 | f | provenance | Src:MaD:1 | -| tests.go:147:2:147:74 | ... := ...[0] | tests.go:151:8:151:8 | f | provenance | Src:MaD:1 | +| tests.go:55:5:55:78 | extract:0 ... := ... | tests.go:57:3:57:3 | f | provenance | Src:MaD:1 | +| tests.go:67:5:67:76 | extract:0 ... := ... | tests.go:69:3:69:3 | f | provenance | Src:MaD:1 | +| tests.go:109:5:109:78 | extract:0 ... := ... | tests.go:111:9:111:9 | f | provenance | Src:MaD:1 | +| tests.go:126:5:126:78 | extract:0 ... := ... | tests.go:130:3:130:3 | f | provenance | Src:MaD:1 | +| tests.go:147:2:147:74 | extract:0 ... := ... | tests.go:151:8:151:8 | f | provenance | Src:MaD:1 | models | 1 | Source: os; ; false; OpenFile; ; ; ReturnValue[0]; file; manual | nodes diff --git a/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected b/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected index 7cb981667da2..b244c32771ae 100644 --- a/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected +++ b/go/ql/test/query-tests/Security/CWE-022/ZipSlip.expected @@ -1,21 +1,21 @@ #select -| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation | -| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation | -| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation | -| tst.go:23:2:43:2 | range statement[1] | tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation | +| UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | Unsanitized archive entry, which may contain '..', is used in a $@. | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | file system operation | +| ZipSlip.go:11:2:15:2 | extract:1 range statement | ZipSlip.go:11:2:15:2 | extract:1 range statement | ZipSlip.go:14:20:14:20 | p | Unsanitized archive entry, which may contain '..', is used in a $@. | ZipSlip.go:14:20:14:20 | p | file system operation | +| tarslip.go:15:2:15:30 | extract:0 ... := ... | tarslip.go:15:2:15:30 | extract:0 ... := ... | tarslip.go:16:14:16:34 | call to Dir | Unsanitized archive entry, which may contain '..', is used in a $@. | tarslip.go:16:14:16:34 | call to Dir | file system operation | +| tst.go:23:2:43:2 | extract:1 range statement | tst.go:23:2:43:2 | extract:1 range statement | tst.go:29:20:29:23 | path | Unsanitized archive entry, which may contain '..', is used in a $@. | tst.go:29:20:29:23 | path | file system operation | edges | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | provenance | | | UnsafeUnzipSymlinkGood.go:61:53:61:61 | candidate | UnsafeUnzipSymlinkGood.go:61:31:61:62 | call to Join | provenance | FunctionModel Sink:MaD:3 | -| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | provenance | | -| UnsafeUnzipSymlinkGood.go:72:3:72:25 | ... := ...[0] | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | provenance | | +| UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | provenance | | +| UnsafeUnzipSymlinkGood.go:72:3:72:25 | extract:0 ... := ... | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | provenance | | | UnsafeUnzipSymlinkGood.go:76:24:76:38 | selection of Linkname | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | provenance | | | UnsafeUnzipSymlinkGood.go:76:70:76:80 | selection of Name | UnsafeUnzipSymlinkGood.go:52:24:52:32 | definition of candidate | provenance | | -| ZipSlip.go:11:2:15:2 | range statement[1] | ZipSlip.go:12:24:12:29 | selection of Name | provenance | | -| ZipSlip.go:12:3:12:30 | ... := ...[0] | ZipSlip.go:14:20:14:20 | p | provenance | Sink:MaD:1 | -| ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | ... := ...[0] | provenance | MaD:4 | -| tarslip.go:15:2:15:30 | ... := ...[0] | tarslip.go:16:23:16:33 | selection of Name | provenance | | +| ZipSlip.go:11:2:15:2 | extract:1 range statement | ZipSlip.go:12:24:12:29 | selection of Name | provenance | | +| ZipSlip.go:12:3:12:30 | extract:0 ... := ... | ZipSlip.go:14:20:14:20 | p | provenance | Sink:MaD:1 | +| ZipSlip.go:12:24:12:29 | selection of Name | ZipSlip.go:12:3:12:30 | extract:0 ... := ... | provenance | MaD:4 | +| tarslip.go:15:2:15:30 | extract:0 ... := ... | tarslip.go:16:23:16:33 | selection of Name | provenance | | | tarslip.go:16:23:16:33 | selection of Name | tarslip.go:16:14:16:34 | call to Dir | provenance | MaD:5 Sink:MaD:2 | -| tst.go:23:2:43:2 | range statement[1] | tst.go:29:20:29:23 | path | provenance | Sink:MaD:1 | +| tst.go:23:2:43:2 | extract:1 range statement | tst.go:29:20:29:23 | path | provenance | Sink:MaD:1 | models | 1 | Sink: io/ioutil; ; false; WriteFile; ; ; Argument[0]; path-injection; manual | | 2 | Sink: os; ; false; MkdirAll; ; ; Argument[0]; path-injection; manual | diff --git a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected index e8c6848b5695..32f802351a09 100644 --- a/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected +++ b/go/ql/test/query-tests/Security/CWE-089/SqlInjection.expected @@ -31,16 +31,16 @@ edges | SqlInjection.go:11:3:11:17 | call to Query | SqlInjection.go:11:3:11:29 | index expression | provenance | | | SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | []type{args} [array] | provenance | | | SqlInjection.go:11:3:11:29 | index expression | SqlInjection.go:10:7:11:30 | call to Sprintf | provenance | FunctionModel | -| issue48.go:17:2:17:33 | ... := ...[0] | issue48.go:18:17:18:17 | b | provenance | | -| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | ... := ...[0] | provenance | Src:MaD:17 MaD:24 | +| issue48.go:17:2:17:33 | extract:0 ... := ... | issue48.go:18:17:18:17 | b | provenance | | +| issue48.go:17:25:17:32 | selection of Body | issue48.go:17:2:17:33 | extract:0 ... := ... | provenance | Src:MaD:17 MaD:24 | | issue48.go:18:17:18:17 | b | issue48.go:18:20:18:39 | &... [postupdate] | provenance | MaD:22 | | issue48.go:18:20:18:39 | &... [postupdate] | issue48.go:21:3:21:33 | index expression | provenance | | | issue48.go:20:8:21:34 | []type{args} [array] | issue48.go:20:8:21:34 | call to Sprintf | provenance | MaD:23 | | issue48.go:20:8:21:34 | call to Sprintf | issue48.go:22:11:22:12 | q3 | provenance | Sink:MaD:1 | | issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | []type{args} [array] | provenance | | | issue48.go:21:3:21:33 | index expression | issue48.go:20:8:21:34 | call to Sprintf | provenance | FunctionModel | -| issue48.go:27:2:27:34 | ... := ...[0] | issue48.go:28:17:28:18 | b2 | provenance | | -| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | ... := ...[0] | provenance | Src:MaD:17 MaD:24 | +| issue48.go:27:2:27:34 | extract:0 ... := ... | issue48.go:28:17:28:18 | b2 | provenance | | +| issue48.go:27:26:27:33 | selection of Body | issue48.go:27:2:27:34 | extract:0 ... := ... | provenance | Src:MaD:17 MaD:24 | | issue48.go:28:17:28:18 | b2 | issue48.go:28:21:28:41 | &... [postupdate] | provenance | MaD:22 | | issue48.go:28:21:28:41 | &... [postupdate] | issue48.go:31:3:31:31 | selection of Category | provenance | | | issue48.go:30:8:31:32 | []type{args} [array] | issue48.go:30:8:31:32 | call to Sprintf | provenance | MaD:23 | @@ -72,19 +72,19 @@ edges | main.go:30:13:30:39 | index expression | main.go:28:18:31:2 | struct literal [Category] | provenance | | | main.go:33:7:34:23 | []type{args} [array] | main.go:33:7:34:23 | call to Sprintf | provenance | MaD:23 | | main.go:33:7:34:23 | call to Sprintf | main.go:35:11:35:11 | q | provenance | Sink:MaD:1 | -| main.go:34:3:34:13 | RequestData [pointer, Category] | main.go:34:3:34:13 | implicit dereference [Category] | provenance | | -| main.go:34:3:34:13 | implicit dereference [Category] | main.go:34:3:34:22 | selection of Category | provenance | | +| main.go:34:3:34:13 | RequestData [pointer, Category] | main.go:34:3:34:13 | implicit-deref RequestData [Category] | provenance | | +| main.go:34:3:34:13 | implicit-deref RequestData [Category] | main.go:34:3:34:22 | selection of Category | provenance | | | main.go:34:3:34:22 | selection of Category | main.go:33:7:34:23 | []type{args} [array] | provenance | | | main.go:34:3:34:22 | selection of Category | main.go:33:7:34:23 | call to Sprintf | provenance | FunctionModel | | main.go:40:2:40:12 | RequestData [postupdate] [pointer, Category] | main.go:43:3:43:13 | RequestData [pointer, Category] | provenance | | -| main.go:40:2:40:12 | implicit dereference [postupdate] [Category] | main.go:40:2:40:12 | RequestData [postupdate] [pointer, Category] | provenance | | +| main.go:40:2:40:12 | implicit-deref RequestData [postupdate] [Category] | main.go:40:2:40:12 | RequestData [postupdate] [pointer, Category] | provenance | | | main.go:40:25:40:31 | selection of URL | main.go:40:25:40:39 | call to Query | provenance | Src:MaD:21 MaD:26 | | main.go:40:25:40:39 | call to Query | main.go:40:25:40:51 | index expression | provenance | | -| main.go:40:25:40:51 | index expression | main.go:40:2:40:12 | implicit dereference [postupdate] [Category] | provenance | | +| main.go:40:25:40:51 | index expression | main.go:40:2:40:12 | implicit-deref RequestData [postupdate] [Category] | provenance | | | main.go:42:7:43:23 | []type{args} [array] | main.go:42:7:43:23 | call to Sprintf | provenance | MaD:23 | | main.go:42:7:43:23 | call to Sprintf | main.go:44:11:44:11 | q | provenance | Sink:MaD:1 | -| main.go:43:3:43:13 | RequestData [pointer, Category] | main.go:43:3:43:13 | implicit dereference [Category] | provenance | | -| main.go:43:3:43:13 | implicit dereference [Category] | main.go:43:3:43:22 | selection of Category | provenance | | +| main.go:43:3:43:13 | RequestData [pointer, Category] | main.go:43:3:43:13 | implicit-deref RequestData [Category] | provenance | | +| main.go:43:3:43:13 | implicit-deref RequestData [Category] | main.go:43:3:43:22 | selection of Category | provenance | | | main.go:43:3:43:22 | selection of Category | main.go:42:7:43:23 | []type{args} [array] | provenance | | | main.go:43:3:43:22 | selection of Category | main.go:42:7:43:23 | call to Sprintf | provenance | FunctionModel | | main.go:49:3:49:14 | star expression [postupdate] [Category] | main.go:49:4:49:14 | RequestData [postupdate] [pointer, Category] | provenance | | diff --git a/go/ql/test/query-tests/Security/CWE-089/StringBreak.expected b/go/ql/test/query-tests/Security/CWE-089/StringBreak.expected index 5deab249337e..206c63032d8c 100644 --- a/go/ql/test/query-tests/Security/CWE-089/StringBreak.expected +++ b/go/ql/test/query-tests/Security/CWE-089/StringBreak.expected @@ -1,13 +1,13 @@ #select -| StringBreak.go:14:47:14:57 | versionJSON | StringBreak.go:10:2:10:40 | ... := ...[0] | StringBreak.go:14:47:14:57 | versionJSON | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreak.go:10:2:10:40 | ... := ...[0] | JSON value | -| StringBreakMismatched.go:17:26:17:32 | escaped | StringBreakMismatched.go:12:2:12:40 | ... := ...[0] | StringBreakMismatched.go:17:26:17:32 | escaped | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:12:2:12:40 | ... := ...[0] | JSON value | -| StringBreakMismatched.go:29:27:29:33 | escaped | StringBreakMismatched.go:24:2:24:40 | ... := ...[0] | StringBreakMismatched.go:29:27:29:33 | escaped | If this $@ contains a double quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:24:2:24:40 | ... := ...[0] | JSON value | +| StringBreak.go:14:47:14:57 | versionJSON | StringBreak.go:10:2:10:40 | extract:0 ... := ... | StringBreak.go:14:47:14:57 | versionJSON | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreak.go:10:2:10:40 | extract:0 ... := ... | JSON value | +| StringBreakMismatched.go:17:26:17:32 | escaped | StringBreakMismatched.go:12:2:12:40 | extract:0 ... := ... | StringBreakMismatched.go:17:26:17:32 | escaped | If this $@ contains a single quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:12:2:12:40 | extract:0 ... := ... | JSON value | +| StringBreakMismatched.go:29:27:29:33 | escaped | StringBreakMismatched.go:24:2:24:40 | extract:0 ... := ... | StringBreakMismatched.go:29:27:29:33 | escaped | If this $@ contains a double quote, it could break out of the enclosing quotes. | StringBreakMismatched.go:24:2:24:40 | extract:0 ... := ... | JSON value | edges -| StringBreak.go:10:2:10:40 | ... := ...[0] | StringBreak.go:14:47:14:57 | versionJSON | provenance | | -| StringBreakMismatched.go:12:2:12:40 | ... := ...[0] | StringBreakMismatched.go:13:29:13:47 | type conversion | provenance | | +| StringBreak.go:10:2:10:40 | extract:0 ... := ... | StringBreak.go:14:47:14:57 | versionJSON | provenance | | +| StringBreakMismatched.go:12:2:12:40 | extract:0 ... := ... | StringBreakMismatched.go:13:29:13:47 | type conversion | provenance | | | StringBreakMismatched.go:13:13:13:62 | call to Replace | StringBreakMismatched.go:17:26:17:32 | escaped | provenance | | | StringBreakMismatched.go:13:29:13:47 | type conversion | StringBreakMismatched.go:13:13:13:62 | call to Replace | provenance | MaD:1 | -| StringBreakMismatched.go:24:2:24:40 | ... := ...[0] | StringBreakMismatched.go:25:29:25:47 | type conversion | provenance | | +| StringBreakMismatched.go:24:2:24:40 | extract:0 ... := ... | StringBreakMismatched.go:25:29:25:47 | type conversion | provenance | | | StringBreakMismatched.go:25:13:25:61 | call to Replace | StringBreakMismatched.go:29:27:29:33 | escaped | provenance | | | StringBreakMismatched.go:25:29:25:47 | type conversion | StringBreakMismatched.go:25:13:25:61 | call to Replace | provenance | MaD:1 | models diff --git a/go/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected b/go/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected index 66392b22752d..eaee90c44c9c 100644 --- a/go/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected +++ b/go/ql/test/query-tests/Security/CWE-312/CleartextLogging.expected @@ -55,10 +55,10 @@ | passwords.go:131:14:131:21 | selection of y | passwords.go:126:13:126:25 | call to getPassword | passwords.go:131:14:131:21 | selection of y | $@ flows to a logging call. | passwords.go:126:13:126:25 | call to getPassword | Sensitive data returned by a call to getPassword | | protobuf.go:14:14:14:35 | call to GetDescription | protobuf.go:9:2:9:9 | definition of password | protobuf.go:14:14:14:35 | call to GetDescription | $@ flows to a logging call. | protobuf.go:9:2:9:9 | definition of password | Sensitive data returned by an access to password | edges -| klog.go:21:3:26:3 | range statement[1] | klog.go:22:27:22:33 | headers | provenance | | -| klog.go:21:30:21:37 | selection of Header | klog.go:21:3:26:3 | range statement[1] | provenance | Src:MaD:11 Config | -| klog.go:22:4:25:4 | range statement[1] | klog.go:23:15:23:20 | header | provenance | | -| klog.go:22:27:22:33 | headers | klog.go:22:4:25:4 | range statement[1] | provenance | Config | +| klog.go:21:3:26:3 | extract:1 range statement | klog.go:22:27:22:33 | headers | provenance | | +| klog.go:21:30:21:37 | selection of Header | klog.go:21:3:26:3 | extract:1 range statement | provenance | Src:MaD:11 Config | +| klog.go:22:4:25:4 | extract:1 range statement | klog.go:23:15:23:20 | header | provenance | | +| klog.go:22:27:22:33 | headers | klog.go:22:4:25:4 | extract:1 range statement | provenance | Config | | klog.go:29:13:29:20 | selection of Header | klog.go:29:13:29:41 | call to Get | provenance | Src:MaD:11 Config | | main.go:17:2:17:9 | definition of password | main.go:19:12:19:19 | password | provenance | | | main.go:17:2:17:9 | definition of password | main.go:20:19:20:26 | password | provenance | | @@ -143,14 +143,14 @@ edges | passwords.go:130:14:130:19 | config [x] | passwords.go:130:14:130:21 | selection of x | provenance | | | passwords.go:131:14:131:19 | config [y] | passwords.go:131:14:131:21 | selection of y | provenance | | | protobuf.go:9:2:9:9 | definition of password | protobuf.go:12:22:12:29 | password | provenance | | -| protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | provenance | | +| protobuf.go:12:2:12:6 | implicit-deref query [postupdate] [Description] | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | provenance | | | protobuf.go:12:2:12:6 | query [postupdate] [pointer, Description] | protobuf.go:14:14:14:18 | query [pointer, Description] | provenance | | -| protobuf.go:12:22:12:29 | password | protobuf.go:12:2:12:6 | implicit dereference [postupdate] [Description] | provenance | | +| protobuf.go:12:22:12:29 | password | protobuf.go:12:2:12:6 | implicit-deref query [postupdate] [Description] | provenance | | | protobuf.go:14:14:14:18 | query [pointer, Description] | protobuf.go:14:14:14:35 | call to GetDescription | provenance | | | protobuf.go:14:14:14:18 | query [pointer, Description] | protos/query/query.pb.go:117:7:117:7 | definition of x [pointer, Description] | provenance | | | protos/query/query.pb.go:117:7:117:7 | definition of x [pointer, Description] | protos/query/query.pb.go:119:10:119:10 | x [pointer, Description] | provenance | | -| protos/query/query.pb.go:119:10:119:10 | implicit dereference [Description] | protos/query/query.pb.go:119:10:119:22 | selection of Description | provenance | | -| protos/query/query.pb.go:119:10:119:10 | x [pointer, Description] | protos/query/query.pb.go:119:10:119:10 | implicit dereference [Description] | provenance | | +| protos/query/query.pb.go:119:10:119:10 | implicit-deref x [Description] | protos/query/query.pb.go:119:10:119:22 | selection of Description | provenance | | +| protos/query/query.pb.go:119:10:119:10 | x [pointer, Description] | protos/query/query.pb.go:119:10:119:10 | implicit-deref x [Description] | provenance | | models | 1 | Sink: group:logrus; ; false; WithField; ; ; Argument[0..1]; log-injection; manual | | 2 | Sink: group:logrus; ; false; WithFields; ; ; Argument[0]; log-injection; manual | diff --git a/go/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected b/go/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected index 00eb67fea0ba..17c258a601bd 100644 --- a/go/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected +++ b/go/ql/test/query-tests/Security/CWE-327/BrokenCryptoAlgorithm.expected @@ -1,29 +1,29 @@ -| encryption.go:30:2:30:36 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:34:2:34:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:38:2:38:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:42:2:42:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:46:2:46:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:50:2:50:47 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:54:2:54:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:56:22:56:91 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:59:21:59:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:59:22:59:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:59:22:59:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:60:10:60:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:65:2:65:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:69:2:69:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | ... := ...[0] | The cryptographic algorithm DES | -| encryption.go:76:2:76:32 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:80:2:80:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:84:2:84:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:88:2:88:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:92:2:92:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:96:2:96:43 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:100:2:100:41 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:102:22:102:87 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:105:21:105:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:105:22:105:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:105:22:105:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:106:10:106:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:111:2:111:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | -| encryption.go:115:2:115:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | ... := ...[0] | The cryptographic algorithm TRIPLEDES | +| encryption.go:30:2:30:36 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:34:2:34:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:38:2:38:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:42:2:42:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:46:2:46:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:50:2:50:47 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:54:2:54:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:56:22:56:91 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:59:21:59:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:59:22:59:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:59:22:59:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:60:10:60:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:65:2:65:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:69:2:69:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:28:2:28:31 | extract:0 ... := ... | The cryptographic algorithm DES | +| encryption.go:76:2:76:32 | call to Encrypt | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:80:2:80:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:84:2:84:38 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:88:2:88:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:92:2:92:42 | call to Seal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:96:2:96:43 | call to CryptBlocks | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:100:2:100:41 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:102:22:102:87 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:105:21:105:68 | &... [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:105:22:105:68 | struct literal | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:105:22:105:68 | struct literal [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:106:10:106:24 | ctrStreamWriter [postupdate] | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:111:2:111:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | +| encryption.go:115:2:115:45 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:74:2:74:40 | extract:0 ... := ... | The cryptographic algorithm TRIPLEDES | | encryption.go:166:2:166:33 | call to XORKeyStream | $@ is broken or weak, and should not be used. | encryption.go:166:2:166:33 | call to XORKeyStream | The cryptographic algorithm RC4 | diff --git a/go/ql/test/query-tests/Security/CWE-347/MissingJwtSignatureCheck.expected b/go/ql/test/query-tests/Security/CWE-347/MissingJwtSignatureCheck.expected index 2bfca2aa643a..bdb67f69c339 100644 --- a/go/ql/test/query-tests/Security/CWE-347/MissingJwtSignatureCheck.expected +++ b/go/ql/test/query-tests/Security/CWE-347/MissingJwtSignatureCheck.expected @@ -7,8 +7,8 @@ edges | go-jose.v3.go:25:16:25:47 | call to Get | go-jose.v3.go:26:15:26:25 | signedToken | provenance | | | go-jose.v3.go:26:15:26:25 | signedToken | go-jose.v3.go:29:19:29:29 | definition of signedToken | provenance | | | go-jose.v3.go:29:19:29:29 | definition of signedToken | go-jose.v3.go:31:37:31:47 | signedToken | provenance | | -| go-jose.v3.go:31:2:31:48 | ... := ...[0] | go-jose.v3.go:33:12:33:23 | DecodedToken | provenance | Sink:MaD:2 | -| go-jose.v3.go:31:37:31:47 | signedToken | go-jose.v3.go:31:2:31:48 | ... := ...[0] | provenance | MaD:4 | +| go-jose.v3.go:31:2:31:48 | extract:0 ... := ... | go-jose.v3.go:33:12:33:23 | DecodedToken | provenance | Sink:MaD:2 | +| go-jose.v3.go:31:37:31:47 | signedToken | go-jose.v3.go:31:2:31:48 | extract:0 ... := ... | provenance | MaD:4 | | golang-jwt-v5.go:28:16:28:20 | selection of URL | golang-jwt-v5.go:28:16:28:28 | call to Query | provenance | Src:MaD:3 MaD:5 | | golang-jwt-v5.go:28:16:28:28 | call to Query | golang-jwt-v5.go:28:16:28:47 | call to Get | provenance | MaD:6 | | golang-jwt-v5.go:28:16:28:47 | call to Get | golang-jwt-v5.go:29:25:29:35 | signedToken | provenance | | diff --git a/go/ql/test/query-tests/Security/CWE-770/UncontrolledAllocationSize.expected b/go/ql/test/query-tests/Security/CWE-770/UncontrolledAllocationSize.expected index bdcf83b8935f..b3d9f2f0af41 100644 --- a/go/ql/test/query-tests/Security/CWE-770/UncontrolledAllocationSize.expected +++ b/go/ql/test/query-tests/Security/CWE-770/UncontrolledAllocationSize.expected @@ -5,8 +5,8 @@ edges | UncontrolledAllocationSizeBad.go:11:12:11:24 | call to Query | UncontrolledAllocationSizeBad.go:13:15:13:20 | source | provenance | | | UncontrolledAllocationSizeBad.go:13:15:13:20 | source | UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | provenance | MaD:3 | | UncontrolledAllocationSizeBad.go:13:15:13:29 | call to Get | UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | provenance | | -| UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | provenance | | -| UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | UncontrolledAllocationSizeBad.go:14:2:14:37 | ... := ...[0] | provenance | Config | +| UncontrolledAllocationSizeBad.go:14:2:14:37 | extract:0 ... := ... | UncontrolledAllocationSizeBad.go:20:27:20:30 | sink | provenance | | +| UncontrolledAllocationSizeBad.go:14:28:14:36 | sourceStr | UncontrolledAllocationSizeBad.go:14:2:14:37 | extract:0 ... := ... | provenance | Config | models | 1 | Source: net/http; Request; true; URL; ; ; ; remote; manual | | 2 | Summary: net/url; URL; true; Query; ; ; Argument[receiver]; ReturnValue; taint; manual | diff --git a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected index 15b0e179e983..7487b8c2682d 100644 --- a/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected +++ b/go/ql/test/query-tests/Security/CWE-918/RequestForgery.expected @@ -37,9 +37,9 @@ edges | tst.go:11:13:11:35 | call to FormValue | tst.go:39:11:39:29 | ...+... | provenance | Src:MaD:1 | | tst.go:11:13:11:35 | call to FormValue | tst.go:41:11:41:40 | ...+... | provenance | Src:MaD:1 | | tst.go:11:13:11:35 | call to FormValue | tst.go:48:11:48:18 | tainted2 | provenance | Src:MaD:1 | -| tst.go:48:2:48:2 | implicit dereference [postupdate] | tst.go:48:2:48:2 | u [postupdate] | provenance | | +| tst.go:48:2:48:2 | implicit-deref u [postupdate] | tst.go:48:2:48:2 | u [postupdate] | provenance | | | tst.go:48:2:48:2 | u [postupdate] | tst.go:49:11:49:11 | u | provenance | | -| tst.go:48:11:48:18 | tainted2 | tst.go:48:2:48:2 | implicit dereference [postupdate] | provenance | Config | +| tst.go:48:11:48:18 | tainted2 | tst.go:48:2:48:2 | implicit-deref u [postupdate] | provenance | Config | | tst.go:48:11:48:18 | tainted2 | tst.go:48:2:48:2 | u [postupdate] | provenance | Config | | tst.go:49:11:49:11 | u | tst.go:49:11:49:20 | call to String | provenance | MaD:3 | | websocket.go:60:21:60:31 | call to Referer | websocket.go:65:27:65:40 | untrustedInput | provenance | Src:MaD:2 | diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index 33a609d55520..0df08441dc76 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -1510,6 +1510,26 @@ module Make0 Ast> { n2.isAfterValue(pme, any(BooleanSuccessor s | s.getValue() = true)) ) or + exists(PatternMatchExpr pme | + n1.isBefore(pme) and + n2.isBefore(pme.getExpr()) + or + n1.isAfter(pme.getExpr()) and + n2.isIn(pme) + or + n1.isIn(pme) and + n2.isAfterValue(pme, any(BooleanSuccessor s | s.getValue() = false)) + or + n1.isIn(pme) and + n2.isAdditional(pme, patternMatchTrueTag()) + or + n1.isAdditional(pme, patternMatchTrueTag()) and + n2.isBefore(pme.getPattern()) + or + n1.isAfter(pme.getPattern()) and + n2.isAfterValue(pme, any(BooleanSuccessor s | s.getValue() = true)) + ) + or exists(IfStmt ifstmt | n1.isBefore(ifstmt) and (