Skip to main content

code_analyze_core/languages/
go.rs

1// SPDX-FileCopyrightText: 2026 code-analyze-mcp contributors
2// SPDX-License-Identifier: Apache-2.0
3use tree_sitter::Node;
4
5/// Tree-sitter query for extracting Go elements (functions, methods, and types).
6pub const ELEMENT_QUERY: &str = r"
7(function_declaration
8  name: (identifier) @func_name) @function
9(method_declaration
10  name: (field_identifier) @method_name) @function
11(type_spec
12  name: (type_identifier) @type_name
13  type: (struct_type)) @class
14(type_spec
15  name: (type_identifier) @type_name
16  type: (interface_type)) @class
17";
18
19/// Tree-sitter query for extracting function calls.
20pub const CALL_QUERY: &str = r"
21(call_expression
22  function: (identifier) @call)
23(call_expression
24  function: (selector_expression field: (field_identifier) @call))
25";
26
27/// Tree-sitter query for extracting type references.
28pub const REFERENCE_QUERY: &str = r"
29(type_identifier) @type_ref
30";
31
32/// Tree-sitter query for extracting Go imports.
33pub const IMPORT_QUERY: &str = r"
34(import_declaration) @import_path
35";
36
37/// Find method name for a receiver type.
38#[must_use]
39pub fn find_method_for_receiver(
40    node: &Node,
41    source: &str,
42    _depth: Option<usize>,
43) -> Option<String> {
44    if node.kind() != "method_declaration" && node.kind() != "function_declaration" {
45        return None;
46    }
47    node.child_by_field_name("name").and_then(|n| {
48        let start = n.start_byte();
49        let end = n.end_byte();
50        if end <= source.len() {
51            Some(source[start..end].to_string())
52        } else {
53            None
54        }
55    })
56}
57
58/// Extract inheritance information from a Go type node.
59#[must_use]
60pub fn extract_inheritance(node: &Node, source: &str) -> Vec<String> {
61    let mut inherits = Vec::new();
62
63    // Get the type field from type_spec
64    if let Some(type_field) = node.child_by_field_name("type") {
65        match type_field.kind() {
66            "struct_type" => {
67                // For struct embedding, walk children for field_declaration_list
68                for i in 0..type_field.named_child_count() {
69                    if let Some(field_list) =
70                        type_field.named_child(u32::try_from(i).unwrap_or(u32::MAX))
71                        && field_list.kind() == "field_declaration_list"
72                    {
73                        // Walk field_declaration_list for field_declaration without name
74                        for j in 0..field_list.named_child_count() {
75                            if let Some(field) =
76                                field_list.named_child(u32::try_from(j).unwrap_or(u32::MAX))
77                                && field.kind() == "field_declaration"
78                                && field.child_by_field_name("name").is_none()
79                            {
80                                // Embedded type has no name field
81                                if let Some(type_node) = field.child_by_field_name("type") {
82                                    let text =
83                                        &source[type_node.start_byte()..type_node.end_byte()];
84                                    inherits.push(text.to_string());
85                                }
86                            }
87                        }
88                    }
89                }
90            }
91            "interface_type" => {
92                // For interface embedding, walk children for type_elem
93                for i in 0..type_field.named_child_count() {
94                    if let Some(elem) = type_field.named_child(u32::try_from(i).unwrap_or(u32::MAX))
95                        && elem.kind() == "type_elem"
96                    {
97                        let text = &source[elem.start_byte()..elem.end_byte()];
98                        inherits.push(text.to_string());
99                    }
100                }
101            }
102            _ => {}
103        }
104    }
105
106    inherits
107}
108
109#[cfg(all(test, feature = "lang-go"))]
110mod tests {
111    use super::*;
112    use tree_sitter::Parser;
113
114    fn parse_go(source: &str) -> tree_sitter::Tree {
115        let mut parser = Parser::new();
116        parser
117            .set_language(&tree_sitter_go::LANGUAGE.into())
118            .expect("failed to set Go language");
119        parser.parse(source, None).expect("failed to parse source")
120    }
121
122    #[test]
123    fn test_extract_inheritance_struct_no_embeds() {
124        // Arrange: struct with no embedded types
125        let source = "package p\ntype Foo struct { x int }";
126        let tree = parse_go(source);
127        let root = tree.root_node();
128        // find the type_spec node
129        let type_spec = (0..root.named_child_count())
130            .filter_map(|i| root.named_child(i as u32))
131            .find_map(|n| {
132                if n.kind() == "type_declaration" {
133                    (0..n.named_child_count())
134                        .filter_map(|j| n.named_child(j as u32))
135                        .find(|c| c.kind() == "type_spec")
136                } else {
137                    None
138                }
139            })
140            .expect("expected type_spec node");
141        // Act
142        let result = extract_inheritance(&type_spec, source);
143        // Assert
144        assert!(
145            result.is_empty(),
146            "expected no inherited types, got {:?}",
147            result
148        );
149    }
150
151    #[test]
152    fn test_find_method_for_receiver_wrong_kind() {
153        // Arrange: use a struct node (not a method or function declaration)
154        let source = "package p\ntype Bar struct {}";
155        let tree = parse_go(source);
156        let root = tree.root_node();
157        let node = root.named_child(0).expect("expected child");
158        // Act
159        let result = find_method_for_receiver(&node, source, None);
160        // Assert
161        assert_eq!(result, None);
162    }
163}