From f07ab4517edbde3e3136357693e539a3ebaea054 Mon Sep 17 00:00:00 2001 From: Owen Mansel-Chan Date: Fri, 10 Jul 2026 08:16:05 +0100 Subject: [PATCH] Fold implicit literal element index into the lit-init node A positional element of an array or slice literal (e.g. the elements of []T{a, b}) previously created a 'lit-index' node holding its implicit index constant (0, 1, ...), in addition to the 'lit-init' write node. Array and slice content flow is index-insensitive (the store step ignores the element index), map literals always use explicit keys, and literal bases are not tracked by VariableWithFields, so the implicit index value is never actually consumed. Drop the 'lit-index' node and let the lit-init instruction act as its own opaque index. All 100 data-flow library tests pass unchanged, confirming no content-flow loss. --- .../go/controlflow/ControlFlowGraphShared.qll | 21 ++----- go/ql/lib/semmle/go/controlflow/IR.qll | 56 ++----------------- .../ControlFlowNode_getASuccessor.expected | 21 +++---- 3 files changed, 16 insertions(+), 82 deletions(-) diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll index 76254e963d3..7a4db67f273 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -587,14 +587,6 @@ module GoCfg { 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 @@ -1480,17 +1472,12 @@ module GoCfg { not exists(lit.getElement(_)) and n2.isAfter(lit) ) or - // After element → optional lit-index → lit-init → next element or After + // After element → lit-init → next element or After. + // Positional array/slice elements have an implicit index that is + // modelled on the `lit-init` instruction itself (see + // `IR::InitLiteralElementInstruction`) rather than as a separate node. 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 diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index fce3f5f5873..c860ce1908d 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -159,8 +159,6 @@ module IR { or this instanceof InitLiteralComponentInstruction and result = "element init" or - this instanceof ImplicitLiteralElementIndexInstruction and result = "element index" - or this instanceof AssignInstruction and result = "assignment" or this instanceof EvalCompoundAssignRhsInstruction and @@ -578,7 +576,11 @@ module IR { Instruction getIndex() { result = evalExprInstruction(elt.(KeyValueExpr).getKey()) or - result.(ImplicitLiteralElementIndexInstruction).isAdditional(elt, "lit-index") + // A positional array/slice element has an implicit index. Array/slice + // content flow is index-insensitive, so rather than materialise a + // separate index node the element-init instruction acts as its own + // (opaque) index. + not elt instanceof KeyValueExpr and result = this } } @@ -627,54 +629,6 @@ module IR { } } - private predicate noExplicitKeys(CompositeLit lit) { - not lit.getAnElement() instanceof KeyValueExpr - } - - 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) | - noExplicitKeys(lit) and result = i - or - result = elt.(KeyValueExpr).getKey().getIntValue() - or - not elt instanceof KeyValueExpr and - ( - i = 0 and result = 0 - or - result = getElementIndex(lit, i - 1) + 1 - ) - ) - } - - /** - * An IR instruction computing the implicit index of an element in an array or slice literal. - */ - class ImplicitLiteralElementIndexInstruction extends Instruction { - Expr elt; - - ImplicitLiteralElementIndexInstruction() { this.isAdditional(elt, "lit-index") } - - override Type getResultType() { result instanceof IntType } - - override ControlFlow::Root getRoot() { result.isRootOf(elt) } - - override int getIntValue() { - exists(CompositeLit lit, int i | elt = lit.getElement(i) | result = getElementIndex(lit, i)) - } - - override string getStringValue() { none() } - - override string getExactValue() { result = this.getIntValue().toString() } - - override predicate isPlatformIndependentConstant() { any() } - - override predicate isConst() { any() } - } - /** * An instruction assigning to a variable or field. */ 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 79d00c2af2a..3a6dab6b52f 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 @@ -969,9 +969,8 @@ | exprs.go:16:17:16:23 | After struct3 | exprs.go:16:17:16:25 | selection of x | | exprs.go:16:17:16:23 | Before struct3 | exprs.go:16:17:16:23 | struct3 | | exprs.go:16:17:16:23 | struct3 | exprs.go:16:17:16:23 | After struct3 | -| exprs.go:16:17:16:25 | After selection of x | exprs.go:16:17:16:25 | lit-index selection of x | +| exprs.go:16:17:16:25 | After selection of x | exprs.go:16:17:16:25 | lit-init 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 | lit-index selection of x | exprs.go:16:17:16:25 | lit-init selection of x | | exprs.go:16:17:16:25 | lit-init selection of x | exprs.go:16:10:16:26 | After array literal | | exprs.go:16:17:16:25 | selection of x | exprs.go:16:17:16:25 | After selection of x | | exprs.go:17:2:17:40 | ... := ... | exprs.go:17:10:17:40 | Before array literal | @@ -983,9 +982,8 @@ | exprs.go:17:19:17:25 | After struct3 | exprs.go:17:19:17:27 | selection of x | | exprs.go:17:19:17:25 | Before struct3 | exprs.go:17:19:17:25 | struct3 | | exprs.go:17:19:17:25 | struct3 | exprs.go:17:19:17:25 | After struct3 | -| exprs.go:17:19:17:27 | After selection of x | exprs.go:17:19:17:27 | lit-index selection of x | +| exprs.go:17:19:17:27 | After selection of x | exprs.go:17:19:17:27 | lit-init 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 | lit-index selection of x | exprs.go:17:19:17:27 | lit-init selection of x | | exprs.go:17:19:17:27 | lit-init selection of x | exprs.go:17:30:17:39 | Before key-value pair | | exprs.go:17:19:17:27 | selection of x | exprs.go:17:19:17:27 | After selection of x | | exprs.go:17:30:17:30 | 2 | exprs.go:17:30:17:30 | After 2 | @@ -1011,14 +1009,12 @@ | exprs.go:18:9:18:22 | After slice literal | exprs.go:18:2:18:22 | assign:0 ... := ... | | exprs.go:18:9:18:22 | Before slice literal | exprs.go:18:9:18:22 | slice literal | | 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 | After s | exprs.go:18:18:18:18 | lit-init s | | exprs.go:18:18:18:18 | Before s | exprs.go:18:18:18:18 | s | -| exprs.go:18:18:18:18 | lit-index s | exprs.go:18:18:18:18 | lit-init 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 | After s | exprs.go:18:21:18:21 | lit-init s | | exprs.go:18:21:18:21 | Before s | exprs.go:18:21:18:21 | s | -| exprs.go:18:21:18:21 | lit-index s | exprs.go:18:21:18:21 | lit-init s | | exprs.go:18:21:18:21 | lit-init s | exprs.go:18:9:18:22 | After slice literal | | exprs.go:18:21:18:21 | s | exprs.go:18:21:18:21 | After s | | exprs.go:19:2:19:38 | ... := ... | exprs.go:19:8:19:38 | Before map literal | @@ -1373,19 +1369,16 @@ | exprs.go:61:9:61:22 | Before slice literal | exprs.go:61:9:61:22 | slice literal | | 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 | After 1 | exprs.go:61:15:61:15 | lit-init 1 | | exprs.go:61:15:61:15 | Before 1 | exprs.go:61:15:61:15 | 1 | -| exprs.go:61:15:61:15 | lit-index 1 | exprs.go:61:15:61:15 | lit-init 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 | After 2 | exprs.go:61:18:61:18 | lit-init 2 | | exprs.go:61:18:61:18 | Before 2 | exprs.go:61:18:61:18 | 2 | -| exprs.go:61:18:61:18 | lit-index 2 | exprs.go:61:18:61:18 | lit-init 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 | After 3 | exprs.go:61:21:61:21 | lit-init 3 | | exprs.go:61:21:61:21 | Before 3 | exprs.go:61:21:61:21 | 3 | -| exprs.go:61:21:61:21 | lit-index 3 | exprs.go:61:21:61:21 | lit-init 3 | | exprs.go:61:21:61:21 | lit-init 3 | exprs.go:61:9:61:22 | After slice literal | | exprs.go:64:1:64:19 | After variable declaration | exprs.go:65:1:65:24 | variable declaration | | exprs.go:64:1:64:19 | variable declaration | exprs.go:64:5:64:19 | value declaration specifier |