code_analyze_core/languages/
csharp.rs1use tree_sitter::Node;
5
6pub const ELEMENT_QUERY: &str = r"
9(method_declaration name: (identifier) @method_name) @function
10(constructor_declaration name: (identifier) @ctor_name) @function
11(class_declaration name: (identifier) @class_name) @class
12(interface_declaration name: (identifier) @interface_name) @class
13(record_declaration name: (identifier) @record_name) @class
14(struct_declaration name: (identifier) @struct_name) @class
15(enum_declaration name: (identifier) @enum_name) @class
16";
17
18pub const CALL_QUERY: &str = r"
20(invocation_expression
21 function: (member_access_expression name: (identifier) @call))
22(invocation_expression
23 function: (identifier) @call)
24";
25
26pub const REFERENCE_QUERY: &str = r"
28(base_list (identifier) @type_ref)
29(base_list (generic_name (identifier) @type_ref))
30(type_argument_list (identifier) @type_ref)
31(type_parameter_list (type_parameter (identifier) @type_ref))
32";
33
34pub const IMPORT_QUERY: &str = r"
41(using_directive) @import_path
42";
43
44#[must_use]
49pub fn extract_inheritance(node: &Node, source: &str) -> Vec<String> {
50 let mut bases = Vec::new();
51
52 for i in 0..node.child_count() {
54 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX))
55 && child.kind() == "base_list"
56 {
57 bases.extend(extract_base_list(&child, source));
58 break;
59 }
60 }
61
62 bases
63}
64
65fn extract_base_list(node: &Node, source: &str) -> Vec<String> {
67 let mut bases = Vec::new();
68
69 for i in 0..node.named_child_count() {
70 if let Some(child) = node.named_child(u32::try_from(i).unwrap_or(u32::MAX)) {
71 match child.kind() {
72 "identifier" => {
73 let end = child.end_byte();
74 if end <= source.len() {
75 bases.push(source[child.start_byte()..end].to_string());
76 }
77 }
78 "generic_name" => {
79 if let Some(id) = child.named_child(0)
81 && id.kind() == "identifier"
82 {
83 let end = id.end_byte();
84 if end <= source.len() {
85 bases.push(source[id.start_byte()..end].to_string());
86 }
87 }
88 }
89 _ => {}
90 }
91 }
92 }
93
94 bases
95}
96
97#[must_use]
105pub fn find_method_for_receiver(
106 node: &Node,
107 source: &str,
108 _depth: Option<usize>,
109) -> Option<String> {
110 if node.kind() != "method_declaration" && node.kind() != "constructor_declaration" {
111 return None;
112 }
113
114 let mut current = *node;
116 let mut in_type_body = false;
117 while let Some(parent) = current.parent() {
118 match parent.kind() {
119 "class_declaration"
120 | "interface_declaration"
121 | "record_declaration"
122 | "struct_declaration"
123 | "enum_declaration" => {
124 in_type_body = true;
125 break;
126 }
127 _ => {
128 current = parent;
129 }
130 }
131 }
132
133 if !in_type_body {
134 return None;
135 }
136
137 node.child_by_field_name("name").and_then(|n| {
138 let end = n.end_byte();
139 if end <= source.len() {
140 Some(source[n.start_byte()..end].to_string())
141 } else {
142 None
143 }
144 })
145}
146
147#[cfg(all(test, feature = "lang-csharp"))]
148mod tests {
149 use super::*;
150 use tree_sitter::Parser;
151
152 fn parse_csharp(src: &str) -> tree_sitter::Tree {
153 let mut parser = Parser::new();
154 parser
155 .set_language(&tree_sitter_c_sharp::LANGUAGE.into())
156 .expect("Error loading C# language");
157 parser.parse(src, None).expect("Failed to parse C#")
158 }
159
160 #[test]
161 fn test_csharp_method_in_class() {
162 let src = "class Foo { void Bar() { Baz(); } void Baz() {} }";
164 let tree = parse_csharp(src);
165 let root = tree.root_node();
166
167 let mut methods: Vec<String> = Vec::new();
170 let mut stack = vec![root];
171 while let Some(node) = stack.pop() {
172 if node.kind() == "method_declaration" {
173 if let Some(name_node) = node.child_by_field_name("name") {
174 methods.push(src[name_node.start_byte()..name_node.end_byte()].to_string());
175 }
176 }
177 for i in 0..node.child_count() {
178 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
179 stack.push(child);
180 }
181 }
182 }
183 methods.sort();
184
185 assert_eq!(methods, vec!["Bar", "Baz"]);
187 }
188
189 #[test]
190 fn test_csharp_constructor() {
191 let src = "class Foo { public Foo() {} }";
193 let tree = parse_csharp(src);
194 let root = tree.root_node();
195
196 let mut ctors: Vec<String> = Vec::new();
198 let mut stack = vec![root];
199 while let Some(node) = stack.pop() {
200 if node.kind() == "constructor_declaration" {
201 if let Some(name_node) = node.child_by_field_name("name") {
202 ctors.push(src[name_node.start_byte()..name_node.end_byte()].to_string());
203 }
204 }
205 for i in 0..node.child_count() {
206 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
207 stack.push(child);
208 }
209 }
210 }
211
212 assert_eq!(ctors, vec!["Foo"]);
214 }
215
216 #[test]
217 fn test_csharp_interface() {
218 let src = "interface IBar { void Do(); }";
220 let tree = parse_csharp(src);
221 let root = tree.root_node();
222
223 let mut interfaces: Vec<String> = Vec::new();
225 let mut stack = vec![root];
226 while let Some(node) = stack.pop() {
227 if node.kind() == "interface_declaration" {
228 if let Some(name_node) = node.child_by_field_name("name") {
229 interfaces.push(src[name_node.start_byte()..name_node.end_byte()].to_string());
230 }
231 }
232 for i in 0..node.child_count() {
233 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
234 stack.push(child);
235 }
236 }
237 }
238
239 assert_eq!(interfaces, vec!["IBar"]);
241 }
242
243 #[test]
244 fn test_csharp_using_directive() {
245 let src = "using System;";
247 let tree = parse_csharp(src);
248 let root = tree.root_node();
249
250 let mut imports: Vec<String> = Vec::new();
252 let mut stack = vec![root];
253 while let Some(node) = stack.pop() {
254 if node.kind() == "using_directive" {
255 imports.push(src[node.start_byte()..node.end_byte()].to_string());
256 }
257 for i in 0..node.child_count() {
258 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
259 stack.push(child);
260 }
261 }
262 }
263
264 assert_eq!(imports, vec!["using System;"]);
266 }
267
268 #[test]
269 fn test_csharp_async_method() {
270 let src = "class C { async Task Foo() { await Bar(); } Task Bar() { return Task.CompletedTask; } }";
272 let tree = parse_csharp(src);
273 let root = tree.root_node();
274
275 let mut methods: Vec<String> = Vec::new();
277 let mut stack = vec![root];
278 while let Some(node) = stack.pop() {
279 if node.kind() == "method_declaration" {
280 if let Some(name_node) = node.child_by_field_name("name") {
281 methods.push(src[name_node.start_byte()..name_node.end_byte()].to_string());
282 }
283 }
284 for i in 0..node.child_count() {
285 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
286 stack.push(child);
287 }
288 }
289 }
290
291 assert!(methods.contains(&"Foo".to_string()));
293 }
294
295 #[test]
296 fn test_csharp_generic_class() {
297 let src = "class Generic<T> { T value; }";
299 let tree = parse_csharp(src);
300 let root = tree.root_node();
301
302 let mut classes: Vec<String> = Vec::new();
304 let mut stack = vec![root];
305 while let Some(node) = stack.pop() {
306 if node.kind() == "class_declaration" {
307 if let Some(name_node) = node.child_by_field_name("name") {
308 classes.push(src[name_node.start_byte()..name_node.end_byte()].to_string());
309 }
310 }
311 for i in 0..node.child_count() {
312 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
313 stack.push(child);
314 }
315 }
316 }
317
318 assert_eq!(classes, vec!["Generic"]);
320 }
321
322 #[test]
323 fn test_csharp_inheritance_extraction() {
324 let src = "class Dog : Animal, ICanRun {}";
326 let tree = parse_csharp(src);
327 let root = tree.root_node();
328
329 let mut base_list_node: Option<Node> = None;
331 let mut stack = vec![root];
332 while let Some(node) = stack.pop() {
333 if node.kind() == "base_list" {
334 base_list_node = Some(node);
335 break;
336 }
337 for i in 0..node.child_count() {
338 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
339 stack.push(child);
340 }
341 }
342 }
343
344 let mut class_node: Option<Node> = None;
346 let mut stack2 = vec![root];
347 while let Some(node) = stack2.pop() {
348 if node.kind() == "class_declaration" {
349 class_node = Some(node);
350 break;
351 }
352 for i in 0..node.child_count() {
353 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
354 stack2.push(child);
355 }
356 }
357 }
358 let class = class_node.expect("class_declaration not found");
359 let _ = base_list_node; let bases = extract_inheritance(&class, src);
361
362 assert_eq!(bases, vec!["Animal", "ICanRun"]);
364 }
365
366 #[test]
367 fn test_csharp_find_method_for_receiver() {
368 let src = "class MyClass { void MyMethod() {} }";
370 let tree = parse_csharp(src);
371 let root = tree.root_node();
372
373 let mut method_node: Option<Node> = None;
375 let mut stack = vec![root];
376 while let Some(node) = stack.pop() {
377 if node.kind() == "method_declaration" {
378 method_node = Some(node);
379 break;
380 }
381 for i in 0..node.child_count() {
382 if let Some(child) = node.child(u32::try_from(i).unwrap_or(u32::MAX)) {
383 stack.push(child);
384 }
385 }
386 }
387
388 let method = method_node.expect("method_declaration not found");
389 let name = find_method_for_receiver(&method, src, None);
390
391 assert_eq!(name, Some("MyMethod".to_string()));
393 }
394}