#[allow(clippy::wildcard_imports)]
use super::*;
pub(crate) fn extract_direct_call_info(
node: &Node<'_>,
src: &[u8],
handler: &GrammarHandler,
) -> Option<(Option<String>, Vec<String>)> {
if let Some(info) = handler
.direct_call_info_extractor
.and_then(|extract| extract(*node, src, handler))
{
return Some(info);
}
if !handler.is_call(node.kind()) {
return transparent_direct_call_child(node, handler)
.and_then(|child| extract_direct_call_info(&child, src, handler));
}
let full = normalize_call_name_whitespace(&parsed_call_target(node, src, handler)?.full_text);
if full.is_empty() {
return None;
}
let mut args: Vec<String> = Vec::new();
let argument_containers = call_argument_containers(*node, handler);
for container in &argument_containers {
let arguments = if handler.is_call(container.kind()) {
vec![*container]
} else {
let mut cursor = container.walk();
container.named_children(&mut cursor).collect()
};
for argument in arguments {
let (name, value) = argument_name_and_value(argument, src, handler);
if name.is_some() {
continue;
}
let t = normalize_call_name_whitespace(node_text(&value, src));
if !t.is_empty() {
args.push(t);
}
}
}
if argument_containers.is_empty() && handler.has_special_form(SyntaxSpecialForm::DirectCallArguments) {
let mut cursor = node.walk();
if cursor.goto_first_child() {
loop {
let child = cursor.node();
let excluded = cursor
.field_name()
.is_some_and(|field| handler.direct_call_argument_excluded_fields.contains(&field));
if child.is_named() && !excluded {
let t = normalize_call_name_whitespace(node_text(&child, src));
if !t.is_empty() {
args.push(t);
}
}
if !cursor.goto_next_sibling() {
break;
}
}
}
}
Some((Some(full), args))
}
pub(super) fn grouping_list_kind(kind: &str, handler: &GrammarHandler) -> bool {
handler.single_expression_group_kinds.contains(&kind)
}
pub(super) fn transparent_direct_call_child<'tree>(
node: &Node<'tree>,
handler: &GrammarHandler,
) -> Option<Node<'tree>> {
if grouping_list_kind(node.kind(), handler) {
return (node.named_child_count() == 1)
.then(|| first_named_child(node))
.flatten();
}
if !handler.transparent_call_wrapper_kinds.contains(&node.kind()) {
return None;
}
let mut cursor = node.walk();
let mut candidates = node.named_children(&mut cursor).filter(|child| {
handler.is_call(child.kind())
|| handler.transparent_call_wrapper_kinds.contains(&child.kind())
|| (grouping_list_kind(child.kind(), handler) && child.named_child_count() == 1)
});
let candidate = candidates.next()?;
candidates.next().is_none().then_some(candidate)
}
pub(super) fn parameter_list_is_variadic(fn_node: &Node<'_>, handler: &GrammarHandler) -> bool {
let Some(container) = fn_node.child_by_field_name("parameters").or_else(|| {
handler
.parameter_container_kinds
.iter()
.find_map(|kind| first_named_child_of_kind(fn_node, kind))
}) else {
return false;
};
let mut cursor = container.walk();
let params: Vec<Node<'_>> = container.named_children(&mut cursor).collect();
for param in params {
if handler.variadic_parameter_kinds.contains(¶m.kind()) {
return true;
}
if handler.destructured_parameter_kinds.contains(¶m.kind()) {
continue;
}
let mut inner = param.walk();
let nested: Vec<Node<'_>> = param.named_children(&mut inner).collect();
if nested
.iter()
.any(|child| handler.variadic_parameter_kinds.contains(&child.kind()))
{
return true;
}
}
false
}
pub(super) fn qualified_method_name_node<'tree>(node: &Node<'tree>) -> Option<Node<'tree>> {
let inner = if node.kind() == "function_declarator" {
node.child_by_field_name("declarator")?
} else {
node.child_by_field_name("declarator")
.filter(|d| d.kind() == "function_declarator")
.and_then(|d| d.child_by_field_name("declarator"))?
};
if matches!(inner.kind(), "qualified_identifier" | "scoped_identifier") {
inner.child_by_field_name("name")
} else {
None
}
}
pub(super) fn first_call_descendant<'tree>(
node: Node<'tree>,
handler: &GrammarHandler,
) -> Option<Node<'tree>> {
let mut cursor = node.walk();
let children: Vec<Node<'tree>> = node.named_children(&mut cursor).collect();
for child in children {
if handler.is_call(child.kind()) {
return Some(child);
}
if let Some(found) = first_call_descendant(child, handler) {
return Some(found);
}
}
None
}
pub(super) fn next_named_sibling_within<'tree>(
parent: &Node<'tree>,
after: Node<'tree>,
handler: &GrammarHandler,
) -> Option<Node<'tree>> {
let mut cursor = parent.walk();
let mut seen = false;
for child in parent.named_children(&mut cursor) {
if child.id() == after.id() {
seen = true;
continue;
}
if !seen {
continue;
}
if handler.branch_arm_kinds.contains(&child.kind()) {
return Some(child);
}
}
None
}