Skip to main content

java_lang/ast/
expr.rs

1//! Expression types.
2
3use crate::{ident::Ident, span::Span};
4
5use super::{
6    attribute::Annotation,
7    lit::Lit,
8    op::{AssignOpToken, BinOpToken, UnaryOp},
9    pat::Pattern,
10    path::{Path, TypeArguments},
11    stmt::Block,
12    ty::Type,
13};
14
15/// A Java expression.
16///
17/// Expressions are represented in a mostly-desugared form, following the JLS
18/// grammar hierarchy.
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub enum Expr {
21    /// A literal value.
22    Literal(Lit),
23    /// An identifier expression.
24    Ident(Ident),
25    /// `this`
26    This(Span),
27    /// `Super`
28    Super(Span),
29    /// Parenthesized expression: `(expr)`
30    Paren {
31        paren_span: (Span, Span),
32        expr: Box<Expr>,
33    },
34    /// Class literal: `String.class`, `int[].class`
35    ClassLit {
36        type_expr: Box<Type>,
37        dot_span: Span,
38        class_span: Span,
39    },
40    /// Field access: `expr.field`
41    FieldAccess(FieldAccessExpr),
42    /// Method invocation: `method(args)`, `obj.method(args)`
43    MethodCall(MethodCallExpr),
44    /// Array access: `array[index]`
45    ArrayAccess(ArrayAccessExpr),
46    /// Method reference: `String::valueOf`, `System.out::println`
47    MethodRef(MethodRefExpr),
48    /// Array creation: `new int[]{1, 2, 3}`, `new String[10]`
49    ArrayNew(ArrayNewExpr),
50    /// Type cast: `(int) expr`
51    Cast(CastExpr),
52    /// Binary operation: `a + b`
53    Binary(BinaryExpr),
54    /// Unary operation: `-x`, `!flag`
55    Unary(UnaryExpr),
56    /// instanceof: `obj instanceof String`, `obj instanceof String s`
57    Instanceof(InstanceofExpr),
58    /// Assignment: `x = 5`, `x += 1`
59    Assign(AssignExpr),
60    /// Ternary conditional: `cond ? a : b`
61    Conditional(ConditionalExpr),
62    /// Lambda expression: `x -> x + 1`, `(x, y) -> x + y`
63    Lambda(LambdaExpr),
64    /// Switch expression (Java 14+): `switch (x) { case 1 -> "one"; ... }`
65    Switch(SwitchExpr),
66    /// Class instance creation (anonymous class): `new Foo() { ... }`
67    NewClass(NewClassExpr),
68    /// Array initializer: `{1, 2, 3}`
69    ArrayInit(ArrayInitExpr),
70}
71
72impl Expr {
73    pub fn span(&self) -> Span {
74        match self {
75            Self::Literal(lit) => lit.span(),
76            Self::Ident(ident) => ident.span(),
77            Self::This(s) => *s,
78            Self::Super(s) => *s,
79            Self::Paren { paren_span, .. } => paren_span.0.join(paren_span.1),
80            Self::ClassLit {
81                type_expr,
82                class_span,
83                ..
84            } => type_expr.span().join(*class_span),
85            Self::FieldAccess(e) => e.span(),
86            Self::MethodCall(e) => e.span(),
87            Self::ArrayAccess(e) => e.span(),
88            Self::MethodRef(e) => e.span(),
89            Self::ArrayNew(e) => e.span(),
90            Self::Cast(e) => e.span(),
91            Self::Binary(e) => e.span(),
92            Self::Unary(e) => e.span(),
93            Self::Instanceof(e) => e.span(),
94            Self::Assign(e) => e.span(),
95            Self::Conditional(e) => e.span(),
96            Self::Lambda(e) => e.span,
97            Self::Switch(e) => e.span(),
98            Self::NewClass(e) => e.span(),
99            Self::ArrayInit(e) => e.brace_span.0.join(e.brace_span.1),
100        }
101    }
102}
103
104/// Field access expression: `expr.field`.
105#[derive(Debug, Clone, PartialEq, Eq, Hash)]
106pub struct FieldAccessExpr {
107    pub target: Box<Expr>,
108    pub dot_span: Span,
109    pub field: Ident,
110}
111
112impl FieldAccessExpr {
113    pub fn span(&self) -> Span {
114        self.target.span().join(self.field.span())
115    }
116}
117
118/// Method invocation expression.
119#[derive(Debug, Clone, PartialEq, Eq, Hash)]
120pub struct MethodCallExpr {
121    /// The method receiver (None for unqualified calls like `method()`).
122    pub receiver: Option<Box<Expr>>,
123    /// Type arguments for the method call.
124    pub type_args: Option<TypeArguments>,
125    /// The method name.
126    pub method: Ident,
127    /// Parenthesized arguments.
128    pub paren_span: (Span, Span),
129    /// The arguments passed to the method.
130    pub args: Vec<Expr>,
131}
132
133impl MethodCallExpr {
134    pub fn span(&self) -> Span {
135        let start = match &self.receiver {
136            Some(r) => r.span(),
137            None => self.method.span(),
138        };
139        start.join(self.paren_span.1)
140    }
141}
142
143/// Array access expression: `array[index]`.
144#[derive(Debug, Clone, PartialEq, Eq, Hash)]
145pub struct ArrayAccessExpr {
146    pub array: Box<Expr>,
147    pub index: Box<Expr>,
148    pub bracket_span: (Span, Span),
149}
150
151impl ArrayAccessExpr {
152    pub fn span(&self) -> Span {
153        self.array.span().join(self.bracket_span.1)
154    }
155}
156
157/// Method reference expression: `String::valueOf`.
158#[derive(Debug, Clone, PartialEq, Eq, Hash)]
159pub struct MethodRefExpr {
160    pub target: MethodRefTarget,
161    pub colon_colon_span: Span,
162    pub type_args: Option<TypeArguments>,
163    pub method_name: Ident,
164}
165
166impl MethodRefExpr {
167    pub fn span(&self) -> Span {
168        let start = self.target.span();
169        start.join(self.method_name.span())
170    }
171}
172
173/// The target of a method reference.
174#[derive(Debug, Clone, PartialEq, Eq, Hash)]
175pub enum MethodRefTarget {
176    /// `TypeName::method`
177    Type(Path),
178    /// `expr::method`
179    Expr(Box<Expr>),
180    /// `super::method`
181    Super(Span),
182    /// `TypeName.super::method`
183    SuperFromType {
184        type_name: Path,
185        dot_span: Span,
186        super_span: Span,
187    },
188}
189
190impl MethodRefTarget {
191    pub fn span(&self) -> Span {
192        match self {
193            Self::Type(p) => p.span,
194            Self::Expr(e) => e.span(),
195            Self::Super(s) => *s,
196            Self::SuperFromType {
197                type_name,
198                super_span,
199                ..
200            } => type_name.span.join(*super_span),
201        }
202    }
203}
204
205/// Class instance creation expression with optional body (anonymous class).
206#[derive(Debug, Clone, PartialEq, Eq, Hash)]
207pub struct NewClassExpr {
208    pub new_span: Span,
209    pub type_args: Option<TypeArguments>,
210    pub class_type: Path,
211    pub paren_span: (Span, Span),
212    pub args: Vec<Expr>,
213    pub body: Option<super::item::ClassBodyDeclList>,
214}
215
216impl NewClassExpr {
217    pub fn span(&self) -> Span {
218        let end = match &self.body {
219            Some(b) => b.brace_span.1,
220            None => self.paren_span.1,
221        };
222        self.new_span.join(end)
223    }
224}
225
226/// Array creation expression: `new int[10]` or `new int[]{1, 2, 3}`.
227#[derive(Debug, Clone, PartialEq, Eq, Hash)]
228pub struct ArrayNewExpr {
229    pub new_span: Span,
230    pub elem_type: Type,
231    pub dim_exprs: Vec<ArrayDimExpr>,
232    pub dims: Vec<super::ty::ArrayDim>,
233    pub initializer: Option<ArrayInitExpr>,
234}
235
236impl ArrayNewExpr {
237    pub fn span(&self) -> Span {
238        let end = match &self.initializer {
239            Some(init) => init.brace_span.1,
240            None => {
241                if let Some(dim) = self.dims.last() {
242                    dim.bracket_span.1
243                } else if let Some(dim_expr) = self.dim_exprs.last() {
244                    dim_expr.bracket_span.1
245                } else {
246                    self.elem_type.span()
247                }
248            }
249        };
250        self.new_span.join(end)
251    }
252}
253
254/// A dimension expression in array creation: `[expr]`.
255#[derive(Debug, Clone, PartialEq, Eq, Hash)]
256pub struct ArrayDimExpr {
257    pub annotations: Vec<Annotation>,
258    pub bracket_span: (Span, Span),
259    pub expr: Box<Expr>,
260}
261
262/// Array initializer: `{1, 2, 3}`.
263#[derive(Debug, Clone, PartialEq, Eq, Hash)]
264pub struct ArrayInitExpr {
265    pub brace_span: (Span, Span),
266    pub elements: Vec<Expr>,
267    pub trailing_comma: bool,
268}
269
270/// Type cast expression: `(int) expr`.
271#[derive(Debug, Clone, PartialEq, Eq, Hash)]
272pub struct CastExpr {
273    pub paren_span: (Span, Span),
274    pub target_type: Type,
275    pub expr: Box<Expr>,
276}
277
278impl CastExpr {
279    pub fn span(&self) -> Span {
280        self.paren_span.0.join(self.expr.span())
281    }
282}
283
284/// Binary expression: `a + b`.
285#[derive(Debug, Clone, PartialEq, Eq, Hash)]
286pub struct BinaryExpr {
287    pub left: Box<Expr>,
288    pub op: BinOpToken,
289    pub right: Box<Expr>,
290}
291
292impl BinaryExpr {
293    pub fn span(&self) -> Span {
294        self.left.span().join(self.right.span())
295    }
296}
297
298/// Unary expression: `-x`, `!flag`, `++x`.
299#[derive(Debug, Clone, PartialEq, Eq, Hash)]
300pub struct UnaryExpr {
301    pub op: UnaryOp,
302    pub op_span: Span,
303    pub expr: Box<Expr>,
304    /// True if operator is postfix (e.g., `x++`).
305    pub is_postfix: bool,
306}
307
308impl UnaryExpr {
309    pub fn span(&self) -> Span {
310        if self.is_postfix {
311            self.expr.span().join(self.op_span)
312        } else {
313            self.op_span.join(self.expr.span())
314        }
315    }
316}
317
318/// instanceof expression: `obj instanceof String` or `obj instanceof String s`.
319#[derive(Debug, Clone, PartialEq, Eq, Hash)]
320pub struct InstanceofExpr {
321    pub expr: Box<Expr>,
322    pub instanceof_span: Span,
323    pub pattern: InstanceofPattern,
324}
325
326impl InstanceofExpr {
327    pub fn span(&self) -> Span {
328        match &self.pattern {
329            InstanceofPattern::Type(t) => self.expr.span().join(t.span()),
330            InstanceofPattern::Pattern(p) => self.expr.span().join(p.span()),
331        }
332    }
333}
334
335/// The right-hand side of an instanceof expression.
336#[derive(Debug, Clone, PartialEq, Eq, Hash)]
337pub enum InstanceofPattern {
338    /// Simple type check: `obj instanceof String`
339    Type(Type),
340    /// Pattern matching: `obj instanceof String s`
341    Pattern(Pattern),
342}
343
344/// Assignment expression: `x = 5`, `x += 1`.
345#[derive(Debug, Clone, PartialEq, Eq, Hash)]
346pub struct AssignExpr {
347    pub target: AssignTarget,
348    pub op: AssignOpToken,
349    pub value: Box<Expr>,
350}
351
352impl AssignExpr {
353    pub fn span(&self) -> Span {
354        self.target.span().join(self.value.span())
355    }
356}
357
358/// The target of an assignment expression.
359#[derive(Debug, Clone, PartialEq, Eq, Hash)]
360pub enum AssignTarget {
361    /// Simple variable name: `x`
362    Ident(Ident),
363    /// Field access: `obj.field`
364    FieldAccess(FieldAccessExpr),
365    /// Array access: `arr[i]`
366    ArrayAccess(ArrayAccessExpr),
367}
368
369impl AssignTarget {
370    pub fn span(&self) -> Span {
371        match self {
372            Self::Ident(i) => i.span(),
373            Self::FieldAccess(f) => f.span(),
374            Self::ArrayAccess(a) => a.span(),
375        }
376    }
377}
378
379/// Ternary conditional expression: `cond ? a : b`.
380#[derive(Debug, Clone, PartialEq, Eq, Hash)]
381pub struct ConditionalExpr {
382    pub cond: Box<Expr>,
383    pub question_span: Span,
384    pub then_expr: Box<Expr>,
385    pub colon_span: Span,
386    pub else_expr: Box<Expr>,
387}
388
389impl ConditionalExpr {
390    pub fn span(&self) -> Span {
391        self.cond.span().join(self.else_expr.span())
392    }
393}
394
395/// Lambda expression: `x -> x + 1`, `(x, y) -> { return x + y; }`.
396#[derive(Debug, Clone, PartialEq, Eq, Hash)]
397pub struct LambdaExpr {
398    pub params: LambdaParams,
399    pub arrow_span: Span,
400    pub body: LambdaBody,
401    pub span: Span,
402}
403
404/// Lambda parameters.
405#[derive(Debug, Clone, PartialEq, Eq, Hash)]
406pub enum LambdaParams {
407    /// `(Type name, Type name, ...)`
408    List {
409        paren_span: (Span, Span),
410        params: Vec<LambdaParam>,
411    },
412    /// `(name, name, ...)` (inferred types)
413    IdentList {
414        paren_span: (Span, Span),
415        idents: Vec<Ident>,
416    },
417    /// `name` (single inferred parameter)
418    Single(Ident),
419}
420
421/// A lambda parameter with explicit type.
422#[derive(Debug, Clone, PartialEq, Eq, Hash)]
423pub struct LambdaParam {
424    pub modifiers: Vec<super::item::Modifier>,
425    pub ty: Type,
426    pub name: Ident,
427}
428
429/// Lambda body: either an expression or a block.
430#[derive(Debug, Clone, PartialEq, Eq, Hash)]
431pub enum LambdaBody {
432    Expr(Box<Expr>),
433    Block(Block),
434}
435
436impl LambdaBody {
437    pub fn span(&self) -> Span {
438        match self {
439            Self::Expr(e) => e.span(),
440            Self::Block(b) => b.span(),
441        }
442    }
443}
444
445/// Switch expression (Java 14+): `switch (x) { case 1 -> "one"; ... }`.
446#[derive(Debug, Clone, PartialEq, Eq, Hash)]
447pub struct SwitchExpr {
448    pub switch_span: Span,
449    pub paren_span: (Span, Span),
450    pub selector: Box<Expr>,
451    pub brace_span: (Span, Span),
452    pub cases: Vec<SwitchArm>,
453}
454
455impl SwitchExpr {
456    pub fn span(&self) -> Span {
457        self.switch_span.join(self.brace_span.1)
458    }
459}
460
461/// A single arm of a switch expression.
462#[derive(Debug, Clone, PartialEq, Eq, Hash)]
463pub enum SwitchArm {
464    /// `case pattern -> expr;`
465    Expr(SwitchCase, Span, Expr),
466    /// `case pattern -> { ... }`
467    Block(SwitchCase, Span, Block),
468    /// `case pattern -> throw ...;`
469    Throw(SwitchCase, Span, Expr),
470    /// `case pattern: statements` (colon-style, from switch statements)
471    Colon(SwitchCase, Vec<super::stmt::Stmt>),
472}
473
474impl SwitchArm {
475    pub fn span(&self) -> Span {
476        match self {
477            Self::Expr(case, _arrow, expr) => case.span().join(expr.span()),
478            Self::Block(case, _arrow, block) => case.span().join(block.brace_span.1),
479            Self::Throw(case, _arrow, expr) => case.span().join(expr.span()),
480            Self::Colon(case, stmts) => {
481                let end = stmts.last().map(|s| s.span()).unwrap_or(case.span());
482                case.span().join(end)
483            }
484        }
485    }
486}
487
488/// A case label in a switch.
489#[derive(Debug, Clone, PartialEq, Eq, Hash)]
490pub enum SwitchCase {
491    /// `case 1`, `case 1, 2, 3`
492    CaseValues { case_span: Span, values: Vec<Expr> },
493    /// `case null`
494    CaseNull { case_span: Span, null_span: Span },
495    /// `case null, default`
496    CaseNullDefault {
497        case_span: Span,
498        null_span: Span,
499        default_span: Span,
500    },
501    /// `case String s`, `case String s when s.length() > 0`
502    CasePattern {
503        case_span: Span,
504        pattern: Pattern,
505        guard: Box<Option<super::pat::Guard>>,
506    },
507    /// `default`
508    Default { default_span: Span },
509}
510
511impl SwitchCase {
512    pub fn span(&self) -> Span {
513        match self {
514            Self::CaseValues { case_span, values } => {
515                let end = values.last().map(|v| v.span()).unwrap_or(*case_span);
516                case_span.join(end)
517            }
518            Self::CaseNull {
519                case_span,
520                null_span,
521            } => case_span.join(*null_span),
522            Self::CaseNullDefault {
523                case_span,
524                default_span,
525                ..
526            } => case_span.join(*default_span),
527            Self::CasePattern {
528                case_span,
529                pattern,
530                guard,
531            } => {
532                let end = match guard.as_ref() {
533                    Some(g) => g.expr.span(),
534                    None => pattern.span(),
535                };
536                case_span.join(end)
537            }
538            Self::Default { default_span } => *default_span,
539        }
540    }
541}