use super::super::configs::LanguageValueConfig;
use super::super::store::{Assignment, RawParseValues};
use super::super::types::SymbolicValue;
use super::helpers::node_text;
use super::symbolic::node_to_symbolic;
use crate::models::Function;
pub fn extract_file_values(
tree: &tree_sitter::Tree,
source: &str,
config: &LanguageValueConfig,
functions: &[Function],
file_qualified_prefix: &str,
) -> RawParseValues {
let source_bytes = source.as_bytes();
let root = tree.root_node();
let mut raw = RawParseValues::default();
let func_lookup: Vec<(u32, u32, &str)> = functions
.iter()
.map(|f| (f.line_start, f.line_end, f.qualified_name.as_str()))
.collect();
let mut cursor = root.walk();
for child in root.children(&mut cursor) {
process_top_level_node(
child,
source_bytes,
config,
file_qualified_prefix,
&func_lookup,
&mut raw,
);
}
raw
}
const FUNCTION_DEF_KINDS: &[&str] = &[
"function_definition", "function_declaration", "function_item", "method_definition", "method_declaration", "arrow_function", "generator_function_declaration", "constructor_declaration", ];
const CLASS_DEF_KINDS: &[&str] = &[
"class_definition", "class_declaration", "class_body", "struct_item", "impl_item", "interface_declaration", "struct_specifier", "class_specifier", ];
const WRAPPER_KINDS: &[&str] = &[
"decorated_definition", "export_statement", "export_default_declaration", ];
fn is_kind_in(kind: &str, kinds: &[&str]) -> bool {
kinds.contains(&kind)
}
fn process_top_level_node(
child: tree_sitter::Node,
source_bytes: &[u8],
config: &LanguageValueConfig,
file_qualified_prefix: &str,
func_lookup: &[(u32, u32, &str)],
raw: &mut RawParseValues,
) {
let kind = child.kind();
if kind == "expression_statement" {
let mut inner_cursor = child.walk();
for inner in child.named_children(&mut inner_cursor) {
if LanguageValueConfig::matches(config.assignment_kinds, inner.kind()) {
extract_assignment(
inner,
source_bytes,
config,
file_qualified_prefix,
&mut raw.module_constants,
None,
);
}
}
return;
}
if LanguageValueConfig::matches(config.assignment_kinds, kind) {
extract_assignment(
child,
source_bytes,
config,
file_qualified_prefix,
&mut raw.module_constants,
None,
);
return;
}
if is_kind_in(kind, FUNCTION_DEF_KINDS) {
process_function_node(child, source_bytes, config, func_lookup, raw);
return;
}
if is_kind_in(kind, CLASS_DEF_KINDS) {
extract_class_body(child, source_bytes, config, func_lookup, raw);
return;
}
if is_kind_in(kind, WRAPPER_KINDS) {
let mut inner_cursor = child.walk();
for inner in child.named_children(&mut inner_cursor) {
let ik = inner.kind();
if is_kind_in(ik, FUNCTION_DEF_KINDS) {
process_function_node(inner, source_bytes, config, func_lookup, raw);
} else if is_kind_in(ik, CLASS_DEF_KINDS) {
extract_class_body(inner, source_bytes, config, func_lookup, raw);
} else if LanguageValueConfig::matches(config.assignment_kinds, ik) {
extract_assignment(
inner,
source_bytes,
config,
file_qualified_prefix,
&mut raw.module_constants,
None,
);
}
}
}
}
fn process_function_node(
func_node: tree_sitter::Node,
source_bytes: &[u8],
config: &LanguageValueConfig,
func_lookup: &[(u32, u32, &str)],
raw: &mut RawParseValues,
) {
let func_line = func_node.start_position().row as u32 + 1;
let func_qn = func_lookup
.iter()
.find(|(start, end, _)| func_line >= *start && func_line <= *end)
.map(|(_, _, qn)| *qn);
if let Some(qn) = func_qn {
extract_function_body(func_node, source_bytes, config, qn, raw);
}
}
fn extract_assignment(
node: tree_sitter::Node,
source: &[u8],
config: &LanguageValueConfig,
prefix: &str,
module_constants: &mut Vec<(String, SymbolicValue)>,
mut func_assignments: Option<&mut Vec<Assignment>>,
) {
let kind = node.kind();
if let (Some(left_node), Some(right_node)) = (
node.child_by_field_name("left"),
node.child_by_field_name("right"),
) {
push_assignment(
node_text(left_node, source),
node_to_symbolic(right_node, source, config, prefix),
node,
prefix,
module_constants,
&mut func_assignments,
);
return;
}
if let (Some(pat_node), Some(val_node)) = (
node.child_by_field_name("pattern"),
node.child_by_field_name("value"),
) {
push_assignment(
node_text(pat_node, source),
node_to_symbolic(val_node, source, config, prefix),
node,
prefix,
module_constants,
&mut func_assignments,
);
return;
}
if kind == "variable_declaration"
|| kind == "lexical_declaration"
|| kind == "local_variable_declaration"
{
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "variable_declarator" {
if let (Some(name_node), Some(val_node)) = (
child.child_by_field_name("name"),
child.child_by_field_name("value"),
) {
push_assignment(
node_text(name_node, source),
node_to_symbolic(val_node, source, config, prefix),
node,
prefix,
module_constants,
&mut func_assignments,
);
}
}
}
return;
}
if kind == "declaration" {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "init_declarator" {
if let (Some(decl_node), Some(val_node)) = (
child.child_by_field_name("declarator"),
child.child_by_field_name("value"),
) {
push_assignment(
node_text(decl_node, source),
node_to_symbolic(val_node, source, config, prefix),
node,
prefix,
module_constants,
&mut func_assignments,
);
}
}
}
return;
}
if kind == "var_declaration" {
let mut cursor = node.walk();
for child in node.named_children(&mut cursor) {
if child.kind() == "var_spec" {
if let Some(val_node) = child.child_by_field_name("value") {
if let Some(name_node) = child.child_by_field_name("name") {
push_assignment(
node_text(name_node, source),
node_to_symbolic(val_node, source, config, prefix),
node,
prefix,
module_constants,
&mut func_assignments,
);
}
}
}
}
}
}
fn push_assignment(
var_name: &str,
value: SymbolicValue,
node: tree_sitter::Node,
prefix: &str,
module_constants: &mut Vec<(String, SymbolicValue)>,
func_assignments: &mut Option<&mut Vec<Assignment>>,
) {
let line = node.start_position().row as u32 + 1;
let column = node.start_position().column as u32;
if let Some(ref mut func_assigns) = func_assignments {
func_assigns.push(Assignment {
variable: var_name.to_string(),
value,
line,
column,
});
} else {
let qualified_name = format!("{prefix}.{var_name}");
module_constants.push((qualified_name, value));
}
}
fn extract_function_body(
func_node: tree_sitter::Node,
source: &[u8],
config: &LanguageValueConfig,
func_qn: &str,
raw: &mut RawParseValues,
) {
let body_node = func_node.child_by_field_name("body").or_else(|| {
if func_node.kind() == "arrow_function" {
func_node.named_child(func_node.named_child_count().saturating_sub(1))
} else {
None
}
});
let body_node = match body_node {
Some(b) => b,
None => return,
};
let mut assignments: Vec<Assignment> = Vec::new();
let mut last_return: Option<SymbolicValue> = None;
walk_function_body(
body_node,
source,
config,
func_qn,
&mut assignments,
&mut last_return,
);
if !assignments.is_empty() {
raw.function_assignments
.insert(func_qn.to_string(), assignments);
}
if let Some(ret) = last_return {
raw.return_expressions.insert(func_qn.to_string(), ret);
}
}
fn walk_function_body(
node: tree_sitter::Node,
source: &[u8],
config: &LanguageValueConfig,
func_qn: &str,
assignments: &mut Vec<Assignment>,
last_return: &mut Option<SymbolicValue>,
) {
let mut cursor = node.walk();
for child in node.children(&mut cursor) {
let kind = child.kind();
if LanguageValueConfig::matches(config.assignment_kinds, kind) {
extract_assignment(
child,
source,
config,
func_qn,
&mut Vec::new(), Some(assignments),
);
continue;
}
if LanguageValueConfig::matches(config.return_kinds, kind) {
if let Some(expr) = child.named_child(0) {
*last_return = Some(node_to_symbolic(expr, source, config, func_qn));
}
continue;
}
if kind == "expression_statement" {
let mut inner_cursor = child.walk();
for inner in child.named_children(&mut inner_cursor) {
if LanguageValueConfig::matches(config.assignment_kinds, inner.kind()) {
extract_assignment(
inner,
source,
config,
func_qn,
&mut Vec::new(),
Some(assignments),
);
}
}
continue;
}
if matches!(
kind,
"if_statement"
| "for_statement"
| "while_statement"
| "try_statement"
| "with_statement"
| "block"
| "statement_block" | "if_expression" | "match_expression" | "loop_expression" | "for_expression" | "while_expression" | "if_let_expression" | "else_clause" | "elif_clause" | "else_if_clause" | "switch_statement" | "switch_case" | "switch_section" | "case_clause" | "do_statement" | "for_in_statement" | "for_of_statement" | "enhanced_for_statement" | "foreach_statement" | "try_expression" | "catch_clause" | "finally_clause" | "except_clause" | "using_statement" | "unsafe_block" | "match_arm" ) {
walk_function_body(child, source, config, func_qn, assignments, last_return);
}
}
}
fn extract_class_body(
class_node: tree_sitter::Node,
source: &[u8],
config: &LanguageValueConfig,
func_lookup: &[(u32, u32, &str)],
raw: &mut RawParseValues,
) {
let body = class_node.child_by_field_name("body");
let body_node = match body {
Some(b) => b,
None => return,
};
let mut cursor = body_node.walk();
for child in body_node.children(&mut cursor) {
let kind = child.kind();
if is_kind_in(kind, FUNCTION_DEF_KINDS) {
process_function_node(child, source, config, func_lookup, raw);
} else if is_kind_in(kind, WRAPPER_KINDS) {
let mut inner_cursor = child.walk();
for inner in child.named_children(&mut inner_cursor) {
if is_kind_in(inner.kind(), FUNCTION_DEF_KINDS) {
process_function_node(inner, source, config, func_lookup, raw);
}
}
}
}
}