Skip to main content

icb_parser/lang/
go.rs

1//! Go language parser using tree-sitter-go.
2//!
3//! Extracts function declarations, method declarations, call expressions,
4//! and type declarations (struct/interface) from Go source files.
5
6use crate::facts::RawNode;
7use icb_common::{IcbError, Language, NodeKind};
8use tree_sitter::Parser;
9
10use super::common::{child_of_kind, traverse_node};
11
12/// Parse Go source code and return the extracted facts.
13pub fn parse_go(source: &str) -> Result<Vec<RawNode>, IcbError> {
14    let mut parser = Parser::new();
15    parser
16        .set_language(&tree_sitter_go::language())
17        .map_err(|e| IcbError::Parse(format!("cannot set tree-sitter-go language: {e}")))?;
18
19    let tree = parser
20        .parse(source, None)
21        .ok_or_else(|| IcbError::Parse("tree-sitter parse returned None for Go source".into()))?;
22
23    let mut facts = Vec::new();
24
25    let classifier =
26        |node: &tree_sitter::Node, source: &str| -> Option<(NodeKind, Option<String>, bool)> {
27            match node.kind() {
28                "function_declaration" => {
29                    let name = child_of_kind(*node, "identifier")
30                        .and_then(|n| n.utf8_text(source.as_bytes()).ok())
31                        .map(|s| s.to_string());
32                    Some((NodeKind::Function, name, true))
33                }
34                "method_declaration" => {
35                    let name = child_of_kind(*node, "field_identifier")
36                        .and_then(|n| n.utf8_text(source.as_bytes()).ok())
37                        .map(|s| s.to_string());
38                    Some((NodeKind::Function, name, true))
39                }
40                "type_declaration" => {
41                    // type_declaration может содержать type_spec или type_spec_list
42                    if let Some(type_spec) = child_of_kind(*node, "type_spec").or_else(|| {
43                        child_of_kind(*node, "type_spec_list")
44                            .and_then(|list| child_of_kind(list, "type_spec"))
45                    }) {
46                        if child_of_kind(type_spec, "struct_type").is_some()
47                            || child_of_kind(type_spec, "interface_type").is_some()
48                        {
49                            let name = child_of_kind(type_spec, "type_identifier")
50                                .or_else(|| child_of_kind(type_spec, "identifier"))
51                                .and_then(|n| n.utf8_text(source.as_bytes()).ok())
52                                .map(|s| s.to_string());
53                            return Some((NodeKind::Class, name, true));
54                        }
55                    }
56                    None
57                }
58                "call_expression" => {
59                    let name_node = child_of_kind(*node, "identifier")
60                        .or_else(|| child_of_kind(*node, "selector_expression"));
61                    let name = name_node
62                        .and_then(|n| n.utf8_text(source.as_bytes()).ok())
63                        .map(|s| s.to_string());
64                    Some((NodeKind::CallSite, name, false))
65                }
66                _ => None,
67            }
68        };
69
70    traverse_node(
71        tree.root_node(),
72        source,
73        &mut facts,
74        None,
75        Language::Go,
76        &classifier,
77    );
78    Ok(facts)
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use icb_common::NodeKind;
85
86    #[test]
87    fn test_simple_function() {
88        let code = "package main\nfunc foo() {}\n";
89        let facts = parse_go(code).unwrap();
90        let funcs: Vec<_> = facts
91            .iter()
92            .filter(|n| n.kind == NodeKind::Function)
93            .collect();
94        assert_eq!(funcs.len(), 1);
95        assert_eq!(funcs[0].name.as_deref(), Some("foo"));
96    }
97
98    #[test]
99    fn test_method() {
100        let code = "package main\ntype S struct{}\nfunc (s S) bar() {}\n";
101        let facts = parse_go(code).unwrap();
102        let methods: Vec<_> = facts
103            .iter()
104            .filter(|n| n.kind == NodeKind::Function && n.name.as_deref() == Some("bar"))
105            .collect();
106        assert!(!methods.is_empty(), "expected method 'bar'");
107    }
108
109    #[test]
110    fn test_call_expression() {
111        let code = "package main\nfunc baz() { foo() }\n";
112        let facts = parse_go(code).unwrap();
113        let calls: Vec<_> = facts
114            .iter()
115            .filter(|n| n.kind == NodeKind::CallSite)
116            .collect();
117        assert_eq!(calls.len(), 1);
118        assert_eq!(calls[0].name.as_deref(), Some("foo"));
119    }
120
121    #[test]
122    fn test_struct_type() {
123        let code = "package main\ntype MyStruct struct {}\n";
124        let facts = parse_go(code).unwrap();
125        let classes: Vec<_> = facts
126            .iter()
127            .filter(|n| n.kind == NodeKind::Class && n.name.as_deref() == Some("MyStruct"))
128            .collect();
129        assert!(!classes.is_empty(), "expected class 'MyStruct'");
130    }
131}