Skip to main content

code_analyze_core/languages/
mod.rs

1// SPDX-FileCopyrightText: 2026 code-analyze-mcp contributors
2// SPDX-License-Identifier: Apache-2.0
3//! Language-specific handlers and query definitions for tree-sitter parsing.
4//!
5//! Provides query strings and extraction handlers for supported languages.
6//! Language support is controlled by Cargo `lang-*` features (by default all
7//! available language handlers are enabled): Rust, Go, Java, JavaScript, Python,
8//! TypeScript, TSX, Fortran, C/C++, and C#.
9
10#[cfg(feature = "lang-cpp")]
11pub mod cpp;
12#[cfg(feature = "lang-csharp")]
13pub mod csharp;
14#[cfg(feature = "lang-fortran")]
15pub mod fortran;
16#[cfg(feature = "lang-go")]
17pub mod go;
18#[cfg(feature = "lang-java")]
19pub mod java;
20#[cfg(feature = "lang-javascript")]
21pub mod javascript;
22#[cfg(feature = "lang-python")]
23pub mod python;
24#[cfg(feature = "lang-rust")]
25pub mod rust;
26#[cfg(any(feature = "lang-typescript", feature = "lang-tsx"))]
27pub mod typescript;
28
29use tree_sitter::{Language, Node};
30
31/// Handler to extract function name from a node.
32pub type ExtractFunctionNameHandler = fn(&Node, &str, &str) -> Option<String>;
33
34/// Handler to find method name for a receiver type.
35pub type FindMethodForReceiverHandler = fn(&Node, &str, Option<usize>) -> Option<String>;
36
37/// Handler to find receiver type for a method.
38pub type FindReceiverTypeHandler = fn(&Node, &str) -> Option<String>;
39
40/// Handler to extract inheritance information from a class node.
41pub type ExtractInheritanceHandler = fn(&Node, &str) -> Vec<String>;
42
43/// Information about a supported language for code analysis.
44pub struct LanguageInfo {
45    pub name: &'static str,
46    pub language: Language,
47    pub element_query: &'static str,
48    pub call_query: &'static str,
49    pub reference_query: Option<&'static str>,
50    pub import_query: Option<&'static str>,
51    pub impl_query: Option<&'static str>,
52    pub impl_trait_query: Option<&'static str>,
53    pub defuse_query: Option<&'static str>,
54    pub extract_function_name: Option<ExtractFunctionNameHandler>,
55    pub find_method_for_receiver: Option<FindMethodForReceiverHandler>,
56    pub find_receiver_type: Option<FindReceiverTypeHandler>,
57    pub extract_inheritance: Option<ExtractInheritanceHandler>,
58}
59
60/// Get language information by language name.
61#[allow(clippy::too_many_lines)] // exhaustive match over all supported languages; splitting harms readability
62pub fn get_language_info(lang_name: &str) -> Option<LanguageInfo> {
63    match lang_name {
64        #[cfg(feature = "lang-rust")]
65        "rust" => Some(LanguageInfo {
66            name: "rust",
67            language: tree_sitter_rust::LANGUAGE.into(),
68            element_query: rust::ELEMENT_QUERY,
69            call_query: rust::CALL_QUERY,
70            reference_query: Some(rust::REFERENCE_QUERY),
71            import_query: Some(rust::IMPORT_QUERY),
72            impl_query: Some(rust::IMPL_QUERY),
73            impl_trait_query: Some(rust::IMPL_TRAIT_QUERY),
74            defuse_query: Some(rust::DEFUSE_QUERY),
75            extract_function_name: Some(rust::extract_function_name),
76            find_method_for_receiver: Some(rust::find_method_for_receiver),
77            find_receiver_type: Some(rust::find_receiver_type),
78            extract_inheritance: Some(rust::extract_inheritance),
79        }),
80        #[cfg(feature = "lang-python")]
81        "python" => Some(LanguageInfo {
82            name: "python",
83            language: tree_sitter_python::LANGUAGE.into(),
84            element_query: python::ELEMENT_QUERY,
85            call_query: python::CALL_QUERY,
86            reference_query: Some(python::REFERENCE_QUERY),
87            import_query: Some(python::IMPORT_QUERY),
88            impl_query: None,
89            impl_trait_query: None,
90            defuse_query: Some(python::DEFUSE_QUERY),
91            extract_function_name: None,
92            find_method_for_receiver: None,
93            find_receiver_type: None,
94            extract_inheritance: Some(python::extract_inheritance),
95        }),
96        #[cfg(feature = "lang-typescript")]
97        "typescript" => Some(LanguageInfo {
98            name: "typescript",
99            language: tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
100            element_query: typescript::ELEMENT_QUERY,
101            call_query: typescript::CALL_QUERY,
102            reference_query: Some(typescript::REFERENCE_QUERY),
103            import_query: Some(typescript::IMPORT_QUERY),
104            impl_query: None,
105            impl_trait_query: None,
106            defuse_query: Some(typescript::DEFUSE_QUERY),
107            extract_function_name: None,
108            find_method_for_receiver: None,
109            find_receiver_type: None,
110            extract_inheritance: Some(typescript::extract_inheritance),
111        }),
112        #[cfg(feature = "lang-tsx")]
113        "tsx" => Some(LanguageInfo {
114            name: "tsx",
115            language: tree_sitter_typescript::LANGUAGE_TSX.into(),
116            element_query: typescript::ELEMENT_QUERY,
117            call_query: typescript::CALL_QUERY,
118            reference_query: Some(typescript::REFERENCE_QUERY),
119            import_query: Some(typescript::IMPORT_QUERY),
120            impl_query: None,
121            impl_trait_query: None,
122            defuse_query: Some(typescript::DEFUSE_QUERY),
123            extract_function_name: None,
124            find_method_for_receiver: None,
125            find_receiver_type: None,
126            extract_inheritance: Some(typescript::extract_inheritance),
127        }),
128        #[cfg(feature = "lang-go")]
129        "go" => Some(LanguageInfo {
130            name: "go",
131            language: tree_sitter_go::LANGUAGE.into(),
132            element_query: go::ELEMENT_QUERY,
133            call_query: go::CALL_QUERY,
134            reference_query: Some(go::REFERENCE_QUERY),
135            import_query: Some(go::IMPORT_QUERY),
136            impl_query: None,
137            impl_trait_query: None,
138            defuse_query: Some(go::DEFUSE_QUERY),
139            extract_function_name: None,
140            find_method_for_receiver: Some(go::find_method_for_receiver),
141            find_receiver_type: None,
142            extract_inheritance: Some(go::extract_inheritance),
143        }),
144        #[cfg(feature = "lang-cpp")]
145        "c" | "cpp" => Some(LanguageInfo {
146            name: if lang_name == "c" { "c" } else { "cpp" },
147            language: tree_sitter_cpp::LANGUAGE.into(),
148            element_query: cpp::ELEMENT_QUERY,
149            call_query: cpp::CALL_QUERY,
150            reference_query: Some(cpp::REFERENCE_QUERY),
151            import_query: Some(cpp::IMPORT_QUERY),
152            impl_query: None,
153            impl_trait_query: None,
154            defuse_query: Some(cpp::DEFUSE_QUERY),
155            extract_function_name: Some(cpp::extract_function_name),
156            find_method_for_receiver: Some(cpp::find_method_for_receiver),
157            find_receiver_type: None,
158            extract_inheritance: Some(cpp::extract_inheritance),
159        }),
160        #[cfg(feature = "lang-java")]
161        "java" => Some(LanguageInfo {
162            name: "java",
163            language: tree_sitter_java::LANGUAGE.into(),
164            element_query: java::ELEMENT_QUERY,
165            call_query: java::CALL_QUERY,
166            reference_query: Some(java::REFERENCE_QUERY),
167            import_query: Some(java::IMPORT_QUERY),
168            impl_query: None,
169            impl_trait_query: None,
170            defuse_query: Some(java::DEFUSE_QUERY),
171            extract_function_name: None,
172            find_method_for_receiver: None,
173            find_receiver_type: None,
174            extract_inheritance: Some(java::extract_inheritance),
175        }),
176        #[cfg(feature = "lang-fortran")]
177        "fortran" => Some(LanguageInfo {
178            name: "fortran",
179            language: tree_sitter_fortran::LANGUAGE.into(),
180            element_query: fortran::ELEMENT_QUERY,
181            call_query: fortran::CALL_QUERY,
182            reference_query: Some(fortran::REFERENCE_QUERY),
183            import_query: Some(fortran::IMPORT_QUERY),
184            impl_query: None,
185            impl_trait_query: None,
186            defuse_query: None,
187            extract_function_name: None,
188            find_method_for_receiver: None,
189            find_receiver_type: None,
190            extract_inheritance: Some(fortran::extract_inheritance),
191        }),
192        #[cfg(feature = "lang-csharp")]
193        "csharp" => Some(LanguageInfo {
194            name: "csharp",
195            language: tree_sitter_c_sharp::LANGUAGE.into(),
196            element_query: csharp::ELEMENT_QUERY,
197            call_query: csharp::CALL_QUERY,
198            reference_query: Some(csharp::REFERENCE_QUERY),
199            import_query: Some(csharp::IMPORT_QUERY),
200            impl_query: None,
201            impl_trait_query: None,
202            defuse_query: Some(csharp::DEFUSE_QUERY),
203            extract_function_name: None,
204            find_method_for_receiver: Some(csharp::find_method_for_receiver),
205            find_receiver_type: None,
206            extract_inheritance: Some(csharp::extract_inheritance),
207        }),
208        #[cfg(feature = "lang-javascript")]
209        "javascript" => Some(LanguageInfo {
210            name: "javascript",
211            language: tree_sitter_javascript::LANGUAGE.into(),
212            element_query: javascript::ELEMENT_QUERY,
213            call_query: javascript::CALL_QUERY,
214            reference_query: None,
215            import_query: Some(javascript::IMPORT_QUERY),
216            impl_query: None,
217            impl_trait_query: None,
218            defuse_query: Some(javascript::DEFUSE_QUERY),
219            extract_function_name: None,
220            find_method_for_receiver: None,
221            find_receiver_type: None,
222            extract_inheritance: Some(javascript::extract_inheritance),
223        }),
224        _ => None,
225    }
226}
227
228/// Get the tree-sitter Language object for a given language name.
229///
230/// Returns `None` if the language is not supported or not compiled in.
231#[must_use]
232pub fn get_ts_language(lang_name: &str) -> Option<Language> {
233    match lang_name {
234        #[cfg(feature = "lang-rust")]
235        "rust" => Some(tree_sitter_rust::LANGUAGE.into()),
236        #[cfg(feature = "lang-python")]
237        "python" => Some(tree_sitter_python::LANGUAGE.into()),
238        #[cfg(feature = "lang-typescript")]
239        "typescript" => Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
240        #[cfg(feature = "lang-tsx")]
241        "tsx" => Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
242        #[cfg(feature = "lang-go")]
243        "go" => Some(tree_sitter_go::LANGUAGE.into()),
244        #[cfg(feature = "lang-cpp")]
245        "c" | "cpp" => Some(tree_sitter_cpp::LANGUAGE.into()),
246        #[cfg(feature = "lang-java")]
247        "java" => Some(tree_sitter_java::LANGUAGE.into()),
248        #[cfg(feature = "lang-fortran")]
249        "fortran" => Some(tree_sitter_fortran::LANGUAGE.into()),
250        #[cfg(feature = "lang-csharp")]
251        "csharp" => Some(tree_sitter_c_sharp::LANGUAGE.into()),
252        #[cfg(feature = "lang-javascript")]
253        "javascript" => Some(tree_sitter_javascript::LANGUAGE.into()),
254        _ => None,
255    }
256}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn test_get_language_info_known() {
264        // Happy path: known languages return Some
265        assert!(
266            get_language_info("rust").is_some(),
267            "expected Some for 'rust'"
268        );
269        assert!(get_language_info("go").is_some(), "expected Some for 'go'");
270        assert!(
271            get_language_info("python").is_some(),
272            "expected Some for 'python'"
273        );
274    }
275
276    #[test]
277    fn test_get_language_info_unknown() {
278        // Edge case: unknown language returns None
279        assert!(
280            get_language_info("cobol").is_none(),
281            "expected None for 'cobol'"
282        );
283    }
284
285    #[test]
286    fn test_get_ts_language_known() {
287        // Happy path: known language returns Some
288        assert!(
289            get_ts_language("rust").is_some(),
290            "expected Some for 'rust'"
291        );
292    }
293
294    #[test]
295    fn test_get_ts_language_unknown() {
296        // Edge case: unknown language returns None
297        assert!(
298            get_ts_language("cobol").is_none(),
299            "expected None for 'cobol'"
300        );
301    }
302}