Skip to main content

code_analyze_core/languages/
rust.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 Rust elements (functions and structs/enums/traits).
6pub 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
15/// Tree-sitter query for extracting function calls.
16pub 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
22/// Tree-sitter query for extracting type references.
23pub const REFERENCE_QUERY: &str = r"
24(type_identifier) @type_ref
25";
26
27/// Tree-sitter query for extracting imports.
28pub const IMPORT_QUERY: &str = r"
29(use_declaration argument: (_) @import_path) @import
30";
31
32/// Tree-sitter query for extracting `impl Trait for Type` blocks.
33/// Captures the trait name and the concrete implementor type.
34// Note: matches only simple trait names (type_identifier). Scoped traits
35// (e.g. `impl io::Sink for T`) are not matched; scoped coverage is out of scope for v1.
36pub const IMPL_TRAIT_QUERY: &str = r"
37(impl_item
38  trait: (type_identifier) @trait_name
39  type: (type_identifier) @impl_type)
40";
41
42/// Tree-sitter query for extracting impl blocks and methods.
43pub 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
52/// Extract function name from a function node.
53#[must_use]
54pub fn extract_function_name(node: &Node, source: &str, _query_name: &str) -> Option<String> {
55    if node.kind() != "function_item" {
56        return None;
57    }
58    node.child_by_field_name("name").and_then(|n| {
59        let start = n.start_byte();
60        let end = n.end_byte();
61        if end <= source.len() {
62            Some(source[start..end].to_string())
63        } else {
64            None
65        }
66    })
67}
68
69/// Find method name for a receiver type.
70#[must_use]
71pub fn find_method_for_receiver(
72    node: &Node,
73    source: &str,
74    _depth: Option<usize>,
75) -> Option<String> {
76    if node.kind() != "method_item" && node.kind() != "function_item" {
77        return None;
78    }
79    node.child_by_field_name("name").and_then(|n| {
80        let start = n.start_byte();
81        let end = n.end_byte();
82        if end <= source.len() {
83            Some(source[start..end].to_string())
84        } else {
85            None
86        }
87    })
88}
89
90/// Find receiver type for a method.
91#[must_use]
92pub fn find_receiver_type(node: &Node, source: &str) -> Option<String> {
93    if node.kind() != "impl_item" {
94        return None;
95    }
96    node.child_by_field_name("type").and_then(|n| {
97        let start = n.start_byte();
98        let end = n.end_byte();
99        if end <= source.len() {
100            Some(source[start..end].to_string())
101        } else {
102            None
103        }
104    })
105}
106
107/// Extract inheritance information from a Rust class node.
108/// Rust class nodes (`struct_item`, `enum_item`, `trait_item`) have no syntactic inheritance.
109/// Inheritance is via `impl` blocks, not on the type declaration itself.
110#[must_use]
111pub fn extract_inheritance(_node: &Node, _source: &str) -> Vec<String> {
112    Vec::new()
113}
114
115#[cfg(all(test, feature = "lang-rust"))]
116mod tests {
117    use super::*;
118    use tree_sitter::Parser;
119
120    fn parse_rust(source: &str) -> tree_sitter::Tree {
121        let mut parser = Parser::new();
122        parser
123            .set_language(&tree_sitter_rust::LANGUAGE.into())
124            .expect("failed to set Rust language");
125        parser.parse(source, None).expect("failed to parse source")
126    }
127
128    #[test]
129    fn test_extract_function_name_happy_path() {
130        // Arrange
131        let source = "fn foo() {}";
132        let tree = parse_rust(source);
133        let root = tree.root_node();
134        // find the function_item node
135        let func_node = root.named_child(0).expect("expected child");
136        assert_eq!(func_node.kind(), "function_item");
137        // Act
138        let result = extract_function_name(&func_node, source, "function");
139        // Assert
140        assert_eq!(result, Some("foo".to_string()));
141    }
142
143    #[test]
144    fn test_extract_function_name_wrong_kind() {
145        // Arrange: parse a struct_item; not a function_item
146        let source = "struct Bar {}";
147        let tree = parse_rust(source);
148        let root = tree.root_node();
149        let node = root.named_child(0).expect("expected child");
150        assert_eq!(node.kind(), "struct_item");
151        // Act
152        let result = extract_function_name(&node, source, "class");
153        // Assert
154        assert_eq!(result, None);
155    }
156
157    #[test]
158    fn test_find_method_for_receiver_happy_path() {
159        // Arrange: function_item works for find_method_for_receiver
160        let source = "fn bar() {}";
161        let tree = parse_rust(source);
162        let root = tree.root_node();
163        let node = root.named_child(0).expect("expected child");
164        // Act
165        let result = find_method_for_receiver(&node, source, None);
166        // Assert
167        assert_eq!(result, Some("bar".to_string()));
168    }
169
170    #[test]
171    fn test_find_method_for_receiver_wrong_kind() {
172        // Arrange: struct_item is not method_item or function_item
173        let source = "struct Baz {}";
174        let tree = parse_rust(source);
175        let root = tree.root_node();
176        let node = root.named_child(0).expect("expected child");
177        // Act
178        let result = find_method_for_receiver(&node, source, None);
179        // Assert
180        assert_eq!(result, None);
181    }
182
183    #[test]
184    fn test_find_receiver_type_happy_path() {
185        // Arrange: impl block is an impl_item
186        let source = "struct Foo; impl Foo { fn x() {} }";
187        let tree = parse_rust(source);
188        let root = tree.root_node();
189        // find impl_item node
190        let impl_node = (0..root.named_child_count())
191            .filter_map(|i| root.named_child(i as u32))
192            .find(|n| n.kind() == "impl_item")
193            .expect("expected impl_item node");
194        // Act
195        let result = find_receiver_type(&impl_node, source);
196        // Assert
197        assert_eq!(result, Some("Foo".to_string()));
198    }
199
200    #[test]
201    fn test_find_receiver_type_wrong_kind() {
202        // Arrange: function_item is not impl_item
203        let source = "fn qux() {}";
204        let tree = parse_rust(source);
205        let root = tree.root_node();
206        let node = root.named_child(0).expect("expected child");
207        // Act
208        let result = find_receiver_type(&node, source);
209        // Assert
210        assert_eq!(result, None);
211    }
212}