Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
93a594e
Cfg: Support Throw expressions.
aschackmull Mar 5, 2026
a53cffc
Cfg: Support GotoStmt.
aschackmull Mar 5, 2026
0b6c416
Cfg: Support short-circuiting compound assignments.
aschackmull Mar 3, 2026
035b83c
C#: Introduce ControlFlowElementOrCallable.
aschackmull Mar 9, 2026
6ffed85
Cfg/Java: Move InstanceOfExpr CFG into shared lib.
aschackmull Mar 11, 2026
88aaff8
Cfg: Extend consistency checks.
aschackmull Mar 12, 2026
61976e3
C#: Rename ControlFlow::Node to ControlFlowNode.
aschackmull Mar 12, 2026
b85b02a
Cfg: Add dominance predicates to shared ControlFlowNode.
aschackmull Mar 18, 2026
03f6bdb
C#: Update some references in preparation for CFG swap.
aschackmull Mar 18, 2026
b878ae3
C#: Update some references to ControlFlow::Nodes.
aschackmull Mar 18, 2026
13a4141
C#: Rename remaining references to ControlFlow::Nodes.
aschackmull Mar 18, 2026
9cf9a36
C#: Rename ControlFlow::BasicBlock to BasicBlock.
aschackmull Mar 18, 2026
ff978d1
C#: Replace CFG.
aschackmull Mar 9, 2026
b179033
C#: Fix test.
aschackmull Mar 20, 2026
700d56f
C#: Fix UncheckedCastInEquals.
aschackmull Mar 20, 2026
ac88b73
C#: Bugfix in enclosing callable.
aschackmull Mar 24, 2026
093eb57
C#: Fix CFG position of property setter calls.
aschackmull Mar 26, 2026
43fe411
C#: Accept SSA location changes.
aschackmull Mar 26, 2026
1a6670a
C#: Phi nodes are not expected to have associated Elements.
aschackmull Mar 26, 2026
6010640
C#: Accept bugfix.
aschackmull Mar 30, 2026
a5c99f9
C#: Accept harmless CFG changes.
aschackmull Mar 30, 2026
5d58909
C#: Accept CFG changes.
aschackmull Mar 30, 2026
49cc931
C#: Compile-time constants no longer have CFG nodes.
aschackmull Mar 30, 2026
e90243c
C#: Accept irrelevant changes.
aschackmull Mar 31, 2026
88256ee
C#: GuardedExpr no longer contains expressions guarded solely by disj…
aschackmull Mar 31, 2026
773881f
C#: Accept data flow inconsistency check for read+write calls.
aschackmull Mar 31, 2026
a997d9f
C#: Accept fixed consistency check.
aschackmull Mar 31, 2026
a695819
C#: Accept CFG changes for "first" relation.
aschackmull Mar 31, 2026
a7d4b00
C#: Accept changed location for phi nodes.
aschackmull Apr 7, 2026
371bc30
C#: CFG and data flow nodes now exist for LHSs.
aschackmull Apr 7, 2026
1d9c0ae
C#: Fix perf.
aschackmull Apr 8, 2026
bfbd0f7
C#: Fix some bad join orders.
aschackmull Apr 9, 2026
bbd403d
C#: Rework DataFlowCallable-to-cfg relation in terms of basic blocks …
aschackmull Apr 9, 2026
2d5a184
C#: Accept new CFG in tests.
aschackmull Apr 9, 2026
aaf9bb2
C#: Accept fewer CallContextSpecificCall due to no splitting.
aschackmull Apr 9, 2026
452913f
C#: Improve perf of UnsynchronizedStaticAccess.ql.
aschackmull Apr 10, 2026
d5c9fd1
C#/Cfg: A bit more qldoc.
aschackmull Apr 10, 2026
88160ef
C#: Add change note.
aschackmull Apr 13, 2026
e928c22
C#/Cfg: Some simple review fixes.
aschackmull Apr 20, 2026
3ceb96a
C#: Eliminate Completion.qll.
aschackmull Apr 20, 2026
b6f50f5
C#: Simplify.
aschackmull Apr 20, 2026
9de02b7
Cfg: Use consistent casing in additional node tags.
aschackmull Apr 21, 2026
a2a4e82
C#: Deprecate ControlFlowElement.getAControlFlowNode and remove some …
aschackmull Apr 21, 2026
67c0515
Cfg: Undo consistency check change.
aschackmull Apr 21, 2026
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
2 changes: 1 addition & 1 deletion csharp/ql/lib/semmle/code/csharp/Callable.qll
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ private import TypeRef
* an anonymous function (`AnonymousFunctionExpr`), or a local function
* (`LocalFunction`).
*/
class Callable extends Parameterizable, ExprOrStmtParent, @callable {
class Callable extends Parameterizable, ControlFlowElementOrCallable, @callable {
/** Gets the return type of this callable. */
Type getReturnType() { none() }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ private import ControlFlow::BasicBlocks
private import semmle.code.csharp.Caching
private import internal.ControlFlowGraphImpl as Impl

private class TControlFlowElementOrCallable = @callable or @control_flow_element;

class ControlFlowElementOrCallable extends ExprOrStmtParent, TControlFlowElementOrCallable { }

/**
* A program element that can possess control flow. That is, either a statement or
* an expression.
Expand All @@ -17,7 +21,7 @@ private import internal.ControlFlowGraphImpl as Impl
* control flow elements and control flow nodes. This allows control flow
* splitting, for example modeling the control flow through `finally` blocks.
*/
class ControlFlowElement extends ExprOrStmtParent, @control_flow_element {
class ControlFlowElement extends ControlFlowElementOrCallable, @control_flow_element {
/** Gets the enclosing callable of this element, if any. */
Callable getEnclosingCallable() { none() }

Expand Down
66 changes: 31 additions & 35 deletions java/ql/lib/semmle/code/java/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,13 @@ private module Ast implements AstSig<Location> {

class ContinueStmt = J::ContinueStmt;

class GotoStmt extends Stmt {
GotoStmt() { none() }
}

class ReturnStmt = J::ReturnStmt;

class ThrowStmt = J::ThrowStmt;
class Throw = J::ThrowStmt;

final private class FinalTryStmt = J::TryStmt;

Expand Down Expand Up @@ -181,11 +185,37 @@ private module Ast implements AstSig<Location> {

class LogicalNotExpr = LogNotExpr;

class Assignment = J::Assignment;

class AssignExpr = J::AssignExpr;

class CompoundAssignment = J::AssignOp;

class AssignLogicalAndExpr extends CompoundAssignment {
AssignLogicalAndExpr() { none() }
}

class AssignLogicalOrExpr extends CompoundAssignment {
AssignLogicalOrExpr() { none() }
}

class AssignNullCoalescingExpr extends CompoundAssignment {
AssignNullCoalescingExpr() { none() }
}

final private class FinalBooleanLiteral = J::BooleanLiteral;

class BooleanLiteral extends FinalBooleanLiteral {
boolean getValue() { result = this.getBooleanValue() }
}

final private class FinalInstanceOfExpr = J::InstanceOfExpr;

class PatternMatchExpr extends FinalInstanceOfExpr {
PatternMatchExpr() { this.isPattern() }

AstNode getPattern() { result = super.getPattern() }
}
}

private module Exceptions {
Expand Down Expand Up @@ -522,14 +552,8 @@ private module Input implements InputSig1, InputSig2 {

private string assertThrowNodeTag() { result = "[assert-throw]" }

private string instanceofTrueNodeTag() { result = "[instanceof-true]" }

predicate additionalNode(Ast::AstNode n, string tag, NormalSuccessor t) {
n instanceof AssertStmt and tag = assertThrowNodeTag() and t instanceof DirectSuccessor
or
n.(InstanceOfExpr).isPattern() and
tag = instanceofTrueNodeTag() and
t.(BooleanSuccessor).getValue() = true
}

/**
Expand Down Expand Up @@ -571,34 +595,6 @@ private module Input implements InputSig1, InputSig2 {

/** Holds if there is a local non-abrupt step from `n1` to `n2`. */
predicate step(PreControlFlowNode n1, PreControlFlowNode n2) {
exists(InstanceOfExpr ioe |
// common
n1.isBefore(ioe) and
n2.isBefore(ioe.getExpr())
or
n1.isAfter(ioe.getExpr()) and
n2.isIn(ioe)
or
// std postorder:
not ioe.isPattern() and
n1.isIn(ioe) and
n2.isAfter(ioe)
or
// pattern case:
ioe.isPattern() and
n1.isIn(ioe) and
n2.isAfterValue(ioe, any(BooleanSuccessor s | s.getValue() = false))
or
n1.isIn(ioe) and
n2.isAdditional(ioe, instanceofTrueNodeTag())
or
n1.isAdditional(ioe, instanceofTrueNodeTag()) and
n2.isBefore(ioe.getPattern())
or
n1.isAfter(ioe.getPattern()) and
n2.isAfterValue(ioe, any(BooleanSuccessor s | s.getValue() = true))
)
or
exists(AssertStmt assertstmt |
n1.isBefore(assertstmt) and
n2.isBefore(assertstmt.getExpr())
Expand Down
125 changes: 112 additions & 13 deletions shared/controlflow/codeql/controlflow/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,13 @@ signature module AstSig<LocationSig Location> {
*/
class ContinueStmt extends Stmt;

/**
* A `goto` statement.
*
* Goto statements complete abruptly and jump to a labeled statement.
*/
class GotoStmt extends Stmt;

/**
* A `return` statement.
*
Expand All @@ -155,11 +162,11 @@ signature module AstSig<LocationSig Location> {
}

/**
* A `throw` statement.
* A `throw` statement or expression.
*
* Throw statements complete abruptly and throw an exception.
* Throw statements/expressions complete abruptly and throw an exception.
*/
class ThrowStmt extends Stmt {
class Throw extends AstNode {
/** Gets the expression being thrown. */
Expr getExpr();
}
Expand Down Expand Up @@ -302,11 +309,51 @@ signature module AstSig<LocationSig Location> {
/** A logical NOT expression. */
class LogicalNotExpr extends UnaryExpr;

/**
* An assignment expression, either compound or simple.
*
* Examples:
*
* ```
* x = y
* sum += element
* ```
*/
class Assignment extends BinaryExpr;

/** A simple assignment expression, for example `x = y`. */
class AssignExpr extends Assignment;

/** A compound assignment expression, for example `x += y` or `x ??= y`. */
class CompoundAssignment extends Assignment;

/** A short-circuiting logical AND compound assignment expression. */
class AssignLogicalAndExpr extends CompoundAssignment;

/** A short-circuiting logical OR compound assignment expression. */
class AssignLogicalOrExpr extends CompoundAssignment;

/** A short-circuiting null-coalescing compound assignment expression. */
class AssignNullCoalescingExpr extends CompoundAssignment;

/** A boolean literal expression. */
class BooleanLiteral extends Expr {
/** Gets the boolean value of this literal. */
boolean getValue();
}

/**
* A pattern matching expression.
*
* In Java this is `x instanceof Pattern`, and in C# this is `x is Pattern`.
*/
class PatternMatchExpr extends Expr {
/** Gets the expression being matched. */
Expr getExpr();

/** Gets the pattern being matched against. */
AstNode getPattern();
}
}

/**
Expand Down Expand Up @@ -426,12 +473,14 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
or
n instanceof ReturnStmt
or
n instanceof ThrowStmt
n instanceof Throw
or
n instanceof BreakStmt
or
n instanceof ContinueStmt
or
n instanceof GotoStmt
or
n instanceof Expr and
exists(getChild(n, _)) and
not Input1::preOrderExpr(n) and
Expand All @@ -449,11 +498,14 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
* is the value that causes the short-circuit.
*/
private predicate shortCircuiting(BinaryExpr expr, ConditionalSuccessor shortcircuitValue) {
expr instanceof LogicalAndExpr and shortcircuitValue.(BooleanSuccessor).getValue() = false
(expr instanceof LogicalAndExpr or expr instanceof AssignLogicalAndExpr) and
shortcircuitValue.(BooleanSuccessor).getValue() = false
or
expr instanceof LogicalOrExpr and shortcircuitValue.(BooleanSuccessor).getValue() = true
(expr instanceof LogicalOrExpr or expr instanceof AssignLogicalOrExpr) and
shortcircuitValue.(BooleanSuccessor).getValue() = true
or
expr instanceof NullCoalescingExpr and shortcircuitValue.(NullnessSuccessor).getValue() = true
(expr instanceof NullCoalescingExpr or expr instanceof AssignNullCoalescingExpr) and
shortcircuitValue.(NullnessSuccessor).getValue() = false
}

/**
Expand All @@ -463,9 +515,10 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
private predicate propagatesValue(AstNode child, AstNode parent) {
Input1::propagatesValue(child, parent)
or
// For now, the `not postOrInOrder(parent)` is superfluous, as we don't
// have any short-circuiting post-order expressions yet, but this will
// change once we add support for e.g. C#'s `??=`.
// Short-circuiting post-order expressions, i.e. short-circuiting
// compound assignments, e.g. C#'s `??=`, cannot propagate the value of
// the right-hand side to the parent, as the assignment must take place
// in-between, so propagating the value would imply splitting.
shortCircuiting(parent, _) and
not postOrInOrder(parent) and
parent.(BinaryExpr).getRightOperand() = child
Expand Down Expand Up @@ -535,6 +588,8 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {

private string loopHeaderTag() { result = "[LoopHeader]" }

private string patternMatchTrueTag() { result = "[match-true]" }
Comment thread
hvitved marked this conversation as resolved.
Outdated

/**
* Holds if an additional node tagged with `tag` should be created for
* `n`. Edges targeting such nodes are labeled with `t` and therefore `t`
Expand All @@ -546,6 +601,10 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n instanceof LoopStmt and
tag = loopHeaderTag() and
t instanceof DirectSuccessor
or
n instanceof PatternMatchExpr and
tag = patternMatchTrueTag() and
t.(BooleanSuccessor).getValue() = true
}

/**
Expand All @@ -561,9 +620,11 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
or
n instanceof ContinueStmt
or
n instanceof GotoStmt
or
n instanceof ReturnStmt
or
n instanceof ThrowStmt
n instanceof Throw
or
cannotTerminateNormally(n.(BlockStmt).getLastStmt())
or
Expand Down Expand Up @@ -992,14 +1053,17 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
ast instanceof ReturnStmt and
c.getSuccessorType() instanceof ReturnSuccessor
or
ast instanceof ThrowStmt and
ast instanceof Throw and
c.getSuccessorType() instanceof ExceptionSuccessor
or
ast instanceof BreakStmt and
c.getSuccessorType() instanceof BreakSuccessor
or
ast instanceof ContinueStmt and
c.getSuccessorType() instanceof ContinueSuccessor
or
ast instanceof GotoStmt and
c.getSuccessorType() instanceof GotoSuccessor
) and
(
not Input1::hasLabel(ast, _) and not c.hasLabel(_)
Expand All @@ -1020,6 +1084,11 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
)
}

private Stmt getAStmtInBlock(AstNode block) {
result = block.(BlockStmt).getStmt(_) or
result = block.(Switch).getStmt(_)
}

/**
* Holds if an abrupt completion `c` from within `ast` is caught with
* flow continuing at `n`.
Expand Down Expand Up @@ -1098,6 +1167,16 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
Input1::hasLabel(switch, l)
)
)
or
exists(AstNode block, Input1::Label l, Stmt lblstmt |
ast = getAStmtInBlock(block) and
lblstmt = getAStmtInBlock(block) and
not lblstmt instanceof GotoStmt and
Input1::hasLabel(pragma[only_bind_into](lblstmt), l) and
n.isBefore(lblstmt) and
c.getSuccessorType() instanceof GotoSuccessor and
c.hasLabel(l)
)
}

/**
Expand Down Expand Up @@ -1214,7 +1293,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n1.isAfterValue(binexpr.getLeftOperand(), shortcircuitValue) and
n2.isAfterValue(binexpr, shortcircuitValue)
or
// short-circuiting operations with side-effects (e.g. `x &&= y`, `x?.Prop = y`) are in post-order:
// short-circuiting operations with side-effects (e.g. `x &&= y`) are in post-order:
n1.isAfter(binexpr.getRightOperand()) and
n2.isIn(binexpr)
or
Expand Down Expand Up @@ -1249,6 +1328,26 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
n2.isAfterValue(boollit, any(BooleanSuccessor t | t.getValue() = boollit.getValue()))
)
or
exists(PatternMatchExpr pme |
n1.isBefore(pme) and
n2.isBefore(pme.getExpr())
or
n1.isAfter(pme.getExpr()) and
n2.isIn(pme)
or
n1.isIn(pme) and
n2.isAfterValue(pme, any(BooleanSuccessor s | s.getValue() = false))
or
n1.isIn(pme) and
n2.isAdditional(pme, patternMatchTrueTag())
or
n1.isAdditional(pme, patternMatchTrueTag()) and
n2.isBefore(pme.getPattern())
or
n1.isAfter(pme.getPattern()) and
n2.isAfterValue(pme, any(BooleanSuccessor s | s.getValue() = true))
)
or
exists(IfStmt ifstmt |
n1.isBefore(ifstmt) and
n2.isBefore(ifstmt.getCondition())
Expand Down