From 43f1b84025ab28f1d764d40c43b3b757f11a7a11 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 Feb 2026 13:02:09 +0100 Subject: [PATCH 01/11] Prepare to share PrintGraph with DFG viewer --- shared/controlflow/codeql/controlflow/Cfg.qll | 11 ++-- .../codeql/controlflow/PrintGraph.qll | 51 +++++++++---------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index 157bf0ffd4f3..74b52629f8fc 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -1315,13 +1315,18 @@ module MakeWithSplitting< private import PrintGraph as Pp + final private class FinalNode = Node; + private module PrintGraphInput implements Pp::InputSig { class Callable = CfgScope; - class ControlFlowNode = Node; + class Node = FinalNode; - ControlFlowNode getASuccessor(ControlFlowNode n, SuccessorType t) { - result = n.getASuccessor(t) + Node getASuccessor(Node n, string s) { + exists(SuccessorType t | + result = n.getASuccessor(t) and + if t instanceof DirectSuccessor then s = "" else s = t.toString() + ) } } diff --git a/shared/controlflow/codeql/controlflow/PrintGraph.qll b/shared/controlflow/codeql/controlflow/PrintGraph.qll index c4a942feab23..85b474ffbaa4 100644 --- a/shared/controlflow/codeql/controlflow/PrintGraph.qll +++ b/shared/controlflow/codeql/controlflow/PrintGraph.qll @@ -1,6 +1,6 @@ /** - * Provides modules for printing control flow graphs in VSCode via the "View - * CFG" query. Also provides modules for printing control flow graphs in tests + * Provides modules for printing control flow and data flow graphs in VSCode via the + * "View CFG/DFG" queries. Also provides modules for printing such graphs in tests * and as Mermaid diagrams. */ overlay[local?] @@ -8,12 +8,11 @@ module; private import codeql.util.FileSystem private import codeql.util.Location -private import SuccessorType signature module InputSig { class Callable; - class ControlFlowNode { + class Node { Callable getEnclosingCallable(); Location getLocation(); @@ -21,34 +20,28 @@ signature module InputSig { string toString(); } - ControlFlowNode getASuccessor(ControlFlowNode n, SuccessorType t); + Node getASuccessor(Node n, string label); } -/** Provides modules for printing control flow graphs. */ +/** Provides modules for printing flow graphs. */ module PrintGraph Input> { private import Input /** A node to be included in the output of `TestOutput`. */ - signature class RelevantNodeSig extends ControlFlowNode; + signature class RelevantNodeSig extends Node; /** - * Import this module into a `.ql` file to output a CFG. The + * Import this module into a `.ql` file to output a graph. The * graph is restricted to nodes from `RelevantNode`. */ module TestOutput { - /** Holds if `pred -> succ` is an edge in the CFG. */ + /** Holds if `pred -> succ` is an edge in the graph. */ query predicate edges(RelevantNode pred, RelevantNode succ, string label) { - label = - strictconcat(SuccessorType t, string s | - succ = getASuccessor(pred, t) and - if t instanceof DirectSuccessor then s = "" else s = t.toString() - | - s, ", " order by s - ) + label = strictconcat(string s | succ = getASuccessor(pred, s) | s, ", " order by s) } /** - * Provides logic for representing a CFG as a [Mermaid diagram](https://mermaid.js.org/). + * Provides logic for representing a graph as a [Mermaid diagram](https://mermaid.js.org/). */ module Mermaid { private string nodeId(RelevantNode n) { @@ -103,8 +96,8 @@ module PrintGraph Input> { } } - /** Provides the input to `ViewCfgQuery`. */ - signature module ViewCfgQueryInputSig { + /** Provides the input to `ViewGraphQuery`. */ + signature module ViewGraphQueryInputSig { /** Gets the source file selected in the IDE. Should be an `external` predicate. */ string selectedSourceFile(); @@ -118,15 +111,15 @@ module PrintGraph Input> { * Holds if `callable` spans column `startColumn` of line `startLine` to * column `endColumn` of line `endLine` in `file`. */ - predicate cfgScopeSpan( + predicate callableSpan( Callable callable, File file, int startLine, int startColumn, int endLine, int endColumn ); } /** - * Provides an implementation for a `View CFG` query. + * Provides an implementation for a `View CFG` or `View DFG` query. * - * Import this module into a `.ql` that looks like + * Import this module into a `.ql` that looks like (for the `View CFG` query): * * ```ql * @name Print CFG @@ -136,15 +129,17 @@ module PrintGraph Input> { * @kind graph * @tags ide-contextual-queries/print-cfg * ``` + * + * For the `View DFG` query replace "cfg" with "dfg" above and "control flow" with "data flow". */ - module ViewCfgQuery ViewCfgQueryInput> { - private import ViewCfgQueryInput + module ViewGraphQuery ViewGraphQueryInput> { + private import ViewGraphQueryInput bindingset[file, line, column] private Callable smallestEnclosingScope(File file, int line, int column) { result = min(Callable callable, int startLine, int startColumn, int endLine, int endColumn | - cfgScopeSpan(callable, file, startLine, startColumn, endLine, endColumn) and + callableSpan(callable, file, startLine, startColumn, endLine, endColumn) and ( startLine < line or @@ -162,9 +157,9 @@ module PrintGraph Input> { private import IdeContextual - final private class FinalControlFlowNode = ControlFlowNode; + final private class FinalNode = Node; - private class RelevantNode extends FinalControlFlowNode { + private class RelevantNode extends FinalNode { RelevantNode() { this.getEnclosingCallable() = smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()), @@ -178,7 +173,7 @@ module PrintGraph Input> { import Output::Mermaid - /** Holds if `pred` -> `succ` is an edge in the CFG. */ + /** Holds if `pred` -> `succ` is an edge in the graph. */ query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) { attr = "semmle.label" and Output::edges(pred, succ, val) From 654fa9eef76b349d463bcf7d82f142bde86bcfdf Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 Feb 2026 13:05:23 +0100 Subject: [PATCH 02/11] Update uses of ViewCfgQuery to use new name --- csharp/ql/lib/printCfg.ql | 6 +++--- java/ql/lib/printCfg.ql | 6 +++--- ruby/ql/lib/ide-contextual-queries/printCfg.ql | 6 +++--- rust/ql/lib/ide-contextual-queries/PrintCfg.ql | 6 +++--- swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql | 6 +++--- 5 files changed, 15 insertions(+), 15 deletions(-) diff --git a/csharp/ql/lib/printCfg.ql b/csharp/ql/lib/printCfg.ql index aa92b1192042..fab7ea4061fc 100644 --- a/csharp/ql/lib/printCfg.ql +++ b/csharp/ql/lib/printCfg.ql @@ -21,14 +21,14 @@ external int selectedSourceColumn(); private predicate selectedSourceColumnAlias = selectedSourceColumn/0; -module ViewCfgQueryInput implements ViewCfgQueryInputSig { +module ViewGraphQueryInput implements ViewGraphQueryInputSig { predicate selectedSourceFile = selectedSourceFileAlias/0; predicate selectedSourceLine = selectedSourceLineAlias/0; predicate selectedSourceColumn = selectedSourceColumnAlias/0; - predicate cfgScopeSpan( + predicate callableSpan( CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn ) { file = scope.getFile() and @@ -47,4 +47,4 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig { } } -import ViewCfgQuery +import ViewGraphQuery diff --git a/java/ql/lib/printCfg.ql b/java/ql/lib/printCfg.ql index 5e3cc22644ef..42b0db86cc4b 100644 --- a/java/ql/lib/printCfg.ql +++ b/java/ql/lib/printCfg.ql @@ -21,14 +21,14 @@ external int selectedSourceColumn(); private predicate selectedSourceColumnAlias = selectedSourceColumn/0; -module ViewCfgQueryInput implements ViewCfgQueryInputSig { +module ViewCfgQueryInput implements ViewGraphQueryInputSig { predicate selectedSourceFile = selectedSourceFileAlias/0; predicate selectedSourceLine = selectedSourceLineAlias/0; predicate selectedSourceColumn = selectedSourceColumnAlias/0; - predicate cfgScopeSpan( + predicate callableSpan( Callable callable, File file, int startLine, int startColumn, int endLine, int endColumn ) { file = callable.getFile() and @@ -42,4 +42,4 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig { } } -import ViewCfgQuery +import ViewGraphQuery diff --git a/ruby/ql/lib/ide-contextual-queries/printCfg.ql b/ruby/ql/lib/ide-contextual-queries/printCfg.ql index 1c4cec61a3ef..136b6e7e3d93 100644 --- a/ruby/ql/lib/ide-contextual-queries/printCfg.ql +++ b/ruby/ql/lib/ide-contextual-queries/printCfg.ql @@ -23,14 +23,14 @@ external int selectedSourceColumn(); private predicate selectedSourceColumnAlias = selectedSourceColumn/0; -module ViewCfgQueryInput implements ViewCfgQueryInputSig { +module ViewCfgQueryInput implements ViewGraphQueryInputSig { predicate selectedSourceFile = selectedSourceFileAlias/0; predicate selectedSourceLine = selectedSourceLineAlias/0; predicate selectedSourceColumn = selectedSourceColumnAlias/0; - predicate cfgScopeSpan( + predicate callableSpan( CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn ) { file = scope.getFile() and @@ -38,4 +38,4 @@ module ViewCfgQueryInput implements ViewCfgQueryInputSig { } } -import ViewCfgQuery +import ViewGraphQuery diff --git a/rust/ql/lib/ide-contextual-queries/PrintCfg.ql b/rust/ql/lib/ide-contextual-queries/PrintCfg.ql index e7b25ea8df8e..70078feffc96 100644 --- a/rust/ql/lib/ide-contextual-queries/PrintCfg.ql +++ b/rust/ql/lib/ide-contextual-queries/PrintCfg.ql @@ -32,14 +32,14 @@ external int selectedSourceColumn(); private predicate selectedSourceColumnAlias = selectedSourceColumn/0; -private module ViewCfgQueryInput implements ViewCfgQueryInputSig { +private module ViewCfgQueryInput implements ViewGraphQueryInputSig { predicate selectedSourceFile = selectedSourceFileAlias/0; predicate selectedSourceLine = selectedSourceLineAlias/0; predicate selectedSourceColumn = selectedSourceColumnAlias/0; - predicate cfgScopeSpan( + predicate callableSpan( CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn ) { file = scope.getFile() and @@ -47,4 +47,4 @@ private module ViewCfgQueryInput implements ViewCfgQueryInputSig { } } -import ViewCfgQuery +import ViewGraphQuery diff --git a/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql b/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql index 2f9a0572111c..28d0bf9401e5 100644 --- a/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql +++ b/swift/ql/lib/codeql/swift/controlflow/internal/PrintCFG.ql @@ -33,14 +33,14 @@ external int selectedSourceColumn(); private predicate selectedSourceColumnAlias = selectedSourceColumn/0; -module ViewCfgQueryInput implements Impl::ViewCfgQueryInputSig { +module ViewCfgQueryInput implements Impl::ViewGraphQueryInputSig { predicate selectedSourceFile = selectedSourceFileAlias/0; predicate selectedSourceLine = selectedSourceLineAlias/0; predicate selectedSourceColumn = selectedSourceColumnAlias/0; - predicate cfgScopeSpan( + predicate callableSpan( CfgInput::CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn ) { file = scope.getFile() and @@ -48,4 +48,4 @@ module ViewCfgQueryInput implements Impl::ViewCfgQueryInputSig { } } -import Impl::ViewCfgQuery +import Impl::ViewGraphQuery From cfd8f904750a8880e792bce7bca1aa00b2a55480 Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 Feb 2026 13:07:52 +0100 Subject: [PATCH 03/11] Wrap Java's instantiation in a module since it's in the top-level scope Unlike other languages, Java exported 'ViewCfgQuery' in top-level of the default import (java.qll). But after the rename to ViewGraphQuery the name is too ambiguous. We can't make an alias for a parameterised module, so instead wrapping it in a module named 'PrintCfg'. --- java/ql/lib/printCfg.ql | 1 + java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/java/ql/lib/printCfg.ql b/java/ql/lib/printCfg.ql index 42b0db86cc4b..b176918ffa33 100644 --- a/java/ql/lib/printCfg.ql +++ b/java/ql/lib/printCfg.ql @@ -8,6 +8,7 @@ */ import java +import PrintCfg external string selectedSourceFile(); diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index 64449b6f93d7..ef594a76a269 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -1788,4 +1788,7 @@ private module PrintGraphInput implements PrintGraph::InputSig { ControlFlowNode getASuccessor(ControlFlowNode n, SuccessorType t) { result = n.getASuccessor(t) } } -import PrintGraph::PrintGraph +/** Provides utilities for visualising the CFG. */ +module PrintCfg { + import PrintGraph::PrintGraph +} From 9e08b0bedb0064cd3cbbeabaf99e11290bdf7411 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 23 Feb 2026 11:47:41 +0100 Subject: [PATCH 04/11] Add getOrderDisambiguation to signature and use it --- shared/controlflow/codeql/controlflow/Cfg.qll | 4 +++- .../controlflow/codeql/controlflow/PrintGraph.qll | 13 +++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index 74b52629f8fc..caed3df5aff4 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -1320,7 +1320,9 @@ module MakeWithSplitting< private module PrintGraphInput implements Pp::InputSig { class Callable = CfgScope; - class Node = FinalNode; + class Node extends FinalNode { + string getOrderDisambiguation() { result = "" } + } Node getASuccessor(Node n, string s) { exists(SuccessorType t | diff --git a/shared/controlflow/codeql/controlflow/PrintGraph.qll b/shared/controlflow/codeql/controlflow/PrintGraph.qll index 85b474ffbaa4..5b34818c9c2e 100644 --- a/shared/controlflow/codeql/controlflow/PrintGraph.qll +++ b/shared/controlflow/codeql/controlflow/PrintGraph.qll @@ -18,6 +18,9 @@ signature module InputSig { Location getLocation(); string toString(); + + /** Gets a string to distinguish nodes that have the same location and toString value. */ + string getOrderDisambiguation(); } Node getASuccessor(Node n, string label); @@ -53,7 +56,10 @@ module PrintGraph Input> { p.getLocation() .hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn) | - p order by filePath, startLine, startColumn, endLine, endColumn, p.toString() + p + order by + filePath, startLine, startColumn, endLine, endColumn, p.toString(), + p.getOrderDisambiguation() ) ).toString() } @@ -87,7 +93,8 @@ module PrintGraph Input> { | edge, "\n" order by - filePath, startLine, startColumn, endLine, endColumn, pred.toString() + filePath, startLine, startColumn, endLine, endColumn, pred.toString(), + pred.getOrderDisambiguation() ) } @@ -165,8 +172,6 @@ module PrintGraph Input> { smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()), selectedSourceLine(), selectedSourceColumn()) } - - string getOrderDisambiguation() { result = "" } } private module Output = TestOutput; From a44a13e70544b47bbcb760b1af65f7cb70d95df9 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 23 Feb 2026 11:49:11 +0100 Subject: [PATCH 05/11] Move PrintGraph to codeql.util --- shared/controlflow/codeql/controlflow/Cfg.qll | 2 +- .../codeql/controlflow => util/codeql/util}/PrintGraph.qll | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename shared/{controlflow/codeql/controlflow => util/codeql/util}/PrintGraph.qll (100%) diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index caed3df5aff4..6ed5a583db8d 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -1313,7 +1313,7 @@ module MakeWithSplitting< } } - private import PrintGraph as Pp + private import codeql.util.PrintGraph as Pp final private class FinalNode = Node; diff --git a/shared/controlflow/codeql/controlflow/PrintGraph.qll b/shared/util/codeql/util/PrintGraph.qll similarity index 100% rename from shared/controlflow/codeql/controlflow/PrintGraph.qll rename to shared/util/codeql/util/PrintGraph.qll From e87d0da0470cf0eb8147fd1dcd1b1a27f01e3965 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 23 Feb 2026 11:56:36 +0100 Subject: [PATCH 06/11] Refactor 'getASuccessor' to edges/3 This interface works better when defining the DFG edges in the next commit --- shared/controlflow/codeql/controlflow/Cfg.qll | 6 +++--- shared/util/codeql/util/PrintGraph.qll | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index 6ed5a583db8d..c38f902fb84e 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -1324,10 +1324,10 @@ module MakeWithSplitting< string getOrderDisambiguation() { result = "" } } - Node getASuccessor(Node n, string s) { + predicate edge(Node node1, string label, Node node2) { exists(SuccessorType t | - result = n.getASuccessor(t) and - if t instanceof DirectSuccessor then s = "" else s = t.toString() + node2 = node1.getASuccessor(t) and + if t instanceof DirectSuccessor then label = "" else label = t.toString() ) } } diff --git a/shared/util/codeql/util/PrintGraph.qll b/shared/util/codeql/util/PrintGraph.qll index 5b34818c9c2e..bbeb9332ed8c 100644 --- a/shared/util/codeql/util/PrintGraph.qll +++ b/shared/util/codeql/util/PrintGraph.qll @@ -23,7 +23,7 @@ signature module InputSig { string getOrderDisambiguation(); } - Node getASuccessor(Node n, string label); + predicate edge(Node node1, string label, Node node2); } /** Provides modules for printing flow graphs. */ @@ -40,7 +40,7 @@ module PrintGraph Input> { module TestOutput { /** Holds if `pred -> succ` is an edge in the graph. */ query predicate edges(RelevantNode pred, RelevantNode succ, string label) { - label = strictconcat(string s | succ = getASuccessor(pred, s) | s, ", " order by s) + label = strictconcat(string s | edge(pred, s, succ) | s, ", " order by s) } /** From c37bcdae40eaec1f8603decadb190e811a81030e Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 Feb 2026 13:08:12 +0100 Subject: [PATCH 07/11] Update instantiation for Java --- java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index ef594a76a269..f477651b15db 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -1776,16 +1776,25 @@ class ConditionNode extends ControlFlow::Node { ExprParent getCondition() { result = this.asExpr() or result = this.asStmt() } } -private import codeql.controlflow.PrintGraph as PrintGraph +private import codeql.util.PrintGraph as PrintGraph private module PrintGraphInput implements PrintGraph::InputSig { private import java as J class Callable = J::Callable; - class ControlFlowNode = J::ControlFlowNode; + final private class FinalControlFlowNode = J::ControlFlowNode; - ControlFlowNode getASuccessor(ControlFlowNode n, SuccessorType t) { result = n.getASuccessor(t) } + class Node extends FinalControlFlowNode { + string getOrderDisambiguation() { result = "" } + } + + predicate edge(Node node1, string s, Node node2) { + exists(SuccessorType t | + node2 = node1.getASuccessor(t) and + if t instanceof DirectSuccessor then s = "" else s = t.toString() + ) + } } /** Provides utilities for visualising the CFG. */ From 094f33e280fa8013b4e75cd2fb9a74d4dba3699c Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 23 Feb 2026 13:05:13 +0100 Subject: [PATCH 08/11] Add PrintDfg.qll Adding this directly in the DataFlow module would expose it publicly via the `DataFlow::` prefix which does not seem desirable. We just want to be able to access it ourselves, so I've put it in its own file. --- shared/dataflow/codeql/dataflow/DataFlow.qll | 6 +++ shared/dataflow/codeql/dataflow/PrintDfg.qll | 45 ++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 shared/dataflow/codeql/dataflow/PrintDfg.qll diff --git a/shared/dataflow/codeql/dataflow/DataFlow.qll b/shared/dataflow/codeql/dataflow/DataFlow.qll index 7f9c0194374b..3a7afd7e873e 100644 --- a/shared/dataflow/codeql/dataflow/DataFlow.qll +++ b/shared/dataflow/codeql/dataflow/DataFlow.qll @@ -361,6 +361,12 @@ signature module InputSig { * visible. */ default predicate isEvaluatingInOverlay() { none() } + + /** + * Gets a string to distinguish nodes that have the same location and toString value, + * for use when generating graphs with `PrintDfg.qll`. + */ + default string nodeGetOrderDisambiguation(Node node) { result = "" } } module Configs Lang> { diff --git a/shared/dataflow/codeql/dataflow/PrintDfg.qll b/shared/dataflow/codeql/dataflow/PrintDfg.qll new file mode 100644 index 000000000000..5b3a836d7050 --- /dev/null +++ b/shared/dataflow/codeql/dataflow/PrintDfg.qll @@ -0,0 +1,45 @@ +/** + * Provides a module for implementing the `View DFG` query based on inputs to the data flow library. + */ + +private import codeql.util.Location +private import codeql.dataflow.DataFlow as DF +private import codeql.dataflow.TaintTracking as TT + +module MakePrintDfg< + LocationSig Location, DF::InputSig DataFlowLang, + TT::InputSig TaintTrackingLang> +{ + private import DataFlowLang + private import codeql.util.PrintGraph as Pp + + final private class FinalNode = Node; + + private module PrintGraphInput implements Pp::InputSig { + class Callable = DataFlowLang::DataFlowCallable; + + class Node extends FinalNode { + string getOrderDisambiguation() { result = DataFlowLang::nodeGetOrderDisambiguation(this) } + + Callable getEnclosingCallable() { result = DataFlowLang::nodeGetEnclosingCallable(this) } + } + + predicate edge(Node node1, string label, Node node2) { + simpleLocalFlowStep(node1, node2, _) and label = "value" + or + jumpStep(node1, node2) and label = "jump" + or + TaintTrackingLang::defaultAdditionalTaintStep(node1, node2, _) and label = "taint" + or + exists(ContentSet c | + readStep(node1, c, node2) and label = "read[" + c.toString() + "]" + or + storeStep(node1, c, node2) and label = "store[" + c.toString() + "]" + ) + or + node1 = node2.(PostUpdateNode).getPreUpdateNode() and label = "post-update" + } + } + + import Pp::PrintGraph +} From 6d0598653a4ab31e968a9b8204bd051d46ad5e22 Mon Sep 17 00:00:00 2001 From: Asger F Date: Mon, 23 Feb 2026 13:05:26 +0100 Subject: [PATCH 09/11] Add 'View DFG' queries for JS, Java, Ruby, C#, Rust --- csharp/ql/lib/printDfg.ql | 53 +++++++++++++++++++ java/ql/lib/printDfg.ql | 50 +++++++++++++++++ javascript/ql/lib/printDfg.ql | 48 +++++++++++++++++ .../ql/lib/ide-contextual-queries/printDfg.ql | 44 +++++++++++++++ .../ql/lib/ide-contextual-queries/PrintDfg.ql | 44 +++++++++++++++ 5 files changed, 239 insertions(+) create mode 100644 csharp/ql/lib/printDfg.ql create mode 100644 java/ql/lib/printDfg.ql create mode 100644 javascript/ql/lib/printDfg.ql create mode 100644 ruby/ql/lib/ide-contextual-queries/printDfg.ql create mode 100644 rust/ql/lib/ide-contextual-queries/PrintDfg.ql diff --git a/csharp/ql/lib/printDfg.ql b/csharp/ql/lib/printDfg.ql new file mode 100644 index 000000000000..8d8b6f9a4fa7 --- /dev/null +++ b/csharp/ql/lib/printDfg.ql @@ -0,0 +1,53 @@ +/** + * @name Print DFG + * @description Produces a representation of a file's Data Flow Graph. + * This query is used by the VS Code extension. + * @id cs/print-dfg + * @kind graph + * @tags ide-contextual-queries/print-dfg + */ + +import csharp +private import semmle.code.csharp.dataflow.internal.DataFlowImplSpecific as DF +private import semmle.code.csharp.dataflow.internal.TaintTrackingImplSpecific as TT +private import codeql.dataflow.PrintDfg +private import MakePrintDfg + +external string selectedSourceFile(); + +private predicate selectedSourceFileAlias = selectedSourceFile/0; + +external int selectedSourceLine(); + +private predicate selectedSourceLineAlias = selectedSourceLine/0; + +external int selectedSourceColumn(); + +private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + +module ViewDfgQueryInput implements ViewGraphQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + predicate callableSpan( + DF::CsharpDataFlow::DataFlowCallable callable, File file, int startLine, int startColumn, + int endLine, int endColumn + ) { + exists(Callable c | + c = callable.asCallable(_) and + file = c.getFile() and + callable.getLocation().getStartLine() = startLine and + callable.getLocation().getStartColumn() = startColumn and + exists(Location loc | + loc.getEndLine() = endLine and + loc.getEndColumn() = endColumn and + loc = c.getBody().getLocation() + ) + ) + } +} + +import ViewGraphQuery diff --git a/java/ql/lib/printDfg.ql b/java/ql/lib/printDfg.ql new file mode 100644 index 000000000000..6913c9407d7d --- /dev/null +++ b/java/ql/lib/printDfg.ql @@ -0,0 +1,50 @@ +/** + * @name Print DFG + * @description Produces a representation of a file's Data Flow Graph. + * This query is used by the VS Code extension. + * @id java/print-cfg + * @kind graph + * @tags ide-contextual-queries/print-dfg + */ + +import java +private import semmle.code.java.dataflow.internal.DataFlowImplSpecific as DF +private import semmle.code.java.dataflow.internal.TaintTrackingImplSpecific as TT +private import codeql.dataflow.PrintDfg +private import MakePrintDfg + +external string selectedSourceFile(); + +private predicate selectedSourceFileAlias = selectedSourceFile/0; + +external int selectedSourceLine(); + +private predicate selectedSourceLineAlias = selectedSourceLine/0; + +external int selectedSourceColumn(); + +private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + +module ViewDfgQueryInput implements ViewGraphQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + predicate callableSpan( + DF::JavaDataFlow::DataFlowCallable callable, File file, int startLine, int startColumn, + int endLine, int endColumn + ) { + file = callable.asCallable().getFile() and + callable.getLocation().getStartLine() = startLine and + callable.getLocation().getStartColumn() = startColumn and + exists(Location loc | + loc.getEndLine() = endLine and + loc.getEndColumn() = endColumn and + loc = callable.asCallable().getBody().getLocation() + ) + } +} + +import ViewGraphQuery diff --git a/javascript/ql/lib/printDfg.ql b/javascript/ql/lib/printDfg.ql new file mode 100644 index 000000000000..c9d055bbb323 --- /dev/null +++ b/javascript/ql/lib/printDfg.ql @@ -0,0 +1,48 @@ +/** + * @name Print DFG + * @description Produces a representation of a file's Data Flow Graph. + * This query is used by the VS Code extension. + * @id js/print-dfg + * @kind graph + * @tags ide-contextual-queries/print-dfg + */ + +private import javascript +private import semmle.javascript.dataflow.internal.sharedlib.DataFlowArg +private import codeql.dataflow.PrintDfg +import MakePrintDfg + +external string selectedSourceFile(); + +private predicate selectedSourceFileAlias = selectedSourceFile/0; + +external int selectedSourceLine(); + +private predicate selectedSourceLineAlias = selectedSourceLine/0; + +external int selectedSourceColumn(); + +private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + +module ViewGraphInput implements ViewGraphQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + /** + * Holds if `callable` spans column `startColumn` of line `startLine` to + * column `endColumn` of line `endLine` in `file`. + */ + predicate callableSpan( + JSDataFlow::DataFlowCallable callable, File file, int startLine, int startColumn, int endLine, + int endColumn + ) { + callable + .getLocation() + .hasLocationInfo(file.getAbsolutePath(), startLine, startColumn, endLine, endColumn) + } +} + +import ViewGraphQuery diff --git a/ruby/ql/lib/ide-contextual-queries/printDfg.ql b/ruby/ql/lib/ide-contextual-queries/printDfg.ql new file mode 100644 index 000000000000..731925fe7d7c --- /dev/null +++ b/ruby/ql/lib/ide-contextual-queries/printDfg.ql @@ -0,0 +1,44 @@ +/** + * @name Print DFG + * @description Produces a representation of a file's Data Flow Graph. + * This query is used by the VS Code extension. + * @id rb/print-dfg + * @kind graph + * @tags ide-contextual-queries/print-dfg + */ + +private import codeql.Locations +private import codeql.ruby.dataflow.internal.DataFlowImplSpecific as DF +private import codeql.ruby.dataflow.internal.TaintTrackingImplSpecific as TT +private import codeql.dataflow.PrintDfg +private import MakePrintDfg + +external string selectedSourceFile(); + +private predicate selectedSourceFileAlias = selectedSourceFile/0; + +external int selectedSourceLine(); + +private predicate selectedSourceLineAlias = selectedSourceLine/0; + +external int selectedSourceColumn(); + +private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + +module ViewDfgQueryInput implements ViewGraphQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + predicate callableSpan( + DF::RubyDataFlow::DataFlowCallable callable, File file, int startLine, int startColumn, + int endLine, int endColumn + ) { + file = callable.asCfgScope().getFile() and + callable.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn) + } +} + +import ViewGraphQuery diff --git a/rust/ql/lib/ide-contextual-queries/PrintDfg.ql b/rust/ql/lib/ide-contextual-queries/PrintDfg.ql new file mode 100644 index 000000000000..8121bfbbbab2 --- /dev/null +++ b/rust/ql/lib/ide-contextual-queries/PrintDfg.ql @@ -0,0 +1,44 @@ +/** + * @name Print DFG + * @description Produces a representation of a file's Data Flow Graph. + * This query is used by the VS Code extension. + * @id rust/print-dfg + * @kind graph + * @tags ide-contextual-queries/print-dfg + */ + +private import rust +private import codeql.rust.dataflow.internal.DataFlowImpl as DF +private import codeql.rust.dataflow.internal.TaintTrackingImpl as TT +private import codeql.dataflow.PrintDfg +private import MakePrintDfg + +external string selectedSourceFile(); + +private predicate selectedSourceFileAlias = selectedSourceFile/0; + +external int selectedSourceLine(); + +private predicate selectedSourceLineAlias = selectedSourceLine/0; + +external int selectedSourceColumn(); + +private predicate selectedSourceColumnAlias = selectedSourceColumn/0; + +private module ViewDfgQueryInput implements ViewGraphQueryInputSig { + predicate selectedSourceFile = selectedSourceFileAlias/0; + + predicate selectedSourceLine = selectedSourceLineAlias/0; + + predicate selectedSourceColumn = selectedSourceColumnAlias/0; + + predicate callableSpan( + DF::RustDataFlow::DataFlowCallable callable, File file, int startLine, int startColumn, + int endLine, int endColumn + ) { + file = callable.asCfgScope().getFile() and + callable.getLocation().hasLocationInfo(_, startLine, startColumn, endLine, endColumn) + } +} + +import ViewGraphQuery From a1489be3bfe856970b39f079d98b1beea693936f Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 Feb 2026 13:21:41 +0100 Subject: [PATCH 10/11] Add overlay annotation to shared module --- shared/dataflow/codeql/dataflow/PrintDfg.qll | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shared/dataflow/codeql/dataflow/PrintDfg.qll b/shared/dataflow/codeql/dataflow/PrintDfg.qll index 5b3a836d7050..e007930e322c 100644 --- a/shared/dataflow/codeql/dataflow/PrintDfg.qll +++ b/shared/dataflow/codeql/dataflow/PrintDfg.qll @@ -1,6 +1,8 @@ /** * Provides a module for implementing the `View DFG` query based on inputs to the data flow library. */ +overlay[local?] +module; private import codeql.util.Location private import codeql.dataflow.DataFlow as DF From 3fe40d3af1c97ea69b8a31356f1ef99820ea266b Mon Sep 17 00:00:00 2001 From: Asger F Date: Fri, 27 Feb 2026 14:09:47 +0100 Subject: [PATCH 11/11] Replace getOrderDisambiguation with a module predicate --- .../ql/test/library-tests/controlflow/graph/Common.qll | 2 ++ java/ql/lib/semmle/code/java/ControlFlowGraph.qll | 6 +----- shared/controlflow/codeql/controlflow/Cfg.qll | 6 ++---- shared/dataflow/codeql/dataflow/PrintDfg.qll | 6 ++++-- shared/util/codeql/util/PrintGraph.qll | 10 +++++----- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/csharp/ql/test/library-tests/controlflow/graph/Common.qll b/csharp/ql/test/library-tests/controlflow/graph/Common.qll index f6f9b26f1cdd..5c8453b0a02b 100644 --- a/csharp/ql/test/library-tests/controlflow/graph/Common.qll +++ b/csharp/ql/test/library-tests/controlflow/graph/Common.qll @@ -15,6 +15,8 @@ class SourceControlFlowNode extends ControlFlow::Node { not this.getLocation().getFile() instanceof StubFile and not this.getLocation().getFile().fromLibrary() } + + string getOrderDisambiguation() { result = "" } } class SourceBasicBlock extends ControlFlow::BasicBlock { diff --git a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll index f477651b15db..972e49c76da5 100644 --- a/java/ql/lib/semmle/code/java/ControlFlowGraph.qll +++ b/java/ql/lib/semmle/code/java/ControlFlowGraph.qll @@ -1783,11 +1783,7 @@ private module PrintGraphInput implements PrintGraph::InputSig { class Callable = J::Callable; - final private class FinalControlFlowNode = J::ControlFlowNode; - - class Node extends FinalControlFlowNode { - string getOrderDisambiguation() { result = "" } - } + class Node = J::ControlFlowNode; predicate edge(Node node1, string s, Node node2) { exists(SuccessorType t | diff --git a/shared/controlflow/codeql/controlflow/Cfg.qll b/shared/controlflow/codeql/controlflow/Cfg.qll index c38f902fb84e..085923a7a384 100644 --- a/shared/controlflow/codeql/controlflow/Cfg.qll +++ b/shared/controlflow/codeql/controlflow/Cfg.qll @@ -1315,14 +1315,12 @@ module MakeWithSplitting< private import codeql.util.PrintGraph as Pp - final private class FinalNode = Node; + private class NodeAlias = Node; private module PrintGraphInput implements Pp::InputSig { class Callable = CfgScope; - class Node extends FinalNode { - string getOrderDisambiguation() { result = "" } - } + class Node = NodeAlias; predicate edge(Node node1, string label, Node node2) { exists(SuccessorType t | diff --git a/shared/dataflow/codeql/dataflow/PrintDfg.qll b/shared/dataflow/codeql/dataflow/PrintDfg.qll index e007930e322c..766dbb4e2f7f 100644 --- a/shared/dataflow/codeql/dataflow/PrintDfg.qll +++ b/shared/dataflow/codeql/dataflow/PrintDfg.qll @@ -21,8 +21,6 @@ module MakePrintDfg< class Callable = DataFlowLang::DataFlowCallable; class Node extends FinalNode { - string getOrderDisambiguation() { result = DataFlowLang::nodeGetOrderDisambiguation(this) } - Callable getEnclosingCallable() { result = DataFlowLang::nodeGetEnclosingCallable(this) } } @@ -41,6 +39,10 @@ module MakePrintDfg< or node1 = node2.(PostUpdateNode).getPreUpdateNode() and label = "post-update" } + + string nodeGetOrderDisambiguation(Node node) { + result = DataFlowLang::nodeGetOrderDisambiguation(node) + } } import Pp::PrintGraph diff --git a/shared/util/codeql/util/PrintGraph.qll b/shared/util/codeql/util/PrintGraph.qll index bbeb9332ed8c..3ad90ddfe056 100644 --- a/shared/util/codeql/util/PrintGraph.qll +++ b/shared/util/codeql/util/PrintGraph.qll @@ -18,12 +18,12 @@ signature module InputSig { Location getLocation(); string toString(); - - /** Gets a string to distinguish nodes that have the same location and toString value. */ - string getOrderDisambiguation(); } predicate edge(Node node1, string label, Node node2); + + /** Gets a string to distinguish nodes that have the same location and toString value. */ + default string nodeGetOrderDisambiguation(Node node) { result = "" } } /** Provides modules for printing flow graphs. */ @@ -59,7 +59,7 @@ module PrintGraph Input> { p order by filePath, startLine, startColumn, endLine, endColumn, p.toString(), - p.getOrderDisambiguation() + nodeGetOrderDisambiguation(p) ) ).toString() } @@ -94,7 +94,7 @@ module PrintGraph Input> { edge, "\n" order by filePath, startLine, startColumn, endLine, endColumn, pred.toString(), - pred.getOrderDisambiguation() + nodeGetOrderDisambiguation(pred) ) }