use std::collections::HashSet;
use rucc_base::Symbol;
use rucc_ir::Module;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Elsewhere {
names: HashSet<Symbol>,
}
impl Elsewhere {
#[must_use]
pub fn of(module: &Module) -> Self {
module.funcs().filter(|&id| module[id].is_declaration()).map(|id| module[id].name).collect()
}
#[must_use]
pub fn holds(&self, name: Symbol) -> bool {
self.names.contains(&name)
}
}
impl FromIterator<Symbol> for Elsewhere {
fn from_iter<T: IntoIterator<Item = Symbol>>(names: T) -> Self {
Self { names: names.into_iter().collect() }
}
}
#[cfg(test)]
mod tests {
use super::*;
use rucc_base::Interner;
use rucc_ir::{Func, Signature};
use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
fn module(names: &mut Interner) -> Module {
let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
let mut module = Module::new(names.intern("test.c"), &target);
let mut defined = Func::new(names.intern("here"), Signature::new());
defined.create_block();
module.add_func(defined);
module.add_func(Func::new(names.intern("exit"), Signature::new()));
module
}
#[test]
fn a_function_this_file_only_declares_is_reached_through_the_table() {
let mut names = Interner::new();
let module = module(&mut names);
let elsewhere = Elsewhere::of(&module);
assert!(elsewhere.holds(names.intern("exit")));
}
#[test]
fn a_function_this_file_defines_is_not() {
let mut names = Interner::new();
let module = module(&mut names);
let elsewhere = Elsewhere::of(&module);
assert!(!elsewhere.holds(names.intern("here")));
}
#[test]
fn a_name_the_module_does_not_carry_at_all_is_not() {
let mut names = Interner::new();
let module = module(&mut names);
let elsewhere = Elsewhere::of(&module);
assert!(!elsewhere.holds(names.intern("nowhere")));
}
}