1use crate::code::node_source;
2use crate::code::File;
3use crate::lang::Language;
4use tree_sitter::Node;
5
6const CLASS_QUERY: &str = r#"
7[
8 (class_declaration
9 name: (identifier) @name)
10 (interface_declaration
11 name: (identifier) @name)
12] @definition.class
13"#;
14
15const FUNCTION_DECLARATION_QUERY: &str = r#"
16[
17 (method_declaration
18 name: (identifier) @name
19 parameters: (_) @parameters)
20 (constructor_declaration
21 name: (identifier) @name
22 parameters: (_) @parameters)
23] @definition.function
24"#;
25
26const FIELD_QUERY: &str = r#"
27(field_declaration
28 declarator: (variable_declarator
29 name: (identifier) @name)) @field
30"#;
31
32pub struct Java {
33 pub class_query: tree_sitter::Query,
34 pub function_declaration_query: tree_sitter::Query,
35 pub field_query: tree_sitter::Query,
36}
37
38impl Java {
39 pub const SELF: &'static str = "this";
40 pub const BINARY: &'static str = "binary_expression";
41 pub const BLOCK: &'static str = "block";
42 pub const BREAK: &'static str = "break_statement";
43 pub const CATCH: &'static str = "catch_clause";
44 pub const CASE: &'static str = "switch_block_statement_group";
45 pub const LINE_COMMENT: &'static str = "line_comment";
46 pub const BLOCK_COMMENT: &'static str = "block_comment";
47 pub const CONTINUE: &'static str = "continue_statement";
48 pub const DO: &'static str = "do_statement";
49 pub const FIELD_ACCESS: &'static str = "field_access";
50 pub const FIELD_DECLARATION: &'static str = "field_declaration";
51 pub const FOR_IN: &'static str = "enhanced_for_statement";
52 pub const FOR: &'static str = "for_statement";
53 pub const METHOD_DECLARATION: &'static str = "method_declaration";
54 pub const METHOD_INVOCATION: &'static str = "method_invocation";
55 pub const IDENTIFIER: &'static str = "identifier";
56 pub const IF: &'static str = "if_statement";
57 pub const LAMBDA: &'static str = "lambda_expression";
58 pub const PROGRAM: &'static str = "program";
59 pub const RETURN: &'static str = "return_statement";
60 pub const STRING: &'static str = "string_literal";
61 pub const SWITCH: &'static str = "switch_expression";
62 pub const TEMPLATE_STRING: &'static str = "template_expression";
63 pub const TERNARY: &'static str = "ternary_expression";
64 pub const TRY: &'static str = "try_statement";
65 pub const TRY_WITH_RESOURCES: &'static str = "try_with_resources_statement";
66 pub const WHILE: &'static str = "while_statement";
67
68 pub const AND: &'static str = "&&";
69 pub const OR: &'static str = "||";
70}
71
72impl Default for Java {
73 fn default() -> Self {
74 let language = tree_sitter_java::language();
75
76 Self {
77 class_query: tree_sitter::Query::new(&language, CLASS_QUERY).unwrap(),
78 field_query: tree_sitter::Query::new(&language, FIELD_QUERY).unwrap(),
79 function_declaration_query: tree_sitter::Query::new(
80 &language,
81 FUNCTION_DECLARATION_QUERY,
82 )
83 .unwrap(),
84 }
85 }
86}
87
88impl Language for Java {
89 fn name(&self) -> &str {
90 "java"
91 }
92
93 fn self_keyword(&self) -> Option<&str> {
94 Some(Self::SELF)
95 }
96
97 fn class_query(&self) -> &tree_sitter::Query {
98 &self.class_query
99 }
100
101 fn function_declaration_query(&self) -> &tree_sitter::Query {
102 &self.function_declaration_query
103 }
104
105 fn field_query(&self) -> &tree_sitter::Query {
106 &self.field_query
107 }
108
109 fn if_nodes(&self) -> Vec<&str> {
110 vec![Self::IF]
111 }
112
113 fn block_nodes(&self) -> Vec<&str> {
114 vec![Self::BLOCK]
115 }
116
117 fn conditional_assignment_nodes(&self) -> Vec<&str> {
118 vec![]
119 }
120
121 fn invisible_container_nodes(&self) -> Vec<&str> {
122 vec![Self::PROGRAM]
123 }
124
125 fn switch_nodes(&self) -> Vec<&str> {
126 vec![Self::SWITCH]
127 }
128
129 fn case_nodes(&self) -> Vec<&str> {
130 vec![Self::CASE]
131 }
132
133 fn ternary_nodes(&self) -> Vec<&str> {
134 vec![Self::TERNARY]
135 }
136
137 fn loop_nodes(&self) -> Vec<&str> {
138 vec![Self::FOR, Self::FOR_IN, Self::WHILE, Self::DO]
139 }
140
141 fn except_nodes(&self) -> Vec<&str> {
142 vec![Self::CATCH]
143 }
144
145 fn try_expression_nodes(&self) -> Vec<&str> {
146 vec![Self::TRY, Self::TRY_WITH_RESOURCES]
147 }
148
149 fn jump_nodes(&self) -> Vec<&str> {
150 vec![Self::BREAK, Self::CONTINUE]
151 }
152
153 fn return_nodes(&self) -> Vec<&str> {
154 vec![Self::RETURN]
155 }
156
157 fn binary_nodes(&self) -> Vec<&str> {
158 vec![Self::BINARY]
159 }
160
161 fn boolean_operator_nodes(&self) -> Vec<&str> {
162 vec![Self::AND, Self::OR]
163 }
164
165 fn field_nodes(&self) -> Vec<&str> {
166 vec![Self::FIELD_DECLARATION]
167 }
168
169 fn call_nodes(&self) -> Vec<&str> {
170 vec![Self::METHOD_INVOCATION]
171 }
172
173 fn function_nodes(&self) -> Vec<&str> {
174 vec![Self::METHOD_DECLARATION]
175 }
176
177 fn closure_nodes(&self) -> Vec<&str> {
178 vec![Self::LAMBDA]
179 }
180
181 fn comment_nodes(&self) -> Vec<&str> {
182 vec![Self::LINE_COMMENT, Self::BLOCK_COMMENT]
183 }
184
185 fn string_nodes(&self) -> Vec<&str> {
186 vec![Self::STRING, Self::TEMPLATE_STRING]
187 }
188
189 fn is_jump_label(&self, node: &Node) -> bool {
190 node.kind() == Self::IDENTIFIER
191 }
192
193 fn has_labeled_jumps(&self) -> bool {
194 true
195 }
196
197 fn call_identifiers(&self, source_file: &File, node: &Node) -> (Option<String>, String) {
198 match node.kind() {
199 Self::METHOD_INVOCATION => {
200 let (receiver, object) = self.field_identifiers(source_file, node);
201
202 (Some(receiver), object)
203 }
204 _ => (Some("<UNKNOWN>".to_string()), "<UNKNOWN>".to_string()),
205 }
206 }
207
208 fn field_identifiers(&self, source_file: &File, node: &Node) -> (String, String) {
209 let object_node = node.child_by_field_name("object");
210 let property_node = node
211 .child_by_field_name("name")
212 .or_else(|| node.child_by_field_name("field"));
213
214 match (&object_node, &property_node) {
215 (Some(obj), Some(prop)) if obj.kind() == Self::FIELD_ACCESS => {
216 let object_source =
217 get_node_source_or_default(obj.child_by_field_name("field"), source_file);
218 let property_source = get_node_source_or_default(Some(*prop), source_file);
219 (object_source, property_source)
220 }
221 (Some(obj), Some(prop)) => (
222 get_node_source_or_default(Some(*obj), source_file),
223 get_node_source_or_default(Some(*prop), source_file),
224 ),
225 (None, Some(prop)) => (
226 Self::SELF.to_owned(),
227 get_node_source_or_default(Some(*prop), source_file),
228 ),
229 _ => ("<UNKNOWN>".to_string(), "<UNKNOWN>".to_string()),
230 }
231 }
232
233 fn tree_sitter_language(&self) -> tree_sitter::Language {
234 tree_sitter_java::language()
235 }
236}
237
238fn get_node_source_or_default(node: Option<Node>, source_file: &File) -> String {
239 node.as_ref()
240 .map(|n| node_source(n, source_file))
241 .unwrap_or("<UNKNOWN>".to_string())
242}
243
244#[cfg(test)]
245mod test {
246 use super::*;
247 use std::collections::HashSet;
248 use tree_sitter::Tree;
249
250 #[test]
251 fn mutually_exclusive() {
252 let lang = Java::default();
253 let mut kinds: Vec<&str> = vec![];
254
255 kinds.extend(lang.if_nodes());
256 kinds.extend(lang.conditional_assignment_nodes());
257 kinds.extend(lang.switch_nodes());
258 kinds.extend(lang.case_nodes());
259 kinds.extend(lang.ternary_nodes());
260 kinds.extend(lang.loop_nodes());
261 kinds.extend(lang.except_nodes());
262 kinds.extend(lang.try_expression_nodes());
263 kinds.extend(lang.jump_nodes());
264 kinds.extend(lang.return_nodes());
265 kinds.extend(lang.binary_nodes());
266 kinds.extend(lang.field_nodes());
267 kinds.extend(lang.call_nodes());
268 kinds.extend(lang.function_nodes());
269 kinds.extend(lang.closure_nodes());
270 kinds.extend(lang.comment_nodes());
271 kinds.extend(lang.string_nodes());
272 kinds.extend(lang.boolean_operator_nodes());
273 kinds.extend(lang.block_nodes());
274
275 let unique: HashSet<_> = kinds.iter().cloned().collect();
276 assert_eq!(unique.len(), kinds.len());
277 }
278
279 #[test]
280 fn field_identifier_read() {
281 let source_file = File::from_string("java", "this.foo;");
282 let tree = source_file.parse();
283 let root_node = tree.root_node();
284 let expression = root_node.named_child(0).unwrap();
285 let field = expression.named_child(0).unwrap();
286 let language = Java::default();
287
288 assert_eq!(
289 language.field_identifiers(&source_file, &field),
290 ("this".to_string(), "foo".to_string())
291 );
292 }
293
294 #[test]
295 fn field_identifier_write() {
296 let source_file = File::from_string("java", "this.foo = 1;");
297 let tree = source_file.parse();
298 let root_node = tree.root_node();
299 let expression = root_node.named_child(0).unwrap();
300 let assignment = expression.named_child(0).unwrap();
301 let field = assignment.named_child(0).unwrap();
302 let language = Java::default();
303
304 assert_eq!(
305 language.field_identifiers(&source_file, &field),
306 ("this".to_string(), "foo".to_string())
307 );
308 }
309
310 #[test]
311 fn field_identifier_collaborator() {
312 let source_file = File::from_string("java", "other.foo;");
313 let tree = source_file.parse();
314 let root_node = tree.root_node();
315 let expression = root_node.named_child(0).unwrap();
316 let field = expression.named_child(0).unwrap();
317 let language = Java::default();
318
319 assert_eq!(
320 language.field_identifiers(&source_file, &field),
321 ("other".to_string(), "foo".to_string())
322 );
323 }
324
325 #[test]
326 fn call_identifier() {
327 let source_file = File::from_string("java", "foo()");
328 let tree = source_file.parse();
329 let call = call_node(&tree);
330 let language = Java::default();
331
332 assert_eq!(
333 language.call_identifiers(&source_file, &call),
334 (Some("this".to_string()), "foo".to_string())
335 );
336 }
337
338 #[test]
339 fn call_member() {
340 let source_file = File::from_string("java", "foo.bar()");
341 let tree = source_file.parse();
342 let call = call_node(&tree);
343 let language = Java::default();
344
345 assert_eq!(
346 language.call_identifiers(&source_file, &call),
347 (Some("foo".into()), "bar".into())
348 );
349 }
350
351 #[test]
352 fn call_with_custom_context() {
353 let source_file = File::from_string("java", "foo.call(context);");
354 let tree = source_file.parse();
355 let call = call_node(&tree);
356 let language = Java::default();
357
358 assert_eq!(
359 language.call_identifiers(&source_file, &call),
360 (Some("foo".to_string()), "call".to_string())
361 );
362 }
363
364 #[test]
365 fn method_call_on_nested_object() {
366 let source_file = File::from_string("java", "obj.nestedObj.foo();");
367 let tree = source_file.parse();
368 let call = call_node(&tree);
369 let language = Java::default();
370
371 assert_eq!(
372 language.call_identifiers(&source_file, &call),
373 (Some("nestedObj".to_string()), "foo".to_string())
374 );
375 }
376
377 #[test]
378 fn nested_field_access() {
379 let source_file = File::from_string("java", "obj.nestedObj.oneMoreObj;");
380 let tree = source_file.parse();
381 let root_node = tree.root_node();
382 let expression = root_node.named_child(0).unwrap();
383 let field = expression.named_child(0).unwrap();
384 let language = Java::default();
385
386 assert_eq!(
387 language.field_identifiers(&source_file, &field),
388 ("nestedObj".to_string(), "oneMoreObj".to_string())
389 );
390 }
391
392 #[test]
393 fn call_function_property() {
394 let source_file = File::from_string("java", "foo.bar()");
395 let tree = source_file.parse();
396 let call = call_node(&tree);
397 let language = Java::default();
398
399 assert_eq!(
400 language.call_identifiers(&source_file, &call),
401 (Some("foo".to_string()), "bar".to_string())
402 );
403 }
404
405 fn call_node(tree: &Tree) -> Node {
406 let root_node = tree.root_node();
407 let expression = root_node.named_child(0).unwrap();
408 expression.named_child(0).unwrap()
409 }
410}