Skip to main content

comindly_core/lang/
java.rs

1//! Java syntax adapter (tree-sitter-java). Packages come from the `package` declaration.
2
3use tree_sitter::Node;
4
5use super::{field_text, line, named_children, spring, text, LangSpec, RawContract, Target};
6use crate::facts::{Import, ImportItem, RefKind, TypeSource, ValueExpr};
7use crate::model::SymbolKind;
8
9pub struct Java;
10
11impl LangSpec for Java {
12    fn receiver(&self) -> &'static str {
13        "this"
14    }
15
16    fn package(&self, root: Node, src: &[u8]) -> Option<String> {
17        let decl = named_children(root)
18            .into_iter()
19            .find(|n| n.kind() == "package_declaration")?;
20        named_children(decl)
21            .into_iter()
22            .find(|n| matches!(n.kind(), "scoped_identifier" | "identifier"))
23            .map(|n| text(n, src).to_string())
24    }
25
26    fn def(&self, node: Node, src: &[u8], _in_class: bool) -> Option<(SymbolKind, String)> {
27        let kind = match node.kind() {
28            "class_declaration" | "record_declaration" => SymbolKind::Class,
29            "interface_declaration" | "annotation_type_declaration" => SymbolKind::Interface,
30            "enum_declaration" => SymbolKind::Enum,
31            "method_declaration" => SymbolKind::Method,
32            "enum_constant" => SymbolKind::Constant,
33            _ => return None,
34        };
35        Some((kind, field_text(node, "name", src)?.to_string()))
36    }
37
38    fn variable(&self, _node: Node, _src: &[u8]) -> Option<String> {
39        None
40    }
41
42    fn contract(&self, node: Node, src: &[u8]) -> Option<RawContract> {
43        if let Some(r) = spring::route(node, src) {
44            return Some(r);
45        }
46        if node.kind() != "method_invocation" {
47            return None;
48        }
49        let method = field_text(node, "name", src)?;
50        if method == "uri" {
51            let inner = node
52                .child_by_field_name("object")
53                .filter(|o| o.kind() == "method_invocation")?;
54            let verb = spring::webclient_verb(field_text(inner, "name", src)?)?;
55            return Some(RawContract::Op {
56                receiver: vec![spring::WEBCLIENT.into()],
57                method: verb,
58                args: spring::args(node.child_by_field_name("arguments")?, src),
59            });
60        }
61        if !spring::REST_TEMPLATE.iter().any(|(m, _)| *m == method) {
62            return None;
63        }
64        Some(RawContract::Op {
65            receiver: node
66                .child_by_field_name("object")
67                .and_then(|o| path(o, src))
68                .unwrap_or_default(),
69            method: method.to_string(),
70            args: spring::args(node.child_by_field_name("arguments")?, src),
71        })
72    }
73
74    fn value(&self, node: Node, src: &[u8]) -> Option<(String, ValueExpr)> {
75        if !matches!(
76            node.kind(),
77            "field_declaration" | "local_variable_declaration" | "constant_declaration"
78        ) {
79            return None;
80        }
81        let d = node.child_by_field_name("declarator")?;
82        Some((
83            field_text(d, "name", src)?.to_string(),
84            spring::value(d.child_by_field_name("value")?, src)?,
85        ))
86    }
87
88    fn import(&self, node: Node, src: &[u8]) -> Option<Vec<Import>> {
89        if node.kind() != "import_declaration" {
90            return None;
91        }
92        let kids = named_children(node);
93        let full = kids
94            .iter()
95            .find(|n| matches!(n.kind(), "scoped_identifier" | "identifier"))
96            .map(|n| text(*n, src).to_string())?;
97        let all = kids.iter().any(|n| n.kind() == "asterisk");
98        let (module, items) = if all {
99            (full, vec![ImportItem::All])
100        } else {
101            let (m, name) = full.rsplit_once('.')?;
102            (
103                m.to_string(),
104                vec![ImportItem::Name {
105                    name: name.to_string(),
106                    alias: None,
107                }],
108            )
109        };
110        Some(vec![Import {
111            module,
112            level: 0,
113            items,
114            line: line(node),
115            file: None,
116        }])
117    }
118
119    fn reference(&self, node: Node, src: &[u8]) -> Option<(RefKind, Vec<String>)> {
120        match node.kind() {
121            "method_invocation" => {
122                let name = field_text(node, "name", src)?.to_string();
123                let mut p = match node.child_by_field_name("object") {
124                    Some(o) => path(o, src)?,
125                    None => Vec::new(),
126                };
127                p.push(name);
128                Some((RefKind::Call, p))
129            }
130            "object_creation_expression" => Some((
131                RefKind::New,
132                type_path(node.child_by_field_name("type")?, src)?,
133            )),
134            _ => None,
135        }
136    }
137
138    fn assignment(&self, node: Node, src: &[u8]) -> Option<(Target, TypeSource)> {
139        let local = match node.kind() {
140            "field_declaration" => false,
141            "local_variable_declaration" => true,
142            _ => return None,
143        };
144        let decl = node.child_by_field_name("declarator")?;
145        let name = field_text(decl, "name", src)?.to_string();
146        let target = if local {
147            Target::Var(name)
148        } else {
149            Target::Attr(name)
150        };
151        let ty = node.child_by_field_name("type")?;
152        if text(ty, src) != "var" {
153            return Some((target, TypeSource::Annotation(type_path(ty, src)?)));
154        }
155        let v = decl.child_by_field_name("value")?;
156        let source = match v.kind() {
157            "object_creation_expression" => {
158                TypeSource::Construct(type_path(v.child_by_field_name("type")?, src)?)
159            }
160            "method_invocation" => TypeSource::Construct(self.reference(v, src)?.1),
161            _ => return None,
162        };
163        Some((target, source))
164    }
165
166    fn bases(&self, def: Node, src: &[u8]) -> Vec<(RefKind, Vec<String>)> {
167        let mut out = Vec::new();
168        let types = |n: Node| -> Vec<Vec<String>> {
169            let list = named_children(n)
170                .into_iter()
171                .find(|c| c.kind() == "type_list")
172                .map(named_children)
173                .unwrap_or_else(|| named_children(n));
174            list.into_iter().filter_map(|t| type_path(t, src)).collect()
175        };
176        for c in named_children(def) {
177            let kind = match c.kind() {
178                "superclass" | "extends_interfaces" => RefKind::Inherits,
179                "super_interfaces" => RefKind::Implements,
180                _ => continue,
181            };
182            out.extend(types(c).into_iter().map(|p| (kind, p)));
183        }
184        out
185    }
186
187    fn returns(&self, def: Node, src: &[u8]) -> Option<Vec<String>> {
188        type_path(def.child_by_field_name("type")?, src)
189    }
190
191    fn params(&self, def: Node, src: &[u8]) -> Vec<(String, Vec<String>, bool)> {
192        let Some(ps) = def.child_by_field_name("parameters") else {
193            return Vec::new();
194        };
195        named_children(ps)
196            .into_iter()
197            .filter(|p| p.kind() == "formal_parameter")
198            .filter_map(|p| {
199                Some((
200                    field_text(p, "name", src)?.to_string(),
201                    type_path(p.child_by_field_name("type")?, src)?,
202                    false,
203                ))
204            })
205            .collect()
206    }
207}
208
209fn path(node: Node, src: &[u8]) -> Option<Vec<String>> {
210    match node.kind() {
211        "identifier" | "type_identifier" => Some(vec![text(node, src).to_string()]),
212        "this" => Some(vec!["this".into()]),
213        "field_access" => {
214            let mut p = path(node.child_by_field_name("object")?, src)?;
215            p.push(field_text(node, "field", src)?.to_string());
216            Some(p)
217        }
218        "scoped_identifier" => {
219            let mut p = path(node.child_by_field_name("scope")?, src)?;
220            p.push(field_text(node, "name", src)?.to_string());
221            Some(p)
222        }
223        _ => None,
224    }
225}
226
227fn type_path(node: Node, src: &[u8]) -> Option<Vec<String>> {
228    match node.kind() {
229        "type_identifier" | "identifier" => Some(vec![text(node, src).to_string()]),
230        "scoped_type_identifier" => {
231            let mut out = Vec::new();
232            for c in named_children(node) {
233                out.extend(type_path(c, src)?);
234            }
235            (!out.is_empty()).then_some(out)
236        }
237        "generic_type" => type_path(named_children(node).into_iter().next()?, src),
238        _ => None,
239    }
240}