use crate::binding::{BindingRegistry, ImplStatus};
use crate::error::{Severity, Violation};
use crate::schema::Contract;
#[derive(Debug, Clone)]
pub struct AuditReport {
pub equations: usize,
pub obligations: usize,
pub falsification_tests: usize,
pub kani_harnesses: usize,
pub violations: Vec<Violation>,
}
pub fn audit_contract(contract: &Contract) -> AuditReport {
let mut violations = Vec::new();
let is_kernel_kind = contract.requires_proofs();
if is_kernel_kind && contract.falsification_tests.is_empty() {
violations.push(Violation {
severity: Severity::Warning,
rule: "AUDIT-001".to_string(),
message: "No falsification tests — contract is \
not falsifiable"
.to_string(),
location: Some("falsification_tests".to_string()),
});
}
let obligation_props: Vec<&str> = contract
.proof_obligations
.iter()
.map(|o| o.property.as_str())
.collect();
for harness in &contract.kani_harnesses {
if harness.obligation.is_empty() {
violations.push(Violation {
severity: Severity::Error,
rule: "AUDIT-002".to_string(),
message: format!(
"Kani harness {} has empty obligation \
reference",
harness.id
),
location: Some(format!("kani_harnesses.{}.obligation", harness.id)),
});
}
}
if is_kernel_kind && !contract.equations.is_empty() && contract.proof_obligations.is_empty() {
violations.push(Violation {
severity: Severity::Warning,
rule: "AUDIT-003".to_string(),
message: "Equations defined but no proof obligations \
— obligations should be derived from equations"
.to_string(),
location: Some("proof_obligations".to_string()),
});
}
let _ = obligation_props;
AuditReport {
equations: contract.equations.len(),
obligations: contract.proof_obligations.len(),
falsification_tests: contract.falsification_tests.len(),
kani_harnesses: contract.kani_harnesses.len(),
violations,
}
}
#[derive(Debug, Clone)]
pub struct BindingAuditReport {
pub total_equations: usize,
pub bound_equations: usize,
pub implemented: usize,
pub partial: usize,
pub not_implemented: usize,
pub total_obligations: usize,
pub covered_obligations: usize,
pub violations: Vec<Violation>,
}
pub fn audit_binding(
contracts: &[(&str, &Contract)],
binding: &BindingRegistry,
) -> BindingAuditReport {
let mut violations = Vec::new();
let mut total_equations = 0usize;
let mut bound_equations = 0usize;
let mut implemented = 0usize;
let mut partial = 0usize;
let mut not_implemented = 0usize;
let mut total_obligations = 0usize;
let mut covered_obligations = 0usize;
for &(contract_file, contract) in contracts {
let eq_count = contract.equations.len();
total_equations += eq_count;
total_obligations += contract.proof_obligations.len();
for eq_name in contract.equations.keys() {
let matching = binding.find_binding(contract_file, eq_name);
match matching {
Some(b) => {
bound_equations += 1;
match b.status {
ImplStatus::Implemented => {
implemented += 1;
covered_obligations += obligations_for_equation(contract);
}
ImplStatus::Partial => {
partial += 1;
violations.push(Violation {
severity: Severity::Warning,
rule: "BIND-002".to_string(),
message: format!(
"Equation '{eq_name}' in \
{contract_file} is partially \
implemented"
),
location: Some(format!("bindings.{eq_name}")),
});
}
ImplStatus::NotImplemented => {
not_implemented += 1;
violations.push(Violation {
severity: Severity::Warning,
rule: "BIND-003".to_string(),
message: format!(
"Equation '{eq_name}' in \
{contract_file} has no \
implementation"
),
location: Some(format!("bindings.{eq_name}")),
});
}
ImplStatus::Pending => {
not_implemented += 1;
violations.push(Violation {
severity: Severity::Warning,
rule: "BIND-004".to_string(),
message: format!(
"Equation '{eq_name}' in \
{contract_file} is pending \
implementation"
),
location: Some(format!("bindings.{eq_name}")),
});
}
}
}
None => {
violations.push(Violation {
severity: Severity::Error,
rule: "BIND-001".to_string(),
message: format!(
"Equation '{eq_name}' in {contract_file} \
has no binding entry"
),
location: Some(format!("{contract_file}.equations.{eq_name}")),
});
}
}
}
}
BindingAuditReport {
total_equations,
bound_equations,
implemented,
partial,
not_implemented,
total_obligations,
covered_obligations,
violations,
}
}
fn obligations_for_equation(contract: &Contract) -> usize {
contract
.proof_obligations
.iter()
.filter(|o| o.applies_to != Some(crate::schema::AppliesTo::Simd))
.count()
}
#[cfg(test)]
mod tests {
include!("mod_tests.rs");
}