1use super::{node_text, LanguageExtractor};
7use crate::ast::{ExtractedSymbol, FunctionCall, Import, Parameter, SymbolKind, Visibility};
8use crate::error::Result;
9use tree_sitter::{Language, Node, Tree};
10
11pub struct GoExtractor;
13
14impl LanguageExtractor for GoExtractor {
15 fn language(&self) -> Language {
16 tree_sitter_go::LANGUAGE.into()
17 }
18
19 fn name(&self) -> &'static str {
20 "go"
21 }
22
23 fn extensions(&self) -> &'static [&'static str] {
24 &["go"]
25 }
26
27 fn extract_symbols(&self, tree: &Tree, source: &str) -> Result<Vec<ExtractedSymbol>> {
28 let mut symbols = Vec::new();
29 let root = tree.root_node();
30 self.extract_symbols_recursive(&root, source, &mut symbols, None);
31 Ok(symbols)
32 }
33
34 fn extract_imports(&self, tree: &Tree, source: &str) -> Result<Vec<Import>> {
35 let mut imports = Vec::new();
36 let root = tree.root_node();
37 self.extract_imports_recursive(&root, source, &mut imports);
38 Ok(imports)
39 }
40
41 fn extract_calls(
42 &self,
43 tree: &Tree,
44 source: &str,
45 current_function: Option<&str>,
46 ) -> Result<Vec<FunctionCall>> {
47 let mut calls = Vec::new();
48 let root = tree.root_node();
49 self.extract_calls_recursive(&root, source, &mut calls, current_function);
50 Ok(calls)
51 }
52
53 fn extract_doc_comment(&self, node: &Node, source: &str) -> Option<String> {
54 let mut comments = Vec::new();
56 let mut current = node.prev_sibling();
57
58 while let Some(prev) = current {
59 if prev.kind() == "comment" {
60 let comment = node_text(&prev, source);
61 comments.push(comment.trim_start_matches("//").trim().to_string());
62 current = prev.prev_sibling();
63 } else {
64 break;
65 }
66 }
67
68 if comments.is_empty() {
69 None
70 } else {
71 comments.reverse();
72 Some(comments.join("\n"))
73 }
74 }
75}
76
77impl GoExtractor {
78 fn extract_symbols_recursive(
79 &self,
80 node: &Node,
81 source: &str,
82 symbols: &mut Vec<ExtractedSymbol>,
83 parent: Option<&str>,
84 ) {
85 match node.kind() {
86 "function_declaration" => {
87 if let Some(sym) = self.extract_function(node, source, parent) {
88 symbols.push(sym);
89 }
90 }
91
92 "method_declaration" => {
93 if let Some(sym) = self.extract_method(node, source) {
94 symbols.push(sym);
95 }
96 }
97
98 "type_declaration" => {
99 self.extract_type_declaration(node, source, symbols, parent);
100 }
101
102 "const_declaration" | "var_declaration" => {
103 self.extract_var_declaration(node, source, symbols, parent);
104 }
105
106 _ => {}
107 }
108
109 let mut cursor = node.walk();
111 for child in node.children(&mut cursor) {
112 self.extract_symbols_recursive(&child, source, symbols, parent);
113 }
114 }
115
116 fn extract_function(
117 &self,
118 node: &Node,
119 source: &str,
120 parent: Option<&str>,
121 ) -> Option<ExtractedSymbol> {
122 let name_node = node.child_by_field_name("name")?;
123 let name = node_text(&name_node, source).to_string();
124
125 let mut sym = ExtractedSymbol::new(
126 name.clone(),
127 SymbolKind::Function,
128 node.start_position().row + 1,
129 node.end_position().row + 1,
130 )
131 .with_columns(node.start_position().column, node.end_position().column);
132
133 if name
135 .chars()
136 .next()
137 .map(|c| c.is_uppercase())
138 .unwrap_or(false)
139 {
140 sym = sym.exported();
141 sym.visibility = Visibility::Public;
142 } else {
143 sym.visibility = Visibility::Private;
144 }
145
146 if let Some(params) = node.child_by_field_name("parameters") {
148 self.extract_parameters(¶ms, source, &mut sym);
149 }
150
151 if let Some(result) = node.child_by_field_name("result") {
153 sym.return_type = Some(node_text(&result, source).to_string());
154 }
155
156 sym.doc_comment = self.extract_doc_comment(node, source);
157
158 if let Some(p) = parent {
159 sym = sym.with_parent(p);
160 }
161
162 sym.signature = Some(self.build_function_signature(node, source));
163
164 Some(sym)
165 }
166
167 fn extract_method(&self, node: &Node, source: &str) -> Option<ExtractedSymbol> {
168 let name_node = node.child_by_field_name("name")?;
169 let name = node_text(&name_node, source).to_string();
170
171 let mut sym = ExtractedSymbol::new(
172 name.clone(),
173 SymbolKind::Method,
174 node.start_position().row + 1,
175 node.end_position().row + 1,
176 );
177
178 if name
180 .chars()
181 .next()
182 .map(|c| c.is_uppercase())
183 .unwrap_or(false)
184 {
185 sym = sym.exported();
186 sym.visibility = Visibility::Public;
187 } else {
188 sym.visibility = Visibility::Private;
189 }
190
191 if let Some(receiver) = node.child_by_field_name("receiver") {
193 let receiver_text = node_text(&receiver, source);
194 let type_name = receiver_text
196 .trim_matches(|c| c == '(' || c == ')')
197 .split_whitespace()
198 .last()
199 .unwrap_or("")
200 .trim_start_matches('*');
201 sym = sym.with_parent(type_name);
202 }
203
204 if let Some(params) = node.child_by_field_name("parameters") {
206 self.extract_parameters(¶ms, source, &mut sym);
207 }
208
209 if let Some(result) = node.child_by_field_name("result") {
211 sym.return_type = Some(node_text(&result, source).to_string());
212 }
213
214 sym.doc_comment = self.extract_doc_comment(node, source);
215 sym.signature = Some(self.build_method_signature(node, source));
216
217 Some(sym)
218 }
219
220 fn extract_type_declaration(
221 &self,
222 node: &Node,
223 source: &str,
224 symbols: &mut Vec<ExtractedSymbol>,
225 parent: Option<&str>,
226 ) {
227 let mut cursor = node.walk();
228 for child in node.children(&mut cursor) {
229 if child.kind() == "type_spec" {
230 if let Some(name_node) = child.child_by_field_name("name") {
231 let name = node_text(&name_node, source).to_string();
232
233 let type_node = child.child_by_field_name("type");
235 let kind = type_node
236 .map(|t| match t.kind() {
237 "struct_type" => SymbolKind::Struct,
238 "interface_type" => SymbolKind::Interface,
239 _ => SymbolKind::TypeAlias,
240 })
241 .unwrap_or(SymbolKind::TypeAlias);
242
243 let mut sym = ExtractedSymbol::new(
244 name.clone(),
245 kind,
246 child.start_position().row + 1,
247 child.end_position().row + 1,
248 );
249
250 if name
252 .chars()
253 .next()
254 .map(|c| c.is_uppercase())
255 .unwrap_or(false)
256 {
257 sym = sym.exported();
258 sym.visibility = Visibility::Public;
259 } else {
260 sym.visibility = Visibility::Private;
261 }
262
263 sym.doc_comment = self.extract_doc_comment(&child, source);
264
265 if let Some(p) = parent {
266 sym = sym.with_parent(p);
267 }
268
269 symbols.push(sym);
270
271 if kind == SymbolKind::Struct {
273 if let Some(type_node) = type_node {
274 self.extract_struct_fields(&type_node, source, symbols, Some(&name));
275 }
276 }
277
278 if kind == SymbolKind::Interface {
280 if let Some(type_node) = type_node {
281 self.extract_interface_methods(
282 &type_node,
283 source,
284 symbols,
285 Some(&name),
286 );
287 }
288 }
289 }
290 }
291 }
292 }
293
294 fn extract_struct_fields(
295 &self,
296 node: &Node,
297 source: &str,
298 symbols: &mut Vec<ExtractedSymbol>,
299 struct_name: Option<&str>,
300 ) {
301 let mut cursor = node.walk();
303 for child in node.children(&mut cursor) {
304 if child.kind() == "field_declaration_list" {
305 let mut field_cursor = child.walk();
306 for field in child.children(&mut field_cursor) {
307 if field.kind() == "field_declaration" {
308 self.extract_single_field(&field, source, symbols, struct_name);
309 }
310 }
311 } else if child.kind() == "field_declaration" {
312 self.extract_single_field(&child, source, symbols, struct_name);
314 }
315 }
316 }
317
318 fn extract_single_field(
319 &self,
320 field: &Node,
321 source: &str,
322 symbols: &mut Vec<ExtractedSymbol>,
323 struct_name: Option<&str>,
324 ) {
325 let mut field_cursor = field.walk();
326 for field_child in field.children(&mut field_cursor) {
327 if field_child.kind() == "field_identifier" {
328 let name = node_text(&field_child, source).to_string();
329
330 let mut sym = ExtractedSymbol::new(
331 name.clone(),
332 SymbolKind::Field,
333 field.start_position().row + 1,
334 field.end_position().row + 1,
335 );
336
337 if name
339 .chars()
340 .next()
341 .map(|c| c.is_uppercase())
342 .unwrap_or(false)
343 {
344 sym = sym.exported();
345 sym.visibility = Visibility::Public;
346 } else {
347 sym.visibility = Visibility::Private;
348 }
349
350 if let Some(type_node) = field.child_by_field_name("type") {
351 sym.type_info = Some(node_text(&type_node, source).to_string());
352 }
353
354 if let Some(p) = struct_name {
355 sym = sym.with_parent(p);
356 }
357
358 symbols.push(sym);
359 }
360 }
361 }
362
363 fn extract_interface_methods(
364 &self,
365 node: &Node,
366 source: &str,
367 symbols: &mut Vec<ExtractedSymbol>,
368 interface_name: Option<&str>,
369 ) {
370 let mut cursor = node.walk();
371 for child in node.children(&mut cursor) {
372 if child.kind() == "method_spec" {
373 if let Some(name_node) = child.child_by_field_name("name") {
374 let name = node_text(&name_node, source).to_string();
375
376 let mut sym = ExtractedSymbol::new(
377 name.clone(),
378 SymbolKind::Method,
379 child.start_position().row + 1,
380 child.end_position().row + 1,
381 );
382
383 if name
384 .chars()
385 .next()
386 .map(|c| c.is_uppercase())
387 .unwrap_or(false)
388 {
389 sym = sym.exported();
390 sym.visibility = Visibility::Public;
391 } else {
392 sym.visibility = Visibility::Private;
393 }
394
395 if let Some(params) = child.child_by_field_name("parameters") {
396 self.extract_parameters(¶ms, source, &mut sym);
397 }
398
399 if let Some(result) = child.child_by_field_name("result") {
400 sym.return_type = Some(node_text(&result, source).to_string());
401 }
402
403 if let Some(p) = interface_name {
404 sym = sym.with_parent(p);
405 }
406
407 symbols.push(sym);
408 }
409 }
410 }
411 }
412
413 fn extract_var_declaration(
414 &self,
415 node: &Node,
416 source: &str,
417 symbols: &mut Vec<ExtractedSymbol>,
418 parent: Option<&str>,
419 ) {
420 let is_const = node.kind() == "const_declaration";
421
422 let mut cursor = node.walk();
423 for child in node.children(&mut cursor) {
424 if child.kind() == "const_spec" || child.kind() == "var_spec" {
425 let mut name_cursor = child.walk();
426 for name_child in child.children(&mut name_cursor) {
427 if name_child.kind() == "identifier" {
428 let name = node_text(&name_child, source).to_string();
429
430 let mut sym = ExtractedSymbol::new(
431 name.clone(),
432 if is_const {
433 SymbolKind::Constant
434 } else {
435 SymbolKind::Variable
436 },
437 child.start_position().row + 1,
438 child.end_position().row + 1,
439 );
440
441 if name
442 .chars()
443 .next()
444 .map(|c| c.is_uppercase())
445 .unwrap_or(false)
446 {
447 sym = sym.exported();
448 sym.visibility = Visibility::Public;
449 } else {
450 sym.visibility = Visibility::Private;
451 }
452
453 if let Some(type_node) = child.child_by_field_name("type") {
454 sym.type_info = Some(node_text(&type_node, source).to_string());
455 }
456
457 if let Some(p) = parent {
458 sym = sym.with_parent(p);
459 }
460
461 symbols.push(sym);
462 }
463 }
464 }
465 }
466 }
467
468 fn extract_parameters(&self, params: &Node, source: &str, sym: &mut ExtractedSymbol) {
469 let mut cursor = params.walk();
470 for child in params.children(&mut cursor) {
471 if child.kind() == "parameter_declaration" {
472 let name = child
473 .child_by_field_name("name")
474 .map(|n| node_text(&n, source).to_string())
475 .unwrap_or_default();
476
477 let type_info = child
478 .child_by_field_name("type")
479 .map(|n| node_text(&n, source).to_string());
480
481 let is_rest = type_info
483 .as_ref()
484 .map(|t| t.starts_with("..."))
485 .unwrap_or(false);
486
487 sym.add_parameter(Parameter {
488 name,
489 type_info,
490 default_value: None,
491 is_rest,
492 is_optional: false,
493 });
494 }
495 }
496 }
497
498 fn extract_imports_recursive(&self, node: &Node, source: &str, imports: &mut Vec<Import>) {
499 if node.kind() == "import_declaration" {
500 self.parse_import_declaration(node, source, imports);
501 }
502
503 let mut cursor = node.walk();
504 for child in node.children(&mut cursor) {
505 self.extract_imports_recursive(&child, source, imports);
506 }
507 }
508
509 fn parse_import_declaration(&self, node: &Node, source: &str, imports: &mut Vec<Import>) {
510 let mut cursor = node.walk();
511 for child in node.children(&mut cursor) {
512 if child.kind() == "import_spec" || child.kind() == "import_spec_list" {
513 self.parse_import_spec(&child, source, imports);
514 }
515 }
516 }
517
518 fn parse_import_spec(&self, node: &Node, source: &str, imports: &mut Vec<Import>) {
519 if node.kind() == "import_spec_list" {
520 let mut cursor = node.walk();
521 for child in node.children(&mut cursor) {
522 if child.kind() == "import_spec" {
523 self.parse_import_spec(&child, source, imports);
524 }
525 }
526 return;
527 }
528
529 let path = node
530 .child_by_field_name("path")
531 .map(|n| node_text(&n, source).trim_matches('"').to_string())
532 .unwrap_or_default();
533
534 let alias = node
535 .child_by_field_name("name")
536 .map(|n| node_text(&n, source).to_string());
537
538 let is_dot_import = alias.as_ref().map(|a| a == ".").unwrap_or(false);
539
540 imports.push(Import {
541 source: path,
542 names: Vec::new(),
543 is_default: false,
544 is_namespace: is_dot_import,
545 line: node.start_position().row + 1,
546 });
547 }
548
549 fn extract_calls_recursive(
550 &self,
551 node: &Node,
552 source: &str,
553 calls: &mut Vec<FunctionCall>,
554 current_function: Option<&str>,
555 ) {
556 if node.kind() == "call_expression" {
557 if let Some(call) = self.parse_call(node, source, current_function) {
558 calls.push(call);
559 }
560 }
561
562 let func_name = match node.kind() {
563 "function_declaration" | "method_declaration" => node
564 .child_by_field_name("name")
565 .map(|n| node_text(&n, source)),
566 _ => None,
567 };
568
569 let current = func_name
570 .map(String::from)
571 .or_else(|| current_function.map(String::from));
572
573 let mut cursor = node.walk();
574 for child in node.children(&mut cursor) {
575 self.extract_calls_recursive(&child, source, calls, current.as_deref());
576 }
577 }
578
579 fn parse_call(
580 &self,
581 node: &Node,
582 source: &str,
583 current_function: Option<&str>,
584 ) -> Option<FunctionCall> {
585 let function = node.child_by_field_name("function")?;
586
587 let (callee, is_method, receiver) = match function.kind() {
588 "selector_expression" => {
589 let operand = function
590 .child_by_field_name("operand")
591 .map(|n| node_text(&n, source).to_string());
592 let field = function
593 .child_by_field_name("field")
594 .map(|n| node_text(&n, source).to_string())?;
595 (field, true, operand)
596 }
597 "identifier" => (node_text(&function, source).to_string(), false, None),
598 _ => return None,
599 };
600
601 Some(FunctionCall {
602 caller: current_function.unwrap_or("<package>").to_string(),
603 callee,
604 line: node.start_position().row + 1,
605 is_method,
606 receiver,
607 })
608 }
609
610 fn build_function_signature(&self, node: &Node, source: &str) -> String {
611 let name = node
612 .child_by_field_name("name")
613 .map(|n| node_text(&n, source))
614 .unwrap_or("unknown");
615
616 let params = node
617 .child_by_field_name("parameters")
618 .map(|n| node_text(&n, source))
619 .unwrap_or("()");
620
621 let result = node
622 .child_by_field_name("result")
623 .map(|n| format!(" {}", node_text(&n, source)))
624 .unwrap_or_default();
625
626 format!("func {}{}{}", name, params, result)
627 }
628
629 fn build_method_signature(&self, node: &Node, source: &str) -> String {
630 let receiver = node
631 .child_by_field_name("receiver")
632 .map(|n| format!("{} ", node_text(&n, source)))
633 .unwrap_or_default();
634
635 let name = node
636 .child_by_field_name("name")
637 .map(|n| node_text(&n, source))
638 .unwrap_or("unknown");
639
640 let params = node
641 .child_by_field_name("parameters")
642 .map(|n| node_text(&n, source))
643 .unwrap_or("()");
644
645 let result = node
646 .child_by_field_name("result")
647 .map(|n| format!(" {}", node_text(&n, source)))
648 .unwrap_or_default();
649
650 format!("func {}{}{}{}", receiver, name, params, result)
651 }
652}
653
654#[cfg(test)]
655mod tests {
656 use super::*;
657
658 fn parse_go(source: &str) -> (Tree, String) {
659 let mut parser = tree_sitter::Parser::new();
660 parser
661 .set_language(&tree_sitter_go::LANGUAGE.into())
662 .unwrap();
663 let tree = parser.parse(source, None).unwrap();
664 (tree, source.to_string())
665 }
666
667 #[test]
668 fn test_extract_function() {
669 let source = r#"
670package main
671
672func Greet(name string) string {
673 return "Hello, " + name + "!"
674}
675"#;
676 let (tree, src) = parse_go(source);
677 let extractor = GoExtractor;
678 let symbols = extractor.extract_symbols(&tree, &src).unwrap();
679
680 assert!(symbols
681 .iter()
682 .any(|s| s.name == "Greet" && s.kind == SymbolKind::Function));
683 }
684
685 #[test]
686 fn test_extract_struct() {
687 let source = r#"
688package main
689
690type User struct {
691 Name string
692 age int
693}
694"#;
695 let (tree, src) = parse_go(source);
696 let extractor = GoExtractor;
697 let symbols = extractor.extract_symbols(&tree, &src).unwrap();
698
699 assert!(symbols
700 .iter()
701 .any(|s| s.name == "User" && s.kind == SymbolKind::Struct));
702 assert!(symbols
703 .iter()
704 .any(|s| s.name == "Name" && s.kind == SymbolKind::Field && s.exported));
705 assert!(symbols
706 .iter()
707 .any(|s| s.name == "age" && s.kind == SymbolKind::Field && !s.exported));
708 }
709
710 #[test]
711 fn test_extract_method() {
712 let source = r#"
713package main
714
715func (u *User) Greet() string {
716 return "Hello, " + u.Name + "!"
717}
718"#;
719 let (tree, src) = parse_go(source);
720 let extractor = GoExtractor;
721 let symbols = extractor.extract_symbols(&tree, &src).unwrap();
722
723 assert!(symbols
724 .iter()
725 .any(|s| s.name == "Greet" && s.kind == SymbolKind::Method));
726 }
727
728 #[test]
729 fn test_extract_interface() {
730 let source = r#"
731package main
732
733type Greeter interface {
734 Greet() string
735 Farewell() string
736}
737"#;
738 let (tree, src) = parse_go(source);
739 let extractor = GoExtractor;
740 let symbols = extractor.extract_symbols(&tree, &src).unwrap();
741
742 assert!(symbols
743 .iter()
744 .any(|s| s.name == "Greeter" && s.kind == SymbolKind::Interface));
745 }
746}