1use std::{
2 collections::HashMap,
3 path::{Path, PathBuf},
4};
5
6use gitcortex_core::{
7 error::{GitCortexError, Result},
8 graph::{Edge, Node, NodeId, NodeMetadata, Span},
9 schema::{EdgeConfidence, EdgeKind, NodeKind, Visibility},
10};
11use tree_sitter::{Node as TsNode, Parser};
12
13use super::{capture_definition, LanguageParser, ParseResult};
14
15pub struct JavaParser {
16 language: tree_sitter::Language,
17}
18
19impl JavaParser {
20 pub fn new() -> Self {
21 Self {
22 language: tree_sitter_java::LANGUAGE.into(),
23 }
24 }
25}
26
27impl Default for JavaParser {
28 fn default() -> Self {
29 Self::new()
30 }
31}
32
33impl LanguageParser for JavaParser {
34 fn extensions(&self) -> &[&str] {
35 &["java"]
36 }
37
38 fn parse(&self, path: &Path, source: &str) -> Result<ParseResult> {
39 let mut parser = Parser::new();
40 parser
41 .set_language(&self.language)
42 .map_err(|e| GitCortexError::Parse {
43 file: path.to_owned(),
44 message: e.to_string(),
45 })?;
46
47 let tree = parser
48 .parse(source, None)
49 .ok_or_else(|| GitCortexError::Parse {
50 file: path.to_owned(),
51 message: "tree-sitter returned no parse tree".into(),
52 })?;
53
54 let mut visitor = FileVisitor::new(path, source);
55 visitor.collect_names(tree.root_node());
56 visitor.visit_program(tree.root_node());
57 visitor.collect_imports(tree.root_node());
58
59 Ok(ParseResult {
60 nodes: visitor.nodes,
61 edges: visitor.edges,
62 deferred_calls: visitor.deferred_calls,
63 deferred_uses: visitor.deferred_uses,
64 deferred_implements: visitor.deferred_implements,
65 deferred_imports: visitor.deferred_imports,
66 deferred_inherits: visitor.deferred_inherits,
67 deferred_throws: visitor.deferred_throws,
68 deferred_annotated: visitor.deferred_annotated,
69 })
70 }
71}
72
73struct FileVisitor<'src> {
76 source: &'src [u8],
77 file: PathBuf,
78 package_id: NodeId,
80 nodes: Vec<Node>,
81 edges: Vec<Edge>,
82 type_index: HashMap<String, NodeId>,
84 fn_index: HashMap<String, NodeId>,
86 deferred_calls: Vec<(NodeId, String, u32)>,
87 deferred_uses: Vec<(NodeId, String)>,
88 deferred_implements: Vec<(NodeId, String)>,
89 deferred_imports: Vec<(NodeId, String)>,
90 deferred_inherits: Vec<(NodeId, String)>,
91 deferred_throws: Vec<(NodeId, String)>,
92 deferred_annotated: Vec<(NodeId, String)>,
93}
94
95impl<'src> FileVisitor<'src> {
96 fn new(file: &Path, source: &'src str) -> Self {
97 let package_id = NodeId::new();
98 let unit_name = file
100 .file_stem()
101 .and_then(|s| s.to_str())
102 .unwrap_or("Unknown")
103 .to_owned();
104 let package_node = Node {
105 id: package_id.clone(),
106 qualified_name: unit_name.clone(),
107 kind: NodeKind::Module,
108 name: unit_name,
109 file: file.to_owned(),
110 span: Span {
111 start_line: 1,
112 end_line: 1,
113 },
114 metadata: NodeMetadata {
115 loc: source.lines().count() as u32,
116 visibility: Visibility::Pub,
117 is_async: false,
118 is_unsafe: false,
119 ..Default::default()
120 },
121 };
122 let nodes = vec![package_node];
123 Self {
124 source: source.as_bytes(),
125 file: file.to_owned(),
126 package_id,
127 nodes,
128 edges: Vec::new(),
129 type_index: HashMap::new(),
130 fn_index: HashMap::new(),
131 deferred_calls: Vec::new(),
132 deferred_uses: Vec::new(),
133 deferred_implements: Vec::new(),
134 deferred_imports: Vec::new(),
135 deferred_inherits: Vec::new(),
136 deferred_throws: Vec::new(),
137 deferred_annotated: Vec::new(),
138 }
139 }
140
141 fn text<'t>(&self, node: TsNode<'t>) -> &'src str {
142 node.utf8_text(self.source).unwrap_or("")
143 }
144
145 fn span(node: TsNode<'_>) -> Span {
146 Span {
147 start_line: node.start_position().row as u32 + 1,
148 end_line: node.end_position().row as u32 + 1,
149 }
150 }
151
152 fn visibility(node: TsNode<'_>, source: &[u8]) -> Visibility {
153 let mut cursor = node.walk();
154 for child in node.children(&mut cursor) {
155 if child.kind() == "modifiers" {
156 let text = child.utf8_text(source).unwrap_or("");
157 if text.contains("public") {
158 return Visibility::Pub;
159 }
160 if text.contains("protected") {
161 return Visibility::PubCrate;
162 }
163 return Visibility::Private;
164 }
165 }
166 Visibility::PubCrate
168 }
169
170 fn is_async(_node: TsNode<'_>) -> bool {
171 false
173 }
174
175 fn modifiers_text<'t>(node: TsNode<'t>, source: &'t [u8]) -> &'t str {
177 let mut cursor = node.walk();
178 for child in node.children(&mut cursor) {
179 if child.kind() == "modifiers" {
180 return child.utf8_text(source).unwrap_or("");
181 }
182 }
183 ""
184 }
185
186 fn qualified(scope: &[String], name: &str) -> String {
187 if scope.is_empty() {
188 name.to_owned()
189 } else {
190 format!("{}.{name}", scope.join("."))
191 }
192 }
193
194 fn make_node(
195 &self,
196 id: NodeId,
197 kind: NodeKind,
198 name: String,
199 scope: &[String],
200 ts_node: TsNode<'_>,
201 ) -> Node {
202 let mods = Self::modifiers_text(ts_node, self.source);
203 Node {
204 id,
205 qualified_name: Self::qualified(scope, &name),
206 kind,
207 name,
208 file: self.file.clone(),
209 span: Self::span(ts_node),
210 metadata: NodeMetadata {
211 loc: (ts_node.end_position().row - ts_node.start_position().row + 1) as u32,
212 visibility: Self::visibility(ts_node, self.source),
213 is_async: Self::is_async(ts_node),
214 is_unsafe: false,
215 is_abstract: mods.contains("abstract"),
216 is_final: mods.contains("final"),
217 is_static: mods.contains("static"),
218 definition: capture_definition(self.source, ts_node),
219 ..Default::default()
220 },
221 }
222 }
223
224 fn collect_names(&mut self, node: TsNode<'_>) {
227 let mut cursor = node.walk();
228 let children: Vec<TsNode<'_>> = node.named_children(&mut cursor).collect();
229 for child in children {
230 match child.kind() {
231 "class_declaration"
232 | "interface_declaration"
233 | "enum_declaration"
234 | "annotation_type_declaration"
235 | "record_declaration" => {
236 if let Some(name_node) = child.child_by_field_name("name") {
237 let name = self.text(name_node).to_owned();
238 self.type_index.entry(name).or_default();
239 }
240 }
241 _ => {}
242 }
243 }
244 }
245
246 fn visit_program(&mut self, node: TsNode<'_>) {
249 let mut cursor = node.walk();
250 let children: Vec<TsNode<'_>> = node.named_children(&mut cursor).collect();
251 for child in children {
252 self.visit_top_level(child, &[]);
253 }
254 }
255
256 fn visit_top_level(&mut self, node: TsNode<'_>, scope: &[String]) {
257 match node.kind() {
258 "class_declaration" => self.visit_class(node, scope),
259 "interface_declaration" => self.visit_interface(node, scope),
260 "enum_declaration" => self.visit_enum(node, scope),
261 "record_declaration" => self.visit_record(node, scope),
262 _ => {}
263 }
264 }
265
266 fn visit_class(&mut self, node: TsNode<'_>, scope: &[String]) {
267 let Some(name_node) = node.child_by_field_name("name") else {
268 return;
269 };
270 let name = self.text(name_node).to_owned();
271 let id = self
272 .type_index
273 .get(&name)
274 .cloned()
275 .unwrap_or_else(NodeId::new);
276 let graph_node = self.make_node(id.clone(), NodeKind::Struct, name.clone(), scope, node);
277 self.nodes.push(graph_node);
278
279 if let Some(superclass) = node.child_by_field_name("superclass") {
281 let type_name = self.extract_simple_type(superclass);
282 if let Some(t) = type_name {
283 self.deferred_inherits.push((id.clone(), t));
284 }
285 }
286
287 if let Some(interfaces) = node.child_by_field_name("interfaces") {
289 let mut c = interfaces.walk();
290 let iface_children: Vec<TsNode<'_>> = interfaces.named_children(&mut c).collect();
291 for iface in iface_children {
292 if let Some(t) = self.extract_simple_type(iface) {
293 self.deferred_implements.push((id.clone(), t));
294 }
295 }
296 }
297
298 self.extract_annotation_uses(node, &id);
300
301 let mut class_scope = scope.to_vec();
302 class_scope.push(name.clone());
303
304 if let Some(body) = node.child_by_field_name("body") {
306 let mut c = body.walk();
307 let body_children: Vec<TsNode<'_>> = body.named_children(&mut c).collect();
308 for child in body_children {
309 match child.kind() {
310 "method_declaration" | "constructor_declaration" => {
311 self.visit_method(child, &class_scope, id.clone());
312 }
313 "class_declaration" => {
314 let nested_id = self.visit_class_nested(child, &class_scope);
315 if let Some(nid) = nested_id {
316 self.edges.push(Edge {
317 src: id.clone(),
318 dst: nid,
319 kind: EdgeKind::Contains,
320 line: None,
321 confidence: EdgeConfidence::Extracted,
322 });
323 }
324 }
325 "interface_declaration" => {
326 let nested_id = self.visit_interface_nested(child, &class_scope);
327 if let Some(nid) = nested_id {
328 self.edges.push(Edge {
329 src: id.clone(),
330 dst: nid,
331 kind: EdgeKind::Contains,
332 line: None,
333 confidence: EdgeConfidence::Extracted,
334 });
335 }
336 }
337 "record_declaration" => {
338 let nested_id = self.visit_record_nested(child, &class_scope);
339 if let Some(nid) = nested_id {
340 self.edges.push(Edge {
341 src: id.clone(),
342 dst: nid,
343 kind: EdgeKind::Contains,
344 line: None,
345 confidence: EdgeConfidence::Extracted,
346 });
347 }
348 }
349 "field_declaration" => {
350 self.extract_field_uses(child, &id);
351 }
352 _ => {}
353 }
354 }
355 }
356 }
357
358 fn visit_class_nested(&mut self, node: TsNode<'_>, scope: &[String]) -> Option<NodeId> {
360 let name_node = node.child_by_field_name("name")?;
361 let name = self.text(name_node).to_owned();
362 let id = self
363 .type_index
364 .get(&name)
365 .cloned()
366 .unwrap_or_else(NodeId::new);
367 let mut graph_node =
368 self.make_node(id.clone(), NodeKind::Struct, name.clone(), scope, node);
369 let mods = Self::modifiers_text(node, self.source);
371 graph_node.metadata.is_static = mods.contains("static");
372 self.nodes.push(graph_node);
373
374 if let Some(superclass) = node.child_by_field_name("superclass") {
375 if let Some(t) = self.extract_simple_type(superclass) {
376 self.deferred_inherits.push((id.clone(), t));
377 }
378 }
379 if let Some(interfaces) = node.child_by_field_name("interfaces") {
380 let mut c = interfaces.walk();
381 for iface in interfaces.named_children(&mut c).collect::<Vec<_>>() {
382 if let Some(t) = self.extract_simple_type(iface) {
383 self.deferred_implements.push((id.clone(), t));
384 }
385 }
386 }
387 self.extract_annotation_uses(node, &id);
388
389 let mut nested_scope = scope.to_vec();
390 nested_scope.push(name);
391 if let Some(body) = node.child_by_field_name("body") {
392 let mut c = body.walk();
393 for child in body.named_children(&mut c).collect::<Vec<_>>() {
394 if matches!(
395 child.kind(),
396 "method_declaration" | "constructor_declaration"
397 ) {
398 self.visit_method(child, &nested_scope, id.clone());
399 } else if child.kind() == "record_declaration" {
400 if let Some(nid) = self.visit_record_nested(child, &nested_scope) {
401 self.edges.push(Edge {
402 src: id.clone(),
403 dst: nid,
404 kind: EdgeKind::Contains,
405 line: None,
406 confidence: EdgeConfidence::Extracted,
407 });
408 }
409 } else if child.kind() == "field_declaration" {
410 self.extract_field_uses(child, &id);
411 }
412 }
413 }
414 Some(id)
415 }
416
417 fn visit_interface_nested(&mut self, node: TsNode<'_>, scope: &[String]) -> Option<NodeId> {
419 let name_node = node.child_by_field_name("name")?;
420 let name = self.text(name_node).to_owned();
421 let id = self
422 .type_index
423 .get(&name)
424 .cloned()
425 .unwrap_or_else(NodeId::new);
426 let is_functional = self.has_functional_interface_annotation(node);
427 let mut graph_node =
428 self.make_node(id.clone(), NodeKind::Interface, name.clone(), scope, node);
429 if is_functional {
430 graph_node.metadata.is_abstract = true;
431 }
432 self.nodes.push(graph_node);
433
434 if let Some(extends) = node.child_by_field_name("extends") {
435 let mut c = extends.walk();
436 for ext in extends.named_children(&mut c).collect::<Vec<_>>() {
437 if let Some(t) = self.extract_simple_type(ext) {
438 self.deferred_implements.push((id.clone(), t));
439 }
440 }
441 }
442 self.extract_annotation_uses(node, &id);
443
444 let mut nested_scope = scope.to_vec();
445 nested_scope.push(name);
446 if let Some(body) = node.child_by_field_name("body") {
447 let mut c = body.walk();
448 for child in body.named_children(&mut c).collect::<Vec<_>>() {
449 if matches!(child.kind(), "method_declaration" | "constant_declaration") {
450 self.visit_method(child, &nested_scope, id.clone());
451 }
452 }
453 }
454 Some(id)
455 }
456
457 fn visit_interface(&mut self, node: TsNode<'_>, scope: &[String]) {
458 let Some(name_node) = node.child_by_field_name("name") else {
459 return;
460 };
461 let name = self.text(name_node).to_owned();
462 let id = self
463 .type_index
464 .get(&name)
465 .cloned()
466 .unwrap_or_else(NodeId::new);
467 let is_functional = self.has_functional_interface_annotation(node);
468 let mut graph_node =
469 self.make_node(id.clone(), NodeKind::Interface, name.clone(), scope, node);
470 if is_functional {
471 graph_node.metadata.is_abstract = true;
472 }
473 self.nodes.push(graph_node);
474
475 self.extract_annotation_uses(node, &id);
477
478 if let Some(extends) = node.child_by_field_name("extends") {
480 let mut c = extends.walk();
481 let ext_children: Vec<TsNode<'_>> = extends.named_children(&mut c).collect();
482 for ext in ext_children {
483 if let Some(t) = self.extract_simple_type(ext) {
484 self.deferred_implements.push((id.clone(), t));
485 }
486 }
487 }
488
489 let mut iface_scope = scope.to_vec();
490 iface_scope.push(name.clone());
491
492 if let Some(body) = node.child_by_field_name("body") {
494 let mut c = body.walk();
495 let body_children: Vec<TsNode<'_>> = body.named_children(&mut c).collect();
496 for child in body_children {
497 if matches!(child.kind(), "method_declaration" | "constant_declaration") {
498 self.visit_method(child, &iface_scope, id.clone());
499 }
500 }
501 }
502 }
503
504 fn visit_enum(&mut self, node: TsNode<'_>, scope: &[String]) {
505 let Some(name_node) = node.child_by_field_name("name") else {
506 return;
507 };
508 let name = self.text(name_node).to_owned();
509 let id = self
510 .type_index
511 .get(&name)
512 .cloned()
513 .unwrap_or_else(NodeId::new);
514 let graph_node = self.make_node(id.clone(), NodeKind::Enum, name.clone(), scope, node);
515 self.nodes.push(graph_node);
516
517 if let Some(interfaces) = node.child_by_field_name("interfaces") {
519 let mut c = interfaces.walk();
520 let iface_children: Vec<TsNode<'_>> = interfaces.named_children(&mut c).collect();
521 for iface in iface_children {
522 if let Some(t) = self.extract_simple_type(iface) {
523 self.deferred_implements.push((id.clone(), t));
524 }
525 }
526 }
527
528 let mut enum_scope = scope.to_vec();
529 enum_scope.push(name.clone());
530
531 if let Some(body) = node.child_by_field_name("body") {
532 let mut c = body.walk();
533 let body_children: Vec<TsNode<'_>> = body.named_children(&mut c).collect();
534 for child in body_children {
535 if child.kind() == "method_declaration" {
536 self.visit_method(child, &enum_scope, id.clone());
537 }
538 }
539 }
540 }
541
542 fn visit_record(&mut self, node: TsNode<'_>, scope: &[String]) {
543 let Some(name_node) = node.child_by_field_name("name") else {
544 return;
545 };
546 let name = self.text(name_node).to_owned();
547 let id = self
549 .type_index
550 .get(&name)
551 .cloned()
552 .unwrap_or_else(NodeId::new);
553 let graph_node = self.make_node(id.clone(), NodeKind::Struct, name.clone(), scope, node);
554 self.nodes.push(graph_node);
555
556 let mut record_scope = scope.to_vec();
557 record_scope.push(name);
558
559 if let Some(body) = node.child_by_field_name("body") {
560 let mut c = body.walk();
561 let body_children: Vec<TsNode<'_>> = body.named_children(&mut c).collect();
562 for child in body_children {
563 if child.kind() == "method_declaration" {
564 self.visit_method(child, &record_scope, id.clone());
565 }
566 }
567 }
568 }
569
570 fn visit_record_nested(&mut self, node: TsNode<'_>, scope: &[String]) -> Option<NodeId> {
572 let name_node = node.child_by_field_name("name")?;
573 let name = self.text(name_node).to_owned();
574 let id = self
575 .type_index
576 .get(&name)
577 .cloned()
578 .unwrap_or_else(NodeId::new);
579 let graph_node = self.make_node(id.clone(), NodeKind::Struct, name.clone(), scope, node);
580 self.nodes.push(graph_node);
581
582 let mut record_scope = scope.to_vec();
583 record_scope.push(name);
584
585 if let Some(body) = node.child_by_field_name("body") {
586 let mut c = body.walk();
587 for child in body.named_children(&mut c).collect::<Vec<_>>() {
588 if child.kind() == "method_declaration" {
589 self.visit_method(child, &record_scope, id.clone());
590 }
591 }
592 }
593 Some(id)
594 }
595
596 fn visit_method(&mut self, node: TsNode<'_>, scope: &[String], container_id: NodeId) {
597 let Some(name_node) = node.child_by_field_name("name") else {
598 return;
599 };
600 let name = self.text(name_node).to_owned();
601 let id = self
602 .fn_index
603 .get(&name)
604 .cloned()
605 .unwrap_or_else(NodeId::new);
606 self.fn_index.insert(name.clone(), id.clone());
612 let kind = if node.kind() == "constructor_declaration" {
613 NodeKind::Function
614 } else {
615 NodeKind::Method
616 };
617 let mut graph_node = self.make_node(id.clone(), kind, name, scope, node);
618 if let Some(body) = node.child_by_field_name("body") {
619 graph_node.metadata.lld.complexity = Some(super::cyclomatic_complexity(
620 body,
621 &super::complexity::java_decision,
622 ));
623 }
624 self.edges.push(Edge {
625 src: container_id,
626 dst: id.clone(),
627 kind: EdgeKind::Contains,
628 line: None,
629 confidence: EdgeConfidence::Extracted,
630 });
631 self.nodes.push(graph_node);
632
633 if let Some(params) = node.child_by_field_name("parameters") {
635 let mut c = params.walk();
636 let param_list: Vec<TsNode<'_>> = params.named_children(&mut c).collect();
637 for param in param_list {
638 if param.kind() == "formal_parameter" || param.kind() == "spread_parameter" {
639 if let Some(type_node) = param.child_by_field_name("type") {
640 for tname in self.collect_type_names(type_node) {
641 self.deferred_uses.push((id.clone(), tname));
642 }
643 }
644 }
645 }
646 }
647
648 if let Some(ret) = node.child_by_field_name("type") {
650 for tname in self.collect_type_names(ret) {
651 self.deferred_uses.push((id.clone(), tname));
652 }
653 }
654
655 self.extract_annotation_uses(node, &id);
657
658 if let Some(throws) = node.child_by_field_name("throws") {
660 let mut c = throws.walk();
661 for exc in throws.named_children(&mut c).collect::<Vec<_>>() {
662 if let Some(t) = self.extract_simple_type(exc) {
663 self.deferred_throws.push((id.clone(), t));
664 }
665 }
666 }
667
668 if let Some(body) = node.child_by_field_name("body") {
670 self.collect_calls(body, &id);
671 }
672 }
673
674 fn collect_imports(&mut self, node: TsNode<'_>) {
677 let mut cursor = node.walk();
678 let children: Vec<TsNode<'_>> = node.named_children(&mut cursor).collect();
679 for child in children {
680 if child.kind() != "import_declaration" {
681 continue;
682 }
683 let raw = self.text(child);
686 let clean = raw
687 .trim_start_matches("import")
688 .trim_start_matches(" static")
689 .trim()
690 .trim_end_matches(';')
691 .trim();
692 let leaf = clean.split('.').next_back().unwrap_or(clean);
694 if leaf == "*" {
696 continue;
697 }
698 self.deferred_imports
699 .push((self.package_id.clone(), leaf.to_owned()));
700 }
701 }
702
703 fn extract_annotation_uses(&mut self, node: TsNode<'_>, node_id: &NodeId) {
707 let mut c = node.walk();
708 let children: Vec<TsNode<'_>> = node.named_children(&mut c).collect();
709 for child in children {
710 if child.kind() == "modifiers" {
711 let mut cc = child.walk();
712 let mod_children: Vec<TsNode<'_>> = child.named_children(&mut cc).collect();
713 for mc in mod_children {
714 if mc.kind() == "annotation" || mc.kind() == "marker_annotation" {
715 let mut ccc = mc.walk();
717 let ann_children: Vec<TsNode<'_>> = mc.named_children(&mut ccc).collect();
718 if let Some(ann_name_node) = ann_children.first() {
719 let ann_name = self.text(*ann_name_node).to_owned();
720 if !ann_name.is_empty() {
721 self.deferred_annotated.push((node_id.clone(), ann_name));
722 }
723 }
724 }
725 }
726 }
727 }
728 }
729
730 fn has_functional_interface_annotation(&self, node: TsNode<'_>) -> bool {
732 let mut c = node.walk();
733 for child in node.named_children(&mut c).collect::<Vec<_>>() {
734 if child.kind() == "modifiers" {
735 let mut cc = child.walk();
736 for mc in child.named_children(&mut cc).collect::<Vec<_>>() {
737 if mc.kind() == "annotation" || mc.kind() == "marker_annotation" {
738 let mut ccc = mc.walk();
739 if let Some(ann) = mc.named_children(&mut ccc).collect::<Vec<_>>().first() {
740 if self.text(*ann) == "FunctionalInterface" {
741 return true;
742 }
743 }
744 }
745 }
746 }
747 }
748 false
749 }
750
751 fn extract_field_uses(&mut self, field_decl: TsNode<'_>, container_id: &NodeId) {
753 if let Some(type_node) = field_decl.child_by_field_name("type") {
754 for tname in self.collect_type_names(type_node) {
755 self.deferred_uses.push((container_id.clone(), tname));
756 }
757 }
758 }
759
760 fn extract_simple_type(&self, node: TsNode<'_>) -> Option<String> {
762 match node.kind() {
763 "type_identifier" | "identifier" => Some(self.text(node).to_owned()),
764 "generic_type" => {
770 let mut c = node.walk();
771 let children: Vec<TsNode<'_>> = node.named_children(&mut c).collect();
772 children.into_iter().find_map(|ch| match ch.kind() {
773 "type_identifier" | "scoped_type_identifier" | "identifier" => {
774 Some(self.text(ch).to_owned())
775 }
776 _ => None,
777 })
778 }
779 "scoped_type_identifier" => self.text(node).rsplit('.').next().map(|s| s.to_owned()),
781 _ => {
782 let mut c = node.walk();
784 let children: Vec<TsNode<'_>> = node.named_children(&mut c).collect();
785 children
786 .into_iter()
787 .find_map(|ch| self.extract_simple_type(ch))
788 }
789 }
790 }
791
792 fn collect_type_names(&self, node: TsNode<'_>) -> Vec<String> {
794 let mut names = Vec::new();
795 self.walk_type_names(node, &mut names);
796 names
797 }
798
799 fn walk_type_names(&self, node: TsNode<'_>, out: &mut Vec<String>) {
800 match node.kind() {
801 "type_identifier" => {
802 let name = self.text(node).to_owned();
803 if !is_builtin_java_type(&name) {
804 out.push(name);
805 }
806 }
807 "integral_type" | "floating_point_type" | "boolean_type" | "void_type" => {}
809 _ => {
810 let mut c = node.walk();
811 for child in node.named_children(&mut c) {
812 self.walk_type_names(child, out);
813 }
814 }
815 }
816 }
817
818 fn collect_calls(&mut self, node: TsNode<'_>, caller_id: &NodeId) {
821 let mut cursor = node.walk();
822 let children: Vec<TsNode<'_>> = node.named_children(&mut cursor).collect();
823 for child in children {
824 match child.kind() {
825 "method_invocation" | "object_creation_expression" => {
826 if let Some(callee) = self.callee_name(child) {
827 let line = child.start_position().row as u32 + 1;
828 self.record_call(caller_id.clone(), callee, line);
829 }
830 if let Some(args) = child.child_by_field_name("arguments") {
832 self.collect_calls(args, caller_id);
833 }
834 }
835 _ => self.collect_calls(child, caller_id),
836 }
837 }
838 }
839
840 fn callee_name(&self, call_expr: TsNode<'_>) -> Option<String> {
841 match call_expr.kind() {
844 "method_invocation" => call_expr
845 .child_by_field_name("name")
846 .map(|n| self.text(n).to_owned()),
847 "object_creation_expression" => {
848 call_expr
849 .child_by_field_name("type")
850 .and_then(|t| match t.kind() {
851 "type_identifier" => Some(self.text(t).to_owned()),
852 "generic_type" => t
853 .child_by_field_name("name")
854 .map(|n| self.text(n).to_owned()),
855 _ => None,
856 })
857 }
858 _ => None,
859 }
860 }
861
862 fn record_call(&mut self, caller_id: NodeId, callee_name: String, line: u32) {
863 if callee_name.is_empty() {
864 return;
865 }
866 if let Some(callee_id) = self.fn_index.get(&callee_name).cloned() {
867 let edge = Edge::call(caller_id, callee_id, line);
868 if !self.edges.contains(&edge) {
869 self.edges.push(edge);
870 }
871 } else if !self
872 .deferred_calls
873 .iter()
874 .any(|(c, n, _)| c == &caller_id && n == &callee_name)
875 {
876 self.deferred_calls.push((caller_id, callee_name, line));
877 }
878 }
879}
880
881fn is_builtin_java_type(name: &str) -> bool {
883 matches!(
884 name,
885 "String"
886 | "Object"
887 | "Integer"
888 | "Long"
889 | "Double"
890 | "Float"
891 | "Boolean"
892 | "Byte"
893 | "Short"
894 | "Character"
895 | "Number"
896 | "Math"
897 | "System"
898 | "StringBuilder"
899 | "StringBuffer"
900 | "Comparable"
901 | "Serializable"
902 | "Cloneable"
903 | "Iterable"
904 | "Iterator"
905 | "Collection"
906 | "List"
907 | "Set"
908 | "Map"
909 | "Queue"
910 | "Deque"
911 | "Stack"
912 | "ArrayList"
913 | "HashMap"
914 | "HashSet"
915 | "LinkedList"
916 | "Optional"
917 | "Stream"
918 | "Collectors"
919 | "Arrays"
920 | "Collections"
921 | "Enum"
922 | "Throwable"
923 | "Exception"
924 | "RuntimeException"
925 | "Error"
926 | "Override"
927 | "Deprecated"
928 | "SuppressWarnings"
929 | "FunctionalInterface"
930 | "void"
931 )
932}
933
934#[cfg(test)]
937mod tests {
938 use super::JavaParser;
939 use crate::parser::LanguageParser;
940 use gitcortex_core::schema::{EdgeKind, NodeKind};
941 use std::path::Path;
942
943 fn parse(
944 src: &str,
945 ) -> (
946 Vec<gitcortex_core::graph::Node>,
947 Vec<gitcortex_core::graph::Edge>,
948 ) {
949 let r = JavaParser::new()
950 .parse(Path::new("Test.java"), src)
951 .unwrap();
952 (r.nodes, r.edges)
953 }
954
955 #[allow(clippy::type_complexity)]
956 fn parse_full(
957 src: &str,
958 ) -> (
959 Vec<gitcortex_core::graph::Node>,
960 Vec<gitcortex_core::graph::Edge>,
961 Vec<(gitcortex_core::graph::NodeId, String, u32)>,
962 Vec<(gitcortex_core::graph::NodeId, String)>,
963 Vec<(gitcortex_core::graph::NodeId, String)>,
964 Vec<(gitcortex_core::graph::NodeId, String)>,
965 Vec<(gitcortex_core::graph::NodeId, String)>,
966 Vec<(gitcortex_core::graph::NodeId, String)>,
967 Vec<(gitcortex_core::graph::NodeId, String)>,
968 ) {
969 let r = JavaParser::new()
970 .parse(Path::new("Test.java"), src)
971 .unwrap();
972 (
973 r.nodes,
974 r.edges,
975 r.deferred_calls,
976 r.deferred_uses,
977 r.deferred_implements,
978 r.deferred_imports,
979 r.deferred_inherits,
980 r.deferred_throws,
981 r.deferred_annotated,
982 )
983 }
984
985 #[test]
986 fn parses_class_and_method() {
987 let src = "public class Greeter { public String greet(String name) { return name; } }";
988 let (nodes, edges) = parse(src);
989 let classes: Vec<_> = nodes
990 .iter()
991 .filter(|n| n.kind == NodeKind::Struct)
992 .collect();
993 let methods: Vec<_> = nodes
994 .iter()
995 .filter(|n| n.kind == NodeKind::Method)
996 .collect();
997 assert_eq!(classes.len(), 1, "expected 1 class");
998 assert_eq!(classes[0].name, "Greeter");
999 assert_eq!(methods.len(), 1, "expected 1 method");
1000 let contains: Vec<_> = edges
1001 .iter()
1002 .filter(|e| e.kind == EdgeKind::Contains)
1003 .collect();
1004 assert!(!contains.is_empty(), "expected Contains edge");
1005 }
1006
1007 #[test]
1008 fn parses_interface() {
1009 let src = "public interface Greeter { String greet(String name); }";
1010 let (nodes, _) = parse(src);
1011 let interfaces: Vec<_> = nodes
1012 .iter()
1013 .filter(|n| n.kind == NodeKind::Interface || n.kind == NodeKind::Trait)
1014 .collect();
1015 assert_eq!(interfaces.len(), 1);
1016 assert_eq!(interfaces[0].name, "Greeter");
1017 }
1018
1019 #[test]
1020 fn parses_enum() {
1021 let src = "public enum Direction { NORTH, SOUTH, EAST, WEST }";
1022 let (nodes, _) = parse(src);
1023 let enums: Vec<_> = nodes.iter().filter(|n| n.kind == NodeKind::Enum).collect();
1024 assert_eq!(enums.len(), 1);
1025 assert_eq!(enums[0].name, "Direction");
1026 }
1027
1028 #[test]
1029 fn detects_extends_and_implements() {
1030 let src = "interface Base {}\nclass Child extends Base implements Base {}";
1031 let (_, _, _, _, implements, _, inherits, ..) = parse_full(src);
1032 let impl_edges: Vec<_> = implements.iter().filter(|(_, n)| n == "Base").collect();
1033 let inh_edges: Vec<_> = inherits.iter().filter(|(_, n)| n == "Base").collect();
1034 assert!(
1035 impl_edges.len() + inh_edges.len() >= 2,
1036 "expected extends+implements edges to Base, implements={implements:?} inherits={inherits:?}"
1037 );
1038 }
1039
1040 #[test]
1041 fn detects_type_annotation_uses() {
1042 let src = "class Service {}\nclass Controller {\n public Service handle(Service svc) { return svc; }\n}";
1043 let (_, _, _, uses, ..) = parse_full(src);
1044 let svc_uses: Vec<_> = uses.iter().filter(|(_, n)| n == "Service").collect();
1045 assert!(
1046 svc_uses.len() >= 2,
1047 "expected Uses edges to Service (param + return), got: {uses:?}"
1048 );
1049 }
1050
1051 #[test]
1052 fn detects_import_declaration() {
1053 let src = "import com.example.MyService;\nimport java.util.List;\npublic class App {}";
1054 let (_, _, _, _, _, imports, ..) = parse_full(src);
1055 assert!(
1056 imports.iter().any(|(_, n)| n == "MyService"),
1057 "expected import 'MyService', got: {imports:?}"
1058 );
1059 }
1060
1061 #[test]
1062 fn module_node_is_emitted() {
1063 let src = "public class App {}";
1064 let (nodes, _) = parse(src);
1065 let modules: Vec<_> = nodes
1066 .iter()
1067 .filter(|n| n.kind == NodeKind::Module)
1068 .collect();
1069 assert_eq!(modules.len(), 1);
1070 assert_eq!(modules[0].name, "Test"); }
1072}