Skip to content

Move Branching EOG Edges and needed DF Edge from CF Nodes to their condition#2749

Open
konradweiss wants to merge 18 commits into
mainfrom
fix/dfg-to-cfnodes
Open

Move Branching EOG Edges and needed DF Edge from CF Nodes to their condition#2749
konradweiss wants to merge 18 commits into
mainfrom
fix/dfg-to-cfnodes

Conversation

@konradweiss
Copy link
Copy Markdown
Collaborator

@konradweiss konradweiss commented May 11, 2026

Removing DF Edge from branching condition to CFG Nodes in an attempt to solve the issue arising with them being expressions. This also requires to move the branching eog edges from the CF node to the condition, to continue capturing indirect data flows. We will change: Do, While, IfElse, For, ForEach, Conditional, Switch and Synchronized

The IfElse will no longer be evaluated as follows:
image
but will look like this:
image

In the case of a while loop it will no longer look like this:
image

instead it will look like this:
image

…to solve the issue arrising with them being expressions
@codecov
Copy link
Copy Markdown

codecov Bot commented May 11, 2026

Codecov Report

❌ Patch coverage is 89.13043% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.83%. Comparing base (5b2b6f1) to head (359eedf).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
.../fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt 88.88% 1 Missing and 2 partials ⚠️
...fer/aisec/cpg/passes/ControlDependenceGraphPass.kt 75.00% 0 Missing and 1 partial ⚠️
...hofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt 88.88% 1 Missing ⚠️
Additional details and impacted files
Files with missing lines Coverage Δ
...ec/cpg/analysis/abstracteval/value/IntegerValue.kt 39.44% <100.00%> (ø)
...fraunhofer/aisec/cpg/graph/EvaluationExtensions.kt 50.00% <100.00%> (+3.84%) ⬆️
...ain/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt 56.14% <ø> (-2.62%) ⬇️
...ec/cpg/helpers/functional/BasicLatticesRedesign.kt 67.56% <100.00%> (ø)
...n/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt 72.99% <ø> (-1.50%) ⬇️
...main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt 76.50% <100.00%> (+0.28%) ⬆️
...fer/aisec/cpg/passes/ControlDependenceGraphPass.kt 70.55% <75.00%> (-9.58%) ⬇️
...hofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt 88.98% <88.88%> (+0.29%) ⬆️
.../fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt 83.33% <88.88%> (-0.27%) ⬇️

... and 6 files with indirect coverage changes

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@konradweiss konradweiss changed the title Remove DF Edges from condition to CF Nodes Move Branching EOG Edges and needed DF Edge from CF Nodes to their condition May 19, 2026
@konradweiss konradweiss marked this pull request as ready for review May 19, 2026 16:02
@oxisto oxisto requested a review from Copilot May 20, 2026 17:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the Evaluation Order Graph (EOG) model for branching constructs by moving branching EOG edges (and previously-added DFG edges) away from control-flow (CF) nodes and onto their actual branching sub-nodes (conditions / selector / foreach variable), to avoid issues when branching statements are represented as expressions and to better preserve indirect data-flow behavior.

Changes:

  • Adjust EOG construction for branching constructs (If/Conditional/Loops/Switch/Synchronized) so branching happens at the condition/selector/variable node and merge happens at the parent node.
  • Remove DFG edges from branching parent nodes to their conditions/selectors that were previously added for “mutually exclusive branching expressions”.
  • Update control-dependence, unreachability analysis, abstract evaluation utilities, docs, and tests to match the new EOG shape.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
docs/docs/CPG/specs/eog.md Updates EOG specification diagrams for affected branching constructs.
cpg-language-java/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt Adjusts Java EOG tests to assert branching at condition nodes and merge at parent.
cpg-language-cxx/src/test/resources/c/switch_eog.c Minor test resource comment formatting update.
cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/StronglyConnectedComponentTest.kt Updates SCC assertions to use loop condition nodes as branching points.
cpg-language-cxx/src/test/kotlin/de/fraunhofer/aisec/cpg/enhancements/EOGTest.kt Adjusts C++ EOG tests to the new branching/merge locations.
cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPassTest.kt Updates pass-level EOG tests for loops/foreach semantics under the new model.
cpg-core/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPassTest.kt Updates CDG expectations to depend on condition/foreach-variable rather than parent CF node.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/Pass.kt Adds per-pass execution print statements during sequential pass execution.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/EvaluationOrderGraphPass.kt Core change: moves attach/branching logic so branching edges originate from condition/selector/variable nodes.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/DFGPass.kt Removes calls that added DFG edges from branching parents to their conditions/selectors.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/ControlDependenceGraphPass.kt Updates conditional-branch detection to align with branching-at-condition representation.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/Util.kt Removes the helper for adding DFG edges for mutually exclusive branching expressions.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/helpers/functional/BasicLatticesRedesign.kt Updates loop-branch detection to use the new “branch node” identification.
cpg-core/src/main/kotlin/de/fraunhofer/aisec/cpg/graph/EvaluationExtensions.kt Adds Node.branchOf(...) helper for detecting branching nodes.
cpg-analysis/src/test/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPassTest.kt Updates unreachability tests to look at outgoing edges of condition nodes.
cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/passes/UnreachableEOGPass.kt Switches unreachability propagation to branch from condition nodes rather than parent nodes.
cpg-analysis/src/main/kotlin/de/fraunhofer/aisec/cpg/analysis/abstracteval/value/IntegerValue.kt Adapts interval refinement to treat the condition node as the branch point.

Comment on lines 339 to +342
// Execute it
println("Executing: ${pass.simpleName}")
executePass(pass, ctx, result, executedFrontends)

println("Finished: ${pass.simpleName}")
child1 --EOG:false--> parent(["While"])
child1 --EOG:true--> child3["statement"]
child3 --EOG--> child1
parent --> next:::outer
@@ -1073,20 +1071,20 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa
handleEOG(node.elseExpression)
openBranchNodes.addAll(currentPredecessors)
setCurrentEOGs(openBranchNodes)
Comment on lines 1165 to 1167
node.elseStatement?.let { handleEOG(it) }
handleContainedBreaksAndContinues(node)
nextEdgeBranch = false
Comment on lines 1171 to 1191
@@ -1178,7 +1174,6 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa
handleEOG(node.conditionDeclaration)
handleEOG(node.condition)

attachToEOG(node) // To have semantic information after the condition evaluation
nextEdgeBranch = true
val tmpEOGNodes = currentPredecessors.toMutableList()

@@ -1192,6 +1187,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa
node.elseStatement?.let { handleEOG(it) }
handleContainedBreaksAndContinues(node)
nextEdgeBranch = false
attachToEOG(node)
}
Comment on lines 1193 to 1213
@@ -1200,7 +1196,6 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa
handleEOG(node.initializerStatement)
handleEOG(node.conditionDeclaration)
handleEOG(node.condition)
attachToEOG(node) // To have semantic information after the condition evaluation
val openConditionEOGs = currentPredecessors.toMutableList()
nextEdgeBranch = true
handleEOG(node.thenStatement)
@@ -1214,6 +1209,7 @@ open class EvaluationOrderGraphPass(ctx: TranslationContext) : TranslationUnitPa
openBranchNodes.addAll(openConditionEOGs)
}
setCurrentEOGs(openBranchNodes)
attachToEOG(node)
}
Comment on lines 339 to 388
/**
* Adds the DFG edge from [For.condition] or [For.conditionDeclaration] to the [For] to show the
* dependence between data and the branching node. Usage of one or the other in the statement is
* mutually exclusive.
*/
protected fun handleFor(node: For) {
Util.addDFGEdgesForMutuallyExclusiveBranchingExpression(
node,
node.condition,
node.conditionDeclaration,
)

if (node.usedAsExpression) {
node.statement?.let { node.prevDFGEdges += it }
node.elseStatement?.let { node.prevDFGEdges += it }
}
handleBreakableNodesAsExpressions(node)
}

/**
* Adds the DFG edge from [IfElse.condition] or [IfElse.conditionDeclaration] to the [IfElse] to
* show the dependence between data and the branching node. Usage of one or the other in the
* statement is mutually exclusive.
*/
protected fun handleIfElse(node: IfElse) {
Util.addDFGEdgesForMutuallyExclusiveBranchingExpression(
node,
node.condition,
node.conditionDeclaration,
)

if (node.usedAsExpression) {
node.thenStatement?.let { node.prevDFGEdges += it }
node.elseStatement?.let { node.prevDFGEdges += it }
}
}

/**
* Adds the DFG edge from [Switch.selector] or [Switch.selectorDeclaration] to the [Switch] to
* show the dependence between data and the branching node. Usage of one or the other in the
* statement is mutually exclusive.
*/
protected fun handleSwitch(node: Switch) {
Util.addDFGEdgesForMutuallyExclusiveBranchingExpression(
node,
node.selector,
node.selectorDeclaration,
)
if (node.usedAsExpression) {
node.statement?.let {
node.prevDFGEdges += it
it.usedAsExpression = true
}
}
handleBreakableNodesAsExpressions(node)
}

/**
* Adds the DFG edge from [While.condition] or [While.conditionDeclaration] to the [While] to
* show the dependence between data and the branching node. Usage of one or the other in the
* statement is mutually exclusive.
*/
protected fun handleWhile(node: While) {
Util.addDFGEdgesForMutuallyExclusiveBranchingExpression(
node,
node.condition,
node.conditionDeclaration,
)

if (node.usedAsExpression) {
Comment on lines 113 to +124
@Test
fun testIfFalse() {
val method = tu.functions["ifFalse"]
assertNotNull(method)

val ifStatement = method.ifs.firstOrNull()
assertNotNull(ifStatement)

assertFalse(ifStatement.nextEOGEdges[1].unreachable)
assertTrue(ifStatement.nextEOGEdges[0].unreachable)
ifStatement.condition?.let {
assertFalse(it.nextEOGEdges[1].unreachable)
assertTrue(it.nextEOGEdges[0].unreachable)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants