#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::handlers::work_contract::{EvidenceType, FalsificationResult};
use anyhow::Result;
use std::path::Path;
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn count_lean_sorry_in_source(source: &str) -> usize {
let mut count = 0;
let mut in_block_comment = 0i32;
for line in source.lines() {
let trimmed = line.trim();
if trimmed.starts_with("--") {
continue;
}
let cleaned = strip_lean_block_comments(trimmed, &mut in_block_comment);
if in_block_comment > 0 {
continue;
}
if contains_sorry_word_boundary(&cleaned) {
count += 1;
}
}
count
}
fn strip_lean_block_comments(line: &str, depth: &mut i32) -> String {
let bytes = line.as_bytes();
let mut result = String::with_capacity(line.len());
let mut i = 0;
while i < bytes.len() {
if i + 1 < bytes.len() && bytes[i] == b'/' && bytes[i + 1] == b'-' {
*depth += 1;
i += 2;
continue;
}
if i + 1 < bytes.len() && bytes[i] == b'-' && bytes[i + 1] == b'/' && *depth > 0 {
*depth -= 1;
i += 2;
continue;
}
if *depth == 0 {
result.push(bytes[i] as char);
}
i += 1;
}
result
}
fn contains_sorry_word_boundary(line: &str) -> bool {
let bytes = line.as_bytes();
let sorry = b"sorry";
let mut pos = 0;
while pos + sorry.len() <= bytes.len() {
if let Some(idx) = line[pos..].find("sorry") {
let abs_idx = pos + idx;
let before_ok = abs_idx == 0
|| !(bytes[abs_idx - 1].is_ascii_alphanumeric() || bytes[abs_idx - 1] == b'_');
let after_ok = abs_idx + sorry.len() >= bytes.len()
|| !(bytes[abs_idx + sorry.len()].is_ascii_alphanumeric()
|| bytes[abs_idx + sorry.len()] == b'_');
if before_ok && after_ok {
return true;
}
pos = abs_idx + 1;
} else {
break;
}
}
false
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub(crate) fn test_formal_proof_verification(
project_path: &Path,
max_sorry_count: usize,
) -> Result<FalsificationResult> {
print!("Scanning .lean files for sorry... ");
let mut total_sorry = 0usize;
let mut sorry_files = Vec::new();
for entry in walkdir::WalkDir::new(project_path)
.into_iter()
.filter_map(|e| e.ok())
{
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "lean") {
if let Ok(content) = std::fs::read_to_string(path) {
let count = count_lean_sorry_in_source(&content);
if count > 0 {
total_sorry += count;
sorry_files.push(path.to_path_buf());
}
}
}
}
if sorry_files.is_empty() && total_sorry == 0 {
return Ok(FalsificationResult::passed(
"No .lean files with sorry found".to_string(),
));
}
if total_sorry <= max_sorry_count {
Ok(FalsificationResult::passed(format!(
"{} sorry occurrence(s) within threshold (max: {})",
total_sorry, max_sorry_count
)))
} else {
Ok(FalsificationResult::failed(
format!(
"{} sorry occurrence(s) exceed threshold (max: {}), in: {}",
total_sorry,
max_sorry_count,
sorry_files
.iter()
.map(|p| p.display().to_string())
.collect::<Vec<_>>()
.join(", ")
),
EvidenceType::FileList(sorry_files),
))
}
}