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, Python,
8//! TypeScript, TSX, and Fortran.
9
10#[cfg(feature = "lang-fortran")]
11pub mod fortran;
12#[cfg(feature = "lang-go")]
13pub mod go;
14#[cfg(feature = "lang-java")]
15pub mod java;
16#[cfg(feature = "lang-python")]
17pub mod python;
18#[cfg(feature = "lang-rust")]
19pub mod rust;
20#[cfg(any(feature = "lang-typescript", feature = "lang-tsx"))]
21pub mod typescript;
22
23use tree_sitter::{Language, Node};
24
25/// Handler to extract function name from a node.
26pub type ExtractFunctionNameHandler = fn(&Node, &str, &str) -> Option<String>;
27
28/// Handler to find method name for a receiver type.
29pub type FindMethodForReceiverHandler = fn(&Node, &str, Option<usize>) -> Option<String>;
30
31/// Handler to find receiver type for a method.
32pub type FindReceiverTypeHandler = fn(&Node, &str) -> Option<String>;
33
34/// Handler to extract inheritance information from a class node.
35pub type ExtractInheritanceHandler = fn(&Node, &str) -> Vec<String>;
36
37/// Information about a supported language for code analysis.
38pub struct LanguageInfo {
39    pub name: &'static str,
40    pub language: Language,
41    pub element_query: &'static str,
42    pub call_query: &'static str,
43    pub reference_query: Option<&'static str>,
44    pub import_query: Option<&'static str>,
45    pub impl_query: Option<&'static str>,
46    pub impl_trait_query: Option<&'static str>,
47    pub extract_function_name: Option<ExtractFunctionNameHandler>,
48    pub find_method_for_receiver: Option<FindMethodForReceiverHandler>,
49    pub find_receiver_type: Option<FindReceiverTypeHandler>,
50    pub extract_inheritance: Option<ExtractInheritanceHandler>,
51}
52
53/// Get language information by language name.
54#[allow(clippy::too_many_lines)] // exhaustive match over all supported languages; splitting harms readability
55pub fn get_language_info(lang_name: &str) -> Option<LanguageInfo> {
56    match lang_name {
57        #[cfg(feature = "lang-rust")]
58        "rust" => Some(LanguageInfo {
59            name: "rust",
60            language: tree_sitter_rust::LANGUAGE.into(),
61            element_query: rust::ELEMENT_QUERY,
62            call_query: rust::CALL_QUERY,
63            reference_query: Some(rust::REFERENCE_QUERY),
64            import_query: Some(rust::IMPORT_QUERY),
65            impl_query: Some(rust::IMPL_QUERY),
66            impl_trait_query: Some(rust::IMPL_TRAIT_QUERY),
67            extract_function_name: Some(rust::extract_function_name),
68            find_method_for_receiver: Some(rust::find_method_for_receiver),
69            find_receiver_type: Some(rust::find_receiver_type),
70            extract_inheritance: Some(rust::extract_inheritance),
71        }),
72        #[cfg(feature = "lang-python")]
73        "python" => Some(LanguageInfo {
74            name: "python",
75            language: tree_sitter_python::LANGUAGE.into(),
76            element_query: python::ELEMENT_QUERY,
77            call_query: python::CALL_QUERY,
78            reference_query: Some(python::REFERENCE_QUERY),
79            import_query: Some(python::IMPORT_QUERY),
80            impl_query: None,
81            impl_trait_query: None,
82            extract_function_name: None,
83            find_method_for_receiver: None,
84            find_receiver_type: None,
85            extract_inheritance: Some(python::extract_inheritance),
86        }),
87        #[cfg(feature = "lang-typescript")]
88        "typescript" => Some(LanguageInfo {
89            name: "typescript",
90            language: tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into(),
91            element_query: typescript::ELEMENT_QUERY,
92            call_query: typescript::CALL_QUERY,
93            reference_query: Some(typescript::REFERENCE_QUERY),
94            import_query: Some(typescript::IMPORT_QUERY),
95            impl_query: None,
96            impl_trait_query: None,
97            extract_function_name: None,
98            find_method_for_receiver: None,
99            find_receiver_type: None,
100            extract_inheritance: Some(typescript::extract_inheritance),
101        }),
102        #[cfg(feature = "lang-tsx")]
103        "tsx" => Some(LanguageInfo {
104            name: "tsx",
105            language: tree_sitter_typescript::LANGUAGE_TSX.into(),
106            element_query: typescript::ELEMENT_QUERY,
107            call_query: typescript::CALL_QUERY,
108            reference_query: Some(typescript::REFERENCE_QUERY),
109            import_query: Some(typescript::IMPORT_QUERY),
110            impl_query: None,
111            impl_trait_query: None,
112            extract_function_name: None,
113            find_method_for_receiver: None,
114            find_receiver_type: None,
115            extract_inheritance: Some(typescript::extract_inheritance),
116        }),
117        #[cfg(feature = "lang-go")]
118        "go" => Some(LanguageInfo {
119            name: "go",
120            language: tree_sitter_go::LANGUAGE.into(),
121            element_query: go::ELEMENT_QUERY,
122            call_query: go::CALL_QUERY,
123            reference_query: Some(go::REFERENCE_QUERY),
124            import_query: Some(go::IMPORT_QUERY),
125            impl_query: None,
126            impl_trait_query: None,
127            extract_function_name: None,
128            find_method_for_receiver: Some(go::find_method_for_receiver),
129            find_receiver_type: None,
130            extract_inheritance: Some(go::extract_inheritance),
131        }),
132        #[cfg(feature = "lang-java")]
133        "java" => Some(LanguageInfo {
134            name: "java",
135            language: tree_sitter_java::LANGUAGE.into(),
136            element_query: java::ELEMENT_QUERY,
137            call_query: java::CALL_QUERY,
138            reference_query: Some(java::REFERENCE_QUERY),
139            import_query: Some(java::IMPORT_QUERY),
140            impl_query: None,
141            impl_trait_query: None,
142            extract_function_name: None,
143            find_method_for_receiver: None,
144            find_receiver_type: None,
145            extract_inheritance: Some(java::extract_inheritance),
146        }),
147        #[cfg(feature = "lang-fortran")]
148        "fortran" => Some(LanguageInfo {
149            name: "fortran",
150            language: tree_sitter_fortran::LANGUAGE.into(),
151            element_query: fortran::ELEMENT_QUERY,
152            call_query: fortran::CALL_QUERY,
153            reference_query: Some(fortran::REFERENCE_QUERY),
154            import_query: Some(fortran::IMPORT_QUERY),
155            impl_query: None,
156            impl_trait_query: None,
157            extract_function_name: None,
158            find_method_for_receiver: None,
159            find_receiver_type: None,
160            extract_inheritance: Some(fortran::extract_inheritance),
161        }),
162        _ => None,
163    }
164}
165
166/// Get the tree-sitter Language object for a given language name.
167///
168/// Returns `None` if the language is not supported or not compiled in.
169#[must_use]
170pub fn get_ts_language(lang_name: &str) -> Option<Language> {
171    match lang_name {
172        #[cfg(feature = "lang-rust")]
173        "rust" => Some(tree_sitter_rust::LANGUAGE.into()),
174        #[cfg(feature = "lang-python")]
175        "python" => Some(tree_sitter_python::LANGUAGE.into()),
176        #[cfg(feature = "lang-typescript")]
177        "typescript" => Some(tree_sitter_typescript::LANGUAGE_TYPESCRIPT.into()),
178        #[cfg(feature = "lang-tsx")]
179        "tsx" => Some(tree_sitter_typescript::LANGUAGE_TSX.into()),
180        #[cfg(feature = "lang-go")]
181        "go" => Some(tree_sitter_go::LANGUAGE.into()),
182        #[cfg(feature = "lang-java")]
183        "java" => Some(tree_sitter_java::LANGUAGE.into()),
184        #[cfg(feature = "lang-fortran")]
185        "fortran" => Some(tree_sitter_fortran::LANGUAGE.into()),
186        _ => None,
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_get_language_info_known() {
196        // Happy path: known languages return Some
197        assert!(
198            get_language_info("rust").is_some(),
199            "expected Some for 'rust'"
200        );
201        assert!(get_language_info("go").is_some(), "expected Some for 'go'");
202        assert!(
203            get_language_info("python").is_some(),
204            "expected Some for 'python'"
205        );
206    }
207
208    #[test]
209    fn test_get_language_info_unknown() {
210        // Edge case: unknown language returns None
211        assert!(
212            get_language_info("cobol").is_none(),
213            "expected None for 'cobol'"
214        );
215    }
216
217    #[test]
218    fn test_get_ts_language_known() {
219        // Happy path: known language returns Some
220        assert!(
221            get_ts_language("rust").is_some(),
222            "expected Some for 'rust'"
223        );
224    }
225
226    #[test]
227    fn test_get_ts_language_unknown() {
228        // Edge case: unknown language returns None
229        assert!(
230            get_ts_language("cobol").is_none(),
231            "expected None for 'cobol'"
232        );
233    }
234}