use std::collections::HashSet;
use tree_sitter::Node;
use super::receivers::{qualifier_type_name, FactTable};
use super::text::{named_children, text};
use super::types::{Fact, LambdaSlot};
pub(super) struct DeferredLambdaParam {
pub(super) name: String,
pub(super) collection: Option<String>,
pub(super) slot: Option<PendingSlot>,
}
pub(super) struct PendingSlot {
qualifier: Option<String>,
member: String,
arg_count: usize,
arg_index: usize,
arity: usize,
index: usize,
}
pub(super) fn lambda_first_arg_receiver(n: Node) -> Option<Node> {
let lambda = match n.kind() {
"implicit_parameter" => n.parent()?,
"parameter" => {
if n.child_by_field_name("type").is_some() {
return None;
}
let list = n.parent()?;
if list.kind() != "parameter_list" || named_children(list).len() != 1 {
return None;
}
list.parent()?
}
_ => return None,
};
if lambda.kind() != "lambda_expression" {
return None;
}
let argument = lambda.parent()?;
if argument.kind() != "argument" {
return None;
}
let argument_list = argument.parent()?;
if argument_list.kind() != "argument_list" {
return None;
}
if named_children(argument_list).first()?.id() != argument.id() {
return None;
}
let invocation = argument_list.parent()?;
if invocation.kind() != "invocation_expression" {
return None;
}
let function = invocation.child_by_field_name("function")?;
if function.kind() != "member_access_expression" {
return None;
}
let receiver = function.child_by_field_name("expression")?;
if receiver.kind() != "identifier" {
return None;
}
Some(receiver)
}
pub(super) fn lambda_argument_slot(n: Node, src: &[u8]) -> Option<PendingSlot> {
let (lambda, arity, index) = match n.kind() {
"implicit_parameter" => (n.parent()?, 1usize, 0usize),
"parameter" => {
if n.child_by_field_name("type").is_some() {
return None;
}
let list = n.parent()?;
if list.kind() != "parameter_list" {
return None;
}
let params = named_children(list);
let index = params.iter().position(|p| p.id() == n.id())?;
(list.parent()?, params.len(), index)
}
_ => return None,
};
if lambda.kind() != "lambda_expression" {
return None;
}
let argument = lambda.parent()?;
if argument.kind() != "argument" || argument.child_by_field_name("name").is_some() {
return None;
}
let argument_list = argument.parent()?;
if argument_list.kind() != "argument_list" {
return None;
}
let args = named_children(argument_list);
let arg_index = args.iter().position(|a| a.id() == argument.id())?;
let arg_count = args.len();
let invocation = argument_list.parent()?;
if invocation.kind() != "invocation_expression" {
return None;
}
let function = invocation.child_by_field_name("function")?;
let (qualifier, member) = match function.kind() {
"member_access_expression" => {
let expr = function.child_by_field_name("expression")?;
let qualifier = match expr.kind() {
"identifier" => Some(text(expr, src)),
"this" => None,
_ => return None,
};
let name_node = function.child_by_field_name("name")?;
if name_node.kind() != "identifier" {
return None;
}
(qualifier, text(name_node, src))
}
"identifier" => (None, text(function, src)),
_ => return None,
};
if member.is_empty() {
return None;
}
Some(PendingSlot {
qualifier,
member,
arg_count,
arg_index,
arity,
index,
})
}
pub(super) fn lambda_slot_fact(
table: &FactTable,
type_facts: &FactTable,
enclosing_type: Option<&str>,
pending: &HashSet<&str>,
slot: &PendingSlot,
) -> Option<Fact> {
let owner = match &slot.qualifier {
None => enclosing_type?.to_string(),
Some(q) => {
if pending.contains(q.as_str()) {
return None;
}
qualifier_type_name(table, type_facts, q)?
}
};
Some(Fact {
type_name: String::new(),
args: None,
call: None,
awaited: false,
is_array: false,
lambda: Some(LambdaSlot {
owner,
member: slot.member.clone(),
arg_count: slot.arg_count,
arg_index: slot.arg_index,
arity: slot.arity,
index: slot.index,
}),
})
}
pub(super) fn lambda_receiver_element_fact(
locals: &FactTable,
type_facts: &FactTable,
name: &str,
) -> Option<Fact> {
let fact = match locals.get(name).or_else(|| type_facts.get(name)) {
Some(Some(fact)) => fact,
_ => return None,
};
if fact.is_array {
return if fact.args.is_none() {
Some(Fact {
type_name: fact.type_name.clone(),
args: None,
call: None,
awaited: false,
is_array: false,
lambda: None,
})
} else {
None
};
}
match fact.args.as_deref() {
Some([arg]) if arg != "*" => Some(Fact {
type_name: arg.clone(),
args: None,
call: None,
awaited: false,
is_array: false,
lambda: None,
}),
_ => None,
}
}