diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll index b1160ec8eaa..a450cf5eb99 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -502,6 +502,11 @@ module GoCfg { exists(int i | ( notBlankIdent(n.(Go::Assignment).getLhs(i)) and + // A compound assignment (`x += y`) folds its write into the + // `compound-rhs` binary-operation instruction rather than emitting a + // separate `assign:i` write node (see + // `IR::EvalCompoundAssignRhsInstruction`). + not n instanceof Go::CompoundAssignStmt 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`), @@ -1139,7 +1144,10 @@ module GoCfg { ) or ( - notBlankIdent(assgn.(Go::Assignment).getLhs(j)) + notBlankIdent(assgn.(Go::Assignment).getLhs(j)) and + // Compound assignments fold their write into `compound-rhs` (ord -1) + // above, so they emit no separate `assign:j` node. + not assgn instanceof Go::CompoundAssignStmt or notBlankIdent(assgn.(Go::ValueSpec).getNameExpr(j)) or diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index b615a4bceba..e7ad8ea7d0d 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -726,8 +726,6 @@ module IR { implicitInitInstruction(any(ValueEntity v | spec.getNameExpr(i) = v.getDeclaration())) ) or - result.(EvalCompoundAssignRhsInstruction).isAdditional(assgn, "compound-rhs") - or result.(ExtractTupleElementInstruction).isAdditional(assgn, "extract:" + i.toString()) } @@ -735,9 +733,11 @@ module IR { } /** - * An instruction that computes the (implicit) right-hand side of a compound assignment. + * An instruction that computes the (implicit) right-hand side of a compound assignment, + * such as the `x + y` in `x += y`, and writes the resulting value to the assignment's + * left-hand side. */ - class EvalCompoundAssignRhsInstruction extends Instruction { + class EvalCompoundAssignRhsInstruction extends WriteInstruction { CompoundAssignStmt assgn; EvalCompoundAssignRhsInstruction() { this.isAdditional(assgn, "compound-rhs") } @@ -745,6 +745,8 @@ module IR { /** Gets the corresponding compound assignment statement. */ CompoundAssignStmt getAssignment() { result = assgn } + override Instruction getRhs() { result = this } + override Type getResultType() { result = assgn.getRhs().getType() } override ControlFlow::Root getRoot() { result.isRootOf(assgn) } @@ -1228,6 +1230,10 @@ module IR { or exists(IncDecStmt ids | write.isIn(ids) | lhs = ids.getOperand().stripParens()) or + exists(CompoundAssignStmt ca | write.isAdditional(ca, "compound-rhs") | + lhs = ca.getLhs().stripParens() + ) + or exists(FuncDef fd, int idx | write.isAdditional(fd.getBody(), "param-init:" + idx.toString()) and lhs = fd.getParameter(idx).getDeclaration() 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 da56e0f6b4f..b8029e74289 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 @@ -1375,8 +1375,7 @@ | exprs.go:51:3:51:5 | res | exprs.go:51:3:51:5 | After res | | exprs.go:51:3:51:14 | ... += ... | exprs.go:51:3:51:5 | Before res | | exprs.go:51:3:51:14 | After ... += ... | exprs.go:50:31:52:2 | After block statement | -| exprs.go:51:3:51:14 | assign:0 ... += ... | exprs.go:51:3:51:14 | After ... += ... | -| exprs.go:51:3:51:14 | compound-rhs ... += ... | exprs.go:51:3:51:14 | assign:0 ... += ... | +| exprs.go:51:3:51:14 | compound-rhs ... += ... | exprs.go:51:3:51:14 | After ... += ... | | exprs.go:51:10:51:11 | After xs | exprs.go:51:13:51:13 | Before i | | exprs.go:51:10:51:11 | Before xs | exprs.go:51:10:51:11 | xs | | exprs.go:51:10:51:11 | xs | exprs.go:51:10:51:11 | After xs | @@ -2013,8 +2012,7 @@ | main.go:17:3:17:3 | y | main.go:17:3:17:3 | After y | | main.go:17:3:17:9 | ... += ... | main.go:17:3:17:3 | Before y | | main.go:17:3:17:9 | After ... += ... | main.go:16:12:18:2 | After block statement | -| main.go:17:3:17:9 | assign:0 ... += ... | main.go:17:3:17:9 | After ... += ... | -| main.go:17:3:17:9 | compound-rhs ... += ... | main.go:17:3:17:9 | assign:0 ... += ... | +| main.go:17:3:17:9 | compound-rhs ... += ... | main.go:17:3:17:9 | After ... += ... | | main.go:17:8:17:9 | 19 | main.go:17:8:17:9 | After 19 | | main.go:17:8:17:9 | After 19 | main.go:17:3:17:9 | compound-rhs ... += ... | | main.go:17:8:17:9 | Before 19 | main.go:17:8:17:9 | 19 | @@ -2131,8 +2129,7 @@ | main.go:35:2:35:3 | star expression | main.go:35:2:35:3 | After star expression | | main.go:35:2:35:9 | ... += ... | main.go:35:2:35:3 | Before star expression | | main.go:35:2:35:9 | After ... += ... | main.go:34:19:36:1 | After block statement | -| main.go:35:2:35:9 | assign:0 ... += ... | main.go:35:2:35:9 | After ... += ... | -| main.go:35:2:35:9 | compound-rhs ... += ... | main.go:35:2:35:9 | assign:0 ... += ... | +| main.go:35:2:35:9 | compound-rhs ... += ... | main.go:35:2:35:9 | After ... += ... | | main.go:35:3:35:3 | After x | main.go:35:2:35:3 | star expression | | main.go:35:3:35:3 | Before x | main.go:35:3:35:3 | x | | main.go:35:3:35:3 | x | main.go:35:3:35:3 | After x | @@ -2968,8 +2965,7 @@ | stmts5.go:8:2:8:7 | selection of val | stmts5.go:8:2:8:7 | After selection of val | | stmts5.go:8:2:8:16 | ... += ... | stmts5.go:8:2:8:7 | Before selection of val | | stmts5.go:8:2:8:16 | After ... += ... | stmts5.go:7:33:9:1 | After block statement | -| stmts5.go:8:2:8:16 | assign:0 ... += ... | stmts5.go:8:2:8:16 | After ... += ... | -| stmts5.go:8:2:8:16 | compound-rhs ... += ... | stmts5.go:8:2:8:16 | assign:0 ... += ... | +| stmts5.go:8:2:8:16 | compound-rhs ... += ... | stmts5.go:8:2:8:16 | After ... += ... | | stmts5.go:8:12:8:16 | After other | stmts5.go:8:2:8:16 | compound-rhs ... += ... | | stmts5.go:8:12:8:16 | Before other | stmts5.go:8:12:8:16 | other | | stmts5.go:8:12:8:16 | other | stmts5.go:8:12:8:16 | After other | 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 5fc981c8cb0..d9f48462ce2 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,6 +1,6 @@ | 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: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 | assign:0 ... += ... | main.go:14:2:14:2 | y | main.go:17:3:17:9 | compound-rhs ... += ... | +| 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 | | main.go:26:28:32:1 | param-init:0 block statement | main.go:26:10:26:10 | x | main.go:26:28:32:1 | arg:0 block statement | | main.go:27:2:27:13 | assign:0 ... := ... | main.go:27:2:27:2 | a | main.go:27:10:27:10 | x | diff --git a/go/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected b/go/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected index a2a04d01188..2d25d18ce96 100644 --- a/go/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected +++ b/go/ql/test/query-tests/RedundantCode/DeadStoreOfLocal/DeadStoreOfLocal.expected @@ -13,13 +13,13 @@ | testdata.go:134:3:134:17 | assign:0 ... = ... | This definition of x is never used. | | testdata.go:143:3:143:17 | assign:0 ... = ... | This definition of x is never used. | | testdata.go:164:3:164:17 | assign:0 ... = ... | This definition of x is never used. | -| testdata.go:172:3:172:18 | assign:0 ... += ... | This definition of x is never used. | +| testdata.go:172:3:172:18 | compound-rhs ... += ... | This definition of x is never used. | | testdata.go:180:3:180:5 | increment statement | This definition of x is never used. | | testdata.go:201:2:201:24 | assign:0 ... = ... | This definition of x is never used. | | testdata.go:262:2:262:16 | assign:0 ... = ... | This definition of x is never used. | | testdata.go:268:2:268:16 | assign:0 ... = ... | This definition of x is never used. | -| testdata.go:309:2:309:7 | assign:0 ... /= ... | This definition of a is never used. | -| testdata.go:321:2:321:7 | assign:0 ... %= ... | This definition of a is never used. | +| testdata.go:309:2:309:7 | compound-rhs ... /= ... | This definition of a is never used. | +| testdata.go:321:2:321:7 | compound-rhs ... %= ... | This definition of a is never used. | | testdata.go:387:3:387:17 | assign:0 ... = ... | This definition of x is never used. | | testdata.go:432:3:432:17 | assign:0 ... = ... | This definition of x is never used. | | testdata.go:434:3:434:17 | assign:0 ... = ... | This definition of x is never used. |