From a264da2bd8bb3c1b6079553a448d0de070858b61 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 07:29:21 +0000 Subject: [PATCH 1/8] Add shared AstSig consistency checks module Agent-Logs-Url: https://github.com/github/codeql/sessions/bf1d9f01-0696-4099-b5c6-3a13d639c3a4 Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../codeql/controlflow/AstConsistency.qll | 168 ++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 shared/controlflow/codeql/controlflow/AstConsistency.qll diff --git a/shared/controlflow/codeql/controlflow/AstConsistency.qll b/shared/controlflow/codeql/controlflow/AstConsistency.qll new file mode 100644 index 000000000000..f16a25947fac --- /dev/null +++ b/shared/controlflow/codeql/controlflow/AstConsistency.qll @@ -0,0 +1,168 @@ +/** + * Provides consistency queries for `AstSig`. + */ +overlay[local?] +module; + +private import ControlFlowGraph + +module MakeAstConsistency Ast> { + private import Ast + + private predicate parentStep(AstNode child, AstNode parent) { child = getChild(parent, _) } + + private predicate astMemberChild(AstNode parent, AstNode child, string member) { + callableGetBody(parent.(Callable)) = child and member = "callableGetBody" + or + callableGetParameter(parent.(Callable), _) = child and member = "callableGetParameter" + or + parent.(Parameter).getDefaultValue() = child and member = "Parameter.getDefaultValue" + or + parent.(BlockStmt).getStmt(_) = child and member = "BlockStmt.getStmt" + or + parent.(BlockStmt).getLastStmt() = child and member = "BlockStmt.getLastStmt" + or + parent.(ExprStmt).getExpr() = child and member = "ExprStmt.getExpr" + or + parent.(IfStmt).getCondition() = child and member = "IfStmt.getCondition" + or + parent.(IfStmt).getThen() = child and member = "IfStmt.getThen" + or + parent.(IfStmt).getElse() = child and member = "IfStmt.getElse" + or + parent.(LoopStmt).getBody() = child and member = "LoopStmt.getBody" + or + parent.(WhileStmt).getCondition() = child and member = "WhileStmt.getCondition" + or + parent.(DoStmt).getCondition() = child and member = "DoStmt.getCondition" + or + parent.(ForStmt).getInit(_) = child and member = "ForStmt.getInit" + or + parent.(ForStmt).getCondition() = child and member = "ForStmt.getCondition" + or + parent.(ForStmt).getUpdate(_) = child and member = "ForStmt.getUpdate" + or + parent.(ForeachStmt).getVariable() = child and member = "ForeachStmt.getVariable" + or + parent.(ForeachStmt).getCollection() = child and member = "ForeachStmt.getCollection" + or + parent.(ReturnStmt).getExpr() = child and member = "ReturnStmt.getExpr" + or + parent.(Throw).getExpr() = child and member = "Throw.getExpr" + or + parent.(TryStmt).getBody() = child and member = "TryStmt.getBody" + or + parent.(TryStmt).getCatch(_) = child and member = "TryStmt.getCatch" + or + parent.(TryStmt).getFinally() = child and member = "TryStmt.getFinally" + or + getTryInit(parent.(TryStmt), _) = child and member = "getTryInit" + or + getTryElse(parent.(TryStmt)) = child and member = "getTryElse" + or + parent.(CatchClause).getVariable() = child and member = "CatchClause.getVariable" + or + parent.(CatchClause).getCondition() = child and member = "CatchClause.getCondition" + or + parent.(CatchClause).getBody() = child and member = "CatchClause.getBody" + or + parent.(Switch).getExpr() = child and member = "Switch.getExpr" + or + parent.(Switch).getCase(_) = child and member = "Switch.getCase" + or + parent.(Switch).getStmt(_) = child and member = "Switch.getStmt" + or + parent.(Case).getAPattern() = child and member = "Case.getAPattern" + or + parent.(Case).getGuard() = child and member = "Case.getGuard" + or + parent.(Case).getBody() = child and member = "Case.getBody" + or + parent.(ConditionalExpr).getCondition() = child and member = "ConditionalExpr.getCondition" + or + parent.(ConditionalExpr).getThen() = child and member = "ConditionalExpr.getThen" + or + parent.(ConditionalExpr).getElse() = child and member = "ConditionalExpr.getElse" + or + parent.(BinaryExpr).getLeftOperand() = child and member = "BinaryExpr.getLeftOperand" + or + parent.(BinaryExpr).getRightOperand() = child and member = "BinaryExpr.getRightOperand" + or + parent.(UnaryExpr).getOperand() = child and member = "UnaryExpr.getOperand" + or + parent.(PatternMatchExpr).getExpr() = child and member = "PatternMatchExpr.getExpr" + or + parent.(PatternMatchExpr).getPattern() = child and member = "PatternMatchExpr.getPattern" + } + + private class StructuredAstNode extends AstNode { + StructuredAstNode() { + this instanceof Callable or + this instanceof Parameter or + this instanceof BlockStmt or + this instanceof ExprStmt or + this instanceof IfStmt or + this instanceof LoopStmt or + this instanceof WhileStmt or + this instanceof DoStmt or + this instanceof ForStmt or + this instanceof ForeachStmt or + this instanceof ReturnStmt or + this instanceof Throw or + this instanceof TryStmt or + this instanceof CatchClause or + this instanceof Switch or + this instanceof Case or + this instanceof ConditionalExpr or + this instanceof BinaryExpr or + this instanceof UnaryExpr or + this instanceof PatternMatchExpr + } + } + + module Consistency { + /** Holds if `child` has multiple AST parents. */ + query predicate multipleParents(AstNode child, AstNode parent1, AstNode parent2) { + getChild(parent1, _) = child and + getChild(parent2, _) = child and + parent1 != parent2 + } + + /** + * Holds if `node` has an enclosing callable that is not reachable through + * parent steps. + */ + query predicate nonAncestorEnclosingCallable(AstNode node, Callable callable) { + callable = getEnclosingCallable(node) and + not parentStep*(node, callable) + } + + /** Holds if `child` is assigned multiple child indices under `parent`. */ + query predicate childAtMultipleIndices(AstNode parent, AstNode child, int index1, int index2) { + getChild(parent, index1) = child and + getChild(parent, index2) = child and + index1 < index2 + } + + /** Holds if multiple children of `parent` share the same child index. */ + query predicate siblingsWithIdenticalIndex( + AstNode parent, int index, AstNode child1, AstNode child2 + ) { + getChild(parent, index) = child1 and + getChild(parent, index) = child2 and + child1 != child2 + } + + /** Holds if a member child relation is not reflected by `getChild`. */ + query predicate memberChildMissingFromGetChild(AstNode parent, AstNode child, string member) { + astMemberChild(parent, child, member) and + not getChild(parent, _) = child + } + + /** Holds if a `getChild` relation for a structured AST node has no matching member predicate. */ + query predicate getChildMissingFromMember(StructuredAstNode parent, int index, AstNode child) { + child = getChild(parent, index) and + not astMemberChild(parent, child, _) + } + } +} From 07474325b4fe84cdeba8ef3397852dc98678fb30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 07:30:26 +0000 Subject: [PATCH 2/8] Refine AstConsistency member names Agent-Logs-Url: https://github.com/github/codeql/sessions/bf1d9f01-0696-4099-b5c6-3a13d639c3a4 Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- shared/controlflow/codeql/controlflow/AstConsistency.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/AstConsistency.qll b/shared/controlflow/codeql/controlflow/AstConsistency.qll index f16a25947fac..233451162b64 100644 --- a/shared/controlflow/codeql/controlflow/AstConsistency.qll +++ b/shared/controlflow/codeql/controlflow/AstConsistency.qll @@ -12,9 +12,9 @@ module MakeAstConsistency Ast> { private predicate parentStep(AstNode child, AstNode parent) { child = getChild(parent, _) } private predicate astMemberChild(AstNode parent, AstNode child, string member) { - callableGetBody(parent.(Callable)) = child and member = "callableGetBody" + callableGetBody(parent.(Callable)) = child and member = "Callable.getBody" or - callableGetParameter(parent.(Callable), _) = child and member = "callableGetParameter" + callableGetParameter(parent.(Callable), _) = child and member = "Callable.getParameter" or parent.(Parameter).getDefaultValue() = child and member = "Parameter.getDefaultValue" or From affbd3fefd8c99f99dcdfc179373125e89ad8a28 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 07:45:11 +0000 Subject: [PATCH 3/8] Apply review fixes to AstConsistency.qll Agent-Logs-Url: https://github.com/github/codeql/sessions/85daaeb0-9115-4937-af69-fbb3be5f3d64 Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- .../codeql/controlflow/AstConsistency.qll | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/AstConsistency.qll b/shared/controlflow/codeql/controlflow/AstConsistency.qll index 233451162b64..8578c2651601 100644 --- a/shared/controlflow/codeql/controlflow/AstConsistency.qll +++ b/shared/controlflow/codeql/controlflow/AstConsistency.qll @@ -9,12 +9,12 @@ private import ControlFlowGraph module MakeAstConsistency Ast> { private import Ast - private predicate parentStep(AstNode child, AstNode parent) { child = getChild(parent, _) } + private AstNode getParent(AstNode child) { child = getChild(result, _) } private predicate astMemberChild(AstNode parent, AstNode child, string member) { - callableGetBody(parent.(Callable)) = child and member = "Callable.getBody" + callableGetBody(parent) = child and member = "Callable.getBody" or - callableGetParameter(parent.(Callable), _) = child and member = "Callable.getParameter" + callableGetParameter(parent, _) = child and member = "Callable.getParameter" or parent.(Parameter).getDefaultValue() = child and member = "Parameter.getDefaultValue" or @@ -56,9 +56,9 @@ module MakeAstConsistency Ast> { or parent.(TryStmt).getFinally() = child and member = "TryStmt.getFinally" or - getTryInit(parent.(TryStmt), _) = child and member = "getTryInit" + getTryInit(parent, _) = child and member = "getTryInit" or - getTryElse(parent.(TryStmt)) = child and member = "getTryElse" + getTryElse(parent) = child and member = "getTryElse" or parent.(CatchClause).getVariable() = child and member = "CatchClause.getVariable" or @@ -121,6 +121,27 @@ module MakeAstConsistency Ast> { } module Consistency { + /** Holds if the consistency query `query` has `results` results. */ + query predicate consistencyOverview(string query, int results) { + query = "multipleParents" and + results = strictcount(AstNode child | multipleParents(child, _, _)) + or + query = "nonAncestorEnclosingCallable" and + results = strictcount(AstNode node | nonAncestorEnclosingCallable(node, _)) + or + query = "childAtMultipleIndices" and + results = strictcount(AstNode child | childAtMultipleIndices(_, child, _, _)) + or + query = "siblingsWithIdenticalIndex" and + results = strictcount(AstNode parent, int index | siblingsWithIdenticalIndex(parent, index, _, _)) + or + query = "memberChildMissingFromGetChild" and + results = strictcount(AstNode parent, AstNode child | memberChildMissingFromGetChild(parent, child, _)) + or + query = "getChildMissingFromMember" and + results = strictcount(AstNode parent, int index | getChildMissingFromMember(parent, index, _)) + } + /** Holds if `child` has multiple AST parents. */ query predicate multipleParents(AstNode child, AstNode parent1, AstNode parent2) { getChild(parent1, _) = child and @@ -134,7 +155,7 @@ module MakeAstConsistency Ast> { */ query predicate nonAncestorEnclosingCallable(AstNode node, Callable callable) { callable = getEnclosingCallable(node) and - not parentStep*(node, callable) + not getParent*(node) = callable } /** Holds if `child` is assigned multiple child indices under `parent`. */ From 2ab6263a4f8c2173d057bb08ff0bfa519e2b30d6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 07:54:47 +0000 Subject: [PATCH 4/8] Instantiate MakeAstConsistency in ControlFlowGraph.qll Agent-Logs-Url: https://github.com/github/codeql/sessions/44b8fcaf-0a89-41e7-8685-068e4764ebb3 Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- shared/controlflow/codeql/controlflow/ControlFlowGraph.qll | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index 514a68cba474..c8d8cb2d2c2c 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -2063,6 +2063,7 @@ module Make0 Ast> { */ private import PrintGraph as Pp + private import AstConsistency as AstConsistency_ private class ControlFlowNodeAlias = ControlFlowNode; @@ -2078,6 +2079,9 @@ module Make0 Ast> { import Pp::PrintGraph + /** Provides AST consistency queries. */ + module AstConsistency = AstConsistency_::MakeAstConsistency::Consistency; + /** Provides a set of consistency queries. */ module Consistency { /** Holds if the consistency query `query` has `results` results. */ From d6df3213c8f740a2b9e387e4b712305166abb90f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 08:01:25 +0000 Subject: [PATCH 5/8] Remove unnecessary AstConsistency_ alias from import Agent-Logs-Url: https://github.com/github/codeql/sessions/ffa45b1a-a5ad-4233-889c-25e32c74bcd8 Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- shared/controlflow/codeql/controlflow/ControlFlowGraph.qll | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll index c8d8cb2d2c2c..f384bf0dd8e8 100644 --- a/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll +++ b/shared/controlflow/codeql/controlflow/ControlFlowGraph.qll @@ -2063,7 +2063,7 @@ module Make0 Ast> { */ private import PrintGraph as Pp - private import AstConsistency as AstConsistency_ + private import AstConsistency private class ControlFlowNodeAlias = ControlFlowNode; @@ -2080,7 +2080,7 @@ module Make0 Ast> { import Pp::PrintGraph /** Provides AST consistency queries. */ - module AstConsistency = AstConsistency_::MakeAstConsistency::Consistency; + module AstConsistency = MakeAstConsistency::Consistency; /** Provides a set of consistency queries. */ module Consistency { From d50233ecbeed59cde3e140b933c466ab1725f1a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 08:07:23 +0000 Subject: [PATCH 6/8] Add java/ql/consistency-queries/AstConsistency.ql Agent-Logs-Url: https://github.com/github/codeql/sessions/f4320ed7-5db7-4125-8221-9c6b8a51b4ba Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- java/ql/consistency-queries/AstConsistency.ql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 java/ql/consistency-queries/AstConsistency.ql diff --git a/java/ql/consistency-queries/AstConsistency.ql b/java/ql/consistency-queries/AstConsistency.ql new file mode 100644 index 000000000000..b660d574475a --- /dev/null +++ b/java/ql/consistency-queries/AstConsistency.ql @@ -0,0 +1,2 @@ +import java +import ControlFlow::AstConsistency From ce2186f04e6043b06b8b07ae218d4abad7970ad7 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Fri, 8 May 2026 10:31:48 +0200 Subject: [PATCH 7/8] Apply suggestions from code review Fix compilation. Co-authored-by: Anders Schack-Mulligen --- shared/controlflow/codeql/controlflow/AstConsistency.qll | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/shared/controlflow/codeql/controlflow/AstConsistency.qll b/shared/controlflow/codeql/controlflow/AstConsistency.qll index 8578c2651601..d8bcfd0670f6 100644 --- a/shared/controlflow/codeql/controlflow/AstConsistency.qll +++ b/shared/controlflow/codeql/controlflow/AstConsistency.qll @@ -4,6 +4,7 @@ overlay[local?] module; +private import codeql.util.Location private import ControlFlowGraph module MakeAstConsistency Ast> { @@ -95,7 +96,9 @@ module MakeAstConsistency Ast> { parent.(PatternMatchExpr).getPattern() = child and member = "PatternMatchExpr.getPattern" } - private class StructuredAstNode extends AstNode { + final private class FinalAstNode = AstNode; + + private class StructuredAstNode extends FinalAstNode { StructuredAstNode() { this instanceof Callable or this instanceof Parameter or From 09215013dfbf9247415a9e19245b12a046bcbbb9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 08:47:34 +0000 Subject: [PATCH 8/8] Fix trailing whitespace in AstConsistency.qll Agent-Logs-Url: https://github.com/github/codeql/sessions/3e6eea56-e1c7-49e7-b31e-2351f7cbdfc4 Co-authored-by: aschackmull <28296824+aschackmull@users.noreply.github.com> --- shared/controlflow/codeql/controlflow/AstConsistency.qll | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shared/controlflow/codeql/controlflow/AstConsistency.qll b/shared/controlflow/codeql/controlflow/AstConsistency.qll index d8bcfd0670f6..4170630966c4 100644 --- a/shared/controlflow/codeql/controlflow/AstConsistency.qll +++ b/shared/controlflow/codeql/controlflow/AstConsistency.qll @@ -97,7 +97,7 @@ module MakeAstConsistency Ast> { } final private class FinalAstNode = AstNode; - + private class StructuredAstNode extends FinalAstNode { StructuredAstNode() { this instanceof Callable or