use brink_ir::hir::visit;
use brink_ir::{Diagnostic, DiagnosticCode, FileId, HirFile, Knot};
use crate::determinism::{LookupMap, LookupSet};
use crate::temp_dominance::{DeclSite, ReadCollector, collect_decls};
pub fn check(file: FileId, hir: &HirFile, is_native: bool) -> Vec<Diagnostic> {
let mut out = Vec::new();
for knot in &hir.knots {
if knot.stitches.is_empty() {
continue;
}
check_knot(file, knot, is_native, &mut out);
}
out
}
fn check_knot(file: FileId, knot: &Knot, is_native: bool, out: &mut Vec<Diagnostic>) {
let knot_noun = if is_native { "flow" } else { "knot" };
let decl_keyword = if is_native { "let" } else { "temp" };
let knot_owner = format!("{knot_noun} `{}`", knot.name.text);
let mut knot_decls: LookupMap<String, DeclSite> = LookupMap::new();
collect_decls(&knot.body, &knot_owner, &mut knot_decls);
if knot_decls.is_empty() {
return;
}
for stitch in &knot.stitches {
let stitch_owner = format!("stitch `{}.{}`", knot.name.text, stitch.name.text);
let mut stitch_decls: LookupMap<String, DeclSite> = LookupMap::new();
collect_decls(&stitch.body, &stitch_owner, &mut stitch_decls);
let stitch_params: Vec<&str> = stitch.params.iter().map(|p| p.name.text.as_str()).collect();
let mut reads = Vec::new();
let mut skipped = LookupSet::new();
let mut v = ReadCollector {
reads: &mut reads,
skipped: &mut skipped,
lambda_depth: 0,
};
visit::walk_block(&stitch.body, &mut v);
for read in &reads {
if stitch_params.contains(&read.name.as_str()) {
continue;
}
if stitch_decls.contains_key(&read.name) {
continue;
}
if !knot_decls.contains_key(&read.name) {
continue;
}
let name = &read.name;
let (verb, inklecate_rejection) = if skipped.contains(&read.range) {
(
"written",
format!("Variable could not be found to assign to: '{name}'"),
)
} else {
("read", format!("Unresolved variable: {name}"))
};
out.push(Diagnostic {
file,
range: read.range,
message: format!(
"{}: `{name}` is {verb} here, but the `~ {decl_keyword} {name}` that \
declares it belongs to {knot_owner}'s own root — ink does not consider \
a {knot_noun}'s `~ {decl_keyword}` visible from its stitches, so this \
compiles and plays here but inklecate rejects it (`{inklecate_rejection}`)",
DiagnosticCode::E194.title(),
),
code: DiagnosticCode::E194,
});
}
}
}