use std::collections::{HashMap, HashSet};
use harn_parser::lexical::BindingId;
use harn_parser::{Node, SNode};
use super::Linter;
#[derive(Default)]
pub(crate) struct HarnessFacts {
resolved_identifier_bindings: HashMap<(usize, usize), BindingId>,
harness_bindings: HashSet<BindingId>,
}
impl HarnessFacts {
pub(crate) fn collect(nodes: &[SNode]) -> Self {
let mut facts = Self::default();
harn_parser::visit::walk_program(nodes, &mut |node| {
let (params, body) = match &node.node {
Node::Pipeline { params, body, .. }
| Node::FnDecl { params, body, .. }
| Node::ToolDecl { params, body, .. }
| Node::Closure { params, body, .. } => (params.as_slice(), body.as_slice()),
_ => return,
};
facts.resolved_identifier_bindings.extend(
harn_parser::lexical::resolved_identifier_bindings(params, body),
);
let Some(harness_name) = Linter::callable_harness_param(params) else {
return;
};
if let Some(param) = params.iter().find(|param| param.name == harness_name) {
facts
.harness_bindings
.insert(BindingId::from_declaration(¶m.name, param.span));
}
});
facts
}
pub(crate) fn capability_of<'a>(&self, object: &'a SNode) -> Option<&'a str> {
let (root, properties) = harn_parser::lexical::resolved_receiver_path(
object,
&self.resolved_identifier_bindings,
)?;
self.harness_bindings
.contains(root)
.then(|| properties.first().copied())
.flatten()
}
pub(crate) fn call_names_builtin<'node>(
&self,
node: &'node SNode,
builtin: &str,
) -> Option<&'node [SNode]> {
match &node.node {
Node::FunctionCall { name, args, .. } => (name == builtin).then_some(args.as_slice()),
Node::MethodCall {
object,
method,
args,
}
| Node::OptionalMethodCall {
object,
method,
args,
} => {
let migration = harn_vm::stdlib::harness_migration_for_builtin(builtin)?;
if !matches!(
migration.arguments,
harn_vm::stdlib::HarnessBuiltinArgumentMigration::Forward
) {
return None;
}
(migration.method == method
&& self.capability_of(object) == Some(migration.capability.field_name()))
.then_some(args.as_slice())
}
_ => None,
}
}
pub(crate) fn is_capability_method_call(&self, node: &SNode) -> bool {
match &node.node {
Node::MethodCall { object, .. } | Node::OptionalMethodCall { object, .. } => {
self.capability_of(object).is_some()
}
_ => false,
}
}
}