use std::collections::HashSet;
use yog_ids::script_problem;
use super::Checker;
use super::model::Type;
use crate::ast::{Expr, ExprKind};
impl Checker<'_, '_, '_> {
pub(super) fn handed(&mut self, value: &Expr, t: &Type) {
let ExprKind::Ident(name) = &value.kind else {
return;
};
if self.local(name).is_none() || self.is_copy(t) {
return;
}
self.report.error(
script_problem::PASS,
value.span,
format!(
"a `{t}` isn't copied: hand it on with `move {name}`, lend it with `in {name}`, \
or `{name}.Clone()` it"
),
);
}
pub(super) fn is_copy(&self, t: &Type) -> bool {
self.copy_seen(t, &mut HashSet::new())
}
fn copy_seen(&self, t: &Type, seen: &mut HashSet<String>) -> bool {
match t {
Type::List(_) | Type::Map(..) | Type::Object => false,
Type::Nullable(inner) => self.copy_seen(inner, seen),
Type::Tuple(items) => items.iter().all(|i| self.copy_seen(i, seen)),
Type::Own(name, _) => self.own_copy(name, seen),
_ => true,
}
}
fn own_copy(&self, name: &str, seen: &mut HashSet<String>) -> bool {
let Some(own) = self.own.types.get(name) else {
return true;
};
if own.record {
return true;
}
if own.heap || !seen.insert(name.to_string()) {
return !own.heap;
}
let fields = own
.values
.values()
.chain(own.variants.values().flatten().map(|p| &p.ty));
fields
.into_iter()
.all(|f| match super::written::simple_path(f) {
Some(n) if self.own.types.contains_key(n) => self.own_copy(n, seen),
_ => !written_heap(f),
})
}
}
fn written_heap(t: &crate::ast::Type) -> bool {
use super::written::{LIST, MAP, SET};
match &t.kind {
crate::ast::TypeKind::Path { path, .. } => path
.last()
.is_some_and(|n| [LIST, MAP, SET].contains(&n.text.as_str())),
_ => false,
}
}