big_code_analysis_ast/getter/java.rs
1//! `Getter` implementation for Java.
2#![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
3
4use super::*;
5
6impl Getter for JavaCode {
7 /// Names the space, synthesising one for constructs that carry no
8 /// name token (#1184).
9 ///
10 /// `get_func_space_name` returns `Option<&'a str>` borrowed from
11 /// `code`, so the only name available for a nameless construct is a
12 /// `&'static str` — a per-property spelling like `<get-foo>` would
13 /// need a signature change. Angle brackets follow the existing
14 /// `<anonymous>` convention and cannot collide with a real
15 /// identifier in any of these grammars.
16 ///
17 /// Sibling collisions are accepted, exactly as multiple
18 /// `<anonymous>` siblings already are: two properties each with a
19 /// getter, or two `static { }` blocks in one class, produce two
20 /// spaces with the same name. Nothing enforces name uniqueness
21 /// among siblings, and inventing an index would make the name
22 /// unstable under an unrelated edit.
23 ///
24 /// `<static-init>` rather than the JVM's `<clinit>`: the same
25 /// construct exists in JavaScript, where `<clinit>` would mean
26 /// nothing, and one spelling across languages is worth more here
27 /// than JVM precision.
28 fn get_func_space_name<'a, 'tree>(
29 node: &Node<'tree>,
30 code: &'a [u8],
31 ancestors: Ancestors<'tree, '_>,
32 ) -> Option<&'a str> {
33 if node.kind_id() == Java::StaticInitializer as u16 {
34 return Some("<static-init>");
35 }
36 crate::getter::default_func_space_name(node, code, ancestors)
37 }
38
39 fn get_space_kind(node: &Node) -> SpaceKind {
40 use Java::*;
41
42 // `EnumDeclaration` and `RecordDeclaration` are class-like
43 // (they extend `Object`, hold fields, and can declare methods)
44 // so they share `SpaceKind::Class`. `AnnotationTypeDeclaration`
45 // implicitly extends `java.lang.annotation.Annotation` (an
46 // interface) and its elements are abstract methods at the
47 // bytecode level, so it maps to `SpaceKind::Interface`.
48 match node.kind_id().into() {
49 ClassDeclaration | EnumDeclaration | RecordDeclaration => SpaceKind::Class,
50 // `CompactConstructorDeclaration` is a record's compact
51 // constructor (`R { … }`); it is a constructor with an
52 // implicit parameter list, so it shares the canonical
53 // constructor's space kind (#1160). It carries a required
54 // `name` field holding the record's simple name, so the
55 // default `get_func_space_name` already names the space `R` —
56 // identical to what the canonical spelling would produce.
57 // `StaticInitializer` is `static { … }` (#1184).
58 MethodDeclaration
59 | ConstructorDeclaration
60 | CompactConstructorDeclaration
61 | LambdaExpression
62 | StaticInitializer => SpaceKind::Function,
63 InterfaceDeclaration | AnnotationTypeDeclaration => SpaceKind::Interface,
64 // An anonymous class (`new Runnable() { ... }`) is an
65 // `object_creation_expression` carrying a `class_body` child;
66 // it opens a Class space, matching PHP's `AnonymousClass` and
67 // C#'s anonymous forms (#463). A plain `new Foo()` has no
68 // `class_body`, so it falls through to `Unknown` (no space).
69 // The shared helper keeps this gate identical to the one in
70 // `JavaCode::is_func_space`.
71 ObjectCreationExpression
72 if crate::checker::java_anonymous_class_body(node).is_some() =>
73 {
74 SpaceKind::Class
75 }
76 Program => SpaceKind::Unit,
77 _ => SpaceKind::Unknown,
78 }
79 }
80
81 fn get_op_type<'a>(node: &Node<'a>, ancestors: Ancestors<'a, '_>) -> TokenRole {
82 use Java::*;
83 // Some guides that informed grammar choice for Halstead
84 // keywords, operators, literals: https://docs.oracle.com/javase/specs/jls/se18/html/jls-3.html#jls-3.12
85 // https://www.geeksforgeeks.org/software-engineering-halsteads-software-metrics/?msclkid=5e181114abef11ecbb03527e95a34828
86 match node.kind_id().into() {
87 // Operator: control flow
88 | If | Else | Switch | Case | Try | Catch | Throw | Throws | Throws2 | For
89 | While | Continue | Break | Do | Finally
90 // Operator: keywords
91 | New | Return | Default | Abstract | Assert | Instanceof | Extends | Final
92 | Implements | Transient | Synchronized | VoidType
93 // Operator: brackets and comma and terminators (separators)
94 | SEMI | COMMA | COLONCOLON | DOT | DASHGT | LBRACE | LBRACK | LPAREN
95 // Operator: operators
96 | EQ | LT | GT | BANG | TILDE | QMARK | COLON
97 | EQEQ | LTEQ | GTEQ | BANGEQ | AMPAMP | PIPEPIPE | PLUSPLUS | DASHDASH
98 | PLUS | DASH | STAR | SLASH | AMP | PIPE | CARET | PERCENT | LTLT | GTGT | GTGTGT
99 | PLUSEQ | DASHEQ | STAREQ | SLASHEQ | AMPEQ | PIPEEQ | CARETEQ | PERCENTEQ
100 | LTLTEQ | GTGTEQ | GTGTGTEQ
101 // primitive types
102 | Byte | Short | Int | Long | Char | Float | Double | BooleanType
103 => {
104 TokenRole::Operator
105 },
106 // `super` is the receiver of `super.f()` / `super(x)` /
107 // `super::f` everywhere except a wildcard type bound, where
108 // `? super String` denotes no value and is the mirror image
109 // of `? extends String` — whose `extends` this same match
110 // bills as an operator. Both keywords are direct children of
111 // the same `wildcard` node, so the parent alone separates
112 // the bound from the reference (#1380).
113 Super => match ancestors.parent(node).map(|p| p.kind_id().into()) {
114 Some(Wildcard) => TokenRole::Operator,
115 _ => TokenRole::Operand,
116 },
117 // Operands: variables, constants, literals. `This` joined
118 // them in #1380: a self-reference names the receiver a `.`
119 // or `::` acts on, so billing it as an operator made
120 // `this.x` a binary operator with one operand while `p.x`
121 // has one operator and two. The explicit receiver parameter
122 // (`void m(J J.this)`) reaches this arm too, and is an
123 // operand for the same reason a parameter name is — which is
124 // the opposite call from C#'s indexer declarator in
125 // `csharp.rs`, deliberately: a parameter name is an operand
126 // everywhere here, while a *member* named by a keyword sits
127 // with `operator +`.
128 Identifier | NullLiteral | ClassLiteral | True | False | StringLiteral
129 | CharacterLiteral | HexIntegerLiteral | OctalIntegerLiteral
130 | BinaryIntegerLiteral | DecimalIntegerLiteral | HexFloatingPointLiteral
131 | DecimalFloatingPointLiteral | This => {
132 TokenRole::Operand
133 },
134 _ => {
135 TokenRole::Unknown
136 },
137 }
138 }
139
140 fn get_operator_id_as_str(id: u16) -> &'static str {
141 let typ = id.into();
142 match typ {
143 Java::LPAREN => "()",
144 Java::LBRACK => "[]",
145 Java::LBRACE => "{}",
146 Java::VoidType => "void",
147 _ => typ.into(),
148 }
149 }
150}