Skip to content

Unified: Add control flow graph. - #22479

Open
aschackmull wants to merge 1 commit into
github:mainfrom
aschackmull:unified/cfg
Open

Unified: Add control flow graph.#22479
aschackmull wants to merge 1 commit into
github:mainfrom
aschackmull:unified/cfg

Conversation

@aschackmull

Copy link
Copy Markdown
Contributor

This is a very rough initial version, but it provides the basic functionality including "View CFG" support and consistency queries.

Copilot AI balanced review requested due to automatic review settings September 1, 2026 09:26
@aschackmull aschackmull added the no-change-note-required This PR does not need a change note label Sep 1, 2026
@aschackmull
aschackmull requested a review from a team as a code owner September 1, 2026 09:26
AstNode getUpdate(int index) { none() }
}

class ForeachStmt extends LoopStmt instanceof U::ForEachStmt {

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Foreach, guard-statement, and closure-capture paths currently produce incorrect or unreachable control flow.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review tier: Balanced
Findings: 1 High severity · 1 Medium severity

New issues introduced by this change (2)
Severity Finding
Medium severity unified/​ql/​lib/​codeql/​unified/​internal/​ControlFlowGraph.qll — There are no edge-expectation tests for this new CFG implementation. The consistency query catches…
High severity unified/​ql/​lib/​codeql/​unified/​internal/​ControlFlowGraph.qll — Excluding every child of a callable drops closure capture initializers from the CFG. FunctionExpr
What changed in this PR

Adds an initial unified-language CFG implementation with IDE visualization and consistency checks.

Changes:

  • Adapts unified AST nodes to the shared control-flow framework.
  • Exposes CFG APIs and adds the required dependency.
  • Adds CFG visualization and consistency queries.
File Description
unified/​ql/​lib/​unified.qll Exposes CFG APIs.
unified/​ql/​lib/​qlpack.yml Adds the control-flow dependency.
unified/​ql/​lib/​ide-contextual-queries/​printCfg.ql Adds IDE CFG visualization.
unified/​ql/​lib/​codeql/​unified/​internal/​FacadeAst.qll Adds block-last-statement support.
unified/​ql/​lib/​codeql/​unified/​internal/​ControlFlowGraph.qll Implements unified CFG modeling.
unified/​ql/​consistency-queries/​qlpack.yml Defines the consistency-query pack.
unified/​ql/​consistency-queries/​CfgConsistency.ql Enables shared CFG consistency checks.
Suppressed comments (3)

unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll:85

  • GuardIfStmt is currently left to the default child ordering, which evaluates its condition and then its else block unconditionally. The Swift extractor emits this node for every guard … else statement, so the CFG incorrectly enters the else block even on the true branch. Model it as the shared IfStmt shape, with no then branch, so true flow continues after the guard and false flow enters else.
  class IfStmt extends Stmt {
    IfStmt() { none() }

unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll:132

  • This mapping is empty: getPattern() returns Pattern, while this method requires Expr (the schema makes expr and pattern separate alternatives under expr_or_pattern). Consequently, the shared foreach edge from a non-empty collection to getVariable() has no target, so every non-empty foreach path dead-ends before its body. Generalize the shared foreach-variable API to AstNode and return the pattern through it.
    Expr getVariable() { result = super.getPattern() }

unified/ql/lib/codeql/unified/internal/ControlFlowGraph.qll:129

  • The foreach guard is omitted from the explicit loop CFG. Although it remains an AST child, the shared implementation disables default child traversal once ForeachStmt has explicit steps, so the guard is unreachable and the body is entered without testing it. Extend the shared foreach signature/steps to evaluate the guard each iteration and branch back to the loop header when false.
    // TODO support foreach guard

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +11 to +15
private module Cfg0 = Make0<Location, Ast>;

private module Cfg1 = Make1<Input>;

private module Cfg2 = Make2<Input>;
Comment on lines +31 to +35
result.getParent() = n and
result.getParentIndex() = index and
not n instanceof Callable and
not skipControlFlow(n) and
not skipControlFlow(result)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-change-note-required This PR does not need a change note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants