use decy_analyzer::lock_analysis::LockAnalyzer;
use decy_hir::HirFunction;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LockDisciplineReport {
pub unprotected_accesses: usize,
pub lock_violations: usize,
pub deadlock_warnings: usize,
}
impl LockDisciplineReport {
pub fn is_clean(&self) -> bool {
self.unprotected_accesses == 0 && self.lock_violations == 0 && self.deadlock_warnings == 0
}
}
pub struct LockDisciplineChecker<'a> {
analyzer: &'a LockAnalyzer,
}
impl<'a> LockDisciplineChecker<'a> {
pub fn new(analyzer: &'a LockAnalyzer) -> Self {
Self { analyzer }
}
pub fn check_unprotected_access(&self, func: &HirFunction) -> Vec<String> {
let mut violations = Vec::new();
let mapping = self.analyzer.analyze_lock_data_mapping(func);
let protected_vars: std::collections::HashSet<String> =
mapping.get_locks().iter().flat_map(|lock| mapping.get_protected_data(lock)).collect();
let lock_regions = self.analyzer.find_lock_regions(func);
let body = func.body();
for (idx, stmt) in body.iter().enumerate() {
if self.is_inside_any_region(idx, &lock_regions) {
continue;
}
let accessed_vars = self.collect_accessed_vars(stmt);
for var in accessed_vars {
if protected_vars.contains(&var) {
violations.push(format!(
"Unprotected access to '{}' at statement {} (outside locked region)",
var, idx
));
}
}
}
violations
}
fn is_inside_any_region(
&self,
idx: usize,
regions: &[decy_analyzer::lock_analysis::LockRegion],
) -> bool {
regions.iter().any(|r| idx > r.start_index && idx < r.end_index)
}
fn collect_accessed_vars(&self, stmt: &decy_hir::HirStatement) -> Vec<String> {
use decy_hir::HirStatement;
let mut vars = Vec::new();
match stmt {
HirStatement::Assignment { target, value } => {
vars.push(target.clone());
Self::collect_vars_from_expr(value, &mut vars);
}
HirStatement::VariableDeclaration { initializer: Some(init), .. } => {
Self::collect_vars_from_expr(init, &mut vars);
}
HirStatement::Expression(expr) => {
Self::collect_vars_from_expr(expr, &mut vars);
}
HirStatement::Return(Some(expr)) => {
Self::collect_vars_from_expr(expr, &mut vars);
}
_ => {}
}
vars
}
fn collect_vars_from_expr(expr: &decy_hir::HirExpression, vars: &mut Vec<String>) {
use decy_hir::HirExpression;
match expr {
HirExpression::Variable(name) => {
vars.push(name.clone());
}
HirExpression::BinaryOp { left, right, .. } => {
Self::collect_vars_from_expr(left, vars);
Self::collect_vars_from_expr(right, vars);
}
HirExpression::UnaryOp { operand, .. } => {
Self::collect_vars_from_expr(operand, vars);
}
HirExpression::FunctionCall { arguments, .. } => {
for arg in arguments {
Self::collect_vars_from_expr(arg, vars);
}
}
HirExpression::AddressOf(inner) | HirExpression::Dereference(inner) => {
Self::collect_vars_from_expr(inner, vars);
}
HirExpression::ArrayIndex { array, index } => {
Self::collect_vars_from_expr(array, vars);
Self::collect_vars_from_expr(index, vars);
}
HirExpression::FieldAccess { object, .. } => {
Self::collect_vars_from_expr(object, vars);
}
HirExpression::Cast { expr, .. } => {
Self::collect_vars_from_expr(expr, vars);
}
_ => {}
}
}
pub fn check_deadlock_risk(&self, functions: &[HirFunction]) -> Vec<String> {
let mut warnings = Vec::new();
if functions.is_empty() {
return warnings;
}
let mut lock_orderings: Vec<Vec<String>> = Vec::new();
for func in functions {
let ordering = self.extract_lock_ordering(func);
if !ordering.is_empty() {
lock_orderings.push(ordering);
}
}
for i in 0..lock_orderings.len() {
for j in (i + 1)..lock_orderings.len() {
if let Some(warning) =
self.detect_ordering_conflict(&lock_orderings[i], &lock_orderings[j])
{
warnings.push(warning);
}
}
}
warnings
}
fn extract_lock_ordering(&self, func: &HirFunction) -> Vec<String> {
use decy_hir::{HirExpression, HirStatement};
let mut ordering = Vec::new();
let body = func.body();
for stmt in body {
if let HirStatement::Expression(HirExpression::FunctionCall { function, arguments }) =
stmt
{
if function == "pthread_mutex_lock" {
if let Some(HirExpression::AddressOf(inner)) = arguments.first() {
if let HirExpression::Variable(name) = &**inner {
ordering.push(name.clone());
}
}
}
}
}
ordering
}
fn detect_ordering_conflict(
&self,
ordering1: &[String],
ordering2: &[String],
) -> Option<String> {
for i in 0..ordering1.len() {
for j in (i + 1)..ordering1.len() {
let lock_a = &ordering1[i];
let lock_b = &ordering1[j];
let pos_a_in_2 = ordering2.iter().position(|l| l == lock_a);
let pos_b_in_2 = ordering2.iter().position(|l| l == lock_b);
if let (Some(pos_a), Some(pos_b)) = (pos_a_in_2, pos_b_in_2) {
if pos_b < pos_a {
return Some(format!(
"Potential deadlock: Inconsistent lock ordering detected. \
One function acquires {} then {}, another acquires {} then {}",
lock_a, lock_b, lock_b, lock_a
));
}
}
}
}
None
}
pub fn check_all(&self, func: &HirFunction) -> LockDisciplineReport {
let unprotected = self.check_unprotected_access(func);
let lock_violations = self.analyzer.check_lock_discipline(func);
LockDisciplineReport {
unprotected_accesses: unprotected.len(),
lock_violations: lock_violations.len(),
deadlock_warnings: 0, }
}
}