use crate::parser::{Expression, FunctionDecl, ImplBlock, Item, Statement};
use crate::source_map::Location;
use std::collections::HashMap;
pub fn get_expression_location(expr: &Expression) -> Option<Location> {
expr.location().clone()
}
pub fn get_statement_location(stmt: &Statement) -> Option<Location> {
stmt.location().clone()
}
pub fn get_item_location(item: &Item) -> Option<Location> {
item.location().clone()
}
pub fn format_where_clause(where_clause: &[(String, Vec<String>)]) -> String {
if where_clause.is_empty() {
return String::new();
}
let clauses: Vec<String> = where_clause
.iter()
.map(|(type_param, bounds)| format!(" {}: {}", type_param, bounds.join(" + ")))
.collect();
format!("\nwhere\n{}", clauses.join(",\n"))
}
pub fn qualify_parent_child_external_path(
map: &HashMap<(String, String), String>,
path: &str,
) -> String {
if map.is_empty() || !path.contains("::") {
return path.to_string();
}
let parts: Vec<&str> = path.split("::").collect();
if parts.len() == 2 {
if let Some(child) = map.get(&(parts[0].to_string(), parts[1].to_string())) {
return format!("{}::{}::{}", parts[0], child, parts[1]);
}
} else if parts.len() == 3 && parts[0] == "crate" {
if let Some(child) = map.get(&(parts[1].to_string(), parts[2].to_string())) {
return format!("crate::{}::{}::{}", parts[1], child, parts[2]);
}
}
path.to_string()
}
pub fn infer_impl_header_type_params_from_type_name(type_name: &str) -> Option<Vec<String>> {
let open = type_name.find('<')?;
let close = type_name.rfind('>')?;
if close <= open || close + 1 != type_name.len() {
return None;
}
let inner = type_name[open + 1..close].trim();
if inner.is_empty() {
return None;
}
let params: Vec<String> = inner
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
if params.is_empty() {
return None;
}
if !params.iter().all(|p| is_simple_rust_type_parameter_name(p)) {
return None;
}
Some(params)
}
fn is_simple_rust_type_parameter_name(p: &str) -> bool {
!p.is_empty()
&& p.chars().next().is_some_and(|c| c.is_ascii_uppercase())
&& p.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
}
pub fn impl_block_type_param_names(impl_block: &ImplBlock<'_>) -> Vec<String> {
if !impl_block.type_params.is_empty() {
impl_block
.type_params
.iter()
.map(|p| p.name.clone())
.collect()
} else {
infer_impl_header_type_params_from_type_name(&impl_block.type_name).unwrap_or_default()
}
}
fn expr_is_self_chain(expr: &Expression<'_>) -> bool {
match expr {
Expression::Identifier { name, .. } => name == "self",
Expression::FieldAccess { object, .. } => expr_is_self_chain(object),
_ => false,
}
}
fn is_self_dense_access(expr: &Expression<'_>) -> bool {
match expr {
Expression::FieldAccess { object, field, .. } if field == "dense" => {
expr_is_self_chain(object)
}
_ => false,
}
}
fn clone_receiver_is_self_dense_path(expr: &Expression<'_>) -> bool {
match expr {
Expression::Index { object, .. } => is_self_dense_access(object),
_ => is_self_dense_access(expr),
}
}
fn expr_may_need_generic_clone_bound(expr: &Expression<'_>) -> bool {
match expr {
Expression::MethodCall {
object,
method,
arguments,
..
} => {
if matches!(
method.as_str(),
"clone" | "to_owned" | "to_vec" | "into_iter"
) && clone_receiver_is_self_dense_path(object)
{
return true;
}
if expr_may_need_generic_clone_bound(object) {
return true;
}
for (_, arg) in arguments {
if expr_may_need_generic_clone_bound(arg) {
return true;
}
}
false
}
Expression::Binary { left, right, .. } => {
expr_may_need_generic_clone_bound(left) || expr_may_need_generic_clone_bound(right)
}
Expression::Unary { operand, .. } => expr_may_need_generic_clone_bound(operand),
Expression::Call {
function,
arguments,
..
} => {
if expr_may_need_generic_clone_bound(function) {
return true;
}
for (_, arg) in arguments {
if expr_may_need_generic_clone_bound(arg) {
return true;
}
}
false
}
Expression::FieldAccess { object, field, .. } => {
if field == "dense" && is_self_dense_access(expr) {
return true;
}
expr_may_need_generic_clone_bound(object)
}
Expression::Index { object, index, .. } => {
if is_self_dense_access(object) {
return true;
}
expr_may_need_generic_clone_bound(object) || expr_may_need_generic_clone_bound(index)
}
Expression::StructLiteral { fields, .. } => fields
.iter()
.any(|(_, e)| expr_may_need_generic_clone_bound(e)),
Expression::Tuple { elements, .. } => elements
.iter()
.any(|e| expr_may_need_generic_clone_bound(e)),
Expression::Array { elements, .. } => elements
.iter()
.any(|e| expr_may_need_generic_clone_bound(e)),
Expression::Block { statements, .. } => statements
.iter()
.any(|s| stmt_may_need_generic_clone_bound(s)),
Expression::Cast { expr, .. } => expr_may_need_generic_clone_bound(expr),
Expression::TryOp { expr, .. } => expr_may_need_generic_clone_bound(expr),
Expression::Await { expr, .. } => expr_may_need_generic_clone_bound(expr),
Expression::Closure { body, .. } => expr_may_need_generic_clone_bound(body),
Expression::MacroInvocation { args, .. } => {
args.iter().any(|e| expr_may_need_generic_clone_bound(e))
}
Expression::MapLiteral { pairs, .. } => pairs.iter().any(|(k, v)| {
expr_may_need_generic_clone_bound(k) || expr_may_need_generic_clone_bound(v)
}),
Expression::Range { start, end, .. } => {
expr_may_need_generic_clone_bound(start) || expr_may_need_generic_clone_bound(end)
}
Expression::ChannelSend { channel, value, .. } => {
expr_may_need_generic_clone_bound(channel) || expr_may_need_generic_clone_bound(value)
}
Expression::ChannelRecv { channel, .. } => expr_may_need_generic_clone_bound(channel),
_ => false,
}
}
fn stmt_may_need_generic_clone_bound(stmt: &Statement<'_>) -> bool {
match stmt {
Statement::Let {
value, else_block, ..
} => {
if expr_may_need_generic_clone_bound(value) {
return true;
}
if let Some(else_b) = else_block {
if else_b.iter().any(|s| stmt_may_need_generic_clone_bound(s)) {
return true;
}
}
false
}
Statement::Const { value, .. } | Statement::Static { value, .. } => {
expr_may_need_generic_clone_bound(value)
}
Statement::Assignment { target, value, .. } => {
expr_may_need_generic_clone_bound(target) || expr_may_need_generic_clone_bound(value)
}
Statement::Return { value, .. } => value
.map(|e| expr_may_need_generic_clone_bound(e))
.unwrap_or(false),
Statement::Expression { expr, .. } => expr_may_need_generic_clone_bound(expr),
Statement::If {
condition,
then_block,
else_block,
..
} => {
expr_may_need_generic_clone_bound(condition)
|| then_block
.iter()
.any(|s| stmt_may_need_generic_clone_bound(s))
|| else_block
.as_ref()
.map(|b| b.iter().any(|s| stmt_may_need_generic_clone_bound(s)))
.unwrap_or(false)
}
Statement::Match { value, arms, .. } => {
expr_may_need_generic_clone_bound(value)
|| arms.iter().any(|arm| {
arm.guard
.map(|g| expr_may_need_generic_clone_bound(g))
.unwrap_or(false)
|| expr_may_need_generic_clone_bound(arm.body)
})
}
Statement::For { iterable, body, .. } => {
expr_may_need_generic_clone_bound(iterable)
|| body.iter().any(|s| stmt_may_need_generic_clone_bound(s))
}
Statement::While {
condition, body, ..
} => {
expr_may_need_generic_clone_bound(condition)
|| body.iter().any(|s| stmt_may_need_generic_clone_bound(s))
}
Statement::Loop { body, .. }
| Statement::Thread { body, .. }
| Statement::Async { body, .. } => {
body.iter().any(|s| stmt_may_need_generic_clone_bound(s))
}
Statement::Break { .. } | Statement::Continue { .. } | Statement::Use { .. } => false,
Statement::Defer { statement, .. } => stmt_may_need_generic_clone_bound(statement),
}
}
fn function_may_need_dense_clone_bound(func: &FunctionDecl<'_>) -> bool {
func.body
.iter()
.any(|s| stmt_may_need_generic_clone_bound(s))
}
pub fn infer_clone_where_bounds_for_impl(impl_block: &ImplBlock<'_>) -> Vec<(String, Vec<String>)> {
let params = impl_block_type_param_names(impl_block);
if params.len() != 1 {
return Vec::new();
}
let needs = impl_block
.functions
.iter()
.any(function_may_need_dense_clone_bound);
if !needs {
return Vec::new();
}
vec![(params[0].clone(), vec!["Clone".to_string()])]
}
pub fn merge_where_clauses(
mut base: Vec<(String, Vec<String>)>,
extra: Vec<(String, Vec<String>)>,
) -> Vec<(String, Vec<String>)> {
for (param, bounds) in extra {
if let Some((_, existing)) = base.iter_mut().find(|(p, _)| p == ¶m) {
for b in bounds {
if !existing.contains(&b) {
existing.push(b);
}
}
} else {
base.push((param, bounds));
}
}
base
}
#[cfg(test)]
mod codegen_helpers_tests {
use super::*;
use std::collections::HashMap;
#[test]
fn qualify_inserts_child_module_segment() {
let mut m = HashMap::new();
m.insert(
("ffi".to_string(), "tilemap_check_collision".to_string()),
"api".to_string(),
);
assert_eq!(
qualify_parent_child_external_path(&m, "ffi::tilemap_check_collision"),
"ffi::api::tilemap_check_collision"
);
assert_eq!(
qualify_parent_child_external_path(&m, "crate::ffi::tilemap_check_collision"),
"crate::ffi::api::tilemap_check_collision"
);
}
#[test]
fn infer_impl_params_from_uniform_t() {
assert_eq!(
infer_impl_header_type_params_from_type_name("Uniform<T>"),
Some(vec!["T".to_string()])
);
}
#[test]
fn infer_impl_params_rejects_concrete_type_args() {
assert!(infer_impl_header_type_params_from_type_name("Vec<i32>").is_none());
}
}