use crate::lexer::Lexer;
use crate::parser::{Expression, Item, Parser};
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
fn is_type_copy_quick_for_library(
ty: &crate::parser::Type,
copy_structs: &HashSet<String>,
copy_enums: &HashSet<String>,
) -> bool {
crate::type_classification::is_type_copy_with_registries(ty, copy_structs, copy_enums)
}
pub(crate) fn collect_global_copy_structs_for_library(
sources: &[(PathBuf, String)],
) -> (HashSet<String>, HashSet<String>, HashSet<String>) {
use crate::parser::ast::EnumVariantData;
struct StructInfo {
name: String,
field_types: Vec<crate::parser::Type>,
}
fn walk_items(
items: &[Item<'_>],
all_structs: &mut Vec<StructInfo>,
global_copy_structs: &mut HashSet<String>,
copy_enums: &mut HashSet<String>,
struct_names: &mut HashSet<String>,
explicit_non_copy: &mut HashSet<String>,
) {
for item in items {
match item {
Item::Struct { decl, .. } => {
let has_derive = decl.decorators.iter().any(|d| d.name == "derive");
let has_copy = decl.decorators.iter().any(|d| {
d.name == "derive"
&& d.arguments.iter().any(|(_, arg)| {
matches!(arg, Expression::Identifier { name, .. } if name == "Copy")
})
});
let field_types: Vec<crate::parser::Type> =
decl.fields.iter().map(|f| f.field_type.clone()).collect();
struct_names.insert(decl.name.clone());
all_structs.push(StructInfo {
name: decl.name.clone(),
field_types,
});
if has_copy {
global_copy_structs.insert(decl.name.clone());
} else if has_derive {
explicit_non_copy.insert(decl.name.clone());
}
}
Item::Enum { decl, .. } => {
let is_unit_only = decl
.variants
.iter()
.all(|v| matches!(v.data, EnumVariantData::Unit));
if is_unit_only {
copy_enums.insert(decl.name.clone());
}
}
Item::Mod { items: inner, .. } => {
walk_items(
inner,
all_structs,
global_copy_structs,
copy_enums,
struct_names,
explicit_non_copy,
);
}
_ => {}
}
}
}
let mut all_structs: Vec<StructInfo> = Vec::new();
let mut global_copy_structs = HashSet::new();
let mut copy_enums = HashSet::new();
let mut struct_names = HashSet::new();
let mut explicit_non_copy = HashSet::new();
for (file, source) in sources {
let mut lexer = Lexer::new(source);
let tokens = lexer.tokenize_with_locations();
let mut parser =
Parser::new_with_source(tokens, file.to_string_lossy().to_string(), source.clone());
let Ok(program) = parser.parse() else {
eprintln!(
"Warning: Skipping file for Copy registry (parse error): {}",
file.display()
);
continue;
};
walk_items(
&program.items,
&mut all_structs,
&mut global_copy_structs,
&mut copy_enums,
&mut struct_names,
&mut explicit_non_copy,
);
}
copy_enums.retain(|name| !struct_names.contains(name));
let mut structs_by_name: HashMap<String, Vec<&StructInfo>> = HashMap::new();
for s in &all_structs {
structs_by_name.entry(s.name.clone()).or_default().push(s);
}
loop {
let mut changed = false;
for (name, variants) in &structs_by_name {
if global_copy_structs.contains(name) || explicit_non_copy.contains(name) {
continue;
}
let all_variants_copy = variants.iter().all(|s| {
s.field_types.is_empty()
|| s.field_types.iter().all(|ty| {
is_type_copy_quick_for_library(ty, &global_copy_structs, ©_enums)
})
});
if all_variants_copy {
global_copy_structs.insert(name.clone());
changed = true;
}
}
if !changed {
break;
}
}
global_copy_structs.extend(copy_enums.iter().cloned());
(global_copy_structs, struct_names, explicit_non_copy)
}
pub(crate) fn collect_non_copy_enums_for_library(sources: &[(PathBuf, String)]) -> HashSet<String> {
use crate::parser::ast::EnumVariantData;
let (copy_structs, _, _) = collect_global_copy_structs_for_library(sources);
fn variant_fields_copy(
variant: &crate::parser::EnumVariant,
copy_structs: &HashSet<String>,
) -> bool {
let field_types: Vec<&crate::parser::Type> = match &variant.data {
EnumVariantData::Unit => return true,
EnumVariantData::Tuple(types) => types.iter().collect(),
EnumVariantData::Struct(fields) => fields.iter().map(|(_, t)| t).collect(),
};
field_types
.iter()
.all(|t| is_type_copy_quick_for_library(t, copy_structs, &HashSet::new()))
}
fn walk_items(
items: &[Item<'_>],
non_copy: &mut HashSet<String>,
copy_structs: &HashSet<String>,
) {
for item in items {
match item {
Item::Enum { decl, .. } => {
if copy_structs.contains(&decl.name) {
continue;
}
let all_copy = decl
.variants
.iter()
.all(|v| variant_fields_copy(v, copy_structs));
if !all_copy {
non_copy.insert(decl.name.clone());
}
}
Item::Mod { items: inner, .. } => walk_items(inner, non_copy, copy_structs),
_ => {}
}
}
}
let mut non_copy = HashSet::new();
for (file, source) in sources {
let mut lexer = Lexer::new(source);
let tokens = lexer.tokenize_with_locations();
let mut parser =
Parser::new_with_source(tokens, file.to_string_lossy().to_string(), source.clone());
let Ok(program) = parser.parse() else {
eprintln!(
"Warning: Skipping file for non-Copy enum registry (parse error): {}",
file.display()
);
continue;
};
walk_items(&program.items, &mut non_copy, ©_structs);
}
non_copy
}