code_analyze_core/languages/
rust.rs1use tree_sitter::Node;
4
5pub const ELEMENT_QUERY: &str = r"
7(function_item
8 name: (identifier) @func_name
9 parameters: (parameters) @params) @function
10(struct_item) @class
11(enum_item) @class
12(trait_item) @class
13";
14
15pub const CALL_QUERY: &str = r"
17(call_expression function: (identifier) @call)
18(call_expression function: (field_expression field: (field_identifier) @call))
19(call_expression function: (scoped_identifier name: (identifier) @call))
20";
21
22pub const REFERENCE_QUERY: &str = r"
24(type_identifier) @type_ref
25";
26
27pub const IMPORT_QUERY: &str = r"
29(use_declaration argument: (_) @import_path) @import
30";
31
32pub const IMPL_TRAIT_QUERY: &str = r"
37(impl_item
38 trait: (type_identifier) @trait_name
39 type: (type_identifier) @impl_type)
40";
41
42pub const IMPL_QUERY: &str = r"
44(impl_item
45 type: (type_identifier) @impl_type
46 body: (declaration_list
47 (function_item
48 name: (identifier) @method_name
49 parameters: (parameters) @method_params) @method))
50";
51
52pub const DEFUSE_QUERY: &str = r"
56(let_declaration pattern: (identifier) @write.decl)
57(assignment_expression left: (identifier) @write.assign)
58(compound_assignment_expr left: (identifier) @writeread.compound)
59(identifier) @read.usage
60";
61
62#[must_use]
64pub fn extract_function_name(node: &Node, source: &str, _query_name: &str) -> Option<String> {
65 if node.kind() != "function_item" {
66 return None;
67 }
68 node.child_by_field_name("name").and_then(|n| {
69 let start = n.start_byte();
70 let end = n.end_byte();
71 if end <= source.len() {
72 Some(source[start..end].to_string())
73 } else {
74 None
75 }
76 })
77}
78
79#[must_use]
81pub fn find_method_for_receiver(
82 node: &Node,
83 source: &str,
84 _depth: Option<usize>,
85) -> Option<String> {
86 if node.kind() != "method_item" && node.kind() != "function_item" {
87 return None;
88 }
89 node.child_by_field_name("name").and_then(|n| {
90 let start = n.start_byte();
91 let end = n.end_byte();
92 if end <= source.len() {
93 Some(source[start..end].to_string())
94 } else {
95 None
96 }
97 })
98}
99
100#[must_use]
102pub fn find_receiver_type(node: &Node, source: &str) -> Option<String> {
103 if node.kind() != "impl_item" {
104 return None;
105 }
106 node.child_by_field_name("type").and_then(|n| {
107 let start = n.start_byte();
108 let end = n.end_byte();
109 if end <= source.len() {
110 Some(source[start..end].to_string())
111 } else {
112 None
113 }
114 })
115}
116
117#[must_use]
121pub fn extract_inheritance(_node: &Node, _source: &str) -> Vec<String> {
122 Vec::new()
123}
124
125#[cfg(all(test, feature = "lang-rust"))]
126mod tests {
127 use super::*;
128 use crate::parser::SemanticExtractor;
129 use crate::types::DefUseKind;
130 use tree_sitter::Parser;
131
132 fn parse_rust(source: &str) -> tree_sitter::Tree {
133 let mut parser = Parser::new();
134 parser
135 .set_language(&tree_sitter_rust::LANGUAGE.into())
136 .expect("failed to set Rust language");
137 parser.parse(source, None).expect("failed to parse source")
138 }
139
140 #[test]
141 fn test_extract_function_name_happy_path() {
142 let source = "fn foo() {}";
144 let tree = parse_rust(source);
145 let root = tree.root_node();
146 let func_node = root.named_child(0).expect("expected child");
148 assert_eq!(func_node.kind(), "function_item");
149 let result = extract_function_name(&func_node, source, "function");
151 assert_eq!(result, Some("foo".to_string()));
153 }
154
155 #[test]
156 fn test_extract_function_name_wrong_kind() {
157 let source = "struct Bar {}";
159 let tree = parse_rust(source);
160 let root = tree.root_node();
161 let node = root.named_child(0).expect("expected child");
162 assert_eq!(node.kind(), "struct_item");
163 let result = extract_function_name(&node, source, "class");
165 assert_eq!(result, None);
167 }
168
169 #[test]
170 fn test_find_method_for_receiver_happy_path() {
171 let source = "fn bar() {}";
173 let tree = parse_rust(source);
174 let root = tree.root_node();
175 let node = root.named_child(0).expect("expected child");
176 let result = find_method_for_receiver(&node, source, None);
178 assert_eq!(result, Some("bar".to_string()));
180 }
181
182 #[test]
183 fn test_find_method_for_receiver_wrong_kind() {
184 let source = "struct Baz {}";
186 let tree = parse_rust(source);
187 let root = tree.root_node();
188 let node = root.named_child(0).expect("expected child");
189 let result = find_method_for_receiver(&node, source, None);
191 assert_eq!(result, None);
193 }
194
195 #[test]
196 fn test_find_receiver_type_happy_path() {
197 let source = "struct Foo; impl Foo { fn x() {} }";
199 let tree = parse_rust(source);
200 let root = tree.root_node();
201 let impl_node = (0..root.named_child_count())
203 .filter_map(|i| root.named_child(i as u32))
204 .find(|n| n.kind() == "impl_item")
205 .expect("expected impl_item node");
206 let result = find_receiver_type(&impl_node, source);
208 assert_eq!(result, Some("Foo".to_string()));
210 }
211
212 #[test]
213 fn test_find_receiver_type_wrong_kind() {
214 let source = "fn qux() {}";
216 let tree = parse_rust(source);
217 let root = tree.root_node();
218 let node = root.named_child(0).expect("expected child");
219 let result = find_receiver_type(&node, source);
221 assert_eq!(result, None);
223 }
224
225 #[test]
226 fn test_defuse_query_write_site() {
227 let source = "fn foo() { let x = 5; }";
229 let sites =
230 SemanticExtractor::extract_def_use_for_file(source, "rust", "x", "test.rs", None);
231 assert!(!sites.is_empty(), "defuse sites should not be empty");
233 let has_write = sites.iter().any(|s| matches!(s.kind, DefUseKind::Write));
234 assert!(has_write, "should contain a Write DefUseSite");
235 }
236}