From d30162b2caaa26ed65293d2798090b6846622c3f Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 10 Jul 2026 17:03:53 +0100 Subject: [PATCH] Fold uninitialised var-decl zero-init and write into one node --- .../go/controlflow/ControlFlowGraphShared.qll | 11 ++++-- go/ql/lib/semmle/go/controlflow/IR.qll | 36 ++++++++++++++++--- .../ControlFlowNode_getASuccessor.expected | 24 +++++-------- .../semmle/go/dataflow/SSA/VarDefs.expected | 6 ++-- 4 files changed, 52 insertions(+), 25 deletions(-) diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll index e243b7a7cb4..68b73c06819 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -508,7 +508,11 @@ module GoCfg { // so the guard itself emits no assignment write node. not n = any(Go::TypeSwitchStmt ts).getAssign() or - notBlankIdent(n.(Go::ValueSpec).getNameExpr(i)) + // A `ValueSpec` without an initializer is written by its `zero-init` + // node directly (see `IR::InitVariableInstruction`), so only specs + // *with* an initializer emit an `assign` write node. + notBlankIdent(n.(Go::ValueSpec).getNameExpr(i)) and + exists(n.(Go::ValueSpec).getAnInit()) or notBlankIdent(n.(Go::RangeElementExpr).getKey()) and i = 0 or @@ -1103,7 +1107,10 @@ module GoCfg { // above, so they emit no separate `assign:j` node. not assgn instanceof Go::CompoundAssignStmt or - notBlankIdent(assgn.(Go::ValueSpec).getNameExpr(j)) + // A `ValueSpec` without an initializer is written by its `zero-init` + // node directly, so only specs *with* an initializer emit `assign:j`. + notBlankIdent(assgn.(Go::ValueSpec).getNameExpr(j)) and + exists(assgn.(Go::ValueSpec).getAnInit()) or notBlankIdent(assgn.(Go::RangeElementExpr).getKey()) and j = 0 or diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index c860ce1908d..7c02e992346 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -641,7 +641,11 @@ module IR { ( exists(assgn.(Assignment).getLhs(i)) or - exists(assgn.(ValueSpec).getNameExpr(i)) + // A `ValueSpec` without an initializer (`var x int`) is written by its + // `zero-init` node directly (see `InitVariableInstruction`), so only + // specs *with* an initializer produce an `assign` node. + exists(assgn.(ValueSpec).getNameExpr(i)) and + exists(assgn.(ValueSpec).getAnInit()) or assgn instanceof RangeElementExpr and i in [0, 1] ) @@ -656,9 +660,6 @@ module IR { exists(ValueSpec spec | spec = assgn | spec.getNumName() = spec.getNumInit() and result = evalExprInstruction(spec.getInit(i)) - or - result = - implicitInitInstruction(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) ) or result.(ExtractTupleElementInstruction).isAdditional(assgn, "extract:" + i.toString()) @@ -1005,6 +1006,28 @@ module IR { override ControlFlow::Root getRoot() { result = res.getFunction() } } + /** + * An instruction initializing a variable declared without an initializer + * (`var x int`) to its zero value. + * + * This is the same node as the `EvalImplicitInitInstruction` that computes the + * zero value: the zero-value instruction performs the write of the variable + * directly, rather than feeding a separate write node. + */ + class InitVariableInstruction extends WriteInstruction { + ValueSpec spec; + int idx; + + InitVariableInstruction() { + this.isAdditional(spec, "zero-init:" + idx.toString()) and + exists(spec.getNameExpr(idx)) + } + + override Instruction getRhs() { result = this } + + override ControlFlow::Root getRoot() { result.isRootOf(spec) } + } + /** An instruction that gets the next key-value pair in a range loop. */ class GetNextEntryInstruction extends Instruction { RangeElementExpr p; @@ -1123,6 +1146,11 @@ module IR { write.isAdditional(fd.getBody(), "result-zero-init:" + idx.toString()) and lhs = fd.getResultVar(idx).getDeclaration() ) + or + exists(ValueSpec spec, int idx | + write.isAdditional(spec, "zero-init:" + idx.toString()) and + lhs = spec.getNameExpr(idx) + ) } or /** A composite literal element target. */ MkLiteralElementTarget(ControlFlow::Node write) { 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 3a6dab6b52f..36595d019d0 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 @@ -1227,17 +1227,15 @@ | exprs.go:40:2:40:12 | declaration statement | exprs.go:40:2:40:12 | variable declaration | | exprs.go:40:2:40:12 | variable declaration | exprs.go:40:6:40:12 | value declaration specifier | | exprs.go:40:6:40:12 | After value declaration specifier | exprs.go:40:2:40:12 | After variable declaration | -| exprs.go:40:6:40:12 | assign:0 value declaration specifier | exprs.go:40:6:40:12 | After value declaration specifier | | exprs.go:40:6:40:12 | value declaration specifier | exprs.go:40:6:40:12 | zero-init:0 value declaration specifier | -| exprs.go:40:6:40:12 | zero-init:0 value declaration specifier | exprs.go:40:6:40:12 | assign:0 value declaration specifier | +| exprs.go:40:6:40:12 | zero-init:0 value declaration specifier | exprs.go:40:6:40:12 | After value declaration specifier | | exprs.go:41:2:41:12 | After declaration statement | exprs.go:42:2:42:20 | ... = ... | | exprs.go:41:2:41:12 | After variable declaration | exprs.go:41:2:41:12 | After declaration statement | | exprs.go:41:2:41:12 | declaration statement | exprs.go:41:2:41:12 | variable declaration | | exprs.go:41:2:41:12 | variable declaration | exprs.go:41:6:41:12 | value declaration specifier | | exprs.go:41:6:41:12 | After value declaration specifier | exprs.go:41:2:41:12 | After variable declaration | -| exprs.go:41:6:41:12 | assign:0 value declaration specifier | exprs.go:41:6:41:12 | After value declaration specifier | | exprs.go:41:6:41:12 | value declaration specifier | exprs.go:41:6:41:12 | zero-init:0 value declaration specifier | -| exprs.go:41:6:41:12 | zero-init:0 value declaration specifier | exprs.go:41:6:41:12 | assign:0 value declaration specifier | +| exprs.go:41:6:41:12 | zero-init:0 value declaration specifier | exprs.go:41:6:41:12 | After value declaration specifier | | exprs.go:42:2:42:20 | ... = ... | exprs.go:42:10:42:20 | Before type assertion | | exprs.go:42:2:42:20 | After ... = ... | exprs.go:43:2:45:2 | if statement | | exprs.go:42:2:42:20 | assign:0 ... = ... | exprs.go:42:2:42:20 | extract:1 ... = ... | @@ -1899,9 +1897,8 @@ | main.go:13:2:13:10 | declaration statement | main.go:13:2:13:10 | variable declaration | | main.go:13:2:13:10 | variable declaration | main.go:13:6:13:10 | value declaration specifier | | main.go:13:6:13:10 | After value declaration specifier | main.go:13:2:13:10 | After variable declaration | -| main.go:13:6:13:10 | assign:0 value declaration specifier | main.go:13:6:13:10 | After value declaration specifier | | main.go:13:6:13:10 | value declaration specifier | main.go:13:6:13:10 | zero-init:0 value declaration specifier | -| main.go:13:6:13:10 | zero-init:0 value declaration specifier | main.go:13:6:13:10 | assign:0 value declaration specifier | +| main.go:13:6:13:10 | zero-init:0 value declaration specifier | main.go:13:6:13:10 | After value declaration specifier | | main.go:14:2:14:8 | ... := ... | main.go:14:7:14:8 | Before 23 | | main.go:14:2:14:8 | After ... := ... | main.go:15:2:15:16 | expression statement | | main.go:14:2:14:8 | assign:0 ... := ... | main.go:14:2:14:8 | After ... := ... | @@ -2207,9 +2204,8 @@ | main.go:67:2:67:10 | declaration statement | main.go:67:2:67:10 | variable declaration | | main.go:67:2:67:10 | variable declaration | main.go:67:6:67:10 | value declaration specifier | | main.go:67:6:67:10 | After value declaration specifier | main.go:67:2:67:10 | After variable declaration | -| main.go:67:6:67:10 | assign:0 value declaration specifier | main.go:67:6:67:10 | After value declaration specifier | | main.go:67:6:67:10 | value declaration specifier | main.go:67:6:67:10 | zero-init:0 value declaration specifier | -| main.go:67:6:67:10 | zero-init:0 value declaration specifier | main.go:67:6:67:10 | assign:0 value declaration specifier | +| main.go:67:6:67:10 | zero-init:0 value declaration specifier | main.go:67:6:67:10 | After value declaration specifier | | main.go:68:2:70:2 | After for statement | main.go:71:2:71:13 | expression statement | | main.go:68:2:70:2 | [LoopHeader] for statement | main.go:68:6:68:11 | Before call to cond | | main.go:68:2:70:2 | for statement | main.go:68:6:68:11 | Before call to cond | @@ -2785,13 +2781,11 @@ | stmts3.go:7:9:7:12 | Before iota | stmts3.go:7:9:7:12 | iota | | stmts3.go:7:9:7:12 | iota | stmts3.go:7:9:7:12 | After iota | | 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:8:3:8:7 | zero-init:0 value declaration specifier | stmts3.go:8:3:8:7 | assign:0 value declaration specifier | +| stmts3.go:8:3:8:7 | zero-init:0 value declaration specifier | stmts3.go:8:3:8:7 | After value declaration specifier | | stmts3.go:9:3:9:6 | After value declaration specifier | stmts3.go:6:2:10:2 | After constant declaration | -| 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:9:3:9:6 | zero-init:0 value declaration specifier | stmts3.go:9:3:9:6 | assign:0 value declaration specifier | +| stmts3.go:9:3:9:6 | zero-init:0 value declaration specifier | stmts3.go:9:3:9:6 | After value declaration specifier | | stmts3.go:11:2:11:26 | Before return statement | stmts3.go:11:9:11:26 | Before ...-... | | stmts3.go:11:2:11:26 | return statement | stmts3.go:5:1:12:1 | Normal Exit | | stmts3.go:11:9:11:11 | After red | stmts3.go:11:15:11:19 | Before green | @@ -3307,17 +3301,15 @@ | stmts.go:47:2:47:17 | declaration statement | stmts.go:47:2:47:17 | variable declaration | | stmts.go:47:2:47:17 | variable declaration | stmts.go:47:6:47:17 | value declaration specifier | | stmts.go:47:6:47:17 | After value declaration specifier | stmts.go:47:2:47:17 | After variable declaration | -| stmts.go:47:6:47:17 | assign:0 value declaration specifier | stmts.go:47:6:47:17 | After value declaration specifier | | stmts.go:47:6:47:17 | value declaration specifier | stmts.go:47:6:47:17 | zero-init:0 value declaration specifier | -| stmts.go:47:6:47:17 | zero-init:0 value declaration specifier | stmts.go:47:6:47:17 | assign:0 value declaration specifier | +| stmts.go:47:6:47:17 | zero-init:0 value declaration specifier | stmts.go:47:6:47:17 | After value declaration specifier | | stmts.go:48:2:48:11 | After declaration statement | stmts.go:50:2:59:2 | Before select statement | | stmts.go:48:2:48:11 | After variable declaration | stmts.go:48:2:48:11 | After declaration statement | | stmts.go:48:2:48:11 | declaration statement | stmts.go:48:2:48:11 | variable declaration | | stmts.go:48:2:48:11 | variable declaration | stmts.go:48:6:48:11 | value declaration specifier | | stmts.go:48:6:48:11 | After value declaration specifier | stmts.go:48:2:48:11 | After variable declaration | -| stmts.go:48:6:48:11 | assign:0 value declaration specifier | stmts.go:48:6:48:11 | After value declaration specifier | | stmts.go:48:6:48:11 | value declaration specifier | stmts.go:48:6:48:11 | zero-init:0 value declaration specifier | -| stmts.go:48:6:48:11 | zero-init:0 value declaration specifier | stmts.go:48:6:48:11 | assign:0 value declaration specifier | +| stmts.go:48:6:48:11 | zero-init:0 value declaration specifier | stmts.go:48:6:48:11 | After value declaration specifier | | stmts.go:50:2:59:2 | After select statement | stmts.go:61:2:61:10 | Before select statement | | stmts.go:50:2:59:2 | Before select statement | stmts.go:51:9:51:11 | Before ch1 | | stmts.go:50:2:59:2 | select statement | stmts.go:51:2:52:31 | comm clause | diff --git a/go/ql/test/library-tests/semmle/go/dataflow/SSA/VarDefs.expected b/go/ql/test/library-tests/semmle/go/dataflow/SSA/VarDefs.expected index 2312cd4fe74..56b5ab62aac 100644 --- a/go/ql/test/library-tests/semmle/go/dataflow/SSA/VarDefs.expected +++ b/go/ql/test/library-tests/semmle/go/dataflow/SSA/VarDefs.expected @@ -1,4 +1,4 @@ -| main.go:13:6:13:10 | assign:0 value declaration specifier | main.go:13:6:13:6 | x | main.go:13:6:13:10 | zero-init:0 value declaration specifier | +| main.go:13:6:13:10 | zero-init:0 value declaration specifier | main.go:13:6:13:6 | x | main.go:13:6:13:10 | zero-init:0 value declaration specifier | | main.go:14:2:14:8 | assign:0 ... := ... | main.go:14:2:14:2 | y | main.go:14:7:14:8 | 23 | | main.go:17:3:17:9 | compound-rhs ... += ... | main.go:14:2:14:2 | y | main.go:17:3:17:9 | compound-rhs ... += ... | | main.go:21:3:21:7 | assign:0 ... = ... | main.go:13:6:13:6 | x | main.go:21:7:21:7 | y | @@ -13,7 +13,7 @@ | main.go:47:25:50:1 | result-zero-init:0 block statement | main.go:47:13:47:18 | result | main.go:47:25:50:1 | result-zero-init:0 block statement | | main.go:48:2:48:12 | assign:0 ... = ... | main.go:47:13:47:18 | result | main.go:48:11:48:12 | 42 | | main.go:52:26:54:1 | result-zero-init:0 block statement | main.go:52:14:52:19 | result | main.go:52:26:54:1 | result-zero-init:0 block statement | -| main.go:57:6:57:10 | assign:0 value declaration specifier | main.go:57:6:57:6 | x | main.go:57:6:57:10 | zero-init:0 value declaration specifier | +| main.go:57:6:57:10 | zero-init:0 value declaration specifier | main.go:57:6:57:6 | x | main.go:57:6:57:10 | zero-init:0 value declaration specifier | | main.go:59:3:59:7 | assign:0 ... = ... | main.go:57:6:57:6 | x | main.go:59:7:59:7 | 2 | | main.go:63:2:63:7 | assign:0 ... := ... | main.go:63:2:63:2 | y | main.go:63:7:63:7 | 1 | | main.go:64:6:64:11 | assign:0 ... := ... | main.go:64:6:64:6 | i | main.go:64:11:64:11 | 0 | @@ -39,7 +39,7 @@ | main.go:113:2:113:7 | assign:0 ... := ... | main.go:113:2:113:2 | x | main.go:113:7:113:7 | 0 | | main.go:115:3:115:12 | assign:0 ... := ... | main.go:115:3:115:3 | y | main.go:115:8:115:12 | ...+... | | main.go:116:3:116:7 | assign:0 ... = ... | main.go:113:2:113:2 | x | main.go:116:7:116:7 | y | -| main.go:128:6:128:8 | assign:0 value declaration specifier | main.go:128:6:128:6 | p | main.go:128:6:128:8 | zero-init:0 value declaration specifier | +| main.go:128:6:128:8 | zero-init:0 value declaration specifier | main.go:128:6:128:6 | p | main.go:128:6:128:8 | zero-init:0 value declaration specifier | | main.go:130:3:130:24 | assign:0 ... = ... | main.go:128:6:128:6 | p | main.go:130:7:130:24 | struct literal | | main.go:130:9:130:9 | lit-init 2 | main.go:122:2:122:2 | a | main.go:130:9:130:9 | 2 | | main.go:130:12:130:18 | lit-init struct literal | main.go:123:2:123:2 | b | main.go:130:12:130:18 | struct literal |