Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions java/ql/lib/printCfg.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @name Print CFG
* @description Produces a representation of a file's Control Flow Graph.
* This query is used by the VS Code extension.
* @id java/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
*/

import java

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 ViewCfgQueryInput implements ViewCfgQueryInputSig<File> {
predicate selectedSourceFile = selectedSourceFileAlias/0;

predicate selectedSourceLine = selectedSourceLineAlias/0;

predicate selectedSourceColumn = selectedSourceColumnAlias/0;

predicate cfgScopeSpan(
Callable callable, File file, int startLine, int startColumn, int endLine, int endColumn
) {
file = callable.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.getBody().getLocation()
)
}
}

import ViewCfgQuery<File, ViewCfgQueryInput>
14 changes: 14 additions & 0 deletions java/ql/lib/semmle/code/java/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1775,3 +1775,17 @@ class ConditionNode extends ControlFlow::Node {
/** Gets the condition of this `ConditionNode`. */
ExprParent getCondition() { result = this.asExpr() or result = this.asStmt() }
}

private import codeql.controlflow.PrintGraph as PrintGraph

private module PrintGraphInput implements PrintGraph::InputSig<Location> {
private import java as J

class Callable = J::Callable;

class ControlFlowNode = J::ControlFlowNode;

ControlFlowNode getASuccessor(ControlFlowNode n, SuccessorType t) { result = n.getASuccessor(t) }
}

import PrintGraph::PrintGraph<Location, PrintGraphInput>
158 changes: 10 additions & 148 deletions shared/controlflow/codeql/controlflow/Cfg.qll
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,9 @@ module MakeWithSplitting<
/** Gets the scope of this node. */
CfgScope getScope() { result = getNodeCfgScope(this) }

/** Gets the enclosing callable of this node. */
CfgScope getEnclosingCallable() { result = this.getScope() }

/** Gets a successor node of a given type, if any. */
Node getASuccessor(SuccessorType t) { result = getASuccessor(this, t) }

Expand Down Expand Up @@ -1310,160 +1313,19 @@ module MakeWithSplitting<
}
}

/** A node to be included in the output of `TestOutput`. */
signature class RelevantNodeSig extends Node;

/**
* Import this module into a `.ql` file to output a CFG. The
* graph is restricted to nodes from `RelevantNode`.
*/
module TestOutput<RelevantNodeSig RelevantNode> {
/** Holds if `pred -> succ` is an edge in the CFG. */
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
)
}

/**
* Provides logic for representing a CFG as a [Mermaid diagram](https://mermaid.js.org/).
*/
module Mermaid {
private string nodeId(RelevantNode n) {
result =
any(int i |
n =
rank[i](RelevantNode p, string filePath, int startLine, int startColumn, int endLine,
int endColumn |
p.getLocation()
.hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn)
|
p order by filePath, startLine, startColumn, endLine, endColumn, p.toString()
)
).toString()
}

private string nodes() {
result =
concat(RelevantNode n, string id, string text |
id = nodeId(n) and
text = n.toString()
|
id + "[\"" + text + "\"]", "\n" order by id
)
}
private import PrintGraph as Pp

private string edge(RelevantNode pred, RelevantNode succ) {
edges(pred, succ, _) and
exists(string label |
edges(pred, succ, label) and
if label = ""
then result = nodeId(pred) + " --> " + nodeId(succ)
else result = nodeId(pred) + " -- " + label + " --> " + nodeId(succ)
)
}
private module PrintGraphInput implements Pp::InputSig<Location> {
class Callable = CfgScope;

private string edges() {
result =
concat(RelevantNode pred, RelevantNode succ, string edge, string filePath, int startLine,
int startColumn, int endLine, int endColumn |
edge = edge(pred, succ) and
pred.getLocation().hasLocationInfo(filePath, startLine, startColumn, endLine, endColumn)
|
edge, "\n"
order by
filePath, startLine, startColumn, endLine, endColumn, pred.toString()
)
}
class ControlFlowNode = Node;

/** Holds if the Mermaid representation is `s`. */
query predicate mermaid(string s) { s = "flowchart TD\n" + nodes() + "\n\n" + edges() }
ControlFlowNode getASuccessor(ControlFlowNode n, SuccessorType t) {
result = n.getASuccessor(t)
}
}

/** Provides the input to `ViewCfgQuery`. */
signature module ViewCfgQueryInputSig<FileSig File> {
/** The source file selected in the IDE. Should be an `external` predicate. */
string selectedSourceFile();

/** The source line selected in the IDE. Should be an `external` predicate. */
int selectedSourceLine();

/** The source column selected in the IDE. Should be an `external` predicate. */
int selectedSourceColumn();

/**
* Holds if CFG scope `scope` spans column `startColumn` of line `startLine` to
* column `endColumn` of line `endLine` in `file`.
*/
predicate cfgScopeSpan(
CfgScope scope, File file, int startLine, int startColumn, int endLine, int endColumn
);
}

/**
* Provides an implementation for a `View CFG` query.
*
* Import this module into a `.ql` that looks like
*
* ```ql
* @name Print CFG
* @description Produces a representation of a file's Control Flow Graph.
* This query is used by the VS Code extension.
* @id <lang>/print-cfg
* @kind graph
* @tags ide-contextual-queries/print-cfg
* ```
*/
module ViewCfgQuery<FileSig File, ViewCfgQueryInputSig<File> ViewCfgQueryInput> {
private import ViewCfgQueryInput

bindingset[file, line, column]
private CfgScope smallestEnclosingScope(File file, int line, int column) {
result =
min(CfgScope scope, int startLine, int startColumn, int endLine, int endColumn |
cfgScopeSpan(scope, file, startLine, startColumn, endLine, endColumn) and
(
startLine < line
or
startLine = line and startColumn <= column
) and
(
endLine > line
or
endLine = line and endColumn >= column
)
|
scope order by startLine desc, startColumn desc, endLine, endColumn
)
}

private import IdeContextual<File>

private class RelevantNode extends Node {
RelevantNode() {
this.getScope() =
smallestEnclosingScope(getFileBySourceArchiveName(selectedSourceFile()),
selectedSourceLine(), selectedSourceColumn())
}

string getOrderDisambiguation() { result = "" }
}

private module Output = TestOutput<RelevantNode>;

import Output::Mermaid

/** Holds if `pred` -> `succ` is an edge in the CFG. */
query predicate edges(RelevantNode pred, RelevantNode succ, string attr, string val) {
attr = "semmle.label" and
Output::edges(pred, succ, val)
}
}
import Pp::PrintGraph<Location, PrintGraphInput>

/** Provides a set of consistency queries. */
module Consistency {
Expand Down
Loading
Loading