use ahash::{AHashMap, AHashSet};
use std::collections::HashSet;
use crate::errors::SqliteGraphError;
use crate::graph::SqliteGraph;
use super::post_dominators::PostDominatorResult;
#[derive(Debug, Clone)]
pub struct ControlDependenceResult {
pub cdg: AHashMap<i64, AHashSet<i64>>,
pub reverse_cdg: AHashMap<i64, AHashSet<i64>>,
}
impl ControlDependenceResult {
pub fn controls(&self, controller: i64, controlled: i64) -> bool {
self.cdg
.get(&controller)
.map(|set| set.contains(&controlled))
.unwrap_or(false)
}
pub fn depends_on(&self, dependent: i64, dependency: i64) -> bool {
self.reverse_cdg
.get(&dependent)
.map(|set| set.contains(&dependency))
.unwrap_or(false)
}
pub fn controlled_by(&self, controller: i64) -> Option<&AHashSet<i64>> {
self.cdg.get(&controller)
}
pub fn dependencies_of(&self, dependent: i64) -> Option<&AHashSet<i64>> {
self.reverse_cdg.get(&dependent)
}
pub fn is_acyclic(&self) -> bool {
let mut visited = HashSet::new();
let mut rec_stack = HashSet::new();
for &node in self.cdg.keys() {
if self.has_cycle_from(node, &mut visited, &mut rec_stack) {
return false;
}
}
true
}
fn has_cycle_from(
&self,
node: i64,
visited: &mut HashSet<i64>,
rec_stack: &mut HashSet<i64>,
) -> bool {
visited.insert(node);
rec_stack.insert(node);
if let Some(controlled) = self.cdg.get(&node) {
for &neighbor in controlled {
if !visited.contains(&neighbor) {
if self.has_cycle_from(neighbor, visited, rec_stack) {
return true;
}
} else if rec_stack.contains(&neighbor) {
return true; }
}
}
rec_stack.remove(&node);
false
}
}
pub fn control_dependence_graph(
graph: &SqliteGraph,
post_result: &PostDominatorResult,
) -> Result<ControlDependenceResult, SqliteGraphError> {
let post_dom = &post_result.post_dom;
let ipdom = &post_result.ipdom;
let mut cdg: AHashMap<i64, AHashSet<i64>> = AHashMap::new();
let mut reverse_cdg: AHashMap<i64, AHashSet<i64>> = AHashMap::new();
let all_nodes = graph.all_entity_ids()?;
if all_nodes.is_empty() {
return Ok(ControlDependenceResult { cdg, reverse_cdg });
}
for &from in &all_nodes {
let outgoing = graph.fetch_outgoing(from)?;
for &to in &outgoing {
if is_control_dependent(from, to, post_dom, ipdom) {
cdg.entry(from).or_default().insert(to);
reverse_cdg.entry(to).or_default().insert(from);
}
}
}
Ok(ControlDependenceResult { cdg, reverse_cdg })
}
pub fn control_dependence_from_exit(
graph: &SqliteGraph,
) -> Result<ControlDependenceResult, SqliteGraphError> {
let post_result = super::post_dominators::post_dominators_auto_exit(graph)?;
control_dependence_graph(graph, &post_result)
}
fn is_control_dependent(
from: i64,
to: i64,
post_dom: &AHashMap<i64, AHashSet<i64>>,
ipdom: &AHashMap<i64, Option<i64>>,
) -> bool {
let from_post_dominates_to = post_dom
.get(&to)
.map(|set| set.contains(&from))
.unwrap_or(false);
if from_post_dominates_to {
return false;
}
let ipdom_of_to = ipdom.get(&to).copied().flatten();
if ipdom_of_to == Some(from) {
return false;
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{GraphEdge, GraphEntity};
fn create_if_then_else_cfg() -> SqliteGraph {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..4 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let edges = vec![(0, 1), (0, 2), (1, 3), (2, 3)];
for (from_idx, to_idx) in edges {
let edge = GraphEdge {
id: 0,
from_id: entity_ids[from_idx],
to_id: entity_ids[to_idx],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
}
graph
}
fn create_loop_cfg() -> SqliteGraph {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..3 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let edges = vec![(0, 1), (1, 2), (2, 1)];
for (from_idx, to_idx) in edges {
let edge = GraphEdge {
id: 0,
from_id: entity_ids[from_idx],
to_id: entity_ids[to_idx],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
}
graph
}
fn create_nested_if_cfg() -> SqliteGraph {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..6 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let edges = vec![(0, 1), (0, 2), (1, 3), (1, 4), (3, 5), (4, 5)];
for (from_idx, to_idx) in edges {
let edge = GraphEdge {
id: 0,
from_id: entity_ids[from_idx],
to_id: entity_ids[to_idx],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
}
graph
}
fn create_while_loop_cfg() -> SqliteGraph {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..4 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let edges = vec![(0, 1), (1, 2), (2, 1), (1, 3)];
for (from_idx, to_idx) in edges {
let edge = GraphEdge {
id: 0,
from_id: entity_ids[from_idx],
to_id: entity_ids[to_idx],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
}
graph
}
fn create_linear_chain() -> SqliteGraph {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..4 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
for i in 0..entity_ids.len().saturating_sub(1) {
let edge = GraphEdge {
id: 0,
from_id: entity_ids[i],
to_id: entity_ids[i + 1],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
}
graph
}
#[test]
fn test_control_dependence_if_then_else() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 1 should control node 2"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 1 should control node 3"
);
assert!(
cdg_result.controls(entity_ids[1], entity_ids[3]),
"Node 2 should control exit"
);
assert!(
cdg_result.controls(entity_ids[2], entity_ids[3]),
"Node 3 should control exit"
);
assert!(
cdg_result
.cdg
.get(&entity_ids[0])
.map(|s| s.contains(&entity_ids[1]))
.unwrap_or(false)
);
assert!(
cdg_result
.cdg
.get(&entity_ids[0])
.map(|s| s.contains(&entity_ids[2]))
.unwrap_or(false)
);
assert!(
cdg_result
.reverse_cdg
.get(&entity_ids[3])
.map(|s| s.contains(&entity_ids[1]))
.unwrap_or(false)
);
assert!(
cdg_result
.reverse_cdg
.get(&entity_ids[3])
.map(|s| s.contains(&entity_ids[2]))
.unwrap_or(false)
);
}
#[test]
fn test_control_dependence_loop() {
let graph = create_loop_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[2];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controls(entity_ids[1], entity_ids[2]),
"Node 1 should control node 2"
);
assert!(
cdg_result.depends_on(entity_ids[2], entity_ids[1]),
"Node 2 should depend on node 1"
);
assert_eq!(cdg_result.controlled_by(entity_ids[2]), None);
}
#[test]
fn test_control_dependence_nested_if() {
let graph = create_nested_if_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[5];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
!cdg_result.cdg.is_empty(),
"CDG should not be empty for nested if"
);
assert!(cdg_result.is_acyclic(), "CDG should be acyclic");
}
#[test]
fn test_control_dependence_linear_chain() {
let graph = create_linear_chain();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert_eq!(
cdg_result.cdg.len(),
3,
"CDG should have 3 control dependence edges for linear chain"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[1], entity_ids[2]),
"Node 1 should control node 2"
);
assert!(
cdg_result.controls(entity_ids[2], entity_ids[3]),
"Node 2 should control node 3"
);
assert!(
!cdg_result.cdg.contains_key(&entity_ids[3]),
"Exit node should not control any node"
);
}
#[test]
fn test_control_dependence_single_node() {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: "single".to_string(),
file_path: Some("single.rs".to_string()),
data: serde_json::json!({}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
let entity_ids = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[0];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert_eq!(cdg_result.cdg.len(), 0);
assert_eq!(cdg_result.reverse_cdg.len(), 0);
}
#[test]
fn test_reverse_cdg_consistency() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
for (&controller, controlled_set) in &cdg_result.cdg {
for &controlled in controlled_set {
assert!(
cdg_result.depends_on(controlled, controller),
"Reverse mapping missing: {} should depend on {}",
controlled,
controller
);
}
}
for (&dependent, depends_on_set) in &cdg_result.reverse_cdg {
for &dependency in depends_on_set {
assert!(
cdg_result.controls(dependency, dependent),
"Forward mapping missing: {} should control {}",
dependency,
dependent
);
}
}
}
#[test]
fn test_reverse_cdg_lookup() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
let deps = cdg_result.dependencies_of(entity_ids[3]);
assert!(deps.is_some(), "Exit should have dependencies");
assert!(
deps.map(|set| set.contains(&entity_ids[1]))
.unwrap_or(false),
"Exit should depend on node 1"
);
assert!(
deps.map(|set| set.contains(&entity_ids[2]))
.unwrap_or(false),
"Exit should depend on node 2"
);
let deps = cdg_result.dependencies_of(entity_ids[0]);
assert_eq!(deps, None, "Node 0 should have no dependencies");
}
#[test]
fn test_reverse_cdg_empty_for_no_dependencies() {
let graph = create_linear_chain();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controlled_by(entity_ids[0]).is_some(),
"Node 0 should control some nodes"
);
assert_eq!(
cdg_result.controlled_by(entity_ids[3]),
None,
"Exit should control nothing"
);
}
#[test]
fn test_control_dependence_empty_graph() {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
let post_result = super::super::post_dominators::post_dominators_auto_exit(&graph)
.expect("Failed on empty graph");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert_eq!(cdg_result.cdg.len(), 0);
assert_eq!(cdg_result.reverse_cdg.len(), 0);
}
#[test]
fn test_control_dependence_single_edge() {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..2 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let edge = GraphEdge {
id: 0,
from_id: entity_ids[0],
to_id: entity_ids[1],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
let post_result = super::super::post_dominators::post_dominators(&graph, entity_ids[1])
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert_eq!(
cdg_result.cdg.len(),
1,
"Single edge should have control dependence"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
}
#[test]
fn test_control_dependence_diamond_no_merge_dep() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 0 should control node 2"
);
assert!(
cdg_result.controls(entity_ids[1], entity_ids[3]),
"Node 1 should control exit"
);
assert!(
cdg_result.controls(entity_ids[2], entity_ids[3]),
"Node 2 should control exit"
);
}
#[test]
fn test_control_dependence_multiple_branches() {
let graph = SqliteGraph::open_in_memory().expect("Failed to create graph");
for i in 0..5 {
let entity = GraphEntity {
id: 0,
kind: "node".to_string(),
name: format!("node_{}", i),
file_path: Some(format!("node_{}.rs", i)),
data: serde_json::json!({"index": i}),
};
graph
.insert_entity(&entity)
.expect("Failed to insert entity");
}
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let edges = vec![(0, 1), (0, 2), (0, 3), (1, 4), (2, 4), (3, 4)];
for (from_idx, to_idx) in edges {
let edge = GraphEdge {
id: 0,
from_id: entity_ids[from_idx],
to_id: entity_ids[to_idx],
edge_type: "next".to_string(),
data: serde_json::json!({}),
};
graph.insert_edge(&edge).expect("Failed to insert edge");
}
let post_result = super::super::post_dominators::post_dominators(&graph, entity_ids[4])
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 0 should control node 2"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[3]),
"Node 0 should control node 3"
);
assert!(
cdg_result.controls(entity_ids[1], entity_ids[4]),
"Node 1 should control exit"
);
assert!(
cdg_result.controls(entity_ids[2], entity_ids[4]),
"Node 2 should control exit"
);
assert!(
cdg_result.controls(entity_ids[3], entity_ids[4]),
"Node 3 should control exit"
);
}
#[test]
fn test_control_dependence_from_post_result() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 0 should control node 2"
);
}
#[test]
fn test_control_dependence_helper_function() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let cdg_result = control_dependence_from_exit(&graph)
.expect("Failed to compute control dependence from exit");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 0 should control node 2"
);
}
#[test]
fn test_control_dependence_with_auto_exit() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let cdg_result = control_dependence_from_exit(&graph)
.expect("Failed to compute control dependence with auto exit");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 0 should control node 2"
);
}
#[test]
fn test_control_dependence_symmetry() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
for (&controller, controlled_set) in &cdg_result.cdg {
for &controlled in controlled_set {
assert!(
cdg_result.controls(controller, controlled),
"cdg should have X -> Y"
);
assert!(
cdg_result.depends_on(controlled, controller),
"reverse_cdg should have Y -> X"
);
}
}
}
#[test]
fn test_control_dependence_acyclic() {
let graph = create_loop_cfg(); let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[2];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.is_acyclic(),
"CDG must be acyclic (fundamental property)"
);
}
#[test]
fn test_control_dependence_acyclic_nested_structures() {
let graph = create_nested_if_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[5];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(cdg_result.is_acyclic(), "CDG must be acyclic");
}
#[test]
fn test_control_dependence_while_loop() {
let graph = create_while_loop_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.depends_on(entity_ids[1], entity_ids[2]),
"Loop header should depend on loop body (back-edge)"
);
assert!(
cdg_result.depends_on(entity_ids[1], entity_ids[0]),
"Loop header should depend on entry"
);
assert!(
cdg_result.depends_on(entity_ids[3], entity_ids[1]),
"Exit should depend on loop header"
);
}
#[test]
fn test_control_dependence_controls_method() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.controls(entity_ids[0], entity_ids[1]),
"Node 0 should control node 1"
);
assert!(
cdg_result.controls(entity_ids[0], entity_ids[2]),
"Node 0 should control node 2"
);
assert!(
!cdg_result.controls(entity_ids[0], entity_ids[3]),
"Node 0 should NOT control exit"
);
assert!(
!cdg_result.controls(entity_ids[3], entity_ids[0]),
"Exit should NOT control node 0"
); }
#[test]
fn test_control_dependence_depends_on_method() {
let graph = create_if_then_else_cfg();
let entity_ids: Vec<i64> = graph.list_entity_ids().expect("Failed to get IDs");
let exit = entity_ids[3];
let post_result = super::super::post_dominators::post_dominators(&graph, exit)
.expect("Failed to compute post-dominators");
let cdg_result = control_dependence_graph(&graph, &post_result)
.expect("Failed to compute control dependence");
assert!(
cdg_result.depends_on(entity_ids[1], entity_ids[0]),
"Node 1 should depend on node 0"
);
assert!(
cdg_result.depends_on(entity_ids[2], entity_ids[0]),
"Node 2 should depend on node 0"
);
assert!(
!cdg_result.depends_on(entity_ids[0], entity_ids[1]),
"Node 0 should NOT depend on node 1"
); }
}