1use std::collections::hash_map::DefaultHasher;
2use std::hash::{Hash, Hasher};
3
4use cha_core::{ClassInfo, FunctionInfo, ImportInfo, SourceFile, SourceModel};
5use tree_sitter::{Node, Parser};
6
7use crate::LanguageParser;
8
9pub struct RustParser;
10
11impl LanguageParser for RustParser {
12 fn language_name(&self) -> &str {
13 "rust"
14 }
15
16 fn parse(&self, file: &SourceFile) -> Option<SourceModel> {
17 let mut parser = Parser::new();
18 parser
19 .set_language(&tree_sitter_rust::LANGUAGE.into())
20 .ok()?;
21 let tree = parser.parse(&file.content, None)?;
22 let root = tree.root_node();
23 let src = file.content.as_bytes();
24
25 let imports_map = crate::rust_imports::build(root, src);
28 let mut ctx = ParseContext::new(src, imports_map);
29 ctx.collect_nodes(root, false);
30
31 Some(SourceModel {
32 language: "rust".into(),
33 total_lines: file.line_count(),
34 functions: ctx.col.functions,
35 classes: ctx.col.classes,
36 imports: ctx.col.imports,
37 comments: collect_comments(root, src),
38 type_aliases: ctx.col.type_aliases,
39 })
40 }
41}
42
43#[derive(Default)]
45struct Collector {
46 functions: Vec<FunctionInfo>,
47 classes: Vec<ClassInfo>,
48 imports: Vec<ImportInfo>,
49 type_aliases: Vec<(String, String)>,
50}
51
52struct ParseContext<'a> {
54 src: &'a [u8],
55 col: Collector,
56 last_self_call_count: usize,
57 last_has_notify: bool,
58 callback_fields: std::collections::HashMap<String, Vec<String>>,
60 imports_map: crate::type_ref::ImportsMap,
62}
63
64impl<'a> ParseContext<'a> {
65 fn new(src: &'a [u8], imports_map: crate::type_ref::ImportsMap) -> Self {
66 Self {
67 src,
68 last_self_call_count: 0,
69 last_has_notify: false,
70 callback_fields: std::collections::HashMap::new(),
71 imports_map,
72 col: Collector::default(),
73 }
74 }
75
76 fn collect_nodes(&mut self, node: Node, exported: bool) {
77 let mut cursor = node.walk();
78 for child in node.children(&mut cursor) {
79 self.collect_single_node(child, exported);
80 }
81 }
82
83 fn collect_single_node(&mut self, child: Node, exported: bool) {
84 match child.kind() {
85 "function_item" => self.push_function(child, exported),
86 "impl_item" => self.extract_impl_methods(child),
87 "struct_item" | "enum_item" | "trait_item" => self.push_struct(child),
88 "type_item" => self.push_type_alias(child),
89 "use_declaration" => self.push_import(child),
90 "mod_item" => self.push_mod_as_import(child),
91 _ => self.collect_nodes(child, false),
92 }
93 }
94
95 fn push_function(&mut self, node: Node, exported: bool) {
96 if let Some(mut f) = extract_function(node, self.src, &self.imports_map) {
97 f.is_exported = exported || has_pub(node);
98 self.col.functions.push(f);
99 }
100 }
101
102 fn push_struct(&mut self, node: Node) {
103 if let Some((mut c, cb_fields)) = extract_struct(node, self.src) {
104 c.is_exported = has_pub(node);
105 if !cb_fields.is_empty() {
106 self.callback_fields.insert(c.name.clone(), cb_fields);
107 }
108 self.col.classes.push(c);
109 }
110 }
111
112 fn push_import(&mut self, node: Node) {
113 if let Some(i) = crate::rust_imports::extract_use(node, self.src) {
114 self.col.imports.push(i);
115 }
116 }
117
118 fn push_type_alias(&mut self, node: Node) {
119 if let Some(pair) = crate::type_aliases::rust(node, self.src) {
120 self.col.type_aliases.push(pair);
121 }
122 }
123
124 fn push_mod_as_import(&mut self, node: Node) {
126 if node.child_by_field_name("body").is_some() {
128 return;
129 }
130 if let Some(name_node) = node.child_by_field_name("name") {
131 let name = node_text(name_node, self.src);
132 self.col.imports.push(crate::ImportInfo {
133 source: format!("{name}.rs"),
134 line: node.start_position().row + 1,
135 col: node.start_position().column,
136 is_module_decl: true,
137 });
138 }
139 }
140
141 fn extract_impl_methods(&mut self, node: Node) {
142 let Some(body) = node.child_by_field_name("body") else {
143 return;
144 };
145 let impl_name = node
146 .child_by_field_name("type")
147 .map(|t| node_text(t, self.src).to_string());
148 let trait_name = node
149 .child_by_field_name("trait")
150 .map(|t| node_text(t, self.src).to_string());
151
152 let cb_fields = impl_name
153 .as_ref()
154 .and_then(|n| self.callback_fields.get(n))
155 .cloned()
156 .unwrap_or_default();
157
158 let (methods, delegating, has_behavior) = self.scan_impl_body(body, &cb_fields);
159
160 if let Some(name) = &impl_name
161 && let Some(class) = self.col.classes.iter_mut().find(|c| &c.name == name)
162 {
163 class.method_count += methods;
164 class.delegating_method_count += delegating;
165 class.has_behavior |= has_behavior;
166 class.self_call_count = class.self_call_count.max(self.last_self_call_count);
167 class.has_notify_method |= self.last_has_notify;
168 if let Some(t) = &trait_name {
169 class.parent_name = Some(t.clone());
170 }
171 }
172 }
173
174 fn scan_impl_body(&mut self, body: Node, cb_fields: &[String]) -> (usize, usize, bool) {
175 let mut methods = 0;
176 let mut delegating = 0;
177 let mut has_behavior = false;
178 let mut max_self_calls = 0;
179 let mut has_notify = false;
180 let mut cursor = body.walk();
181 for child in body.children(&mut cursor) {
182 if child.kind() == "function_item"
183 && let Some(mut f) = extract_function(child, self.src, &self.imports_map)
184 {
185 f.is_exported = has_pub(child);
186 methods += 1;
187 if f.is_delegating {
188 delegating += 1;
189 }
190 if f.line_count > 3 {
191 has_behavior = true;
192 }
193 let fn_body = child.child_by_field_name("body");
194 let self_calls = count_self_method_calls(fn_body, self.src);
195 max_self_calls = max_self_calls.max(self_calls);
196 if !has_notify && has_iterate_and_call(fn_body, self.src, cb_fields) {
198 has_notify = true;
199 }
200 self.col.functions.push(f);
201 }
202 }
203 self.last_self_call_count = max_self_calls;
205 self.last_has_notify = has_notify;
206 (methods, delegating, has_behavior)
207 }
208}
209
210fn node_text<'a>(node: Node, src: &'a [u8]) -> &'a str {
212 node.utf8_text(src).unwrap_or("")
213}
214
215fn has_pub(node: Node) -> bool {
216 let mut cursor = node.walk();
217 node.children(&mut cursor)
218 .any(|c| c.kind() == "visibility_modifier")
219}
220
221fn hash_ast_structure(node: Node) -> u64 {
222 let mut hasher = DefaultHasher::new();
223 walk_hash(node, &mut hasher);
224 hasher.finish()
225}
226
227fn walk_hash(node: Node, hasher: &mut DefaultHasher) {
228 node.kind().hash(hasher);
229 let mut cursor = node.walk();
230 for child in node.children(&mut cursor) {
231 walk_hash(child, hasher);
232 }
233}
234
235fn count_complexity(node: Node) -> usize {
236 let mut complexity = 1;
237 walk_complexity(node, &mut complexity);
238 complexity
239}
240
241fn walk_complexity(node: Node, count: &mut usize) {
242 match node.kind() {
243 "if_expression" | "else_clause" | "for_expression" | "while_expression"
244 | "loop_expression" | "match_arm" | "closure_expression" => {
245 *count += 1;
246 }
247 "binary_expression" => {
248 let mut cursor = node.walk();
249 for child in node.children(&mut cursor) {
250 if child.kind() == "&&" || child.kind() == "||" {
251 *count += 1;
252 }
253 }
254 }
255 _ => {}
256 }
257 let mut cursor = node.walk();
258 for child in node.children(&mut cursor) {
259 walk_complexity(child, count);
260 }
261}
262
263fn extract_function(
264 node: Node,
265 src: &[u8],
266 imports_map: &crate::type_ref::ImportsMap,
267) -> Option<FunctionInfo> {
268 let name_node = node.child_by_field_name("name")?;
269 let name = node_text(name_node, src).to_string();
270 let name_col = name_node.start_position().column;
271 let name_end_col = name_node.end_position().column;
272 let start_line = node.start_position().row + 1;
273 let end_line = node.end_position().row + 1;
274 let body = node.child_by_field_name("body");
275 let body_hash = body.map(hash_ast_structure);
276 let parameter_count = count_parameters(node);
277 let parameter_types = extract_param_types(node, src, imports_map);
278 let chain_depth = body.map(max_chain_depth).unwrap_or(0);
279 let switch_arms = body.map(count_switch_arms).unwrap_or(0);
280 let switch_arm_values = body
281 .map(|b| crate::rust_imports::rust_collect_arm_values(b, src))
282 .unwrap_or_default();
283 let external_refs = body
284 .map(|b| collect_external_refs(b, src))
285 .unwrap_or_default();
286 let is_delegating = body.map(|b| check_delegating(b, src)).unwrap_or(false);
287 let return_type = crate::rust_imports::rust_return_type(node, src, imports_map);
288 Some(FunctionInfo {
289 name,
290 start_line,
291 end_line,
292 name_col,
293 name_end_col,
294 line_count: end_line - start_line + 1,
295 complexity: count_complexity(node),
296 body_hash,
297 is_exported: false,
298 parameter_count,
299 parameter_types,
300 parameter_names: crate::rust_imports::rust_param_names(node, src),
301 chain_depth,
302 switch_arms,
303 switch_arm_values,
304 external_refs,
305 is_delegating,
306 comment_lines: count_comment_lines(node, src),
307 referenced_fields: collect_field_refs(body, src),
308 null_check_fields: collect_null_checks(body, src),
309 switch_dispatch_target: extract_switch_target(body, src),
310 optional_param_count: count_optional_params(node, src),
311 called_functions: collect_calls_rs(body, src),
312 cognitive_complexity: body.map(cognitive_complexity_rs).unwrap_or(0),
313 return_type,
314 })
315}
316
317fn extract_struct(node: Node, src: &[u8]) -> Option<(ClassInfo, Vec<String>)> {
318 let name_node = node.child_by_field_name("name")?;
319 let name = node_text(name_node, src).to_string();
320 let name_col = name_node.start_position().column;
321 let name_end_col = name_node.end_position().column;
322 let start_line = node.start_position().row + 1;
323 let end_line = node.end_position().row + 1;
324 let (field_count, field_names, callback_fields) = extract_fields(node, src);
325 let is_interface = node.kind() == "trait_item";
326 let has_listener_field = !callback_fields.is_empty();
327 Some((
328 ClassInfo {
329 name,
330 start_line,
331 end_line,
332 name_col,
333 name_end_col,
334 method_count: 0,
335 line_count: end_line - start_line + 1,
336 is_exported: false,
337 delegating_method_count: 0,
338 field_count,
339 field_names,
340 field_types: Vec::new(),
341 has_behavior: false,
342 is_interface,
343 parent_name: None,
344 override_count: 0,
345 self_call_count: 0,
346 has_listener_field,
347 has_notify_method: false,
348 },
349 callback_fields,
350 ))
351}
352
353fn count_parameters(node: Node) -> usize {
354 let params = match node.child_by_field_name("parameters") {
355 Some(p) => p,
356 None => return 0,
357 };
358 let mut cursor = params.walk();
359 params
360 .children(&mut cursor)
361 .filter(|c| c.kind() == "parameter" || c.kind() == "self_parameter")
362 .count()
363}
364
365fn extract_param_types(
366 node: Node,
367 src: &[u8],
368 imports_map: &crate::type_ref::ImportsMap,
369) -> Vec<cha_core::TypeRef> {
370 let params = match node.child_by_field_name("parameters") {
371 Some(p) => p,
372 None => return vec![],
373 };
374 let mut types = Vec::new();
375 let mut cursor = params.walk();
376 for child in params.children(&mut cursor) {
377 if child.kind() == "parameter"
378 && let Some(ty) = child.child_by_field_name("type")
379 {
380 types.push(crate::type_ref::resolve(node_text(ty, src), imports_map));
381 }
382 }
383 types
384}
385
386fn max_chain_depth(node: Node) -> usize {
387 let mut max = 0;
388 walk_chain_depth(node, &mut max);
389 max
390}
391
392fn walk_chain_depth(node: Node, max: &mut usize) {
393 if node.kind() == "field_expression" {
394 let depth = measure_chain(node);
395 if depth > *max {
396 *max = depth;
397 }
398 }
399 let mut cursor = node.walk();
400 for child in node.children(&mut cursor) {
401 walk_chain_depth(child, max);
402 }
403}
404
405fn measure_chain(node: Node) -> usize {
407 let mut depth = 0;
408 let mut current = node;
409 while current.kind() == "field_expression" {
410 depth += 1;
411 if let Some(obj) = current.child_by_field_name("value") {
412 current = obj;
413 } else {
414 break;
415 }
416 }
417 depth
418}
419
420fn count_switch_arms(node: Node) -> usize {
421 let mut count = 0;
422 walk_switch_arms(node, &mut count);
423 count
424}
425
426fn walk_switch_arms(node: Node, count: &mut usize) {
427 if node.kind() == "match_arm" {
428 *count += 1;
429 }
430 let mut cursor = node.walk();
431 for child in node.children(&mut cursor) {
432 walk_switch_arms(child, count);
433 }
434}
435
436fn collect_external_refs(node: Node, src: &[u8]) -> Vec<String> {
437 let mut refs = Vec::new();
438 walk_external_refs(node, src, &mut refs);
439 refs.sort();
440 refs.dedup();
441 refs
442}
443
444fn field_chain_root(node: Node) -> Node {
446 let mut current = node;
447 while current.kind() == "field_expression" {
448 match current.child_by_field_name("value") {
449 Some(child) => current = child,
450 None => break,
451 }
452 }
453 current
454}
455
456fn walk_external_refs(node: Node, src: &[u8], refs: &mut Vec<String>) {
457 if node.kind() == "field_expression" {
458 let root = field_chain_root(node);
460 let text = node_text(root, src);
461 if text != "self" && !text.is_empty() {
462 refs.push(text.to_string());
463 }
464 }
465 let mut cursor = node.walk();
466 for child in node.children(&mut cursor) {
467 walk_external_refs(child, src, refs);
468 }
469}
470
471fn single_stmt(body: Node) -> Option<Node> {
473 let mut cursor = body.walk();
474 let stmts: Vec<_> = body
475 .children(&mut cursor)
476 .filter(|c| c.kind() != "{" && c.kind() != "}")
477 .collect();
478 (stmts.len() == 1).then(|| stmts[0])
479}
480
481fn is_external_call(node: Node, src: &[u8]) -> bool {
483 node.kind() == "call_expression"
484 && node.child_by_field_name("function").is_some_and(|func| {
485 func.kind() == "field_expression"
486 && func
487 .child_by_field_name("value")
488 .is_some_and(|obj| node_text(obj, src) != "self")
489 })
490}
491
492fn check_delegating(body: Node, src: &[u8]) -> bool {
493 let Some(stmt) = single_stmt(body) else {
494 return false;
495 };
496 let expr = match stmt.kind() {
497 "expression_statement" => stmt.child(0).unwrap_or(stmt),
498 "return_expression" => stmt.child(1).unwrap_or(stmt),
499 _ => stmt,
500 };
501 is_external_call(expr, src)
502}
503
504fn count_comment_lines(node: Node, src: &[u8]) -> usize {
506 let mut count = 0;
507 let mut cursor = node.walk();
508 for child in node.children(&mut cursor) {
509 if child.kind() == "line_comment" || child.kind() == "block_comment" {
510 count += child.end_position().row - child.start_position().row + 1;
511 }
512 }
513 if let Some(body) = node.child_by_field_name("body") {
515 count += count_comment_lines_recursive(body, src);
516 }
517 count
518}
519
520fn count_comment_lines_recursive(node: Node, _src: &[u8]) -> usize {
521 let mut count = 0;
522 let mut cursor = node.walk();
523 for child in node.children(&mut cursor) {
524 if child.kind() == "line_comment" || child.kind() == "block_comment" {
525 count += child.end_position().row - child.start_position().row + 1;
526 } else if child.child_count() > 0 {
527 count += count_comment_lines_recursive(child, _src);
528 }
529 }
530 count
531}
532
533fn collect_field_refs(body: Option<Node>, src: &[u8]) -> Vec<String> {
536 let Some(body) = body else { return vec![] };
537 let mut refs = Vec::new();
538 collect_self_fields(body, src, &mut refs);
539 refs.sort();
540 refs.dedup();
541 refs
542}
543
544fn collect_self_fields(node: Node, src: &[u8], refs: &mut Vec<String>) {
545 if node.kind() == "field_expression"
546 && let Some(obj) = node.child_by_field_name("value")
547 && node_text(obj, src) == "self"
548 && let Some(field) = node.child_by_field_name("field")
549 {
550 refs.push(node_text(field, src).to_string());
551 }
552 let mut cursor = node.walk();
553 for child in node.children(&mut cursor) {
554 collect_self_fields(child, src, refs);
555 }
556}
557
558fn extract_fields(node: Node, src: &[u8]) -> (usize, Vec<String>, Vec<String>) {
561 let mut names = Vec::new();
562 let mut callback_fields = Vec::new();
563 if let Some(body) = node.child_by_field_name("body") {
564 let mut cursor = body.walk();
565 for child in body.children(&mut cursor) {
566 if child.kind() == "field_declaration"
567 && let Some(name_node) = child.child_by_field_name("name")
568 {
569 let name = node_text(name_node, src).to_string();
570 if let Some(ty) = child.child_by_field_name("type")
571 && is_callback_collection_type_rs(node_text(ty, src))
572 {
573 callback_fields.push(name.clone());
574 }
575 names.push(name);
576 }
577 }
578 }
579 (names.len(), names, callback_fields)
580}
581
582fn is_callback_collection_type_rs(ty: &str) -> bool {
584 if !ty.contains("Vec<") {
585 return false;
586 }
587 ty.contains("Fn(") || ty.contains("FnMut(") || ty.contains("FnOnce(") || ty.contains("fn(")
588}
589
590fn collect_null_checks(body: Option<Node>, src: &[u8]) -> Vec<String> {
592 let Some(body) = body else { return vec![] };
593 let mut fields = Vec::new();
594 walk_null_checks_rs(body, src, &mut fields);
595 fields.sort();
596 fields.dedup();
597 fields
598}
599
600fn walk_null_checks_rs(node: Node, src: &[u8], fields: &mut Vec<String>) {
601 if node.kind() == "if_let_expression" {
602 if let Some(pattern) = node.child_by_field_name("pattern")
604 && node_text(pattern, src).contains("Some")
605 && let Some(value) = node.child_by_field_name("value")
606 {
607 let vtext = node_text(value, src);
608 if let Some(f) = vtext.strip_prefix("self.") {
609 fields.push(f.to_string());
610 }
611 }
612 } else if node.kind() == "if_expression"
613 && let Some(cond) = node.child_by_field_name("condition")
614 {
615 let text = node_text(cond, src);
616 if text.contains("is_some") || text.contains("is_none") {
617 extract_null_checked_fields(text, fields);
618 }
619 }
620 let mut cursor = node.walk();
621 for child in node.children(&mut cursor) {
622 walk_null_checks_rs(child, src, fields);
623 }
624}
625
626fn extract_null_checked_fields(text: &str, fields: &mut Vec<String>) {
628 if !(text.contains("is_some") || text.contains("is_none") || text.contains("Some")) {
629 return;
630 }
631 for part in text.split("self.") {
632 if let Some(field) = part
633 .split(|c: char| !c.is_alphanumeric() && c != '_')
634 .next()
635 && !field.is_empty()
636 && field != "is_some"
637 && field != "is_none"
638 {
639 fields.push(field.to_string());
640 }
641 }
642}
643
644fn extract_switch_target(body: Option<Node>, src: &[u8]) -> Option<String> {
646 let body = body?;
647 find_match_target(body, src)
648}
649
650fn find_match_target(node: Node, src: &[u8]) -> Option<String> {
651 if node.kind() == "match_expression"
652 && let Some(value) = node.child_by_field_name("value")
653 {
654 return Some(node_text(value, src).to_string());
655 }
656 let mut cursor = node.walk();
657 for child in node.children(&mut cursor) {
658 if let Some(t) = find_match_target(child, src) {
659 return Some(t);
660 }
661 }
662 None
663}
664
665fn count_optional_params(node: Node, src: &[u8]) -> usize {
667 let Some(params) = node.child_by_field_name("parameters") else {
668 return 0;
669 };
670 let mut count = 0;
671 let mut cursor = params.walk();
672 for child in params.children(&mut cursor) {
673 if child.kind() == "parameter" {
674 let text = node_text(child, src);
675 if text.contains("Option<") {
676 count += 1;
677 }
678 }
679 }
680 count
681}
682
683fn count_self_method_calls(body: Option<Node>, src: &[u8]) -> usize {
685 let Some(body) = body else { return 0 };
686 let mut count = 0;
687 walk_self_calls(body, src, &mut count);
688 count
689}
690
691fn walk_self_calls(node: Node, src: &[u8], count: &mut usize) {
692 if node.kind() == "call_expression"
693 && let Some(func) = node.child_by_field_name("function")
694 && node_text(func, src).starts_with("self.")
695 {
696 *count += 1;
697 }
698 let mut cursor = node.walk();
699 for child in node.children(&mut cursor) {
700 walk_self_calls(child, src, count);
701 }
702}
703
704fn has_iterate_and_call(body: Option<Node>, src: &[u8], cb_fields: &[String]) -> bool {
707 let Some(body) = body else { return false };
708 for field in cb_fields {
709 let self_field = format!("self.{field}");
710 if walk_for_iterate_call(body, src, &self_field) {
711 return true;
712 }
713 }
714 false
715}
716
717fn walk_for_iterate_call(node: Node, src: &[u8], self_field: &str) -> bool {
718 if node.kind() == "for_expression"
720 && let Some(value) = node.child_by_field_name("value")
721 && node_text(value, src).contains(self_field)
722 && let Some(loop_body) = node.child_by_field_name("body")
723 && has_call_expression(loop_body)
724 {
725 return true;
726 }
727 if node.kind() == "call_expression" {
729 let text = node_text(node, src);
730 if text.contains(self_field) && text.contains("for_each") {
731 return true;
732 }
733 }
734 let mut cursor = node.walk();
735 for child in node.children(&mut cursor) {
736 if walk_for_iterate_call(child, src, self_field) {
737 return true;
738 }
739 }
740 false
741}
742
743fn cognitive_complexity_rs(node: Node) -> usize {
744 let mut score = 0;
745 cc_walk_rs(node, 0, &mut score);
746 score
747}
748
749fn cc_walk_rs(node: Node, nesting: usize, score: &mut usize) {
750 match node.kind() {
751 "if_expression" => {
752 *score += 1 + nesting;
753 cc_children_rs(node, nesting + 1, score);
754 return;
755 }
756 "for_expression" | "while_expression" | "loop_expression" => {
757 *score += 1 + nesting;
758 cc_children_rs(node, nesting + 1, score);
759 return;
760 }
761 "match_expression" => {
762 *score += 1 + nesting;
763 cc_children_rs(node, nesting + 1, score);
764 return;
765 }
766 "else_clause" => {
767 *score += 1;
768 }
769 "binary_expression" => {
770 if let Some(op) = node.child_by_field_name("operator")
771 && (op.kind() == "&&" || op.kind() == "||")
772 {
773 *score += 1;
774 }
775 }
776 "closure_expression" => {
777 cc_children_rs(node, nesting + 1, score);
778 return;
779 }
780 _ => {}
781 }
782 cc_children_rs(node, nesting, score);
783}
784
785fn cc_children_rs(node: Node, nesting: usize, score: &mut usize) {
786 let mut cursor = node.walk();
787 for child in node.children(&mut cursor) {
788 cc_walk_rs(child, nesting, score);
789 }
790}
791
792fn collect_calls_rs(body: Option<tree_sitter::Node>, src: &[u8]) -> Vec<String> {
793 let Some(body) = body else { return Vec::new() };
794 let mut calls = Vec::new();
795 let mut cursor = body.walk();
796 visit_all(body, &mut cursor, &mut |n| {
797 if n.kind() == "call_expression"
798 && let Some(func) = n.child(0)
799 {
800 let name = node_text(func, src).to_string();
801 if !calls.contains(&name) {
802 calls.push(name);
803 }
804 }
805 });
806 calls
807}
808
809fn visit_all<F: FnMut(Node)>(node: Node, cursor: &mut tree_sitter::TreeCursor, f: &mut F) {
810 f(node);
811 if cursor.goto_first_child() {
812 loop {
813 visit_all(cursor.node(), cursor, f);
814 if !cursor.goto_next_sibling() {
815 break;
816 }
817 }
818 cursor.goto_parent();
819 }
820}
821
822fn collect_comments(root: Node, src: &[u8]) -> Vec<cha_core::CommentInfo> {
823 let mut comments = Vec::new();
824 let mut cursor = root.walk();
825 visit_all(root, &mut cursor, &mut |n| {
826 if n.kind().contains("comment") {
827 comments.push(cha_core::CommentInfo {
828 text: node_text(n, src).to_string(),
829 line: n.start_position().row + 1,
830 });
831 }
832 });
833 comments
834}
835
836fn has_call_expression(node: Node) -> bool {
837 if node.kind() == "call_expression" {
838 return true;
839 }
840 let mut cursor = node.walk();
841 for child in node.children(&mut cursor) {
842 if has_call_expression(child) {
843 return true;
844 }
845 }
846 false
847}