From 081f24a3b95c3cf02fa8399332457f26fa74c7eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 24 Apr 2026 15:37:20 +0000 Subject: [PATCH] Fix shared Go CFG expression and return-edge regressions Agent-Logs-Url: https://github.com/github/codeql/sessions/3f96ead2-cda4-479c-9e37-f38ace035870 Co-authored-by: owen-mc <62447351+owen-mc@users.noreply.github.com> --- .../go/controlflow/ControlFlowGraph.qll | 5 ++++- .../go/controlflow/ControlFlowGraphShared.qll | 20 +++++++++++++++++-- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll index 0bbb3045d2d..6122ae6639f 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraph.qll @@ -340,7 +340,10 @@ module ControlFlow { * cannot return normally, but never fails to hold of a function that can return normally. */ predicate mayReturnNormally(FuncDecl f) { - exists(GoCfg::ControlFlow::NormalExitNode exit | exit.getEnclosingCallable() = f) + exists(GoCfg::ControlFlow::NormalExitNode exit | + exit.getEnclosingCallable() = f and + exists(exit.getAPredecessor()) + ) } /** diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll index 00f366ea38c..9db6fa28838 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -79,8 +79,7 @@ module GoCfg { or not node instanceof Callable and not exists(node.getEnclosingFunction()) and - result = node.getFile() and - result instanceof Callable + result = node.getFile() } class Stmt = Go::Stmt; @@ -292,6 +291,10 @@ module GoCfg { predicate preOrderExpr(Ast::Expr e) { none() } predicate postOrInOrder(Ast::AstNode n) { + n instanceof Go::ReferenceExpr + or + n instanceof Go::BasicLit + or n instanceof Go::CallExpr and not n = any(Go::DeferStmt defer).getCall() and not n = any(Go::GoStmt go_).getCall() @@ -633,6 +636,12 @@ module GoCfg { c.hasLabel(TGoLabel(lbl.getLabel())) ) or + exists(Go::FuncDef fd | + ast = fd.getBody() and + c.getSuccessorType() instanceof ReturnSuccessor and + n.isAfter(fd.getBody()) + ) + or exists(Go::LabeledStmt lbl, Go::FuncDef fd | ast = fd.getBody() and n.isBefore(lbl) and @@ -841,6 +850,13 @@ module GoCfg { 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