Skip to main content

java_lang/ast/
stmt.rs

1//! Statement types.
2
3use crate::{ident::Ident, span::Span};
4
5use super::{Comment, expr::Expr, item::Modifier, ty::Type};
6
7/// A Java statement.
8#[derive(Debug, Clone, PartialEq, Eq, Hash)]
9pub enum Stmt {
10    /// An empty statement: `;`
11    Empty(Span),
12    /// A block: `{ ... }`
13    Block(Block),
14    /// A labeled statement: `label: stmt`
15    Labeled(LabeledStmt),
16    /// An expression statement: `expr;`
17    Expr(ExprStmt),
18    /// A local variable declaration: `int x = 5;`
19    LocalVarDecl(LocalVarDeclStmt),
20    /// An if statement: `if (cond) stmt [else stmt]`
21    If(IfStmt),
22    /// An assert statement: `assert cond [: detail];`
23    Assert(AssertStmt),
24    /// A switch statement: `switch (expr) { ... }`
25    Switch(SwitchStmt),
26    /// A while statement: `while (cond) stmt`
27    While(WhileStmt),
28    /// A do-while statement: `do stmt while (cond);`
29    DoWhile(DoWhileStmt),
30    /// A for statement: `for (init; cond; update) stmt`
31    For(ForStmt),
32    /// An enhanced for statement: `for (Type var : iterable) stmt`
33    EnhancedFor(EnhancedForStmt),
34    /// A break statement: `break [label];`
35    Break(JumpStmt),
36    /// A continue statement: `continue [label];`
37    Continue(JumpStmt),
38    /// A return statement: `return [expr];`
39    Return(ReturnStmt),
40    /// A throw statement: `throw expr;`
41    Throw(ThrowStmt),
42    /// A synchronized statement: `synchronized (lock) { ... }`
43    Synchronized(SynchronizedStmt),
44    /// A try statement: `try { ... } catch (...) { ... } [finally { ... }]`
45    Try(TryStmt),
46    /// A yield statement: `yield expr;` (in switch expressions)
47    Yield(YieldStmt),
48    /// A local class or interface declaration.
49    ClassDecl(Box<super::item::TypeDecl>),
50}
51
52impl Stmt {
53    pub fn span(&self) -> Span {
54        match self {
55            Self::Empty(s) => *s,
56            Self::Block(b) => b.span(),
57            Self::Labeled(l) => l.span(),
58            Self::Expr(e) => e.span(),
59            Self::LocalVarDecl(d) => d.span(),
60            Self::If(s) => s.span(),
61            Self::Assert(s) => s.span(),
62            Self::Switch(s) => s.span(),
63            Self::While(s) => s.span(),
64            Self::DoWhile(s) => s.span(),
65            Self::For(s) => s.span(),
66            Self::EnhancedFor(s) => s.span(),
67            Self::Break(s) => s.span(),
68            Self::Continue(s) => s.span(),
69            Self::Return(s) => s.span(),
70            Self::Throw(s) => s.span(),
71            Self::Synchronized(s) => s.span(),
72            Self::Try(s) => s.span(),
73            Self::Yield(s) => s.span(),
74            Self::ClassDecl(d) => d.span(),
75        }
76    }
77}
78
79/// A block: `{ stmt1; stmt2; ... }`.
80#[derive(Debug, Clone, PartialEq, Eq, Hash)]
81pub struct Block {
82    pub leading_comments: Vec<Comment>,
83    pub brace_span: (Span, Span),
84    pub stmts: Vec<Stmt>,
85}
86
87impl Block {
88    pub fn span(&self) -> Span {
89        let start = self
90            .leading_comments
91            .first()
92            .map_or(self.brace_span.0, |c| c.span);
93        start.join(self.brace_span.1)
94    }
95}
96
97/// A labeled statement: `label: stmt`.
98#[derive(Debug, Clone, PartialEq, Eq, Hash)]
99pub struct LabeledStmt {
100    pub leading_comments: Vec<Comment>,
101    pub label: Ident,
102    pub colon_span: Span,
103    pub stmt: Box<Stmt>,
104}
105
106impl LabeledStmt {
107    pub fn span(&self) -> Span {
108        let start = self
109            .leading_comments
110            .first()
111            .map_or(self.label.span(), |c| c.span);
112        start.join(self.stmt.span())
113    }
114}
115
116/// An expression statement: `expr;`.
117#[derive(Debug, Clone, PartialEq, Eq, Hash)]
118pub struct ExprStmt {
119    pub leading_comments: Vec<Comment>,
120    pub expr: Expr,
121    pub semi_span: Span,
122}
123
124impl ExprStmt {
125    pub fn span(&self) -> Span {
126        let start = self
127            .leading_comments
128            .first()
129            .map_or(self.expr.span(), |c| c.span);
130        start.join(self.semi_span)
131    }
132}
133
134/// A local variable declaration statement: `int x = 5, y = 10;`.
135#[derive(Debug, Clone, PartialEq, Eq, Hash)]
136pub struct LocalVarDeclStmt {
137    pub leading_comments: Vec<Comment>,
138    pub modifiers: Vec<Modifier>,
139    pub ty: LocalVarType,
140    pub declarators: Vec<VariableDeclarator>,
141    pub semi_span: Span,
142}
143
144impl LocalVarDeclStmt {
145    pub fn span(&self) -> Span {
146        let start = self
147            .leading_comments
148            .first()
149            .map_or(self.semi_span, |c| c.span);
150        start.join(self.semi_span)
151    }
152}
153
154/// The type of a local variable declaration.
155#[derive(Debug, Clone, PartialEq, Eq, Hash)]
156pub enum LocalVarType {
157    /// Explicit type: `int`, `String`, etc.
158    Type(Type),
159    /// `var` (type inference, Java 10+)
160    Var(Span),
161}
162
163/// A variable declarator: `name [= initializer]`.
164#[derive(Debug, Clone, PartialEq, Eq, Hash)]
165pub struct VariableDeclarator {
166    pub name: Option<Ident>, // None for unnamed variables (Java 21+)
167    pub dims: Vec<super::ty::ArrayDim>,
168    pub initializer: Option<Expr>,
169}
170
171/// An if statement.
172#[derive(Debug, Clone, PartialEq, Eq, Hash)]
173pub struct IfStmt {
174    pub leading_comments: Vec<Comment>,
175    pub if_span: Span,
176    pub paren_span: (Span, Span),
177    pub cond: Expr,
178    pub then_stmt: Box<Stmt>,
179    pub else_clause: Option<(Span, Box<Stmt>)>,
180}
181
182impl IfStmt {
183    pub fn span(&self) -> Span {
184        let end = match &self.else_clause {
185            Some((_, stmt)) => stmt.span(),
186            None => self.then_stmt.span(),
187        };
188        let start = self
189            .leading_comments
190            .first()
191            .map_or(self.if_span, |c| c.span);
192        start.join(end)
193    }
194}
195
196/// An assert statement.
197#[derive(Debug, Clone, PartialEq, Eq, Hash)]
198pub struct AssertStmt {
199    pub leading_comments: Vec<Comment>,
200    pub assert_span: Span,
201    pub cond: Expr,
202    pub detail: Option<(Span, Expr)>,
203    pub semi_span: Span,
204}
205
206impl AssertStmt {
207    pub fn span(&self) -> Span {
208        let start = self
209            .leading_comments
210            .first()
211            .map_or(self.assert_span, |c| c.span);
212        start.join(self.semi_span)
213    }
214}
215
216/// A switch statement.
217#[derive(Debug, Clone, PartialEq, Eq, Hash)]
218pub struct SwitchStmt {
219    pub leading_comments: Vec<Comment>,
220    pub switch_span: Span,
221    pub paren_span: (Span, Span),
222    pub selector: Expr,
223    pub brace_span: (Span, Span),
224    pub cases: Vec<SwitchCaseGroup>,
225}
226
227impl SwitchStmt {
228    pub fn span(&self) -> Span {
229        let start = self
230            .leading_comments
231            .first()
232            .map_or(self.switch_span, |c| c.span);
233        start.join(self.brace_span.1)
234    }
235}
236
237/// A group of case labels and statements in a switch statement (colon-style).
238#[derive(Debug, Clone, PartialEq, Eq, Hash)]
239pub struct SwitchCaseGroup {
240    pub labels: Vec<super::expr::SwitchCase>,
241    pub colon_span: Span,
242    pub stmts: Vec<Stmt>,
243}
244
245/// A while statement.
246#[derive(Debug, Clone, PartialEq, Eq, Hash)]
247pub struct WhileStmt {
248    pub leading_comments: Vec<Comment>,
249    pub while_span: Span,
250    pub paren_span: (Span, Span),
251    pub cond: Expr,
252    pub body: Box<Stmt>,
253}
254
255impl WhileStmt {
256    pub fn span(&self) -> Span {
257        let start = self
258            .leading_comments
259            .first()
260            .map_or(self.while_span, |c| c.span);
261        start.join(self.body.span())
262    }
263}
264
265/// A do-while statement.
266#[derive(Debug, Clone, PartialEq, Eq, Hash)]
267pub struct DoWhileStmt {
268    pub leading_comments: Vec<Comment>,
269    pub do_span: Span,
270    pub body: Box<Stmt>,
271    pub while_span: Span,
272    pub paren_span: (Span, Span),
273    pub cond: Expr,
274    pub semi_span: Span,
275}
276
277impl DoWhileStmt {
278    pub fn span(&self) -> Span {
279        let start = self
280            .leading_comments
281            .first()
282            .map_or(self.do_span, |c| c.span);
283        start.join(self.semi_span)
284    }
285}
286
287/// A for statement: `for (init; cond; update) body`.
288#[derive(Debug, Clone, PartialEq, Eq, Hash)]
289pub struct ForStmt {
290    pub leading_comments: Vec<Comment>,
291    pub for_span: Span,
292    pub paren_span: (Span, Span),
293    pub init: ForInit,
294    pub cond: Option<Expr>,
295    pub semi2_span: Option<Span>,
296    pub update: Vec<Expr>,
297    pub body: Box<Stmt>,
298}
299
300impl ForStmt {
301    pub fn span(&self) -> Span {
302        let start = self
303            .leading_comments
304            .first()
305            .map_or(self.for_span, |c| c.span);
306        start.join(self.body.span())
307    }
308}
309
310/// The initialization part of a for statement.
311#[derive(Debug, Clone, PartialEq, Eq, Hash)]
312pub enum ForInit {
313    /// A list of expression statements.
314    Exprs(Vec<Expr>),
315    /// A local variable declaration.
316    LocalVarDecl(LocalVarDeclStmt),
317}
318
319/// An enhanced for statement: `for (Type var : iterable) body`.
320#[derive(Debug, Clone, PartialEq, Eq, Hash)]
321pub struct EnhancedForStmt {
322    pub leading_comments: Vec<Comment>,
323    pub for_span: Span,
324    pub paren_span: (Span, Span),
325    pub var_decl: LocalVarDeclStmt,
326    pub colon_span: Span,
327    pub iterable: Expr,
328    pub body: Box<Stmt>,
329}
330
331impl EnhancedForStmt {
332    pub fn span(&self) -> Span {
333        let start = self
334            .leading_comments
335            .first()
336            .map_or(self.for_span, |c| c.span);
337        start.join(self.body.span())
338    }
339}
340
341/// A break or continue statement.
342#[derive(Debug, Clone, PartialEq, Eq, Hash)]
343pub struct JumpStmt {
344    pub leading_comments: Vec<Comment>,
345    pub keyword_span: Span,
346    pub label: Option<Ident>,
347    pub semi_span: Span,
348}
349
350impl JumpStmt {
351    pub fn span(&self) -> Span {
352        let start = self
353            .leading_comments
354            .first()
355            .map_or(self.keyword_span, |c| c.span);
356        start.join(self.semi_span)
357    }
358}
359
360/// A return statement.
361#[derive(Debug, Clone, PartialEq, Eq, Hash)]
362pub struct ReturnStmt {
363    pub leading_comments: Vec<Comment>,
364    pub return_span: Span,
365    pub value: Option<Expr>,
366    pub semi_span: Span,
367}
368
369impl ReturnStmt {
370    pub fn span(&self) -> Span {
371        let start = self
372            .leading_comments
373            .first()
374            .map_or(self.return_span, |c| c.span);
375        start.join(self.semi_span)
376    }
377}
378
379/// A throw statement.
380#[derive(Debug, Clone, PartialEq, Eq, Hash)]
381pub struct ThrowStmt {
382    pub leading_comments: Vec<Comment>,
383    pub throw_span: Span,
384    pub expr: Expr,
385    pub semi_span: Span,
386}
387
388impl ThrowStmt {
389    pub fn span(&self) -> Span {
390        let start = self
391            .leading_comments
392            .first()
393            .map_or(self.throw_span, |c| c.span);
394        start.join(self.semi_span)
395    }
396}
397
398/// A synchronized statement.
399#[derive(Debug, Clone, PartialEq, Eq, Hash)]
400pub struct SynchronizedStmt {
401    pub leading_comments: Vec<Comment>,
402    pub synchronized_span: Span,
403    pub paren_span: (Span, Span),
404    pub lock: Expr,
405    pub body: Block,
406}
407
408impl SynchronizedStmt {
409    pub fn span(&self) -> Span {
410        let start = self
411            .leading_comments
412            .first()
413            .map_or(self.synchronized_span, |c| c.span);
414        start.join(self.body.span())
415    }
416}
417
418/// A try statement.
419#[derive(Debug, Clone, PartialEq, Eq, Hash)]
420pub enum TryStmt {
421    /// `try { ... } catches [finally]`
422    Basic {
423        leading_comments: Vec<Comment>,
424        try_span: Span,
425        block: Block,
426        catches: Vec<CatchClause>,
427        finally_block: Option<(Span, Block)>,
428    },
429    /// `try (resources) { ... } catches [finally]`
430    TryWithResources {
431        leading_comments: Vec<Comment>,
432        try_span: Span,
433        paren_span: (Span, Span),
434        resources: Vec<TryResource>,
435        block: Block,
436        catches: Vec<CatchClause>,
437        finally_block: Option<(Span, Block)>,
438    },
439}
440
441impl TryStmt {
442    pub fn span(&self) -> Span {
443        match self {
444            Self::Basic {
445                leading_comments,
446                try_span,
447                block,
448                finally_block,
449                ..
450            } => {
451                let end = match finally_block {
452                    Some((_, b)) => b.brace_span.1,
453                    None => block.brace_span.1,
454                };
455                let start = leading_comments.first().map_or(*try_span, |c| c.span);
456                start.join(end)
457            }
458            Self::TryWithResources {
459                leading_comments,
460                try_span,
461                block,
462                finally_block,
463                ..
464            } => {
465                let end = match finally_block {
466                    Some((_, b)) => b.brace_span.1,
467                    None => block.brace_span.1,
468                };
469                let start = leading_comments.first().map_or(*try_span, |c| c.span);
470                start.join(end)
471            }
472        }
473    }
474}
475
476/// A catch clause.
477#[derive(Debug, Clone, PartialEq, Eq, Hash)]
478pub struct CatchClause {
479    pub catch_span: Span,
480    pub paren_span: (Span, Span),
481    pub param: CatchParam,
482    pub block: Block,
483}
484
485/// The parameter of a catch clause.
486#[derive(Debug, Clone, PartialEq, Eq, Hash)]
487pub struct CatchParam {
488    pub modifiers: Vec<Modifier>,
489    pub ty: CatchType,
490    pub name: Ident,
491}
492
493/// The type in a catch parameter (can be a union type with `|`).
494#[derive(Debug, Clone, PartialEq, Eq, Hash)]
495pub struct CatchType {
496    pub types: Vec<Type>,
497}
498
499/// A resource in a try-with-resources statement.
500#[derive(Debug, Clone, PartialEq, Eq, Hash)]
501pub enum TryResource {
502    /// A variable declaration: `BufferedReader br = new BufferedReader(...)`
503    Decl(LocalVarDeclStmt),
504    /// A final variable reference: `br` (Java 9+)
505    VarRef(Ident),
506}
507
508/// A yield statement (in switch expressions).
509#[derive(Debug, Clone, PartialEq, Eq, Hash)]
510pub struct YieldStmt {
511    pub leading_comments: Vec<Comment>,
512    pub yield_span: Span,
513    pub value: Expr,
514    pub semi_span: Span,
515}
516
517impl YieldStmt {
518    pub fn span(&self) -> Span {
519        let start = self
520            .leading_comments
521            .first()
522            .map_or(self.yield_span, |c| c.span);
523        start.join(self.semi_span)
524    }
525}