use tree_sitter::Node;
pub(super) fn declared_idents<'tree>(node: Node<'tree>, _src: &[u8]) -> Vec<Node<'tree>> {
let mut out = Vec::new();
match node.kind() {
"variable_declarator" | "for_in_statement" => {
if let Some(name) = node.child_by_field_name("left") {
pattern_idents(name, &mut out);
} else if let Some(name) = node.child_by_field_name("name") {
pattern_idents(name, &mut out);
}
}
"required_parameter" | "optional_parameter" => {
if let Some(pattern) = node.child_by_field_name("pattern") {
pattern_idents(pattern, &mut out);
}
}
"formal_parameters" => {
let mut cursor = node.walk();
for parameter in node.named_children(&mut cursor) {
pattern_idents(parameter, &mut out);
}
}
"arrow_function" | "catch_clause" => {
if let Some(param) = node.child_by_field_name("parameter") {
pattern_idents(param, &mut out);
}
}
"function_declaration"
| "generator_function_declaration"
| "function_expression"
| "class_declaration" => {
if let Some(name) = node.child_by_field_name("name")
&& (name.kind() == "identifier" || name.kind() == "type_identifier")
{
out.push(name);
}
}
_ => {}
}
out
}
fn pattern_idents<'tree>(node: Node<'tree>, out: &mut Vec<Node<'tree>>) {
match node.kind() {
"identifier" | "shorthand_property_identifier_pattern" => out.push(node),
"object_pattern" => {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
match child.kind() {
"pair_pattern" => {
if let Some(value) = child.child_by_field_name("value") {
pattern_idents(value, out);
}
}
_ => pattern_idents(child, out),
}
}
}
"array_pattern" | "rest_pattern" => {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
pattern_idents(child, out);
}
}
"assignment_pattern" => {
if let Some(left) = node.child_by_field_name("left") {
pattern_idents(left, out);
}
}
_ => {}
}
}
pub(super) fn is_reference(node: Node<'_>) -> bool {
let Some(parent) = node.parent() else {
return true;
};
match parent.kind() {
"member_expression" => parent.child_by_field_name("property") != Some(node),
"pair" => parent.child_by_field_name("key") != Some(node),
"labeled_statement" => parent.child_by_field_name("label") != Some(node),
_ => true,
}
}
pub(super) fn is_hoisted_name(node: Node<'_>) -> bool {
node.parent().is_some_and(|parent| {
matches!(
parent.kind(),
"function_declaration" | "generator_function_declaration" | "class_declaration"
) && parent.child_by_field_name("name") == Some(node)
})
}
pub(super) fn binds_past(node: Node<'_>, scope_kind: &str) -> bool {
is_var_binding(node) && !is_function_scope(scope_kind)
}
pub(super) fn is_var_binding(node: Node<'_>) -> bool {
let mut current = node.parent();
while let Some(parent) = current {
match parent.kind() {
"variable_declaration" => return true,
"for_in_statement" => {
return parent
.child_by_field_name("kind")
.is_some_and(|kind| kind.kind() == "var");
}
_ => current = parent.parent(),
}
}
false
}
fn is_function_scope(kind: &str) -> bool {
matches!(
kind,
"function_declaration"
| "function_expression"
| "generator_function_declaration"
| "arrow_function"
| "method_definition"
)
}