use rustc_hash::{FxHashMap as HashMap, FxHashSet as HashSet};
use crate::EmitEffects;
use crate::names::go_name;
use crate::{Planner, PreludeType};
use syntax::ast::{Expression, Visibility};
use syntax::program::{DefinitionBody, File};
impl Planner<'_> {
pub(crate) fn collect_local_exported_method_names(&mut self, files: &[&File]) {
for file in files {
for item in &file.items {
if let syntax::ast::Expression::Interface {
visibility: syntax::ast::Visibility::Public,
method_signatures,
..
} = item
{
for method in method_signatures {
let func = method.to_function_definition();
self.module
.record_exported_method_name(func.name.to_string());
}
}
if let syntax::ast::Expression::ImplBlock { methods, .. } = item {
for method in methods {
if let syntax::ast::Expression::Function {
name,
visibility: syntax::ast::Visibility::Public,
..
} = method
{
self.module.record_exported_method_name(name.to_string());
}
}
}
}
}
}
pub(crate) fn collect_escape_remap(&mut self, files: &[&File]) {
let entries: Vec<(&str, String)> = files
.iter()
.flat_map(|f| &f.items)
.filter_map(|item| match item {
Expression::Function {
name,
visibility: Visibility::Private,
..
} => Some((name.as_str(), go_name::escape_reserved(name).into_owned())),
Expression::Const { identifier, .. } => Some((
identifier.as_str(),
go_name::escape_reserved(identifier).into_owned(),
)),
_ => None,
})
.collect();
let mut taken: HashSet<String> = entries
.iter()
.filter(|(name, natural)| *name == natural)
.map(|(_, natural)| natural.clone())
.collect();
for (name, natural) in &entries {
if *name == natural || taken.insert(natural.clone()) {
continue;
}
let fresh = (2..)
.map(|n| format!("{}_{}", name, n))
.find(|c| !taken.contains(c))
.expect("freshening counter is unbounded");
taken.insert(fresh.clone());
self.module.record_escape_remap((*name).to_string(), fresh);
}
}
pub(crate) fn collect_module_aliases(&mut self, files: &[&File]) {
for file in files {
for import in file.imports() {
let Some(alias) = import.effective_alias(self.facts.go_package_names()) else {
continue;
};
self.module
.record_module_alias(import.name.to_string(), alias);
}
}
}
pub(crate) fn record_bound_imports(
&mut self,
generics: &[syntax::ast::Generic],
fx: &mut EmitEffects,
) {
for generic in generics {
for bound in &generic.bounds {
let syntax::ast::Annotation::Constructor { name, .. } = bound else {
continue;
};
let Some((module, _)) = name.split_once('.') else {
continue;
};
if self.facts.is_current_module(module)
|| go_name::is_go_import(module)
|| module == go_name::PRELUDE_MODULE
{
continue;
}
let canonical = self
.module
.module_for_alias(module)
.unwrap_or(module)
.to_string();
self.require_module_import_fx(&canonical, fx);
}
}
}
pub(crate) fn collect_local_make_function_code(
&mut self,
fx: &mut EmitEffects,
) -> HashMap<u32, Vec<String>> {
let module_prefix = format!("{}.", self.facts.current_module());
let mut code: HashMap<u32, Vec<String>> = HashMap::default();
let local_enums: Vec<_> = self
.facts
.iter_definitions()
.filter_map(|(key, definition)| {
let syntax::program::Definition {
name: Some(name),
name_span: Some(name_span),
body: DefinitionBody::Enum { variants, .. },
..
} = definition
else {
return None;
};
if PreludeType::from_name(name).is_some() {
return None;
}
if !key.starts_with(&module_prefix) {
return None;
}
let rest = &key[module_prefix.len()..];
if rest.contains('.') {
return None;
}
Some((key.to_string(), variants.clone(), name_span.file_id))
})
.collect();
for (key, variants, file_id) in local_enums {
for variant in &variants {
let fn_code = self.create_make_function_code(&key, &variant.name, fx);
code.entry(file_id).or_default().push(fn_code);
}
}
code
}
}