Skip to main content

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}