use assura_parser::ast::{ClauseKind, Decl, SpExpr};
use crate::checkers::*;
use crate::{TypeEnv, TypeError};
pub(crate) fn run_frame_checks(
source: &assura_parser::ast::SourceFile,
_type_env: &TypeEnv,
_symbols: &assura_resolve::SymbolTable,
) -> Vec<TypeError> {
let mut errors = Vec::new();
for decl in &source.decls {
let clauses = decl.node.clauses();
if clauses.is_empty() {
continue;
}
let modifies_bodies: Vec<&SpExpr> = clauses
.iter()
.filter(|c| c.kind == ClauseKind::Modifies)
.map(|c| &c.body)
.collect();
if modifies_bodies.is_empty() {
continue;
}
let checker = FrameChecker::new(&modifies_bodies);
if checker.modified_set().is_empty() && !modifies_bodies.is_empty() {
errors.push(TypeError {
code: "A14001".into(),
message: "empty modifies clause; list the variables this function may change"
.into(),
span: decl.span.clone(),
secondary: None,
suggestion: None,
});
}
let ensures_bodies: Vec<&SpExpr> = clauses
.iter()
.filter(|c| c.kind == ClauseKind::Ensures)
.map(|c| &c.body)
.collect();
for ensures_body in &ensures_bodies {
errors.extend(checker.check_ensures_modifications(ensures_body, &decl.span));
}
}
errors
}
pub(crate) fn run_totality_checks(
source: &assura_parser::ast::SourceFile,
) -> (Vec<TypeError>, Vec<PendingDecreaseCheck>) {
let mut checker = TotalityChecker::new();
let mut errors = Vec::new();
let mut pending_smt = Vec::new();
for decl in &source.decls {
if let Decl::FnDef(f) = &decl.node
&& f.clauses
.iter()
.any(|c| matches!(&c.kind, ClauseKind::Other(s) if s == "partial"))
{
checker.mark_partial(f.name.clone());
}
}
let mut fn_defs: Vec<(&assura_parser::ast::FnDef, &std::ops::Range<usize>)> = Vec::new();
for decl in &source.decls {
if let Decl::FnDef(f) = &decl.node {
fn_defs.push((f, &decl.span));
let (te_errors, te_pending) = checker.check_function_totality(f, &decl.span);
for te in te_errors {
errors.push(TypeError {
code: te.code,
message: te.message,
span: te.span,
secondary: None,
suggestion: None,
});
}
pending_smt.extend(te_pending);
}
}
if fn_defs.len() >= 2 {
for te in checker.check_mutual_recursion(&fn_defs) {
errors.push(TypeError {
code: te.code,
message: te.message,
span: te.span,
secondary: None,
suggestion: None,
});
}
}
(errors, pending_smt)
}
#[cfg(test)]
#[path = "frame_totality_tests.rs"]
mod tests;