mirror of
https://github.com/github/codeql.git
synced 2026-06-24 06:07:01 +02:00
Migrate Go type switches to shared CFG switch model (Option B)
This commit is contained in:
committed by
GitHub
parent
587c4b344a
commit
66eaeebd1e
@@ -68,12 +68,19 @@ module GoCfg {
|
||||
or
|
||||
e.getParent*() = any(Go::ArrayTypeExpr ate).getLength()
|
||||
or
|
||||
// The body block of an expression switch is transparent: the shared
|
||||
// switch model wires control flow directly from the switch to its case
|
||||
// clauses (in control-flow order) and between cases, so the enclosing
|
||||
// block must not introduce its own nodes or default left-to-right
|
||||
// sequencing of the case clauses.
|
||||
e = any(Go::ExpressionSwitchStmt sw).getBody()
|
||||
// The body block of a switch (expression or type) is transparent: the
|
||||
// shared switch model wires control flow directly from the switch to its
|
||||
// case clauses (in control-flow order) and between cases, so the
|
||||
// enclosing block must not introduce its own nodes or default
|
||||
// left-to-right sequencing of the case clauses.
|
||||
e = any(Go::SwitchStmt sw).getBody()
|
||||
or
|
||||
// The test statement of a type switch (`y := x.(type)` or the bare
|
||||
// `x.(type)` expression statement) is transparent: the shared switch
|
||||
// model evaluates the underlying type-assertion expression directly as
|
||||
// the switch expression (see `Switch.getExpr`), so the wrapping
|
||||
// statement must not introduce its own assignment or expression nodes.
|
||||
e = any(Go::TypeSwitchStmt ts).getTest()
|
||||
}
|
||||
|
||||
AstNode getChild(AstNode n, int index) {
|
||||
@@ -82,13 +89,18 @@ module GoCfg {
|
||||
not skipCfg(n) and
|
||||
result = n.getChild(index)
|
||||
or
|
||||
// The body block of an expression switch is transparent (see `skipCfg`),
|
||||
// so it is not itself a child and contributes no children. Expose the
|
||||
// case clauses directly as children of the switch instead, so that the
|
||||
// AST child chain stays connected for abrupt-completion propagation
|
||||
// (e.g. a panicking call in a case body reaching the enclosing
|
||||
// function's exceptional exit).
|
||||
result = n.(Go::ExpressionSwitchStmt).getBody().getChild(index)
|
||||
// The body block of a switch (expression or type) is transparent (see
|
||||
// `skipCfg`), so it is not itself a child and contributes no children.
|
||||
// Expose the case clauses directly as children of the switch instead,
|
||||
// so that the AST child chain stays connected for abrupt-completion
|
||||
// propagation (e.g. a panicking call in a case body reaching the
|
||||
// enclosing function's exceptional exit).
|
||||
result = n.(Go::SwitchStmt).getBody().getChild(index)
|
||||
or
|
||||
// The type-switch test statement is transparent (see `skipCfg`), so
|
||||
// expose the underlying type-assertion expression directly as a child
|
||||
// of the type switch, keeping the AST child chain connected.
|
||||
result = n.(Go::TypeSwitchStmt).getExpr() and index = 1
|
||||
) and
|
||||
not skipCfg(result)
|
||||
}
|
||||
@@ -141,7 +153,17 @@ module GoCfg {
|
||||
}
|
||||
}
|
||||
|
||||
class ExprStmt = Go::ExprStmt;
|
||||
class ExprStmt extends Stmt instanceof Go::ExprStmt {
|
||||
// The `x.(type)` test statement of a type switch is transparent (see
|
||||
// `skipCfg`): the shared switch model evaluates the underlying
|
||||
// type-assertion expression directly as the switch expression. It must
|
||||
// therefore not be treated as an ordinary expression statement, whose
|
||||
// value would otherwise be propagated from the expression to the
|
||||
// statement (creating a spurious flow into the transparent wrapper).
|
||||
ExprStmt() { not this = any(Go::TypeSwitchStmt ts).getTest() }
|
||||
|
||||
Expr getExpr() { result = Go::ExprStmt.super.getExpr() }
|
||||
}
|
||||
|
||||
class IfStmt extends Stmt {
|
||||
IfStmt() { this instanceof Go::IfStmt }
|
||||
@@ -234,11 +256,19 @@ module GoCfg {
|
||||
}
|
||||
|
||||
class Switch extends AstNode {
|
||||
Switch() { this instanceof Go::ExpressionSwitchStmt }
|
||||
Switch() { this instanceof Go::SwitchStmt }
|
||||
|
||||
Expr getExpr() { result = this.(Go::ExpressionSwitchStmt).getExpr() }
|
||||
Expr getExpr() {
|
||||
result = this.(Go::ExpressionSwitchStmt).getExpr()
|
||||
or
|
||||
// For a type switch the "switch expression" is the type-assertion
|
||||
// expression `x.(type)`; evaluating it directly (rather than the
|
||||
// wrapping `y := x.(type)` statement) lets the shared switch model
|
||||
// drive the per-case type tests.
|
||||
result = this.(Go::TypeSwitchStmt).getExpr()
|
||||
}
|
||||
|
||||
Case getCase(int index) { result = this.(Go::ExpressionSwitchStmt).getCase(index) }
|
||||
Case getCase(int index) { result = this.(Go::SwitchStmt).getCase(index) }
|
||||
|
||||
Stmt getStmt(int index) {
|
||||
// Go nests each case clause's body statements under the clause rather
|
||||
@@ -256,7 +286,7 @@ module GoCfg {
|
||||
}
|
||||
|
||||
class Case extends AstNode {
|
||||
Case() { this = any(Go::ExpressionSwitchStmt sw).getACase() }
|
||||
Case() { this = any(Go::SwitchStmt sw).getACase() }
|
||||
|
||||
AstNode getPattern(int index) { result = this.(Go::CaseClause).getExpr(index) }
|
||||
|
||||
@@ -270,7 +300,7 @@ module GoCfg {
|
||||
}
|
||||
|
||||
/** Gets the initializer of `switch` statement `switch`, if any. */
|
||||
AstNode getSwitchInit(Switch switch) { result = switch.(Go::ExpressionSwitchStmt).getInit() }
|
||||
AstNode getSwitchInit(Switch switch) { result = switch.(Go::SwitchStmt).getInit() }
|
||||
|
||||
/**
|
||||
* Go has no implicit fall-through between case clauses; a case that runs to
|
||||
@@ -287,12 +317,10 @@ module GoCfg {
|
||||
|
||||
/**
|
||||
* Holds if `s` is the flattened body element at position (`caseIdx`,
|
||||
* `inner`) of expression switch `sw`: either the `caseIdx`-th case clause
|
||||
* itself (with `inner` = -1) or its `inner`-th body statement.
|
||||
* `inner`) of switch `sw`: either the `caseIdx`-th case clause itself (with
|
||||
* `inner` = -1) or its `inner`-th body statement.
|
||||
*/
|
||||
private predicate switchFlatItem(
|
||||
Go::ExpressionSwitchStmt sw, Go::Stmt s, int caseIdx, int inner
|
||||
) {
|
||||
private predicate switchFlatItem(Go::SwitchStmt sw, Go::Stmt s, int caseIdx, int inner) {
|
||||
s = sw.getCase(caseIdx) and inner = -1
|
||||
or
|
||||
s = sw.getCase(caseIdx).getStmt(inner)
|
||||
@@ -469,6 +497,11 @@ module GoCfg {
|
||||
or
|
||||
notBlankIdent(n.(Go::RangeStmt).getValue()) and i = 1
|
||||
) and
|
||||
// The `y := x.(type)` test statement of a type switch is transparent
|
||||
// (see `skipCfg`): the per-case implicit variables are written at the
|
||||
// case match nodes (see `IR::TypeSwitchImplicitVariableInstruction`),
|
||||
// so the guard itself emits no assignment write node.
|
||||
not n = any(Go::TypeSwitchStmt ts).getTest() and
|
||||
tag = "assign:" + i.toString()
|
||||
)
|
||||
or
|
||||
@@ -559,14 +592,6 @@ module GoCfg {
|
||||
not exists(n.(Go::SliceExpr).getMax()) and
|
||||
tag = "implicit-max"
|
||||
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"
|
||||
@@ -943,7 +968,6 @@ module GoCfg {
|
||||
|
||||
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
|
||||
@@ -1007,7 +1031,12 @@ module GoCfg {
|
||||
assgn instanceof Go::Assignment and not assgn instanceof Go::RecvStmt
|
||||
or
|
||||
assgn instanceof Go::ValueSpec
|
||||
)
|
||||
) and
|
||||
// The `y := x.(type)` test statement of a type switch is transparent
|
||||
// (see `skipCfg`); the shared switch model evaluates the underlying
|
||||
// type-assertion expression directly, so this statement has no
|
||||
// assignment flow of its own.
|
||||
not assgn = any(Go::TypeSwitchStmt ts).getTest()
|
||||
|
|
||||
// Route through children (LHS names, RHS expressions)
|
||||
childSequenceStep(assgn, n1, n2)
|
||||
@@ -1429,119 +1458,6 @@ module GoCfg {
|
||||
)
|
||||
}
|
||||
|
||||
private predicate switchStmt(PreControlFlowNode n1, PreControlFlowNode n2) {
|
||||
typeSwitch(n1, n2) or typeCaseClause(n1, n2)
|
||||
}
|
||||
|
||||
private predicate switchCasesStartOrAfter(Go::TypeSwitchStmt 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 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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `cc` is a case clause of a type switch with an assignment that
|
||||
* implicitly declares a variable whose type narrows to the case type. In
|
||||
* this situation the CFG inserts a `type-switch-var` additional node
|
||||
* between the case test and the case body, on which the IR layer
|
||||
* materialises the implicit assignment to that variable.
|
||||
*/
|
||||
private predicate hasTypeSwitchVar(Go::CaseClause cc) {
|
||||
exists(Go::TypeSwitchStmt ts |
|
||||
ts.getACase() = cc and
|
||||
exists(ts.getAssign()) and
|
||||
exists(cc.getImplicitlyDeclaredVariable())
|
||||
)
|
||||
}
|
||||
|
||||
private predicate typeCaseClause(PreControlFlowNode n1, PreControlFlowNode n2) {
|
||||
exists(Go::TypeSwitchStmt sw, Go::CaseClause cc, int i | cc = sw.getNonDefaultCase(i) |
|
||||
n1.isBefore(cc) and n2.isBefore(cc.getExpr(0))
|
||||
or
|
||||
// A type switch is not boolean, so each case type test has a single
|
||||
// "after" node from which control flows both to the case body (on a
|
||||
// match) and on to the next test (on a mismatch).
|
||||
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
|
||||
(
|
||||
hasTypeSwitchVar(cc) and n2.isAdditional(cc, "type-switch-var")
|
||||
or
|
||||
not hasTypeSwitchVar(cc) and
|
||||
(
|
||||
n2.isBefore(cc.getStmt(0))
|
||||
or
|
||||
not exists(cc.getStmt(0)) and n2.isAfter(sw)
|
||||
)
|
||||
)
|
||||
or
|
||||
n1.isAfter(cc.getExpr(last)) and
|
||||
(
|
||||
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::TypeSwitchStmt sw, Go::CaseClause def | def = sw.getDefault() |
|
||||
n1.isBefore(def) and
|
||||
(
|
||||
hasTypeSwitchVar(def) and n2.isAdditional(def, "type-switch-var")
|
||||
or
|
||||
not hasTypeSwitchVar(def) and
|
||||
(
|
||||
n2.isBefore(def.getStmt(0))
|
||||
or
|
||||
not exists(def.getStmt(0)) and n2.isAfter(sw)
|
||||
)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Go::TypeSwitchStmt sw, Go::CaseClause cc |
|
||||
sw.getACase() = cc and
|
||||
hasTypeSwitchVar(cc) and
|
||||
n1.isAdditional(cc, "type-switch-var") and
|
||||
(
|
||||
n2.isBefore(cc.getStmt(0))
|
||||
or
|
||||
not exists(cc.getStmt(0)) and n2.isAfter(sw)
|
||||
)
|
||||
)
|
||||
or
|
||||
exists(Go::TypeSwitchStmt sw, Go::CaseClause cc | cc = sw.getACase() |
|
||||
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(sw)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private predicate commClauseBodyStart(
|
||||
Go::SelectStmt sel, Go::CommClause cc, PreControlFlowNode n
|
||||
) {
|
||||
|
||||
@@ -14,6 +14,7 @@ module;
|
||||
|
||||
import go
|
||||
private import ControlFlowGraphShared
|
||||
private import codeql.controlflow.SuccessorType
|
||||
|
||||
/** Provides predicates and classes for working with IR constructs. */
|
||||
module IR {
|
||||
@@ -43,6 +44,20 @@ module IR {
|
||||
e = any(ParenExpr pe | isInBooleanCondContext(pe)).getExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `n` is the control-flow node representing a successful match of
|
||||
* the type-switch case clause `cc` that implicitly declares a variable.
|
||||
*
|
||||
* This node dominates the case body and is where the implicit per-case
|
||||
* variable declaration/assignment is materialised (see
|
||||
* `TypeSwitchImplicitVariableInstruction`).
|
||||
*/
|
||||
private predicate typeSwitchCaseMatch(ControlFlow::Node n, CaseClause cc) {
|
||||
cc = any(TypeSwitchStmt ts).getACase() and
|
||||
exists(cc.getImplicitlyDeclaredVariable()) and
|
||||
n.isAfterValue(cc, any(MatchingSuccessor t | t.getValue() = true))
|
||||
}
|
||||
|
||||
/**
|
||||
* An IR instruction.
|
||||
*/
|
||||
@@ -56,6 +71,11 @@ module IR {
|
||||
or
|
||||
this.isAfterFalse(_)
|
||||
or
|
||||
// The successful-match node of a type-switch case that binds an implicit
|
||||
// variable hosts that variable's declaration/assignment (see
|
||||
// `TypeSwitchImplicitVariableInstruction`).
|
||||
typeSwitchCaseMatch(this, _)
|
||||
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
|
||||
@@ -1182,7 +1202,7 @@ module IR {
|
||||
class TypeSwitchImplicitVariableInstruction extends Instruction {
|
||||
CaseClause cc;
|
||||
|
||||
TypeSwitchImplicitVariableInstruction() { this.isAdditional(cc, "type-switch-var") }
|
||||
TypeSwitchImplicitVariableInstruction() { typeSwitchCaseMatch(this, cc) }
|
||||
|
||||
override predicate writes(ValueEntity v, Instruction rhs) {
|
||||
v = cc.getImplicitlyDeclaredVariable() and
|
||||
|
||||
@@ -3779,21 +3779,24 @@
|
||||
| stmts.go:112:27:137:1 | block statement | stmts.go:112:27:137:1 | arg:0 block statement |
|
||||
| stmts.go:112:27:137:1 | param-init:0 block statement | stmts.go:113:2:121:2 | type-switch statement |
|
||||
| stmts.go:113:2:121:2 | After type-switch statement | stmts.go:123:2:131:2 | type-switch statement |
|
||||
| stmts.go:113:2:121:2 | type-switch statement | stmts.go:113:9:113:21 | ... := ... |
|
||||
| stmts.go:113:9:113:21 | ... := ... | stmts.go:113:14:113:21 | Before type assertion |
|
||||
| stmts.go:113:9:113:21 | After ... := ... | stmts.go:114:2:115:16 | case clause |
|
||||
| stmts.go:113:9:113:21 | assign:0 ... := ... | stmts.go:113:9:113:21 | After ... := ... |
|
||||
| stmts.go:113:2:121:2 | type-switch statement | stmts.go:113:14:113:21 | Before type assertion |
|
||||
| stmts.go:113:14:113:14 | After x | stmts.go:113:14:113:21 | type assertion |
|
||||
| stmts.go:113:14:113:14 | Before x | stmts.go:113:14:113:14 | x |
|
||||
| stmts.go:113:14:113:14 | x | stmts.go:113:14:113:14 | After x |
|
||||
| stmts.go:113:14:113:21 | After type assertion | stmts.go:113:9:113:21 | assign:0 ... := ... |
|
||||
| stmts.go:113:14:113:21 | After type assertion | stmts.go:114:2:115:16 | case clause |
|
||||
| stmts.go:113:14:113:21 | Before type assertion | stmts.go:113:14:113:14 | Before x |
|
||||
| stmts.go:113:14:113:21 | type assertion | stmts.go:113:14:113:21 | After type assertion |
|
||||
| stmts.go:114:2:115:16 | After case clause [match] | stmts.go:115:3:115:16 | expression statement |
|
||||
| stmts.go:114:2:115:16 | After case clause [no-match] | stmts.go:116:2:118:14 | case clause |
|
||||
| stmts.go:114:2:115:16 | case clause | stmts.go:114:7:114:11 | error |
|
||||
| stmts.go:114:2:115:16 | type-switch-var case clause | stmts.go:115:3:115:16 | expression statement |
|
||||
| stmts.go:114:7:114:11 | error | stmts.go:114:14:114:19 | string |
|
||||
| stmts.go:114:14:114:19 | string | stmts.go:114:2:115:16 | type-switch-var case clause |
|
||||
| stmts.go:114:14:114:19 | string | stmts.go:116:2:118:14 | case clause |
|
||||
| stmts.go:114:7:114:11 | After error [match] | stmts.go:114:2:115:16 | After case clause [match] |
|
||||
| stmts.go:114:7:114:11 | After error [no-match] | stmts.go:114:14:114:19 | string |
|
||||
| stmts.go:114:7:114:11 | error | stmts.go:114:7:114:11 | After error [match] |
|
||||
| stmts.go:114:7:114:11 | error | stmts.go:114:7:114:11 | After error [no-match] |
|
||||
| stmts.go:114:14:114:19 | After string [match] | stmts.go:114:2:115:16 | After case clause [match] |
|
||||
| stmts.go:114:14:114:19 | After string [no-match] | stmts.go:114:2:115:16 | After case clause [no-match] |
|
||||
| stmts.go:114:14:114:19 | string | stmts.go:114:14:114:19 | After string [match] |
|
||||
| stmts.go:114:14:114:19 | string | stmts.go:114:14:114:19 | After string [no-match] |
|
||||
| stmts.go:115:3:115:13 | After selection of Println | stmts.go:115:15:115:15 | Before y |
|
||||
| stmts.go:115:3:115:13 | Before selection of Println | stmts.go:115:3:115:13 | selection of Println |
|
||||
| stmts.go:115:3:115:13 | selection of Println | stmts.go:115:3:115:13 | After selection of Println |
|
||||
@@ -3806,10 +3809,13 @@
|
||||
| stmts.go:115:15:115:15 | After y | stmts.go:115:3:115:16 | call to Println |
|
||||
| stmts.go:115:15:115:15 | Before y | stmts.go:115:15:115:15 | y |
|
||||
| stmts.go:115:15:115:15 | y | stmts.go:115:15:115:15 | After y |
|
||||
| stmts.go:116:2:118:14 | After case clause [match] | stmts.go:117:3:117:13 | expression statement |
|
||||
| stmts.go:116:2:118:14 | After case clause [no-match] | stmts.go:119:2:120:7 | case clause |
|
||||
| stmts.go:116:2:118:14 | case clause | stmts.go:116:7:116:13 | float32 |
|
||||
| stmts.go:116:2:118:14 | type-switch-var case clause | stmts.go:117:3:117:13 | expression statement |
|
||||
| stmts.go:116:7:116:13 | float32 | stmts.go:116:2:118:14 | type-switch-var case clause |
|
||||
| stmts.go:116:7:116:13 | float32 | stmts.go:119:2:120:7 | case clause |
|
||||
| stmts.go:116:7:116:13 | After float32 [match] | stmts.go:116:2:118:14 | After case clause [match] |
|
||||
| stmts.go:116:7:116:13 | After float32 [no-match] | stmts.go:116:2:118:14 | After case clause [no-match] |
|
||||
| stmts.go:116:7:116:13 | float32 | stmts.go:116:7:116:13 | After float32 [match] |
|
||||
| stmts.go:116:7:116:13 | float32 | stmts.go:116:7:116:13 | After float32 [no-match] |
|
||||
| stmts.go:117:3:117:7 | After test5 | stmts.go:117:9:117:12 | Before true |
|
||||
| stmts.go:117:3:117:7 | Before test5 | stmts.go:117:3:117:7 | test5 |
|
||||
| stmts.go:117:3:117:7 | test5 | stmts.go:117:3:117:7 | After test5 |
|
||||
@@ -3834,8 +3840,8 @@
|
||||
| stmts.go:118:9:118:13 | After false | stmts.go:118:3:118:14 | call to test5 |
|
||||
| stmts.go:118:9:118:13 | Before false | stmts.go:118:9:118:13 | false |
|
||||
| stmts.go:118:9:118:13 | false | stmts.go:118:9:118:13 | After false |
|
||||
| stmts.go:119:2:120:7 | case clause | stmts.go:119:2:120:7 | type-switch-var case clause |
|
||||
| stmts.go:119:2:120:7 | type-switch-var case clause | stmts.go:120:3:120:7 | ... = ... |
|
||||
| stmts.go:119:2:120:7 | After case clause [match] | stmts.go:120:3:120:7 | ... = ... |
|
||||
| stmts.go:119:2:120:7 | case clause | stmts.go:119:2:120:7 | After case clause [match] |
|
||||
| stmts.go:120:3:120:7 | ... = ... | stmts.go:120:7:120:7 | Before y |
|
||||
| stmts.go:120:3:120:7 | After ... = ... | stmts.go:113:2:121:2 | After type-switch statement |
|
||||
| stmts.go:120:7:120:7 | After y | stmts.go:120:3:120:7 | After ... = ... |
|
||||
@@ -3844,7 +3850,7 @@
|
||||
| stmts.go:123:2:131:2 | After type-switch statement | stmts.go:133:2:136:2 | type-switch statement |
|
||||
| stmts.go:123:2:131:2 | type-switch statement | stmts.go:123:9:123:14 | ... := ... |
|
||||
| stmts.go:123:9:123:14 | ... := ... | stmts.go:123:14:123:14 | Before x |
|
||||
| stmts.go:123:9:123:14 | After ... := ... | stmts.go:123:17:123:24 | expression statement |
|
||||
| stmts.go:123:9:123:14 | After ... := ... | stmts.go:123:17:123:24 | Before type assertion |
|
||||
| stmts.go:123:9:123:14 | assign:0 ... := ... | stmts.go:123:9:123:14 | After ... := ... |
|
||||
| stmts.go:123:14:123:14 | After x | stmts.go:123:9:123:14 | assign:0 ... := ... |
|
||||
| stmts.go:123:14:123:14 | Before x | stmts.go:123:14:123:14 | x |
|
||||
@@ -3852,15 +3858,20 @@
|
||||
| stmts.go:123:17:123:17 | After y | stmts.go:123:17:123:24 | type assertion |
|
||||
| stmts.go:123:17:123:17 | Before y | stmts.go:123:17:123:17 | y |
|
||||
| stmts.go:123:17:123:17 | y | stmts.go:123:17:123:17 | After y |
|
||||
| stmts.go:123:17:123:24 | After expression statement | stmts.go:124:2:125:16 | case clause |
|
||||
| stmts.go:123:17:123:24 | After type assertion | stmts.go:123:17:123:24 | After expression statement |
|
||||
| stmts.go:123:17:123:24 | After type assertion | stmts.go:124:2:125:16 | case clause |
|
||||
| stmts.go:123:17:123:24 | Before type assertion | stmts.go:123:17:123:17 | Before y |
|
||||
| stmts.go:123:17:123:24 | expression statement | stmts.go:123:17:123:24 | Before type assertion |
|
||||
| stmts.go:123:17:123:24 | type assertion | stmts.go:123:17:123:24 | After type assertion |
|
||||
| stmts.go:124:2:125:16 | After case clause [match] | stmts.go:125:3:125:16 | expression statement |
|
||||
| stmts.go:124:2:125:16 | After case clause [no-match] | stmts.go:126:2:128:14 | case clause |
|
||||
| stmts.go:124:2:125:16 | case clause | stmts.go:124:7:124:11 | error |
|
||||
| stmts.go:124:7:124:11 | error | stmts.go:124:14:124:19 | string |
|
||||
| stmts.go:124:14:124:19 | string | stmts.go:125:3:125:16 | expression statement |
|
||||
| stmts.go:124:14:124:19 | string | stmts.go:126:2:128:14 | case clause |
|
||||
| stmts.go:124:7:124:11 | After error [match] | stmts.go:124:2:125:16 | After case clause [match] |
|
||||
| stmts.go:124:7:124:11 | After error [no-match] | stmts.go:124:14:124:19 | string |
|
||||
| stmts.go:124:7:124:11 | error | stmts.go:124:7:124:11 | After error [match] |
|
||||
| stmts.go:124:7:124:11 | error | stmts.go:124:7:124:11 | After error [no-match] |
|
||||
| stmts.go:124:14:124:19 | After string [match] | stmts.go:124:2:125:16 | After case clause [match] |
|
||||
| stmts.go:124:14:124:19 | After string [no-match] | stmts.go:124:2:125:16 | After case clause [no-match] |
|
||||
| stmts.go:124:14:124:19 | string | stmts.go:124:14:124:19 | After string [match] |
|
||||
| stmts.go:124:14:124:19 | string | stmts.go:124:14:124:19 | After string [no-match] |
|
||||
| stmts.go:125:3:125:13 | After selection of Println | stmts.go:125:15:125:15 | Before y |
|
||||
| stmts.go:125:3:125:13 | Before selection of Println | stmts.go:125:3:125:13 | selection of Println |
|
||||
| stmts.go:125:3:125:13 | selection of Println | stmts.go:125:3:125:13 | After selection of Println |
|
||||
@@ -3873,9 +3884,13 @@
|
||||
| stmts.go:125:15:125:15 | After y | stmts.go:125:3:125:16 | call to Println |
|
||||
| stmts.go:125:15:125:15 | Before y | stmts.go:125:15:125:15 | y |
|
||||
| stmts.go:125:15:125:15 | y | stmts.go:125:15:125:15 | After y |
|
||||
| stmts.go:126:2:128:14 | After case clause [match] | stmts.go:127:3:127:13 | expression statement |
|
||||
| stmts.go:126:2:128:14 | After case clause [no-match] | stmts.go:129:2:130:7 | case clause |
|
||||
| stmts.go:126:2:128:14 | case clause | stmts.go:126:7:126:13 | float32 |
|
||||
| stmts.go:126:7:126:13 | float32 | stmts.go:127:3:127:13 | expression statement |
|
||||
| stmts.go:126:7:126:13 | float32 | stmts.go:129:2:130:7 | case clause |
|
||||
| stmts.go:126:7:126:13 | After float32 [match] | stmts.go:126:2:128:14 | After case clause [match] |
|
||||
| stmts.go:126:7:126:13 | After float32 [no-match] | stmts.go:126:2:128:14 | After case clause [no-match] |
|
||||
| stmts.go:126:7:126:13 | float32 | stmts.go:126:7:126:13 | After float32 [match] |
|
||||
| stmts.go:126:7:126:13 | float32 | stmts.go:126:7:126:13 | After float32 [no-match] |
|
||||
| stmts.go:127:3:127:7 | After test5 | stmts.go:127:9:127:12 | Before true |
|
||||
| stmts.go:127:3:127:7 | Before test5 | stmts.go:127:3:127:7 | test5 |
|
||||
| stmts.go:127:3:127:7 | test5 | stmts.go:127:3:127:7 | After test5 |
|
||||
@@ -3900,7 +3915,8 @@
|
||||
| stmts.go:128:9:128:13 | After false | stmts.go:128:3:128:14 | call to test5 |
|
||||
| stmts.go:128:9:128:13 | Before false | stmts.go:128:9:128:13 | false |
|
||||
| stmts.go:128:9:128:13 | false | stmts.go:128:9:128:13 | After false |
|
||||
| stmts.go:129:2:130:7 | case clause | stmts.go:130:3:130:7 | ... = ... |
|
||||
| stmts.go:129:2:130:7 | After case clause [match] | stmts.go:130:3:130:7 | ... = ... |
|
||||
| stmts.go:129:2:130:7 | case clause | stmts.go:129:2:130:7 | After case clause [match] |
|
||||
| stmts.go:130:3:130:7 | ... = ... | stmts.go:130:7:130:7 | Before y |
|
||||
| stmts.go:130:3:130:7 | After ... = ... | stmts.go:123:2:131:2 | After type-switch statement |
|
||||
| stmts.go:130:7:130:7 | After y | stmts.go:130:3:130:7 | After ... = ... |
|
||||
@@ -3909,7 +3925,7 @@
|
||||
| stmts.go:133:2:136:2 | After type-switch statement | stmts.go:112:27:137:1 | After block statement |
|
||||
| stmts.go:133:2:136:2 | type-switch statement | stmts.go:133:9:133:14 | ... := ... |
|
||||
| stmts.go:133:9:133:14 | ... := ... | stmts.go:133:14:133:14 | Before x |
|
||||
| stmts.go:133:9:133:14 | After ... := ... | stmts.go:133:17:133:24 | expression statement |
|
||||
| stmts.go:133:9:133:14 | After ... := ... | stmts.go:133:17:133:24 | Before type assertion |
|
||||
| stmts.go:133:9:133:14 | assign:0 ... := ... | stmts.go:133:9:133:14 | After ... := ... |
|
||||
| stmts.go:133:14:133:14 | After x | stmts.go:133:9:133:14 | assign:0 ... := ... |
|
||||
| stmts.go:133:14:133:14 | Before x | stmts.go:133:14:133:14 | x |
|
||||
@@ -3917,12 +3933,11 @@
|
||||
| stmts.go:133:17:133:17 | After y | stmts.go:133:17:133:24 | type assertion |
|
||||
| stmts.go:133:17:133:17 | Before y | stmts.go:133:17:133:17 | y |
|
||||
| stmts.go:133:17:133:17 | y | stmts.go:133:17:133:17 | After y |
|
||||
| stmts.go:133:17:133:24 | After expression statement | stmts.go:134:2:135:14 | case clause |
|
||||
| stmts.go:133:17:133:24 | After type assertion | stmts.go:133:17:133:24 | After expression statement |
|
||||
| stmts.go:133:17:133:24 | After type assertion | stmts.go:134:2:135:14 | case clause |
|
||||
| stmts.go:133:17:133:24 | Before type assertion | stmts.go:133:17:133:17 | Before y |
|
||||
| stmts.go:133:17:133:24 | expression statement | stmts.go:133:17:133:24 | Before type assertion |
|
||||
| stmts.go:133:17:133:24 | type assertion | stmts.go:133:17:133:24 | After type assertion |
|
||||
| stmts.go:134:2:135:14 | case clause | stmts.go:135:3:135:14 | expression statement |
|
||||
| stmts.go:134:2:135:14 | After case clause [match] | stmts.go:135:3:135:14 | expression statement |
|
||||
| stmts.go:134:2:135:14 | case clause | stmts.go:134:2:135:14 | After case clause [match] |
|
||||
| stmts.go:135:3:135:7 | After test5 | stmts.go:135:9:135:13 | Before false |
|
||||
| stmts.go:135:3:135:7 | Before test5 | stmts.go:135:3:135:7 | test5 |
|
||||
| stmts.go:135:3:135:7 | test5 | stmts.go:135:3:135:7 | After test5 |
|
||||
|
||||
Reference in New Issue
Block a user