mod exprs;
mod flow;
mod state;
use std::collections::HashSet;
use crate::ast::{Block, Decl, DeclKind, File, MethodBody, Param, Pass, accessor_scope};
use crate::source::token::Span;
use state::{State, Var};
use yog_analysis::Report;
pub(crate) fn check(file: &File, report: &mut Report<'_>) {
Checker {
report,
state: State::new(),
reported: HashSet::new(),
}
.decls(&file.decls);
}
struct Checker<'r, 's> {
report: &'r mut Report<'s>,
state: State,
reported: HashSet<(u32, u32)>,
}
impl Checker<'_, '_> {
fn decls(&mut self, decls: &[Decl]) {
for d in decls {
match &d.kind {
DeclKind::Method(m) => {
if let Some(body) = &m.body {
self.body(&m.params, body);
}
}
DeclKind::Ctor(c) => self.block_body(&c.params, &c.body),
DeclKind::Property { ty, accessors, .. } => {
for a in accessors {
if let Some(body) = &a.body {
self.body(&accessor_scope(ty, a, true), body);
}
}
}
DeclKind::Indexer(ix) => {
for a in &ix.accessors {
if let Some(body) = &a.body {
let params =
[ix.params.clone(), accessor_scope(&ix.ty, a, false)].concat();
self.body(¶ms, body);
}
}
}
DeclKind::System { params, body, .. } => self.block_body(params, body),
DeclKind::Test { body, .. } => self.block_body(&[], body),
kind => self.decls(kind.members()),
}
}
}
fn body(&mut self, params: &[Param], body: &MethodBody) {
self.fresh(params);
match body {
MethodBody::Block(b) => self.block(b),
MethodBody::Expr(e) => self.expr(e),
}
}
fn block_body(&mut self, params: &[Param], body: &Block) {
self.fresh(params);
self.block(body);
}
fn fresh(&mut self, params: &[Param]) {
self.state = State::new();
for p in params {
let var = Var {
lent: p.pass.filter(|p| matches!(p, Pass::In | Pass::Ref)),
moved: None,
};
self.state.declare(&p.name.text, var);
}
}
fn problem(&mut self, code: &str, at: Span, message: String) {
if self.reported.insert((at.start, at.end)) {
self.report.error(code, at, message);
}
}
}