impl ReachabilityAnalyzer {
#[must_use]
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn new() -> Self {
Self {
entry_points: HashSet::new(),
reachable: HashSet::new(),
ffi_exports: HashSet::new(),
dynamic_targets: HashSet::new(),
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn find_entry_points(&mut self, ast: &UnifiedAstNode, file_path: &str) {
self.visit_for_entry_points(ast, file_path);
}
fn visit_for_entry_points(&mut self, node: &UnifiedAstNode, file_path: &str) {
if let AstKind::Function(FunctionKind::Regular) = &node.kind {
if let Some(name) = self.extract_function_name(node) {
if name == "main" {
self.entry_points.insert(SymbolId {
file_path: file_path.to_string(),
function_name: name.clone(),
line_number: node.source_range.start as usize,
});
}
if name.starts_with("test_") || self.has_test_attribute(node) {
self.entry_points.insert(SymbolId {
file_path: file_path.to_string(),
function_name: name.clone(),
line_number: node.source_range.start as usize,
});
}
if name.starts_with("bench_") || self.has_benchmark_attribute(node) {
self.entry_points.insert(SymbolId {
file_path: file_path.to_string(),
function_name: name,
line_number: node.source_range.start as usize,
});
}
}
}
}
fn extract_function_name(&self, _node: &UnifiedAstNode) -> Option<String> {
None
}
fn has_test_attribute(&self, _node: &UnifiedAstNode) -> bool {
false
}
fn has_benchmark_attribute(&self, _node: &UnifiedAstNode) -> bool {
false
}
}
#[cfg(test)]
mod reachability_analyzer_tests {
use super::*;
use crate::models::unified_ast::{
AstKind, FunctionKind, Language, UnifiedAstNode,
};
#[test]
fn test_new_returns_empty_state() {
let analyzer = ReachabilityAnalyzer::new();
assert!(analyzer.entry_points.is_empty());
assert!(analyzer.reachable.is_empty());
assert!(analyzer.ffi_exports.is_empty());
assert!(analyzer.dynamic_targets.is_empty());
}
#[test]
fn test_default_matches_new() {
let from_default = ReachabilityAnalyzer::default();
let from_new = ReachabilityAnalyzer::new();
assert_eq!(
from_default.entry_points.len(),
from_new.entry_points.len()
);
assert_eq!(from_default.reachable.len(), from_new.reachable.len());
}
#[test]
fn test_extract_function_name_returns_none_placeholder() {
let analyzer = ReachabilityAnalyzer::new();
let node = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
Language::Rust,
);
assert!(analyzer.extract_function_name(&node).is_none());
}
#[test]
fn test_has_test_attribute_returns_false_placeholder() {
let analyzer = ReachabilityAnalyzer::new();
let node = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
Language::Rust,
);
assert!(!analyzer.has_test_attribute(&node));
}
#[test]
fn test_has_benchmark_attribute_returns_false_placeholder() {
let analyzer = ReachabilityAnalyzer::new();
let node = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
Language::Rust,
);
assert!(!analyzer.has_benchmark_attribute(&node));
}
#[test]
fn test_find_entry_points_function_node_no_entry_points_due_to_placeholder() {
let mut analyzer = ReachabilityAnalyzer::new();
let node = UnifiedAstNode::new(
AstKind::Function(FunctionKind::Regular),
Language::Rust,
);
analyzer.find_entry_points(&node, "src/a.rs");
assert!(analyzer.entry_points.is_empty());
}
#[test]
fn test_find_entry_points_non_function_node_no_entry_points() {
use crate::models::unified_ast::ClassKind;
let mut analyzer = ReachabilityAnalyzer::new();
let node = UnifiedAstNode::new(
AstKind::Class(ClassKind::Regular),
Language::Rust,
);
analyzer.find_entry_points(&node, "src/a.rs");
assert!(analyzer.entry_points.is_empty());
}
}