use super::super::binding_flow::BindingUseIndex;
use super::super::binding_ref::name_matches_binding;
use super::super::expr_analysis::is_discard_safe_expr;
use crate::ast::common::{AstBindingRef, AstCallKind, AstExpr, AstLocalAttr, AstStmt};
pub(super) fn try_chain_local_method_call_stmt(
stmts: &[AstStmt],
use_index: &BindingUseIndex,
stmt_base: usize,
) -> Option<(AstStmt, usize)> {
let [first, second, third, ..] = stmts else {
return try_chain_local_method_call_stmt_without_dead_alias(stmts, use_index, stmt_base);
};
let AstStmt::LocalDecl(dead_alias) = first else {
return try_chain_local_method_call_stmt_without_dead_alias(stmts, use_index, stmt_base);
};
if dead_alias.bindings.len() != 1
|| dead_alias.values.len() != 1
|| dead_alias.bindings[0].attr != AstLocalAttr::None
{
return try_chain_local_method_call_stmt_without_dead_alias(stmts, use_index, stmt_base);
}
if use_index.count_uses_in_suffix(stmt_base + 1, dead_alias.bindings[0].id) != 0 {
return try_chain_local_method_call_stmt_without_dead_alias(stmts, use_index, stmt_base);
}
if !is_discard_safe_expr(&dead_alias.values[0]) {
return try_chain_local_method_call_stmt_without_dead_alias(stmts, use_index, stmt_base);
}
let chained_binding = single_method_call_local_binding(second)?;
if use_index.count_uses_in_suffix(stmt_base + 3, chained_binding) != 0 {
return try_chain_local_method_call_stmt_without_dead_alias(stmts, use_index, stmt_base);
}
let chained = chain_local_method_call_stmt(second, third, use_index, stmt_base + 2)?;
Some((chained, 3))
}
fn try_chain_local_method_call_stmt_without_dead_alias(
stmts: &[AstStmt],
use_index: &BindingUseIndex,
stmt_base: usize,
) -> Option<(AstStmt, usize)> {
let [first, second, ..] = stmts else {
return None;
};
let chained_binding = single_method_call_local_binding(first)?;
if use_index.count_uses_in_suffix(stmt_base + 2, chained_binding) != 0 {
return None;
}
Some((
chain_local_method_call_stmt(first, second, use_index, stmt_base + 1)?,
2,
))
}
fn single_method_call_local_binding(stmt: &AstStmt) -> Option<AstBindingRef> {
let AstStmt::LocalDecl(local_decl) = stmt else {
return None;
};
if local_decl.bindings.len() != 1
|| local_decl.values.len() != 1
|| local_decl.bindings[0].attr != AstLocalAttr::None
{
return None;
}
if !matches!(local_decl.values[0], AstExpr::MethodCall(_)) {
return None;
}
Some(local_decl.bindings[0].id)
}
fn chain_local_method_call_stmt(
first: &AstStmt,
second: &AstStmt,
use_index: &BindingUseIndex,
second_index: usize,
) -> Option<AstStmt> {
let AstStmt::LocalDecl(local_decl) = first else {
return None;
};
if local_decl.bindings.len() != 1
|| local_decl.values.len() != 1
|| local_decl.bindings[0].attr != AstLocalAttr::None
{
return None;
}
let AstExpr::MethodCall(first_call) = &local_decl.values[0] else {
return None;
};
let AstStmt::CallStmt(call_stmt) = second else {
return None;
};
let AstCallKind::MethodCall(second_call) = &call_stmt.call else {
return None;
};
let AstExpr::Var(name) = &second_call.receiver else {
return None;
};
if !name_matches_binding(name, local_decl.bindings[0].id)
|| use_index.count_uses_in_range(second_index, second_index + 1, local_decl.bindings[0].id)
!= 1
{
return None;
}
Some(AstStmt::CallStmt(Box::new(
crate::ast::common::AstCallStmt {
call: AstCallKind::MethodCall(Box::new(crate::ast::common::AstMethodCallExpr {
receiver: AstExpr::MethodCall(first_call.clone()),
method: second_call.method.clone(),
args: second_call.args.clone(),
})),
},
)))
}