use std::collections::HashSet;
use crate::desugar::desugared_ast::{Expr, ExprKind};
use crate::syntax::names::ScopedName;
use crate::syntax::visitor::ExprVisitor;
trait GraphRefSink {
fn on_ref(&mut self, scoped: &ScopedName);
}
struct GraphRefVisitor<'a, S: GraphRefSink> {
sink: &'a mut S,
known_names: Option<&'a HashSet<&'a str>>,
}
impl<S: GraphRefSink> GraphRefVisitor<'_, S> {
fn should_record(&self, scoped: &ScopedName) -> bool {
self.known_names
.is_none_or(|set| set.contains(scoped.to_string().as_str()))
}
}
impl<S: GraphRefSink> ExprVisitor<crate::syntax::phase::Desugared> for GraphRefVisitor<'_, S> {
type Error = std::convert::Infallible;
fn visit_graph_ref(&mut self, expr: &Expr) -> Result<(), Self::Error> {
if let ExprKind::GraphRef(ident) = &expr.kind
&& self.should_record(&ident.value)
{
self.sink.on_ref(&ident.value);
}
Ok(())
}
}
struct StringNameSink<'a> {
refs: &'a mut HashSet<String>,
}
impl GraphRefSink for StringNameSink<'_> {
fn on_ref(&mut self, scoped: &ScopedName) {
self.refs.insert(scoped.to_string());
}
}
#[expect(
clippy::implicit_hasher,
reason = "internal API always uses default hasher"
)]
pub fn collect_graph_refs(
expr: &Expr,
all_runtime_names: &HashSet<&str>,
refs: &mut HashSet<String>,
) {
let mut sink = StringNameSink { refs };
let mut visitor = GraphRefVisitor {
sink: &mut sink,
known_names: Some(all_runtime_names),
};
let _ = visitor.visit_expr(expr);
}
#[expect(
clippy::implicit_hasher,
reason = "internal API always uses default hasher"
)]
pub fn collect_graph_ref_names(expr: &Expr, refs: &mut HashSet<String>) {
let mut sink = StringNameSink { refs };
let mut visitor = GraphRefVisitor {
sink: &mut sink,
known_names: None,
};
let _ = visitor.visit_expr(expr);
}
#[must_use]
pub fn contains_graph_ref(expr: &Expr) -> bool {
struct DetectorSink(bool);
impl GraphRefSink for DetectorSink {
fn on_ref(&mut self, _scoped: &ScopedName) {
self.0 = true;
}
}
let mut sink = DetectorSink(false);
let mut visitor = GraphRefVisitor {
sink: &mut sink,
known_names: None,
};
let _ = visitor.visit_expr(expr);
sink.0
}