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 extract_function_name: Option<ExtractFunctionNameHandler>,
54    pub find_method_for_receiver: Option<FindMethodForReceiverHandler>,
55    pub find_receiver_type: Option<FindReceiverTypeHandler>,
56    pub extract_inheritance: Option<ExtractInheritanceHandler>,
57}
58
59/// Get language information by language name.
60#[allow(clippy::too_many_lines)] // exhaustive match over all supported languages; splitting harms readability
61pub fn get_language_info(lang_name: &str) -> Option<LanguageInfo> {
62    match lang_name {
63        #[cfg(feature = "lang-rust")]
64        "rust" => Some(LanguageInfo {
65            name: "rust",
66            language: tree_sitter_rust::LANGUAGE.into(),
67            element_query: rust::ELEMENT_QUERY,
68            call_query: rust::CALL_QUERY,
69            reference_query: Some(rust::REFERENCE_QUERY),
70            import_query: Some(rust::IMPORT_QUERY),
71            impl_query: Some(rust::IMPL_QUERY),
72            impl_trait_query: Some(rust::IMPL_TRAIT_QUERY),
73            extract_function_name: Some(rust::extract_function_name),
74            find_method_for_receiver: Some(rust::find_method_for_receiver),
75            find_receiver_type: Some(rust::find_receiver_type),
76            extract_inheritance: Some(rust::extract_inheritance),
77        }),
78        #[cfg(feature = "lang-python")]
79        "python" => Some(LanguageInfo {
80            name: "python",
81            language: tree_sitter_python::LANGUAGE.into(),
82            element_query: python::ELEMENT_QUERY,
83            call_query: python::CALL_QUERY,
84            reference_query: Some(python::REFERENCE_QUERY),
85            import_query: Some(python::IMPORT_QUERY),
86            impl_query: None,
87            impl_trait_query: None,
88            extract_function_name: None,
89            find_method_for_receiver: None,
90            find_receiver_type: None,
91            extract_inheritance: Some(python::extract_inheritance),
92        }),
93        #[cfg(feature = "lang-typescript")]
94        "typescript" => Some(LanguageInfo {
95            name: "typescript",
96            language: tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
97            element_query: typescript::ELEMENT_QUERY,
98            call_query: typescript::CALL_QUERY,
99            reference_query: Some(typescript::REFERENCE_QUERY),
100            import_query: Some(typescript::IMPORT_QUERY),
101            impl_query: None,
102            impl_trait_query: None,
103            extract_function_name: None,
104            find_method_for_receiver: None,
105            find_receiver_type: None,
106            extract_inheritance: Some(typescript::extract_inheritance),
107        }),
108        #[cfg(feature = "lang-tsx")]
109        "tsx" => Some(LanguageInfo {
110            name: "tsx",
111            language: tree_sitter_typescript::LANGUAGE_TSX.into(),
112            element_query: typescript::ELEMENT_QUERY,
113            call_query: typescript::CALL_QUERY,
114            reference_query: Some(typescript::REFERENCE_QUERY),
115            import_query: Some(typescript::IMPORT_QUERY),
116            impl_query: None,
117            impl_trait_query: None,
118            extract_function_name: None,
119            find_method_for_receiver: None,
120            find_receiver_type: None,
121            extract_inheritance: Some(typescript::extract_inheritance),
122        }),
123        #[cfg(feature = "lang-go")]
124        "go" => Some(LanguageInfo {
125            name: "go",
126            language: tree_sitter_go::LANGUAGE.into(),
127            element_query: go::ELEMENT_QUERY,
128            call_query: go::CALL_QUERY,
129            reference_query: Some(go::REFERENCE_QUERY),
130            import_query: Some(go::IMPORT_QUERY),
131            impl_query: None,
132            impl_trait_query: None,
133            extract_function_name: None,
134            find_method_for_receiver: Some(go::find_method_for_receiver),
135            find_receiver_type: None,
136            extract_inheritance: Some(go::extract_inheritance),
137        }),
138        #[cfg(feature = "lang-cpp")]
139        "c" | "cpp" => Some(LanguageInfo {
140            name: if lang_name == "c" { "c" } else { "cpp" },
141            language: tree_sitter_cpp::LANGUAGE.into(),
142            element_query: cpp::ELEMENT_QUERY,
143            call_query: cpp::CALL_QUERY,
144            reference_query: Some(cpp::REFERENCE_QUERY),
145            import_query: Some(cpp::IMPORT_QUERY),
146            impl_query: None,
147            impl_trait_query: None,
148            extract_function_name: Some(cpp::extract_function_name),
149            find_method_for_receiver: Some(cpp::find_method_for_receiver),
150            find_receiver_type: None,
151            extract_inheritance: Some(cpp::extract_inheritance),
152        }),
153        #[cfg(feature = "lang-java")]
154        "java" => Some(LanguageInfo {
155            name: "java",
156            language: tree_sitter_java::LANGUAGE.into(),
157            element_query: java::ELEMENT_QUERY,
158            call_query: java::CALL_QUERY,
159            reference_query: Some(java::REFERENCE_QUERY),
160            import_query: Some(java::IMPORT_QUERY),
161            impl_query: None,
162            impl_trait_query: None,
163            extract_function_name: None,
164            find_method_for_receiver: None,
165            find_receiver_type: None,
166            extract_inheritance: Some(java::extract_inheritance),
167        }),
168        #[cfg(feature = "lang-fortran")]
169        "fortran" => Some(LanguageInfo {
170            name: "fortran",
171            language: tree_sitter_fortran::LANGUAGE.into(),
172            element_query: fortran::ELEMENT_QUERY,
173            call_query: fortran::CALL_QUERY,
174            reference_query: Some(fortran::REFERENCE_QUERY),
175            import_query: Some(fortran::IMPORT_QUERY),
176            impl_query: None,
177            impl_trait_query: None,
178            extract_function_name: None,
179            find_method_for_receiver: None,
180            find_receiver_type: None,
181            extract_inheritance: Some(fortran::extract_inheritance),
182        }),
183        #[cfg(feature = "lang-csharp")]
184        "csharp" => Some(LanguageInfo {
185            name: "csharp",
186            language: tree_sitter_c_sharp::LANGUAGE.into(),
187            element_query: csharp::ELEMENT_QUERY,
188            call_query: csharp::CALL_QUERY,
189            reference_query: Some(csharp::REFERENCE_QUERY),
190            import_query: Some(csharp::IMPORT_QUERY),
191            impl_query: None,
192            impl_trait_query: None,
193            extract_function_name: None,
194            find_method_for_receiver: Some(csharp::find_method_for_receiver),
195            find_receiver_type: None,
196            extract_inheritance: Some(csharp::extract_inheritance),
197        }),
198        #[cfg(feature = "lang-javascript")]
199        "javascript" => Some(LanguageInfo {
200            name: "javascript",
201            language: tree_sitter_javascript::LANGUAGE.into(),
202            element_query: javascript::ELEMENT_QUERY,
203            call_query: javascript::CALL_QUERY,
204            reference_query: None,
205            import_query: Some(javascript::IMPORT_QUERY),
206            impl_query: None,
207            impl_trait_query: None,
208            extract_function_name: None,
209            find_method_for_receiver: None,
210            find_receiver_type: None,
211            extract_inheritance: Some(javascript::extract_inheritance),
212        }),
213        _ => None,
214    }
215}
216
217/// Get the tree-sitter Language object for a given language name.
218///
219/// Returns `None` if the language is not supported or not compiled in.
220#[must_use]
221pub fn get_ts_language(lang_name: &str) -> Option<Language> {
222    match lang_name {
223        #[cfg(feature = "lang-rust")]
224        "rust" => Some(tree_sitter_rust::LANGUAGE.into()),
225        #[cfg(feature = "lang-python")]
226        "python" => Some(tree_sitter_python::LANGUAGE.into()),
227        #[cfg(feature = "lang-typescript")]
228        "typescript" => Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
229        #[cfg(feature = "lang-tsx")]
230        "tsx" => Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
231        #[cfg(feature = "lang-go")]
232        "go" => Some(tree_sitter_go::LANGUAGE.into()),
233        #[cfg(feature = "lang-cpp")]
234        "c" | "cpp" => Some(tree_sitter_cpp::LANGUAGE.into()),
235        #[cfg(feature = "lang-java")]
236        "java" => Some(tree_sitter_java::LANGUAGE.into()),
237        #[cfg(feature = "lang-fortran")]
238        "fortran" => Some(tree_sitter_fortran::LANGUAGE.into()),
239        #[cfg(feature = "lang-csharp")]
240        "csharp" => Some(tree_sitter_c_sharp::LANGUAGE.into()),
241        #[cfg(feature = "lang-javascript")]
242        "javascript" => Some(tree_sitter_javascript::LANGUAGE.into()),
243        _ => None,
244    }
245}
246
247#[cfg(test)]
248mod tests {
249    use super::*;
250
251    #[test]
252    fn test_get_language_info_known() {
253        // Happy path: known languages return Some
254        assert!(
255            get_language_info("rust").is_some(),
256            "expected Some for 'rust'"
257        );
258        assert!(get_language_info("go").is_some(), "expected Some for 'go'");
259        assert!(
260            get_language_info("python").is_some(),
261            "expected Some for 'python'"
262        );
263    }
264
265    #[test]
266    fn test_get_language_info_unknown() {
267        // Edge case: unknown language returns None
268        assert!(
269            get_language_info("cobol").is_none(),
270            "expected None for 'cobol'"
271        );
272    }
273
274    #[test]
275    fn test_get_ts_language_known() {
276        // Happy path: known language returns Some
277        assert!(
278            get_ts_language("rust").is_some(),
279            "expected Some for 'rust'"
280        );
281    }
282
283    #[test]
284    fn test_get_ts_language_unknown() {
285        // Edge case: unknown language returns None
286        assert!(
287            get_ts_language("cobol").is_none(),
288            "expected None for 'cobol'"
289        );
290    }
291}