use crate::proof_status::ProofLevel;
pub const MARKER: &str = "<!-- generated from ProofLevel; do not edit -->";
pub const END_MARKER: &str = "<!-- end generated from ProofLevel -->";
impl ProofLevel {
pub const ALL_DESCENDING: [ProofLevel; 5] = [
ProofLevel::L5,
ProofLevel::L4,
ProofLevel::L3,
ProofLevel::L2,
ProofLevel::L1,
];
#[must_use]
pub fn method(self) -> &'static str {
match self {
ProofLevel::L1 => "Contract YAML with equations",
ProofLevel::L2 => "Falsification tests cover every obligation",
ProofLevel::L3 => "L2 + at least one Kani bounded-model-check harness",
ProofLevel::L4 => {
"Every obligation has a sorry-free in-tree Lean 4 theorem or is not applicable, with at least one proved"
}
ProofLevel::L5 => "L4 + at least one binding, every binding implemented",
}
}
}
#[must_use]
pub fn ladder_block() -> String {
let mut out = String::new();
out.push_str(MARKER);
out.push('\n');
out.push_str("| Level | Method |\n|-------|--------|\n");
for level in ProofLevel::ALL_DESCENDING {
out.push_str(&format!("| {level} | {} |\n", level.method()));
}
out.push_str(
"\nL4 and L5 are grounded only textually until PVL-001 EV-8b lands: a claimed Lean \
proof counts when a sorry-free Lean theorem in this tree matches it (a claim with \
none is reported self-declared and excluded from L4), a not-applicable count is \
taken from the contract's own verification summary, and no checked lake \
discharge summary is read yet.\n",
);
out.push_str(END_MARKER);
out
}
pub fn stale_level_pairings(doc: &str) -> Vec<String> {
let outside: String = match (doc.find(MARKER), doc.find(END_MARKER)) {
(Some(a), Some(b)) if a < b => format!("{}{}", &doc[..a], &doc[b + END_MARKER.len()..]),
_ => doc.to_string(),
};
let mut stale = Vec::new();
for line in outside.lines() {
let low = line.to_lowercase();
let levels = level_tokens(&low);
let wrong = ["kani", "lean"].iter().any(|tool| {
word_starts(&low, tool).any(|t| {
let t_end = t + tool.len();
let nearest = levels.iter().min_by_key(|(start, end, _)| {
if *end <= t {
t - end
} else if *start >= t_end {
start - t_end
} else {
0
}
});
match (*tool, nearest) {
("kani", Some((_, _, n))) => *n >= 4,
("lean", Some((_, _, n))) => *n == 5 && !low.contains("binding"),
_ => false,
}
})
});
if wrong {
stale.push(line.trim().to_string());
}
}
stale
}
fn word_starts<'a>(hay: &'a str, needle: &'a str) -> impl Iterator<Item = usize> + 'a {
hay.match_indices(needle)
.map(|(i, _)| i)
.filter(move |&i| i == 0 || !hay.as_bytes()[i - 1].is_ascii_alphanumeric())
}
fn level_tokens(low: &str) -> Vec<(usize, usize, u8)> {
let mut out = Vec::new();
for n in 1u8..=5 {
for tok in [format!("l{n}"), format!("level {n}")] {
for i in word_starts(low, &tok) {
let end = i + tok.len();
if !low[end..].starts_with(|c: char| c.is_ascii_alphanumeric()) {
out.push((i, end, n));
}
}
}
}
out
}
#[test]
fn readme_and_ladder_docs_match_enum() {
let readme = crate::readme_gen::verification_ladder_table(&[0, 0, 0, 0, 0]);
for level in ProofLevel::ALL_DESCENDING {
let row = format!("| {level} | 0 | {} |", level.method());
assert!(
readme.lines().any(|l| l == row),
"README ladder row for {level} is not the enum's definition.\nwant: {row}\nREADME table:\n{readme}"
);
}
assert!(ProofLevel::L4.method().contains("at least one proved"));
assert!(ProofLevel::L5.method().contains("at least one binding"));
}
#[test]
fn the_ladder_block_names_each_level_once_highest_first() {
let block = ladder_block();
let is_level_row =
|l: &&str| l.len() > 4 && l.starts_with("| L") && l.as_bytes()[3].is_ascii_digit();
let rows: Vec<&str> = block.lines().filter(is_level_row).collect();
assert_eq!(rows.len(), 5, "{block}");
for (row, level) in rows.iter().zip(ProofLevel::ALL_DESCENDING) {
assert!(row.starts_with(&format!("| {level} |")), "{row}");
}
assert!(block.starts_with(MARKER) && block.ends_with(END_MARKER));
}
#[cfg(test)]
#[test]
fn stale_level_pairings_case_table() {
let must_flag = [
"| Obligation Type | Level 1 (Types) | Level 3 (probar) | Level 4 (Kani) | Level 5 (Lean) |",
"| Obligation Type | L1 (Types) | L3 (probar) | L4 (Kani) | L5 (Lean) |",
"3. **L4:** Kani exhaustively verified for ALL inputs within the kernel's",
"3. **Level 4:** Kani has exhaustively verified the property for ALL inputs up",
" L5 Theorem proving Lean 4 True for ALL inputs. Period.",
"Kani is the tool used at Level 4 for bounded checks.",
"The Lean prover is what L5 means here.",
"Falsification is L2 then Kani at L4.",
];
for line in must_flag {
assert_eq!(stale_level_pairings(line).len(), 1, "must flag: {line}");
}
let must_not_flag = [
"| Obligation Type | Types (rustc) | L2 (probar/proptest) | L3 (Kani) | L4 (Lean) |",
"3. **L3:** Kani exhaustively verified it for ALL inputs within the kernel's",
"4. **L4:** a Lean 4 theorem proves it unbounded; **L5** additionally requires",
"## How Kani (L3) and Lean (L4) Compose",
"| L5 | L4 + at least one binding, every binding implemented |",
"E4 and E5 are defined in YAML but not yet run in CI.",
"| **E4** | Logic bugs, overflows | Kani `#[kani::proof]` BMC |",
];
for line in must_not_flag {
assert!(
stale_level_pairings(line).is_empty(),
"must not flag: {line}"
);
}
assert!(stale_level_pairings(&ladder_block()).is_empty());
}