use std::collections::BTreeSet;
use super::super::binding_flow::{
count_binding_uses_in_block_deep, count_binding_uses_in_stmts_deep, name_matches_binding,
};
use super::direct::function_decl_target_from_lvalue;
use crate::ast::common::{
AstBindingRef, AstExpr, AstFunctionDecl, AstFunctionExpr, AstGlobalBindingTarget, AstNamePath,
AstNameRef, AstStmt, AstTargetDialect,
};
pub(super) fn try_lower_forwarded_function_stmt(
stmts: &[AstStmt],
target: AstTargetDialect,
method_fields: &BTreeSet<String>,
) -> Option<(AstStmt, usize)> {
let [AstStmt::LocalDecl(local_decl), next, ..] = stmts else {
return None;
};
if local_decl.bindings.len() != 1 || local_decl.values.len() != 1 {
return None;
}
let binding = local_decl.bindings[0].id;
if local_decl.bindings[0].attr != crate::ast::common::AstLocalAttr::None {
return None;
}
let AstExpr::FunctionExpr(function) = &local_decl.values[0] else {
return None;
};
if function.captured_bindings.contains(&binding)
|| count_binding_uses_in_block_deep(&function.body, binding) != 0
{
return None;
}
if count_binding_uses_in_stmts_deep(&stmts[1..], binding) != 1 {
return None;
}
let function = function.as_ref().clone();
let stmt = inline_function_into_stmt(next, binding, function, target, method_fields)?;
Some((stmt, 2))
}
fn inline_function_into_stmt(
stmt: &AstStmt,
binding: AstBindingRef,
function: AstFunctionExpr,
target: AstTargetDialect,
method_fields: &BTreeSet<String>,
) -> Option<AstStmt> {
match stmt {
AstStmt::GlobalDecl(global_decl)
if global_decl.bindings.len() == 1 && global_decl.values.len() == 1 =>
{
let AstExpr::Var(name) = &global_decl.values[0] else {
return None;
};
if !name_matches_binding(name, binding) {
return None;
}
if global_decl.bindings[0].attr == crate::ast::common::AstGlobalAttr::None
&& target.caps.global_decl
{
let AstGlobalBindingTarget::Name(name) = &global_decl.bindings[0].target else {
return None;
};
return Some(AstStmt::FunctionDecl(Box::new(AstFunctionDecl {
target: crate::ast::common::AstFunctionName::Plain(AstNamePath {
root: AstNameRef::Global(name.clone()),
fields: Vec::new(),
}),
func: function,
})));
}
let mut global_decl = global_decl.as_ref().clone();
global_decl.values[0] = AstExpr::FunctionExpr(Box::new(function));
Some(AstStmt::GlobalDecl(Box::new(global_decl)))
}
AstStmt::Assign(assign) if assign.targets.len() == 1 && assign.values.len() == 1 => {
let AstExpr::Var(name) = &assign.values[0] else {
return None;
};
if !name_matches_binding(name, binding) {
return None;
}
if let Some((target_name, function)) =
function_decl_target_from_lvalue(&assign.targets[0], &function, method_fields)
{
return Some(AstStmt::FunctionDecl(Box::new(AstFunctionDecl {
target: target_name,
func: function,
})));
}
let mut assign = assign.as_ref().clone();
assign.values[0] = AstExpr::FunctionExpr(Box::new(function));
Some(AstStmt::Assign(Box::new(assign)))
}
_ => None,
}
}