code_analyze_core/languages/
go.rs1use tree_sitter::Node;
4
5pub 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
19pub 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
27pub const REFERENCE_QUERY: &str = r"
29(type_identifier) @type_ref
30";
31
32pub const IMPORT_QUERY: &str = r"
34(import_declaration) @import_path
35";
36
37pub const DEFUSE_QUERY: &str = r"
39(short_var_declaration left: (expression_list (identifier) @write.short))
40(assignment_statement left: (expression_list (identifier) @write.assign))
41(var_declaration (var_spec (identifier) @write.var))
42(inc_statement (identifier) @writeread.inc)
43(dec_statement (identifier) @writeread.dec)
44(identifier) @read.usage
45";
46
47#[must_use]
49pub fn find_method_for_receiver(
50 node: &Node,
51 source: &str,
52 _depth: Option<usize>,
53) -> Option<String> {
54 if node.kind() != "method_declaration" && node.kind() != "function_declaration" {
55 return None;
56 }
57 node.child_by_field_name("name").and_then(|n| {
58 let start = n.start_byte();
59 let end = n.end_byte();
60 if end <= source.len() {
61 Some(source[start..end].to_string())
62 } else {
63 None
64 }
65 })
66}
67
68#[must_use]
70pub fn extract_inheritance(node: &Node, source: &str) -> Vec<String> {
71 let mut inherits = Vec::new();
72
73 if let Some(type_field) = node.child_by_field_name("type") {
75 match type_field.kind() {
76 "struct_type" => {
77 for i in 0..type_field.named_child_count() {
79 if let Some(field_list) =
80 type_field.named_child(u32::try_from(i).unwrap_or(u32::MAX))
81 && field_list.kind() == "field_declaration_list"
82 {
83 for j in 0..field_list.named_child_count() {
85 if let Some(field) =
86 field_list.named_child(u32::try_from(j).unwrap_or(u32::MAX))
87 && field.kind() == "field_declaration"
88 && field.child_by_field_name("name").is_none()
89 {
90 if let Some(type_node) = field.child_by_field_name("type") {
92 let text =
93 &source[type_node.start_byte()..type_node.end_byte()];
94 inherits.push(text.to_string());
95 }
96 }
97 }
98 }
99 }
100 }
101 "interface_type" => {
102 for i in 0..type_field.named_child_count() {
104 if let Some(elem) = type_field.named_child(u32::try_from(i).unwrap_or(u32::MAX))
105 && elem.kind() == "type_elem"
106 {
107 let text = &source[elem.start_byte()..elem.end_byte()];
108 inherits.push(text.to_string());
109 }
110 }
111 }
112 _ => {}
113 }
114 }
115
116 inherits
117}
118
119#[cfg(all(test, feature = "lang-go"))]
120mod tests {
121 use super::*;
122 use crate::DefUseKind;
123 use crate::parser::SemanticExtractor;
124 use tree_sitter::Parser;
125
126 fn parse_go(source: &str) -> tree_sitter::Tree {
127 let mut parser = Parser::new();
128 parser
129 .set_language(&tree_sitter_go::LANGUAGE.into())
130 .expect("failed to set Go language");
131 parser.parse(source, None).expect("failed to parse source")
132 }
133
134 #[test]
135 fn test_extract_inheritance_struct_no_embeds() {
136 let source = "package p\ntype Foo struct { x int }";
138 let tree = parse_go(source);
139 let root = tree.root_node();
140 let type_spec = (0..root.named_child_count())
142 .filter_map(|i| root.named_child(i as u32))
143 .find_map(|n| {
144 if n.kind() == "type_declaration" {
145 (0..n.named_child_count())
146 .filter_map(|j| n.named_child(j as u32))
147 .find(|c| c.kind() == "type_spec")
148 } else {
149 None
150 }
151 })
152 .expect("expected type_spec node");
153 let result = extract_inheritance(&type_spec, source);
155 assert!(
157 result.is_empty(),
158 "expected no inherited types, got {:?}",
159 result
160 );
161 }
162
163 #[test]
164 fn test_find_method_for_receiver_wrong_kind() {
165 let source = "package p\ntype Bar struct {}";
167 let tree = parse_go(source);
168 let root = tree.root_node();
169 let node = root.named_child(0).expect("expected child");
170 let result = find_method_for_receiver(&node, source, None);
172 assert_eq!(result, None);
174 }
175
176 #[test]
177 fn test_defuse_query_write_site() {
178 let src = "package p\nfunc main() { x := 1 }\n";
180 let sites = SemanticExtractor::extract_def_use_for_file(src, "go", "x", "test.go", None);
181 assert!(!sites.is_empty(), "defuse sites should not be empty");
182 let has_write = sites.iter().any(|s| matches!(s.kind, DefUseKind::Write));
183 assert!(has_write, "should contain a Write DefUseSite");
184 }
185}