Skip to main content

tsz_parser/parser/
state_expressions.rs

1use tsz_common::diagnostics::diagnostic_codes;
2
3/// Parser state - expression parsing methods
4use super::state::{
5    CONTEXT_FLAG_ARROW_PARAMETERS, CONTEXT_FLAG_ASYNC, CONTEXT_FLAG_GENERATOR,
6    CONTEXT_FLAG_IN_CONDITIONAL_TRUE, ParserState,
7};
8use crate::parser::{
9    NodeIndex, NodeList,
10    node::{
11        AccessExprData, BinaryExprData, CallExprData, ConditionalExprData, FunctionData,
12        IdentifierData, TaggedTemplateData, UnaryExprData, UnaryExprDataEx,
13    },
14    node_flags, syntax_kind_ext,
15};
16use tsz_common::interner::Atom;
17use tsz_scanner::SyntaxKind;
18
19impl ParserState {
20    // =========================================================================
21    // Parse Methods - Expressions
22    // =========================================================================
23
24    // Parse an expression (including comma operator)
25    pub fn parse_expression(&mut self) -> NodeIndex {
26        // Clear the decorator context when parsing Expression, as it should be
27        // unambiguous when parsing a decorator's parenthesized sub-expression.
28        // This matches tsc's parseExpression() behavior.
29        let saved_flags = self.context_flags;
30        self.context_flags &= !crate::parser::state::CONTEXT_FLAG_IN_DECORATOR;
31
32        let start_pos = self.token_pos();
33        let mut left = self.parse_assignment_expression();
34
35        // Handle comma operator: expr, expr, expr
36        // Comma expressions create a sequence, returning the last value
37        while self.is_token(SyntaxKind::CommaToken) {
38            self.next_token(); // consume comma
39            let right = self.parse_assignment_expression();
40            if right.is_none() {
41                // Emit TS1109 for trailing comma or missing expression: expr, [missing]
42                self.error_expression_expected();
43                break; // Exit loop to prevent cascading errors
44            }
45            let end_pos = self.token_end();
46
47            left = self.arena.add_binary_expr(
48                syntax_kind_ext::BINARY_EXPRESSION,
49                start_pos,
50                end_pos,
51                BinaryExprData {
52                    left,
53                    operator_token: SyntaxKind::CommaToken as u16,
54                    right,
55                },
56            );
57        }
58
59        self.context_flags = saved_flags;
60        left
61    }
62
63    // Parse assignment expression
64    pub(crate) fn parse_assignment_expression(&mut self) -> NodeIndex {
65        // Check for arrow function first (including async arrow)
66        let lookahead_token = self.current_token;
67        let lookahead_state = self.scanner.save_state();
68        let is_arrow_start = self.is_start_of_arrow_function();
69        self.scanner.restore_state(lookahead_state);
70        self.current_token = lookahead_token;
71        if is_arrow_start {
72            // Check if it's an async arrow function
73            // Note: `async => x` is a NON-async arrow where 'async' is the parameter name
74            // `async x => x` or `async (x) => x` are async arrow functions
75            if self.is_token(SyntaxKind::AsyncKeyword) {
76                // Need to distinguish:
77                // - `async => expr` (non-async, 'async' is param)
78                // - `async x => expr` or `async (x) => expr` (async arrow)
79                if self.look_ahead_is_simple_arrow_function() {
80                    // async => expr - treat 'async' as identifier parameter
81                    return self.parse_arrow_function_expression_with_async(false);
82                }
83                return self.parse_async_arrow_function_expression();
84            }
85            return self.parse_arrow_function_expression_with_async(false);
86        }
87
88        // Start at precedence 2 to skip comma operator (precedence 1)
89        // Comma expressions are only valid in certain contexts (e.g., for loop)
90        self.parse_binary_expression(2)
91    }
92
93    // Parse async arrow function: async (x) => ... or async x => ...
94    pub(crate) fn parse_async_arrow_function_expression(&mut self) -> NodeIndex {
95        self.parse_expected(SyntaxKind::AsyncKeyword);
96        self.parse_arrow_function_expression_with_async(true)
97    }
98
99    // Check if we're at the start of an arrow function
100    pub(crate) fn is_start_of_arrow_function(&mut self) -> bool {
101        match self.token() {
102            // (params) => ...
103            SyntaxKind::OpenParenToken => self.look_ahead_is_arrow_function(),
104            // async could be:
105            // 1. async (x) => ... or async x => ... (async arrow function)
106            // 2. async => ... (non-async arrow where 'async' is parameter name)
107            SyntaxKind::AsyncKeyword => {
108                // Check if 'async' is immediately followed by '=>'
109                // If so, it's 'async' used as parameter name, not async modifier
110                if self.look_ahead_is_simple_arrow_function() {
111                    // async => expr - treat as simple arrow with 'async' as param
112                    true
113                } else {
114                    // Check for async (x) => ... or async x => ...
115                    self.look_ahead_is_arrow_function_after_async()
116                }
117            }
118            // <T>(x) => ... (generic arrow function)
119            SyntaxKind::LessThanToken => self.look_ahead_is_generic_arrow_function(),
120            _ => {
121                // In generator context, 'yield' is always a yield expression, never an arrow parameter
122                // Example: function * foo(a = yield => yield) {} - first 'yield' is expression, not param
123                if self.in_generator_context() && self.is_token(SyntaxKind::YieldKeyword) {
124                    return false;
125                }
126                // In async context (including parameter defaults), 'await' cannot start an arrow function
127                // Example: async (a = await => x) => {} - 'await' triggers TS1109, not treated as arrow param
128                if self.in_async_context() && self.is_token(SyntaxKind::AwaitKeyword) {
129                    return false;
130                }
131                self.is_identifier_or_keyword() && self.look_ahead_is_simple_arrow_function()
132            }
133        }
134    }
135
136    // Look ahead to see if < starts a generic arrow function: <T>(x) => or <T, U>() =>
137    pub(crate) fn look_ahead_is_generic_arrow_function(&mut self) -> bool {
138        let snapshot = self.scanner.save_state();
139        let current = self.current_token;
140
141        // Skip <
142        self.next_token();
143
144        // Skip type parameters until we find >
145        let mut depth = 1;
146        while depth > 0 && !self.is_token(SyntaxKind::EndOfFileToken) {
147            if self.is_token(SyntaxKind::LessThanToken) {
148                depth += 1;
149            } else if self.is_token(SyntaxKind::GreaterThanToken) {
150                depth -= 1;
151            }
152            self.next_token();
153        }
154
155        // After >, should have (
156        if !self.is_token(SyntaxKind::OpenParenToken) {
157            self.scanner.restore_state(snapshot);
158            self.current_token = current;
159            return false;
160        }
161
162        // Now check if this is an arrow function
163        let result = self.look_ahead_is_arrow_function();
164
165        self.scanner.restore_state(snapshot);
166        self.current_token = current;
167        result
168    }
169
170    // Look ahead after async to see if it's an arrow function: async (x) => or async x => or async <T>(x) =>
171    //
172    // ASI Rule: If there's a line break after 'async', it's NOT an async arrow function.
173    // The line break prevents 'async' from being treated as a modifier.
174    // Example: `async\nx => x` parses as `async; (x => x);` not as an async arrow function.
175    pub(crate) fn look_ahead_is_arrow_function_after_async(&mut self) -> bool {
176        let snapshot = self.scanner.save_state();
177        let current = self.current_token;
178
179        // Skip 'async'
180        self.next_token();
181
182        // Check for line break AFTER 'async' — if the next token has a preceding
183        // line break, 'async' is not a modifier (ASI applies)
184        if self.scanner.has_preceding_line_break() {
185            self.scanner.restore_state(snapshot);
186            self.current_token = current;
187            return false;
188        }
189
190        let result = match self.token() {
191            // async (params) => ...
192            SyntaxKind::OpenParenToken => self.look_ahead_is_arrow_function(),
193            // async x => ...
194            SyntaxKind::Identifier => self.look_ahead_is_simple_arrow_function(),
195            // async <T>(x) => ... (generic async arrow)
196            SyntaxKind::LessThanToken => self.look_ahead_is_generic_arrow_function(),
197            _ => false,
198        };
199
200        self.scanner.restore_state(snapshot);
201        self.current_token = current;
202        result
203    }
204
205    // Look ahead to see if ( starts an arrow function: () => or (x) => or (x, y) =>
206    //
207    // ASI Rule: If there's a line break between ) and =>, it's NOT an arrow function.
208    // Example: `(x)\n=> y` should NOT be parsed as an arrow function.
209    pub(crate) fn look_ahead_is_arrow_function(&mut self) -> bool {
210        let snapshot = self.scanner.save_state();
211        let current = self.current_token;
212
213        // Skip (
214        self.next_token();
215
216        // Empty params: () => or (): type =>
217        if self.is_token(SyntaxKind::CloseParenToken) {
218            self.next_token();
219            // Check for line break before =>
220            let has_line_break = self.scanner.has_preceding_line_break();
221            let is_arrow = if has_line_break {
222                // Line break before => — still parse as arrow function but TS1200 will
223                // be emitted during actual parsing. Empty parens `()` can't be a valid
224                // expression, so this must be arrow function params.
225                self.is_token(SyntaxKind::EqualsGreaterThanToken)
226                    || self.is_token(SyntaxKind::OpenBraceToken)
227            } else if self.is_token(SyntaxKind::ColonToken) {
228                // (): is definitely an arrow function with a return type annotation.
229                // Empty parens () are never a valid expression, so ():
230                // can only appear as arrow function parameters + return type.
231                // Don't try to parse the return type here - the type parser
232                // can greedily consume past the arrow's => (e.g. for function
233                // types like `(): (() => T) => body`).
234                true
235            } else {
236                // Check for => or { (error recovery: user forgot =>)
237                self.is_token(SyntaxKind::EqualsGreaterThanToken)
238                    || self.is_token(SyntaxKind::OpenBraceToken)
239            };
240            self.scanner.restore_state(snapshot);
241            self.current_token = current;
242            return is_arrow;
243        }
244
245        // Skip to matching ) to check for =>
246        let mut depth = 1;
247        while depth > 0 && !self.is_token(SyntaxKind::EndOfFileToken) {
248            if self.is_token(SyntaxKind::OpenParenToken) {
249                depth += 1;
250            } else if self.is_token(SyntaxKind::CloseParenToken) {
251                depth -= 1;
252            }
253            self.next_token();
254        }
255
256        // Check for line break before =>
257        let has_line_break = self.scanner.has_preceding_line_break();
258
259        // Check for optional return type annotation.
260        // Important: check for `:` (return type) BEFORE checking has_line_break.
261        // `(params): type =>` is unambiguously an arrow function regardless of
262        // line breaks — TS1200 handles line terminator errors separately.
263        let is_arrow = if self.is_token(SyntaxKind::ColonToken) {
264            // When we see `:` after `)`, it could be either:
265            // 1. A return type annotation for an arrow function: (x): T => body
266            // 2. The else separator of a conditional: a ? (x) : y
267            // Disambiguate by checking for `=>` after a return type.
268            self.next_token();
269            let saved_arena_len = self.arena.nodes.len();
270            let saved_diagnostics_len = self.parse_diagnostics.len();
271            let _ = self.parse_return_type();
272            // After parsing the return type, check for `=>` or `{`. Line breaks
273            // between the return type and `=>` are allowed here — TS1200 will be
274            // emitted during actual parsing. The `(params): type` prefix is
275            // unambiguous, so we don't need the line break check.
276            let mut result = self.is_token(SyntaxKind::EqualsGreaterThanToken)
277                || self.is_token(SyntaxKind::OpenBraceToken);
278
279            // In the true branch of a conditional expression, only accept
280            // `(x): T => ...` as an arrow function when the simulated body
281            // leaves a `:` token. This matches TypeScript's disambiguation.
282            if (self.context_flags & CONTEXT_FLAG_IN_CONDITIONAL_TRUE) != 0 {
283                if result && self.is_token(SyntaxKind::EqualsGreaterThanToken) {
284                    let body_snapshot = self.scanner.save_state();
285                    let body_token = self.current_token;
286                    let body_arena_len = self.arena.nodes.len();
287                    let body_diagnostics_len = self.parse_diagnostics.len();
288
289                    self.next_token();
290                    let _ = self.parse_assignment_expression();
291                    result = self.is_token(SyntaxKind::ColonToken)
292                        && !self.scanner.has_preceding_line_break();
293
294                    self.arena.nodes.truncate(body_arena_len);
295                    self.parse_diagnostics.truncate(body_diagnostics_len);
296                    self.scanner.restore_state(body_snapshot);
297                    self.current_token = body_token;
298                } else {
299                    result = false;
300                }
301            }
302
303            self.arena.nodes.truncate(saved_arena_len);
304            self.parse_diagnostics.truncate(saved_diagnostics_len);
305
306            result
307        } else if has_line_break {
308            // Line break before => — still parse as arrow function but TS1200 will
309            // be emitted during actual parsing. Parenthesized params `(x, y)` followed
310            // by `=>` are unambiguously arrow function params even with line breaks.
311            self.is_token(SyntaxKind::EqualsGreaterThanToken)
312                || self.is_token(SyntaxKind::OpenBraceToken)
313        } else {
314            // Check for => or { (error recovery: user forgot =>)
315            self.is_token(SyntaxKind::EqualsGreaterThanToken)
316                || self.is_token(SyntaxKind::OpenBraceToken)
317        };
318        self.scanner.restore_state(snapshot);
319        self.current_token = current;
320        is_arrow
321    }
322
323    // Look ahead to see if identifier is followed by => (simple arrow function)
324    //
325    // If there's a line break between the identifier and =>, this is still
326    // recognized as an arrow function but TS1200 will be emitted during parsing.
327    // `=>` cannot start a statement, so there is no ASI ambiguity.
328    pub(crate) fn look_ahead_is_simple_arrow_function(&mut self) -> bool {
329        let snapshot = self.scanner.save_state();
330        let current = self.current_token;
331
332        // Skip identifier
333        self.next_token();
334
335        // Check if => follows the identifier. Line breaks before => are
336        // allowed here — TS1200 will be emitted during actual parsing.
337        let is_arrow = if self.is_token(SyntaxKind::EqualsGreaterThanToken) {
338            true
339        } else if self.is_token(SyntaxKind::ColonToken)
340            && (self.context_flags & CONTEXT_FLAG_IN_CONDITIONAL_TRUE) == 0
341        {
342            // Support single-parameter typed arrows in lookahead: `x: T => expr`.
343            let saved_arena_len = self.arena.nodes.len();
344            let saved_diagnostics_len = self.parse_diagnostics.len();
345
346            self.next_token();
347            let _ = self.parse_type();
348            let result = !self.scanner.has_preceding_line_break()
349                && self.is_token(SyntaxKind::EqualsGreaterThanToken);
350
351            self.arena.nodes.truncate(saved_arena_len);
352            self.parse_diagnostics.truncate(saved_diagnostics_len);
353            result
354        } else {
355            false
356        };
357
358        self.scanner.restore_state(snapshot);
359        self.current_token = current;
360        is_arrow
361    }
362
363    // Parse arrow function expression: (params) => body or x => body or <T>(x) => body
364    pub(crate) fn parse_arrow_function_expression_with_async(
365        &mut self,
366        is_async: bool,
367    ) -> NodeIndex {
368        let start_pos = self.token_pos();
369
370        // Set async context BEFORE parsing parameters
371        // This is important for correctly handling 'await' in parameter defaults:
372        // - `async (a = await) => {}` should emit TS1109 (Expression expected)
373        // - TSC sets async context for the entire async function scope including parameters
374        let saved_flags = self.context_flags;
375
376        // Arrow functions cannot be generators (there's no `*=>` syntax)
377        // Clear generator context to allow 'yield' as an identifier
378        // Example: function * foo(a = yield => yield) {} - both 'yield' are identifiers
379        self.context_flags &= !CONTEXT_FLAG_GENERATOR;
380
381        if is_async {
382            self.context_flags |= CONTEXT_FLAG_ASYNC;
383        }
384
385        // Parse optional type parameters: <T, U extends Foo>
386        let type_parameters = self
387            .is_token(SyntaxKind::LessThanToken)
388            .then(|| self.parse_type_parameters());
389
390        // Parse parameters
391        let parameters = if self.is_token(SyntaxKind::OpenParenToken) {
392            // Parenthesized parameter list: (a, b) =>
393            self.parse_expected(SyntaxKind::OpenParenToken);
394            self.context_flags |= CONTEXT_FLAG_ARROW_PARAMETERS;
395            let params = self.parse_parameter_list();
396            self.context_flags &= !CONTEXT_FLAG_ARROW_PARAMETERS;
397            self.parse_expected(SyntaxKind::CloseParenToken);
398            params
399        } else {
400            // Single identifier parameter: x => or async => (where async is used as identifier)
401            let param_start = self.token_pos();
402            // Use parse_identifier_name to allow keywords like 'async' as parameter names
403            let name = self.parse_identifier_name();
404            let param_end = self.token_end();
405
406            let param = self.arena.add_parameter(
407                syntax_kind_ext::PARAMETER,
408                param_start,
409                param_end,
410                crate::parser::node::ParameterData {
411                    modifiers: None,
412                    dot_dot_dot_token: false,
413                    name,
414                    question_token: false,
415                    type_annotation: NodeIndex::NONE,
416                    initializer: NodeIndex::NONE,
417                },
418            );
419            self.make_node_list(vec![param])
420        };
421
422        // Parse optional return type annotation (supports type predicates: x is T)
423        let type_annotation = if self.parse_optional(SyntaxKind::ColonToken) {
424            self.parse_return_type()
425        } else {
426            NodeIndex::NONE
427        };
428
429        // Check for line terminator before arrow (TS1200)
430        // The spec forbids a line break between `)` and `=>` in arrow functions,
431        // but we still parse it as an arrow function to match TSC behavior.
432        if self.scanner.has_preceding_line_break()
433            && self.is_token(SyntaxKind::EqualsGreaterThanToken)
434        {
435            self.parse_error_at_current_token(
436                "Line terminator not permitted before arrow.",
437                diagnostic_codes::LINE_TERMINATOR_NOT_PERMITTED_BEFORE_ARROW,
438            );
439        }
440
441        // Recovery: Handle missing fat arrow - common typo: (a, b) { return a; }
442        // If we see { immediately after parameters/return type, the user forgot =>
443        if self.is_token(SyntaxKind::OpenBraceToken) {
444            self.parse_error_at_current_token("'=>' expected.", diagnostic_codes::EXPECTED);
445            // Don't consume the {, just continue to body parsing
446            // The arrow is logically present but missing
447        } else {
448            // TypeScript rule: Line terminator is not permitted before arrow (TS1200)
449            // Example: `f(() \n => {})` should emit TS1200 at the =>
450            if self.scanner.has_preceding_line_break()
451                && self.is_token(SyntaxKind::EqualsGreaterThanToken)
452            {
453                self.parse_error_at_current_token(
454                    "Line terminator not permitted before arrow.",
455                    diagnostic_codes::LINE_TERMINATOR_NOT_PERMITTED_BEFORE_ARROW,
456                );
457                // Still consume the => token to continue parsing
458                self.next_token();
459            } else {
460                // Normal case: expect =>
461                self.parse_expected(SyntaxKind::EqualsGreaterThanToken);
462            }
463        }
464
465        // Async context was already set at the start of this function for parameter parsing
466        // and remains set for body parsing
467
468        // Parse body (block or expression)
469        // Push a new label scope for arrow function bodies
470        self.push_label_scope();
471        let body = if self.is_token(SyntaxKind::OpenBraceToken) {
472            self.parse_block()
473        } else {
474            // Check if next token starts a statement but not an expression
475            // This catches cases like `() => var x` where `{` was expected
476            // But NOT semicolons — `() => ;` should emit TS1109 "Expression expected"
477            if self.is_statement_start()
478                && !self.is_expression_start()
479                && !self.is_token(SyntaxKind::SemicolonToken)
480            {
481                self.error_token_expected("{");
482            }
483            let expr = self.parse_assignment_expression();
484            // If no expression was parsed (e.g. `() => ;`), emit TS1109
485            if expr.is_none() {
486                self.error_expression_expected();
487            }
488            expr
489        };
490        self.pop_label_scope();
491
492        // Restore context flags
493        self.context_flags = saved_flags;
494
495        let end_pos = self.token_end();
496
497        self.arena.add_function(
498            syntax_kind_ext::ARROW_FUNCTION,
499            start_pos,
500            end_pos,
501            FunctionData {
502                modifiers: None,
503                is_async,
504                asterisk_token: false,
505                name: NodeIndex::NONE,
506                type_parameters,
507                parameters,
508                type_annotation,
509                body,
510                equals_greater_than_token: true,
511            },
512        )
513    }
514
515    // Parse type parameters: <T, U extends Foo, V = `DefaultType`>
516    pub(crate) fn parse_type_parameters(&mut self) -> NodeList {
517        let mut params = Vec::new();
518
519        self.parse_expected(SyntaxKind::LessThanToken);
520
521        // Check for empty type parameter list: <>
522        // TypeScript reports TS1098: "Type parameter list cannot be empty"
523        if self.is_token(SyntaxKind::GreaterThanToken) {
524            self.parse_error_at_current_token(
525                "Type parameter list cannot be empty.",
526                diagnostic_codes::TYPE_PARAMETER_LIST_CANNOT_BE_EMPTY,
527            );
528        }
529
530        while !self.is_greater_than_or_compound() && !self.is_token(SyntaxKind::EndOfFileToken) {
531            let param = self.parse_type_parameter();
532            params.push(param);
533
534            if !self.parse_optional(SyntaxKind::CommaToken) {
535                break;
536            }
537        }
538
539        self.parse_expected_greater_than();
540
541        self.make_node_list(params)
542    }
543
544    // Parse a single type parameter: T or T extends U or T = Default or T extends U = Default
545    // Also supports modifiers: `const T`, `in T`, `out T`, `in out T`, `const in T`, etc.
546    pub(crate) fn parse_type_parameter(&mut self) -> NodeIndex {
547        let start_pos = self.token_pos();
548
549        // Parse optional modifiers: const, in, out (TypeScript 4.7+ variance, 5.0+ const)
550        let modifiers = self.parse_type_parameter_modifiers();
551
552        // Parse the type parameter name
553        let name = self.parse_identifier();
554
555        // Parse optional constraint: extends SomeType
556        let constraint = if self.parse_optional(SyntaxKind::ExtendsKeyword) {
557            self.parse_type()
558        } else {
559            NodeIndex::NONE
560        };
561
562        // Parse optional default: = DefaultType
563        let default = if self.parse_optional(SyntaxKind::EqualsToken) {
564            self.parse_type()
565        } else {
566            NodeIndex::NONE
567        };
568
569        let end_pos = self.token_end();
570
571        self.arena.add_type_parameter(
572            syntax_kind_ext::TYPE_PARAMETER,
573            start_pos,
574            end_pos,
575            crate::parser::node::TypeParameterData {
576                modifiers,
577                name,
578                constraint,
579                default,
580            },
581        )
582    }
583
584    // Parse type parameter modifiers: `const`, `in`, `out`
585    fn parse_type_parameter_modifiers(&mut self) -> Option<NodeList> {
586        let mut modifiers = Vec::new();
587
588        loop {
589            match self.token() {
590                SyntaxKind::ConstKeyword => {
591                    let pos = self.token_pos();
592                    let end = self.token_end();
593                    self.next_token();
594                    modifiers.push(
595                        self.arena
596                            .add_token(SyntaxKind::ConstKeyword as u16, pos, end),
597                    );
598                }
599                SyntaxKind::InKeyword => {
600                    let pos = self.token_pos();
601                    let end = self.token_end();
602                    self.next_token();
603                    modifiers.push(self.arena.add_token(SyntaxKind::InKeyword as u16, pos, end));
604                }
605                SyntaxKind::OutKeyword => {
606                    let pos = self.token_pos();
607                    let end = self.token_end();
608                    self.next_token();
609                    modifiers.push(
610                        self.arena
611                            .add_token(SyntaxKind::OutKeyword as u16, pos, end),
612                    );
613                }
614                _ => break,
615            }
616        }
617
618        if modifiers.is_empty() {
619            None
620        } else {
621            Some(self.make_node_list(modifiers))
622        }
623    }
624
625    // Parse binary expression with precedence climbing
626    pub(crate) fn parse_binary_expression(&mut self, min_precedence: u8) -> NodeIndex {
627        let start_pos = self.token_pos();
628        if !self.enter_recursion() {
629            return NodeIndex::NONE;
630        }
631
632        let left = self.parse_binary_expression_chain(min_precedence, start_pos);
633        self.exit_recursion();
634        left
635    }
636
637    fn parse_binary_expression_chain(&mut self, min_precedence: u8, start_pos: u32) -> NodeIndex {
638        let mut left = self.parse_unary_expression();
639
640        loop {
641            let op = if self.is_token(SyntaxKind::GreaterThanToken) {
642                self.try_rescan_greater_token()
643            } else {
644                self.token()
645            };
646
647            if !self.in_parenthesized_expression_context()
648                && op == SyntaxKind::BarBarToken
649                && self.is_assignment_target_with_block_bodied_arrow(left)
650            {
651                break;
652            }
653
654            let precedence = self.get_operator_precedence(op);
655            if precedence == 0 || precedence < min_precedence {
656                break;
657            }
658
659            if op == SyntaxKind::AsKeyword || op == SyntaxKind::SatisfiesKeyword {
660                // `as` and `satisfies` do not bind across line terminators.
661                // `x\nas Type` is two statements via ASI, not a type assertion.
662                if self.scanner.has_preceding_line_break() {
663                    break;
664                }
665                left = self.parse_as_or_satisfies_expression(left, start_pos);
666                continue;
667            }
668
669            left = self.parse_binary_expression_remainder(left, start_pos, op, precedence);
670        }
671
672        left
673    }
674
675    fn is_assignment_target_with_block_bodied_arrow(&self, node: NodeIndex) -> bool {
676        let mut current = node;
677        loop {
678            let Some(node_data) = self.arena.get(current) else {
679                return false;
680            };
681            if node_data.kind != syntax_kind_ext::BINARY_EXPRESSION {
682                return false;
683            }
684
685            let Some(binary) = self.arena.get_binary_expr(node_data) else {
686                return false;
687            };
688            let operator =
689                SyntaxKind::try_from_u16(binary.operator_token).unwrap_or(SyntaxKind::Unknown);
690            if !self.is_assignment_operator(operator) {
691                return false;
692            }
693            if self.is_block_bodied_arrow_function(binary.right) {
694                return true;
695            }
696            current = binary.right;
697        }
698    }
699
700    fn is_block_bodied_arrow_function(&self, node: NodeIndex) -> bool {
701        let Some(node_data) = self.arena.get(node) else {
702            return false;
703        };
704        if node_data.kind != syntax_kind_ext::ARROW_FUNCTION {
705            return false;
706        }
707        let Some(function_data) = self.arena.get_function(node_data) else {
708            return false;
709        };
710        let Some(body_node) = self.arena.get(function_data.body) else {
711            return false;
712        };
713
714        body_node.kind == syntax_kind_ext::BLOCK
715    }
716
717    const fn is_assignment_operator(&self, operator: SyntaxKind) -> bool {
718        matches!(
719            operator,
720            SyntaxKind::EqualsToken
721                | SyntaxKind::PlusEqualsToken
722                | SyntaxKind::MinusEqualsToken
723                | SyntaxKind::AsteriskEqualsToken
724                | SyntaxKind::SlashEqualsToken
725                | SyntaxKind::PercentEqualsToken
726                | SyntaxKind::AsteriskAsteriskEqualsToken
727                | SyntaxKind::LessThanLessThanEqualsToken
728                | SyntaxKind::GreaterThanGreaterThanEqualsToken
729                | SyntaxKind::GreaterThanGreaterThanGreaterThanEqualsToken
730                | SyntaxKind::AmpersandEqualsToken
731                | SyntaxKind::CaretEqualsToken
732                | SyntaxKind::BarEqualsToken
733                | SyntaxKind::BarBarEqualsToken
734                | SyntaxKind::AmpersandAmpersandEqualsToken
735                | SyntaxKind::QuestionQuestionEqualsToken
736        )
737    }
738
739    fn parse_binary_expression_remainder(
740        &mut self,
741        left: NodeIndex,
742        start_pos: u32,
743        op: SyntaxKind,
744        precedence: u8,
745    ) -> NodeIndex {
746        let operator_token = op as u16;
747        self.next_token();
748
749        if op == SyntaxKind::QuestionToken {
750            return self.parse_conditional_expression(left, start_pos);
751        }
752
753        let right = self.parse_binary_expression_rhs(left, op, precedence);
754        let end_pos = self.token_end();
755        let final_right = if right.is_none() { left } else { right };
756
757        self.arena.add_binary_expr(
758            syntax_kind_ext::BINARY_EXPRESSION,
759            start_pos,
760            end_pos,
761            BinaryExprData {
762                left,
763                operator_token,
764                right: final_right,
765            },
766        )
767    }
768
769    fn parse_conditional_expression(&mut self, condition: NodeIndex, start_pos: u32) -> NodeIndex {
770        let saved_flags = self.context_flags;
771        self.context_flags |= CONTEXT_FLAG_IN_CONDITIONAL_TRUE;
772
773        let mut when_true = self.parse_assignment_expression();
774        self.context_flags = saved_flags;
775
776        if when_true.is_none() {
777            self.error_expression_expected();
778            when_true = self.create_missing_expression();
779        }
780
781        self.parse_expected(SyntaxKind::ColonToken);
782        let mut when_false = self.parse_assignment_expression();
783        self.context_flags = saved_flags;
784        if when_false.is_none() {
785            self.error_expression_expected();
786            when_false = self.create_missing_expression();
787        }
788        let end_pos = self.token_end();
789
790        self.arena.add_conditional_expr(
791            syntax_kind_ext::CONDITIONAL_EXPRESSION,
792            start_pos,
793            end_pos,
794            ConditionalExprData {
795                condition,
796                when_true,
797                when_false,
798            },
799        )
800    }
801
802    fn parse_binary_expression_rhs(
803        &mut self,
804        left: NodeIndex,
805        op: SyntaxKind,
806        precedence: u8,
807    ) -> NodeIndex {
808        let is_assignment = matches!(
809            op,
810            SyntaxKind::EqualsToken
811                | SyntaxKind::PlusEqualsToken
812                | SyntaxKind::MinusEqualsToken
813                | SyntaxKind::AsteriskEqualsToken
814                | SyntaxKind::SlashEqualsToken
815                | SyntaxKind::PercentEqualsToken
816                | SyntaxKind::AsteriskAsteriskEqualsToken
817                | SyntaxKind::LessThanLessThanEqualsToken
818                | SyntaxKind::GreaterThanGreaterThanEqualsToken
819                | SyntaxKind::GreaterThanGreaterThanGreaterThanEqualsToken
820                | SyntaxKind::AmpersandEqualsToken
821                | SyntaxKind::CaretEqualsToken
822                | SyntaxKind::BarEqualsToken
823                | SyntaxKind::BarBarEqualsToken
824                | SyntaxKind::AmpersandAmpersandEqualsToken
825                | SyntaxKind::QuestionQuestionEqualsToken
826        );
827        let next_min = if op == SyntaxKind::AsteriskAsteriskToken {
828            precedence
829        } else {
830            precedence + 1
831        };
832        let right = if is_assignment {
833            self.parse_assignment_expression()
834        } else {
835            self.parse_binary_expression(next_min)
836        };
837
838        if right.is_none() {
839            self.error_expression_expected();
840            let recovered = self.try_recover_binary_rhs();
841            if recovered.is_none() {
842                self.resync_to_next_expression_boundary();
843                return left;
844            }
845            return recovered;
846        }
847
848        right
849    }
850
851    // Parse as/satisfies expression: expr as Type, expr satisfies Type
852    // Also handles const assertion: expr as const
853    pub(crate) fn parse_as_or_satisfies_expression(
854        &mut self,
855        expression: NodeIndex,
856        start_pos: u32,
857    ) -> NodeIndex {
858        let is_satisfies = self.is_token(SyntaxKind::SatisfiesKeyword);
859        self.next_token(); // consume 'as' or 'satisfies'
860
861        // Handle 'as const' - const assertion
862        let type_node = if !is_satisfies && self.is_token(SyntaxKind::ConstKeyword) {
863            // Create a token node for 'const' keyword
864            let const_start = self.token_pos();
865            let const_end = self.token_end();
866            self.next_token(); // consume 'const'
867            self.arena
868                .add_token(SyntaxKind::ConstKeyword as u16, const_start, const_end)
869        } else {
870            self.parse_type()
871        };
872        let end_pos = self.token_end();
873
874        let result = self.arena.add_type_assertion(
875            if is_satisfies {
876                syntax_kind_ext::SATISFIES_EXPRESSION
877            } else {
878                syntax_kind_ext::AS_EXPRESSION
879            },
880            start_pos,
881            end_pos,
882            crate::parser::node::TypeAssertionData {
883                expression,
884                type_node,
885            },
886        );
887
888        // Allow chaining: x as T as U
889        if self.is_token(SyntaxKind::AsKeyword) || self.is_token(SyntaxKind::SatisfiesKeyword) {
890            return self.parse_as_or_satisfies_expression(result, start_pos);
891        }
892
893        result
894    }
895
896    // Parse unary expression
897    pub(crate) fn parse_unary_expression(&mut self) -> NodeIndex {
898        match self.token() {
899            SyntaxKind::PlusToken
900            | SyntaxKind::MinusToken
901            | SyntaxKind::AsteriskToken
902            | SyntaxKind::TildeToken
903            | SyntaxKind::ExclamationToken
904            | SyntaxKind::PlusPlusToken
905            | SyntaxKind::MinusMinusToken => {
906                let start_pos = self.token_pos();
907                let operator = self.token() as u16;
908                let is_update_operator = operator == SyntaxKind::PlusPlusToken as u16
909                    || operator == SyntaxKind::MinusMinusToken as u16;
910                self.next_token();
911                // TS1109: ++await and --await are invalid because await expressions
912                // are not valid left-hand-side expressions for increment/decrement
913                if is_update_operator && self.token() == SyntaxKind::AwaitKeyword {
914                    self.error_expression_expected();
915                }
916                let operand = self.parse_unary_expression();
917                if operand.is_none() {
918                    if is_update_operator {
919                        // For `++`/`--` with no operand (e.g., `a++ ++;`), emit TS1109
920                        // unconditionally. Bypass should_report_error() because `++;`
921                        // is a distinct syntactic unit — the TS1109 must not be
922                        // suppressed by a prior TS1005 for `';' expected` at `++`.
923
924                        self.parse_error_at_current_token(
925                            "Expression expected.",
926                            diagnostic_codes::EXPRESSION_EXPECTED,
927                        );
928                    } else {
929                        self.error_expression_expected();
930                    }
931                }
932                let end_pos = self.token_end();
933
934                self.arena.add_unary_expr(
935                    syntax_kind_ext::PREFIX_UNARY_EXPRESSION,
936                    start_pos,
937                    end_pos,
938                    UnaryExprData { operator, operand },
939                )
940            }
941            SyntaxKind::TypeOfKeyword | SyntaxKind::VoidKeyword | SyntaxKind::DeleteKeyword => {
942                let start_pos = self.token_pos();
943                let operator = self.token() as u16;
944                self.next_token();
945                let operand = self.parse_unary_expression();
946                if operand.is_none() {
947                    // Emit TS1109 for incomplete unary expression: typeof[missing], void[missing], delete[missing]
948                    self.error_expression_expected();
949                }
950                let end_pos = self.token_end();
951
952                self.arena.add_unary_expr(
953                    syntax_kind_ext::PREFIX_UNARY_EXPRESSION,
954                    start_pos,
955                    end_pos,
956                    UnaryExprData { operator, operand },
957                )
958            }
959            SyntaxKind::AwaitKeyword => {
960                // Check if 'await' is followed by an expression
961                let snapshot = self.scanner.save_state();
962                let current_token = self.current_token;
963                self.next_token(); // consume 'await'
964                let next_token = self.token();
965                self.scanner.restore_state(snapshot);
966                self.current_token = current_token;
967
968                let has_following_expression = !matches!(
969                    next_token,
970                    SyntaxKind::SemicolonToken
971                        | SyntaxKind::CloseBracketToken
972                        | SyntaxKind::CommaToken
973                        | SyntaxKind::ColonToken
974                        | SyntaxKind::EqualsGreaterThanToken
975                        | SyntaxKind::CloseParenToken
976                        | SyntaxKind::EndOfFileToken
977                        | SyntaxKind::CloseBraceToken
978                );
979
980                // In static block context with a following expression, but NOT in an async context
981                // (i.e., directly in the static block, not in a nested async function),
982                // emit TS18037 and parse as await expression for correct AST structure
983                if self.in_static_block_context()
984                    && !self.in_async_context()
985                    && has_following_expression
986                {
987                    self.parse_error_at_current_token(
988                        "'await' expression cannot be used inside a class static block.",
989                        diagnostic_codes::AWAIT_EXPRESSION_CANNOT_BE_USED_INSIDE_A_CLASS_STATIC_BLOCK,
990                    );
991                    // Fall through to parse as await expression
992                } else if !self.in_async_context()
993                    && has_following_expression
994                    && !self.in_parameter_default_context()
995                {
996                    // Parse as await expression - the checker will emit TS1308
997                    // (not TS1359 from the parser) to match TSC behavior
998                } else if self.in_async_context() && self.in_parameter_default_context() {
999                    // TS2524: 'await' expressions cannot be used in a parameter initializer
1000                    // Emit TS2524 in async functions with parameter defaults, regardless of whether
1001                    // there's a following expression.
1002                    // Examples:
1003                    //   - `async (a = await foo)` → emit TS2524
1004                    //   - `async (a = await)` → emit TS2524 + TS1109 (expression expected)
1005
1006                    self.parse_error_at_current_token(
1007                        "'await' expressions cannot be used in a parameter initializer.",
1008                        diagnostic_codes::AWAIT_EXPRESSIONS_CANNOT_BE_USED_IN_A_PARAMETER_INITIALIZER,
1009                    );
1010                    // Fall through to parse as await expression for error recovery
1011                } else if !self.in_async_context() {
1012                    // NOT in async context - 'await' should be treated as identifier
1013                    // In parameter default context of non-async functions, 'await' is a valid identifier
1014                    if self.in_parameter_default_context() && !has_following_expression {
1015                        // Parse 'await' as regular identifier in parameter defaults of non-async functions
1016                        let start_pos = self.token_pos();
1017                        let end_pos = self.token_end(); // capture end before consuming
1018                        let atom = self.scanner.get_token_atom();
1019                        self.next_token(); // consume the await token
1020                        return self.arena.add_identifier(
1021                            SyntaxKind::Identifier as u16,
1022                            start_pos,
1023                            end_pos,
1024                            crate::parser::node::IdentifierData {
1025                                atom,
1026                                escaped_text: String::from("await"),
1027                                original_text: None,
1028                                type_arguments: None,
1029                            },
1030                        );
1031                    }
1032
1033                    // Outside async context or in other contexts, check if await is used as a bare expression
1034                    // If followed by tokens that can't start an expression, report "Expression expected"
1035                    // Examples where await is a reserved identifier but invalid as expression:
1036                    //   await;  // Error: TS1359 in static blocks (reserved word)
1037                    //   await (1);  // Error: Expression expected (in static blocks)
1038                    //   async (a = await => x) => {}  // Error: Expression expected (before arrow)
1039
1040                    // Special case: Don't emit TS1109 for 'await' in computed property names like { [await]: foo }
1041                    // In this context, 'await' is used as an identifier and CloseBracketToken is expected
1042                    let is_computed_property_context = next_token == SyntaxKind::CloseBracketToken;
1043
1044                    if !has_following_expression && !is_computed_property_context {
1045                        // In static blocks, 'await' used as a bare identifier should emit TS1359
1046                        // (reserved word cannot be used here) to match TSC behavior
1047                        if self.in_static_block_context() {
1048                            self.parse_error_at_current_token(
1049                                "Identifier expected. 'await' is a reserved word that cannot be used here.",
1050                                diagnostic_codes::IDENTIFIER_EXPECTED_IS_A_RESERVED_WORD_THAT_CANNOT_BE_USED_HERE,
1051                            );
1052                        } else {
1053                            self.error_expression_expected();
1054                        }
1055                    }
1056
1057                    // Fall through to parse as identifier/postfix expression
1058                    return self.parse_postfix_expression();
1059                }
1060
1061                // In async context, parse as await expression
1062                let start_pos = self.token_pos();
1063                self.consume_keyword(); // TS1260 check for await keyword with escapes
1064
1065                // In parameter-default context, `await =>` should report missing operand.
1066                // Consume `=>` and the following token to prevent cascading errors
1067                // (e.g., TS1359 for `await` used as arrow body in async context).
1068                if self.in_parameter_default_context()
1069                    && self.is_token(SyntaxKind::EqualsGreaterThanToken)
1070                {
1071                    self.error_expression_expected();
1072                    self.next_token(); // consume `=>`
1073                    // Skip the arrow body token (e.g., second `await`) for recovery
1074                    if !self.is_token(SyntaxKind::CloseParenToken)
1075                        && !self.is_token(SyntaxKind::EndOfFileToken)
1076                    {
1077                        self.next_token();
1078                    }
1079                    let end_pos = self.token_end();
1080                    return self.arena.add_unary_expr_ex(
1081                        syntax_kind_ext::AWAIT_EXPRESSION,
1082                        start_pos,
1083                        end_pos,
1084                        UnaryExprDataEx {
1085                            expression: NodeIndex::NONE,
1086                            asterisk_token: false,
1087                        },
1088                    );
1089                }
1090
1091                // Unlike return/throw, `await` does NOT participate in ASI
1092                // for its operand. `await\n1` parses as `await 1`, not `await; 1;`.
1093                // Only emit TS1109 when the next token truly can't start an expression
1094                // (`;`, `)`, `}`, EOF, etc.), not when there's a line break before a valid expr.
1095                if !self.is_expression_start() {
1096                    self.error_expression_expected();
1097                }
1098
1099                let expression = self.parse_unary_expression();
1100                let end_pos = self.token_end();
1101
1102                self.arena.add_unary_expr_ex(
1103                    syntax_kind_ext::AWAIT_EXPRESSION,
1104                    start_pos,
1105                    end_pos,
1106                    UnaryExprDataEx {
1107                        expression,
1108                        asterisk_token: false,
1109                    },
1110                )
1111            }
1112            SyntaxKind::YieldKeyword => {
1113                if self.in_class_member_name()
1114                    && !self.in_generator_context()
1115                    && !self.is_computed_class_member_yield_expression()
1116                {
1117                    return self.parse_identifier_name();
1118                }
1119
1120                // Check if 'yield' is followed by an expression
1121                let snapshot = self.scanner.save_state();
1122                let current_token = self.current_token;
1123                self.next_token(); // consume 'yield'
1124                // Check for asterisk (yield*)
1125                let has_asterisk = self.is_token(SyntaxKind::AsteriskToken);
1126                if has_asterisk {
1127                    self.next_token();
1128                }
1129                let next_token = self.token();
1130                self.scanner.restore_state(snapshot);
1131                self.current_token = current_token;
1132
1133                let has_following_expression = !matches!(
1134                    next_token,
1135                    SyntaxKind::SemicolonToken
1136                        | SyntaxKind::CloseBracketToken
1137                        | SyntaxKind::CommaToken
1138                        | SyntaxKind::ColonToken
1139                        | SyntaxKind::CloseParenToken
1140                        | SyntaxKind::CloseBraceToken
1141                        | SyntaxKind::EndOfFileToken
1142                );
1143
1144                // Outside a generator context with a following expression or asterisk,
1145                // emit TS1163 and parse as yield expression for correct AST structure.
1146                // This handles both static block contexts and top-level/function contexts
1147                // where `yield foo;` should report "yield is only allowed in a generator body"
1148                // rather than falling through to TS1434 "Unexpected keyword or identifier".
1149                if !self.in_generator_context() && (has_following_expression || has_asterisk) {
1150                    self.parse_error_at_current_token(
1151                        "A 'yield' expression is only allowed in a generator body.",
1152                        diagnostic_codes::A_YIELD_EXPRESSION_IS_ONLY_ALLOWED_IN_A_GENERATOR_BODY,
1153                    );
1154                    // Fall through to parse as yield expression
1155                } else if !self.in_generator_context() {
1156                    // Outside a generator context, 'yield' is a regular identifier,
1157                    // not a yield expression. This mirrors how 'await' is handled
1158                    // outside async contexts.
1159                    // e.g., function f(yield = yield) {} -- 'yield' is an identifier here
1160                    let start_pos = self.token_pos();
1161                    let end_pos = self.token_end();
1162                    let atom = self.scanner.get_token_atom();
1163                    self.next_token();
1164                    return self.arena.add_identifier(
1165                        SyntaxKind::Identifier as u16,
1166                        start_pos,
1167                        end_pos,
1168                        IdentifierData {
1169                            atom,
1170                            escaped_text: String::from("yield"),
1171                            original_text: None,
1172                            type_arguments: None,
1173                        },
1174                    );
1175                }
1176
1177                let start_pos = self.token_pos();
1178
1179                // Check if 'yield' is used in a parameter default context
1180                // TS2523: 'yield' expressions cannot be used in a parameter initializer
1181                if self.in_generator_context() && self.in_parameter_default_context() {
1182                    self.parse_error_at_current_token(
1183                        "'yield' expressions cannot be used in a parameter initializer.",
1184                        diagnostic_codes::YIELD_EXPRESSIONS_CANNOT_BE_USED_IN_A_PARAMETER_INITIALIZER,
1185                    );
1186                    // Fall through to parse as yield expression
1187                }
1188
1189                self.consume_keyword(); // TS1260 check for yield keyword with escapes
1190
1191                // Check for yield* (delegate yield)
1192                let asterisk_token = self.parse_optional(SyntaxKind::AsteriskToken);
1193
1194                // Parse the expression (may be empty for bare yield)
1195                let expression = if !self.scanner.has_preceding_line_break()
1196                    && !self.is_token(SyntaxKind::SemicolonToken)
1197                    && !self.is_token(SyntaxKind::CloseBraceToken)
1198                    && !self.is_token(SyntaxKind::CloseParenToken)
1199                    && !self.is_token(SyntaxKind::CloseBracketToken)
1200                    && !self.is_token(SyntaxKind::ColonToken)
1201                    && !self.is_token(SyntaxKind::CommaToken)
1202                    && !self.is_token(SyntaxKind::EqualsGreaterThanToken)
1203                    && !self.is_token(SyntaxKind::EndOfFileToken)
1204                {
1205                    self.parse_assignment_expression()
1206                } else {
1207                    NodeIndex::NONE
1208                };
1209
1210                // yield * requires an expression (TS1109: Expression expected)
1211                if asterisk_token && expression.is_none() {
1212                    self.error_expression_expected();
1213                }
1214
1215                let end_pos = self.token_end();
1216
1217                self.arena.add_unary_expr_ex(
1218                    syntax_kind_ext::YIELD_EXPRESSION,
1219                    start_pos,
1220                    end_pos,
1221                    UnaryExprDataEx {
1222                        expression,
1223                        asterisk_token,
1224                    },
1225                )
1226            }
1227            _ => self.parse_postfix_expression(),
1228        }
1229    }
1230
1231    // Parse postfix expression
1232    pub(crate) fn parse_postfix_expression(&mut self) -> NodeIndex {
1233        let start_pos = self.token_pos();
1234        let mut expr = self.parse_left_hand_side_expression();
1235
1236        // Handle postfix operators
1237        if !self.scanner.has_preceding_line_break()
1238            && (self.is_token(SyntaxKind::PlusPlusToken)
1239                || self.is_token(SyntaxKind::MinusMinusToken))
1240        {
1241            let operator = self.token() as u16;
1242            self.next_token();
1243            let end_pos = self.token_end();
1244
1245            expr = self.arena.add_unary_expr(
1246                syntax_kind_ext::POSTFIX_UNARY_EXPRESSION,
1247                start_pos,
1248                end_pos,
1249                UnaryExprData {
1250                    operator,
1251                    operand: expr,
1252                },
1253            );
1254        }
1255
1256        expr
1257    }
1258
1259    // Parse left-hand side expression (member access, call, etc.)
1260    pub(crate) fn parse_left_hand_side_expression(&mut self) -> NodeIndex {
1261        let start_pos = self.token_pos();
1262        let mut expr = self.parse_primary_expression();
1263
1264        loop {
1265            match self.token() {
1266                SyntaxKind::DotToken => {
1267                    if let Some(node) = self.arena.get(expr)
1268                        && node.kind
1269                            == crate::parser::syntax_kind_ext::EXPRESSION_WITH_TYPE_ARGUMENTS
1270                        && let Some(eta) = self.arena.get_expr_type_args(node)
1271                    {
1272                        // TSC emits TS1477 at the `<…>` type-argument span (from `<` to
1273                        // past `>`), not at the whole expression start. This avoids
1274                        // setting THIS_NODE_HAS_ERROR on the expression identifier itself,
1275                        // which would suppress TS2304 for unresolved names like `List`.
1276                        //
1277                        // `<` starts at expression_node.end; `>` ends at node.end.
1278                        // NodeList.pos/end are always 0, so we can't use type_args span.
1279                        let err_pos = self
1280                            .arena
1281                            .get(eta.expression)
1282                            .map_or(node.pos, |expr_node| expr_node.end);
1283                        let err_len = node.end.saturating_sub(err_pos);
1284                        self.parse_error_at(
1285                            err_pos,
1286                            err_len,
1287                            tsz_common::diagnostics::diagnostic_messages::AN_INSTANTIATION_EXPRESSION_CANNOT_BE_FOLLOWED_BY_A_PROPERTY_ACCESS,
1288                            tsz_common::diagnostics::diagnostic_codes::AN_INSTANTIATION_EXPRESSION_CANNOT_BE_FOLLOWED_BY_A_PROPERTY_ACCESS,
1289                        );
1290                    }
1291                    self.next_token();
1292                    // Handle both regular identifiers and private identifiers (#name)
1293                    let name = if self.is_token(SyntaxKind::PrivateIdentifier) {
1294                        self.parse_private_identifier()
1295                    } else if self.is_identifier_or_keyword() {
1296                        // When there's a line break after the dot and the current token
1297                        // starts a declaration (e.g. `foo.\nvar y = 1;`), don't consume
1298                        // the token as a property name. Instead, emit TS1003 and create
1299                        // a missing identifier. This matches tsc's parseRightSideOfDot.
1300                        if self.scanner.has_preceding_line_break()
1301                            && self.look_ahead_next_is_identifier_or_keyword_on_same_line()
1302                        {
1303                            self.error_identifier_expected();
1304                            NodeIndex::NONE
1305                        } else {
1306                            self.parse_identifier_name()
1307                        }
1308                    } else {
1309                        self.error_identifier_expected();
1310                        NodeIndex::NONE
1311                    };
1312                    let end_pos = self.token_end();
1313
1314                    expr = self.arena.add_access_expr(
1315                        syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION,
1316                        start_pos,
1317                        end_pos,
1318                        AccessExprData {
1319                            expression: expr,
1320                            name_or_argument: name,
1321                            question_dot_token: false,
1322                        },
1323                    );
1324                }
1325                SyntaxKind::OpenBracketToken => {
1326                    // In decorator context, `[` starts a computed property name, not element access
1327                    if (self.context_flags & crate::parser::state::CONTEXT_FLAG_IN_DECORATOR) != 0 {
1328                        break;
1329                    }
1330                    self.next_token();
1331                    let argument = self.parse_expression();
1332                    if argument.is_none() {
1333                        // TS1011: An element access expression should take an argument
1334                        self.parse_error_at_current_token(
1335                            tsz_common::diagnostics::diagnostic_messages::AN_ELEMENT_ACCESS_EXPRESSION_SHOULD_TAKE_AN_ARGUMENT,
1336                            tsz_common::diagnostics::diagnostic_codes::AN_ELEMENT_ACCESS_EXPRESSION_SHOULD_TAKE_AN_ARGUMENT,
1337                        );
1338                    }
1339                    let end_pos = self.token_end();
1340                    self.parse_expected(SyntaxKind::CloseBracketToken);
1341
1342                    expr = self.arena.add_access_expr(
1343                        syntax_kind_ext::ELEMENT_ACCESS_EXPRESSION,
1344                        start_pos,
1345                        end_pos,
1346                        AccessExprData {
1347                            expression: expr,
1348                            name_or_argument: argument,
1349                            question_dot_token: false,
1350                        },
1351                    );
1352                }
1353                SyntaxKind::OpenParenToken => {
1354                    let callee_expr = expr;
1355                    self.next_token();
1356                    let arguments = self.parse_argument_list();
1357                    let end_pos = self.token_end();
1358                    self.parse_expected(SyntaxKind::CloseParenToken);
1359
1360                    let is_optional_chain = self
1361                        .arena
1362                        .get(callee_expr)
1363                        .and_then(|callee_node| self.arena.get_access_expr(callee_node))
1364                        .is_some_and(|access| access.question_dot_token);
1365                    let call_expr = self.arena.add_call_expr(
1366                        syntax_kind_ext::CALL_EXPRESSION,
1367                        start_pos,
1368                        end_pos,
1369                        CallExprData {
1370                            expression: expr,
1371                            type_arguments: None,
1372                            arguments: Some(arguments),
1373                        },
1374                    );
1375                    let optional_chain_flag = self.u16_from_node_flags(node_flags::OPTIONAL_CHAIN);
1376                    if is_optional_chain && let Some(call_node) = self.arena.get_mut(call_expr) {
1377                        call_node.flags |= optional_chain_flag;
1378                    }
1379                    expr = call_expr;
1380                }
1381                // Tagged template literals: tag`template` or tag`head${expr}tail`
1382                SyntaxKind::NoSubstitutionTemplateLiteral | SyntaxKind::TemplateHead => {
1383                    let template = self.parse_template_literal();
1384                    let end_pos = self.token_end();
1385
1386                    expr = self.arena.add_tagged_template(
1387                        syntax_kind_ext::TAGGED_TEMPLATE_EXPRESSION,
1388                        start_pos,
1389                        end_pos,
1390                        TaggedTemplateData {
1391                            tag: expr,
1392                            type_arguments: None,
1393                            template,
1394                        },
1395                    );
1396                }
1397                // Optional chaining: expr?.prop, expr?.[index], expr?.()
1398                SyntaxKind::QuestionDotToken => {
1399                    self.next_token();
1400                    if self.is_less_than_or_compound()
1401                        && let Some(type_args) = self.try_parse_type_arguments_for_call()
1402                    {
1403                        if self.is_token(SyntaxKind::OpenParenToken) {
1404                            // expr?.<T>()
1405                            self.next_token();
1406                            let arguments = self.parse_argument_list();
1407                            let end_pos = self.token_end();
1408                            self.parse_expected(SyntaxKind::CloseParenToken);
1409
1410                            let call_expr = self.arena.add_call_expr(
1411                                syntax_kind_ext::CALL_EXPRESSION,
1412                                start_pos,
1413                                end_pos,
1414                                CallExprData {
1415                                    expression: expr,
1416                                    type_arguments: Some(type_args),
1417                                    arguments: Some(arguments),
1418                                },
1419                            );
1420                            let optional_chain_flag =
1421                                self.u16_from_node_flags(node_flags::OPTIONAL_CHAIN);
1422                            if let Some(call_node) = self.arena.get_mut(call_expr) {
1423                                call_node.flags |= optional_chain_flag;
1424                            }
1425                            expr = call_expr;
1426                            continue;
1427                        } else if self.is_token(SyntaxKind::NoSubstitutionTemplateLiteral)
1428                            || self.is_token(SyntaxKind::TemplateHead)
1429                        {
1430                            let template = self.parse_template_literal();
1431                            let end_pos = self.token_end();
1432
1433                            expr = self.arena.add_tagged_template(
1434                                syntax_kind_ext::TAGGED_TEMPLATE_EXPRESSION,
1435                                start_pos,
1436                                end_pos,
1437                                TaggedTemplateData {
1438                                    tag: expr,
1439                                    type_arguments: Some(type_args),
1440                                    template,
1441                                },
1442                            );
1443                            continue;
1444                        }
1445                    }
1446                    if self.is_token(SyntaxKind::OpenBracketToken) {
1447                        // expr?.[index]
1448                        self.next_token();
1449                        let argument = self.parse_expression();
1450                        let end_pos = self.token_end();
1451                        self.parse_expected(SyntaxKind::CloseBracketToken);
1452
1453                        expr = self.arena.add_access_expr(
1454                            syntax_kind_ext::ELEMENT_ACCESS_EXPRESSION,
1455                            start_pos,
1456                            end_pos,
1457                            AccessExprData {
1458                                expression: expr,
1459                                name_or_argument: argument,
1460                                question_dot_token: true,
1461                            },
1462                        );
1463                    } else if self.is_token(SyntaxKind::OpenParenToken) {
1464                        // expr?.()
1465                        self.next_token();
1466                        let arguments = self.parse_argument_list();
1467                        let end_pos = self.token_end();
1468                        self.parse_expected(SyntaxKind::CloseParenToken);
1469
1470                        let call_expr = self.arena.add_call_expr(
1471                            syntax_kind_ext::CALL_EXPRESSION,
1472                            start_pos,
1473                            end_pos,
1474                            CallExprData {
1475                                expression: expr,
1476                                type_arguments: None,
1477                                arguments: Some(arguments),
1478                            },
1479                        );
1480                        let optional_chain_flag =
1481                            self.u16_from_node_flags(node_flags::OPTIONAL_CHAIN);
1482                        if let Some(call_node) = self.arena.get_mut(call_expr) {
1483                            call_node.flags |= optional_chain_flag;
1484                        }
1485                        expr = call_expr;
1486                    } else {
1487                        // expr?.prop
1488                        let is_private_identifier = self.is_token(SyntaxKind::PrivateIdentifier);
1489                        let name = if is_private_identifier {
1490                            self.parse_private_identifier()
1491                        } else {
1492                            self.parse_identifier_name()
1493                        };
1494
1495                        // TS18030: Optional chain cannot contain private identifiers
1496                        if is_private_identifier && let Some(name_node) = self.arena.get(name) {
1497                            self.parse_error_at(
1498                                    name_node.pos,
1499                                    name_node.end - name_node.pos,
1500                                    "An optional chain cannot contain private identifiers.",
1501                                    diagnostic_codes::AN_OPTIONAL_CHAIN_CANNOT_CONTAIN_PRIVATE_IDENTIFIERS,
1502                                );
1503                        }
1504
1505                        let end_pos = self.token_end();
1506
1507                        expr = self.arena.add_access_expr(
1508                            syntax_kind_ext::PROPERTY_ACCESS_EXPRESSION,
1509                            start_pos,
1510                            end_pos,
1511                            AccessExprData {
1512                                expression: expr,
1513                                name_or_argument: name,
1514                                question_dot_token: true,
1515                            },
1516                        );
1517                    }
1518                }
1519                // Non-null assertion: expr!
1520                SyntaxKind::ExclamationToken => {
1521                    // Non-null assertion only if no line break before
1522                    if self.scanner.has_preceding_line_break() {
1523                        break;
1524                    }
1525                    self.next_token();
1526                    let end_pos = self.token_end();
1527
1528                    expr = self.arena.add_unary_expr_ex(
1529                        syntax_kind_ext::NON_NULL_EXPRESSION,
1530                        start_pos,
1531                        end_pos,
1532                        crate::parser::node::UnaryExprDataEx {
1533                            expression: expr,
1534                            asterisk_token: false,
1535                        },
1536                    );
1537                }
1538                // Type arguments followed by call: expr<T>() or expr<T, U>()
1539                // Also handles `<<` for nested generics: foo<<T>(x: T) => number>(fn)
1540                SyntaxKind::LessThanToken | SyntaxKind::LessThanLessThanToken => {
1541                    // Try to parse as type arguments for a call expression
1542                    // This is tricky because < could be comparison operator
1543                    if let Some(type_args) = self.try_parse_type_arguments_for_call() {
1544                        // After type arguments, we expect ( for a call or ` for tagged template
1545                        if self.is_token(SyntaxKind::OpenParenToken) {
1546                            self.next_token();
1547                            let arguments = self.parse_argument_list();
1548                            let end_pos = self.token_end();
1549                            self.parse_expected(SyntaxKind::CloseParenToken);
1550
1551                            expr = self.arena.add_call_expr(
1552                                syntax_kind_ext::CALL_EXPRESSION,
1553                                start_pos,
1554                                end_pos,
1555                                CallExprData {
1556                                    expression: expr,
1557                                    type_arguments: Some(type_args),
1558                                    arguments: Some(arguments),
1559                                },
1560                            );
1561                        } else if self.is_token(SyntaxKind::NoSubstitutionTemplateLiteral)
1562                            || self.is_token(SyntaxKind::TemplateHead)
1563                        {
1564                            // Tagged template with type arguments: tag<T>`template`
1565                            let template = self.parse_template_literal();
1566                            let end_pos = self.token_end();
1567
1568                            expr = self.arena.add_tagged_template(
1569                                syntax_kind_ext::TAGGED_TEMPLATE_EXPRESSION,
1570                                start_pos,
1571                                end_pos,
1572                                TaggedTemplateData {
1573                                    tag: expr,
1574                                    type_arguments: Some(type_args),
1575                                    template,
1576                                },
1577                            );
1578                        } else {
1579                            // Not a call or tagged template - this is an instantiation expression
1580                            // (e.g., f<string>, new Foo<number>, a<b>?.())
1581                            let end_pos = self.token_end();
1582                            expr = self.arena.add_expr_with_type_args(
1583                                crate::parser::syntax_kind_ext::EXPRESSION_WITH_TYPE_ARGUMENTS,
1584                                start_pos,
1585                                end_pos,
1586                                crate::parser::node::ExprWithTypeArgsData {
1587                                    expression: expr,
1588                                    type_arguments: Some(type_args),
1589                                },
1590                            );
1591                        }
1592                    } else {
1593                        break;
1594                    }
1595                }
1596                _ => break,
1597            }
1598        }
1599
1600        expr
1601    }
1602
1603    // Parse argument list
1604    pub(crate) fn parse_argument_list(&mut self) -> NodeList {
1605        let mut args = Vec::new();
1606
1607        while !self.is_token(SyntaxKind::CloseParenToken) {
1608            if self.is_argument_list_recovery_boundary() {
1609                self.error_argument_expression_expected();
1610                break;
1611            }
1612
1613            if self.is_token(SyntaxKind::DotDotDotToken) {
1614                let spread_start = self.token_pos();
1615                self.next_token();
1616                let expression = self.parse_assignment_expression();
1617                if expression.is_none() {
1618                    // Emit TS1135 for incomplete spread argument: func(...missing)
1619                    self.error_argument_expression_expected();
1620                }
1621                let spread_end = self.token_end();
1622                let spread = self.arena.add_spread(
1623                    syntax_kind_ext::SPREAD_ELEMENT,
1624                    spread_start,
1625                    spread_end,
1626                    crate::parser::node::SpreadData { expression },
1627                );
1628                args.push(spread);
1629            } else if self.is_token(SyntaxKind::CommaToken) {
1630                // TS1135: missing argument before comma: func(a, , c)
1631                self.error_argument_expression_expected();
1632                args.push(NodeIndex::NONE);
1633            } else if self.is_token(SyntaxKind::SemicolonToken) {
1634                // Semicolon terminates argument list — don't emit TS1135 here.
1635                // Let parse_expected(CloseParenToken) emit TS1005 instead,
1636                // matching tsc which treats `;` as a clear boundary.
1637                break;
1638            } else {
1639                let arg = self.parse_assignment_expression();
1640                if arg.is_none() {
1641                    // TS1135 for missing function argument
1642                    self.error_argument_expression_expected();
1643                }
1644                args.push(arg);
1645            }
1646
1647            if !self.parse_optional(SyntaxKind::CommaToken) {
1648                // Missing comma - check if next token looks like another argument
1649                // If so, emit comma error for better diagnostics
1650                if self.is_expression_start()
1651                    && !self.is_token(SyntaxKind::CloseParenToken)
1652                    && !self.is_token(SyntaxKind::EndOfFileToken)
1653                {
1654                    self.error_comma_expected();
1655                    // Continue parsing for error recovery
1656                } else {
1657                    break;
1658                }
1659            }
1660        }
1661
1662        self.make_node_list(args)
1663    }
1664
1665    // Returns true for statement-only keywords that should stop argument parsing
1666    // during recovery to avoid cascading diagnostics.
1667    const fn is_argument_list_recovery_boundary(&self) -> bool {
1668        matches!(
1669            self.token(),
1670            SyntaxKind::ReturnKeyword
1671                | SyntaxKind::BreakKeyword
1672                | SyntaxKind::ContinueKeyword
1673                | SyntaxKind::ThrowKeyword
1674                | SyntaxKind::TryKeyword
1675                | SyntaxKind::CatchKeyword
1676                | SyntaxKind::FinallyKeyword
1677                | SyntaxKind::IfKeyword
1678                | SyntaxKind::ForKeyword
1679                | SyntaxKind::WhileKeyword
1680                | SyntaxKind::DoKeyword
1681                | SyntaxKind::SwitchKeyword
1682                | SyntaxKind::VarKeyword
1683                | SyntaxKind::LetKeyword
1684                | SyntaxKind::ConstKeyword
1685                | SyntaxKind::WithKeyword
1686                | SyntaxKind::DebuggerKeyword
1687                | SyntaxKind::CaseKeyword
1688                | SyntaxKind::DefaultKeyword
1689                | SyntaxKind::ElseKeyword
1690                | SyntaxKind::EndOfFileToken
1691        )
1692    }
1693
1694    // Parse primary expression
1695    pub(crate) fn parse_primary_expression(&mut self) -> NodeIndex {
1696        match self.token() {
1697            SyntaxKind::Identifier => self.parse_identifier(),
1698            SyntaxKind::PrivateIdentifier => self.parse_private_identifier(),
1699            SyntaxKind::NumericLiteral => self.parse_numeric_literal(),
1700            SyntaxKind::BigIntLiteral => self.parse_bigint_literal(),
1701            SyntaxKind::StringLiteral => self.parse_string_literal(),
1702            SyntaxKind::TrueKeyword | SyntaxKind::FalseKeyword => self.parse_boolean_literal(),
1703            SyntaxKind::NullKeyword => self.parse_null_literal(),
1704            SyntaxKind::UndefinedKeyword
1705            | SyntaxKind::AnyKeyword
1706            | SyntaxKind::StringKeyword
1707            | SyntaxKind::NumberKeyword
1708            | SyntaxKind::BooleanKeyword
1709            | SyntaxKind::SymbolKeyword
1710            | SyntaxKind::BigIntKeyword
1711            | SyntaxKind::ObjectKeyword
1712            | SyntaxKind::NeverKeyword
1713            | SyntaxKind::UnknownKeyword
1714            | SyntaxKind::RequireKeyword
1715            | SyntaxKind::ModuleKeyword
1716            | SyntaxKind::AwaitKeyword
1717            | SyntaxKind::YieldKeyword => self.parse_keyword_as_identifier(),
1718            SyntaxKind::ThisKeyword => self.parse_this_expression(),
1719            SyntaxKind::SuperKeyword => self.parse_super_expression(),
1720            SyntaxKind::OpenParenToken => self.parse_parenthesized_expression(),
1721            SyntaxKind::OpenBracketToken => self.parse_array_literal(),
1722            SyntaxKind::OpenBraceToken => self.parse_object_literal(),
1723            SyntaxKind::NewKeyword => self.parse_new_expression(),
1724            SyntaxKind::FunctionKeyword => self.parse_function_expression(),
1725            SyntaxKind::ClassKeyword => self.parse_class_expression(),
1726            SyntaxKind::AtToken => self.parse_decorated_class_expression(),
1727            SyntaxKind::AsyncKeyword => {
1728                // async function expression or async arrow function
1729                if self.look_ahead_is_async_function() {
1730                    self.parse_async_function_expression()
1731                } else {
1732                    // 'async' used as identifier (e.g., variable named async)
1733                    // Use parse_identifier_name since 'async' is a keyword
1734                    self.parse_identifier_name()
1735                }
1736            }
1737            // `<<` at expression start is invalid as a primary expression.
1738            // It is usually an ambiguous generic assertion case that should fall
1739            // through as a malformed left side and then recover with
1740            // TS1109: Expression expected.
1741            SyntaxKind::LessThanLessThanToken => {
1742                self.error_expression_expected();
1743                NodeIndex::NONE
1744            }
1745            SyntaxKind::LessThanToken => self.parse_jsx_element_or_type_assertion(),
1746            SyntaxKind::NoSubstitutionTemplateLiteral => {
1747                self.parse_no_substitution_template_literal()
1748            }
1749            SyntaxKind::TemplateHead => self.parse_template_expression(),
1750            // Regex literal - rescan / or /= as regex
1751            SyntaxKind::SlashToken | SyntaxKind::SlashEqualsToken => self.parse_regex_literal(),
1752            // Dynamic import or import.meta
1753            SyntaxKind::ImportKeyword => self.parse_import_expression(),
1754            // `as` and `satisfies` are binary operators but also valid identifiers.
1755            // When they appear at expression start, they must be identifiers
1756            // (e.g., `var x = as as string` — first `as` is the variable).
1757            SyntaxKind::AsKeyword | SyntaxKind::SatisfiesKeyword => self.parse_identifier_name(),
1758            SyntaxKind::Unknown => {
1759                // TS1127: Invalid character - emit specific error for invalid characters
1760
1761                self.parse_error_at_current_token(
1762                    "Invalid character.",
1763                    diagnostic_codes::INVALID_CHARACTER,
1764                );
1765                let start_pos = self.token_pos();
1766                let end_pos = self.token_end();
1767                self.next_token();
1768                self.arena
1769                    .add_token(SyntaxKind::Unknown as u16, start_pos, end_pos)
1770            }
1771            _ => {
1772                // Don't consume clause boundaries or expression terminators here.
1773                // Let callers decide how to recover so constructs like `switch` can resynchronize
1774                // without losing `case`/`default` tokens.
1775                // ColonToken is a structural delimiter (case clauses, labels, type annotations)
1776                // and must not be consumed as an error token.
1777                if self.is_binary_operator() {
1778                    // Binary operator at expression start means missing LHS.
1779                    // Emit TS1109 matching tsc's parsePrimaryExpression behavior.
1780                    self.error_expression_expected();
1781                    return NodeIndex::NONE;
1782                }
1783                if self.is_at_expression_end()
1784                    || self.is_token(SyntaxKind::CaseKeyword)
1785                    || self.is_token(SyntaxKind::DefaultKeyword)
1786                    || self.is_token(SyntaxKind::ColonToken)
1787                {
1788                    return NodeIndex::NONE;
1789                }
1790
1791                // Statement-only keywords cannot start expressions.
1792                // Return NONE so callers emit TS1109 (Expression expected).
1793                if matches!(
1794                    self.token(),
1795                    SyntaxKind::ReturnKeyword
1796                        | SyntaxKind::BreakKeyword
1797                        | SyntaxKind::ContinueKeyword
1798                        | SyntaxKind::ThrowKeyword
1799                        | SyntaxKind::TryKeyword
1800                        | SyntaxKind::CatchKeyword
1801                        | SyntaxKind::FinallyKeyword
1802                        | SyntaxKind::DoKeyword
1803                        | SyntaxKind::WhileKeyword
1804                        | SyntaxKind::ForKeyword
1805                        | SyntaxKind::SwitchKeyword
1806                        | SyntaxKind::WithKeyword
1807                        | SyntaxKind::DebuggerKeyword
1808                        | SyntaxKind::IfKeyword
1809                        | SyntaxKind::ElseKeyword
1810                ) {
1811                    return NodeIndex::NONE;
1812                }
1813
1814                if self.is_identifier_or_keyword() {
1815                    self.parse_identifier_name()
1816                } else {
1817                    // Unknown primary expression - create an error token
1818                    let start_pos = self.token_pos();
1819                    let end_pos = self.token_end();
1820
1821                    self.error_expression_expected();
1822
1823                    self.next_token();
1824                    self.arena
1825                        .add_token(SyntaxKind::Unknown as u16, start_pos, end_pos)
1826                }
1827            }
1828        }
1829    }
1830
1831    // Parse a decorated class expression: `@dec class C { }`
1832    // Used when `@` is encountered in expression position.
1833    fn parse_decorated_class_expression(&mut self) -> NodeIndex {
1834        let start_pos = self.token_pos();
1835        let decorators = self.parse_decorators();
1836        if self.is_token(SyntaxKind::ClassKeyword) || self.is_token(SyntaxKind::AbstractKeyword) {
1837            self.parse_class_expression_with_decorators(decorators, start_pos)
1838        } else {
1839            // Decorators not followed by class - emit error and create error token
1840            self.error_expression_expected();
1841
1842            self.parse_error_at(
1843                self.token_pos(),
1844                1,
1845                "';' expected.",
1846                diagnostic_codes::EXPECTED,
1847            );
1848            let end_pos = self.token_end();
1849            self.arena
1850                .add_token(SyntaxKind::Unknown as u16, start_pos, end_pos)
1851        }
1852    }
1853
1854    // Parse identifier
1855    // Uses zero-copy accessor and only clones when storing
1856    pub(crate) fn parse_identifier(&mut self) -> NodeIndex {
1857        let start_pos = self.token_pos();
1858        // Capture end position BEFORE consuming the token
1859        let end_pos = self.token_end();
1860
1861        // Check for reserved words that cannot be used as identifiers
1862        // These should emit TS1359 "Identifier expected. '{0}' is a reserved word that cannot be used here."
1863        if self.is_reserved_word() {
1864            self.error_reserved_word_identifier();
1865            // Create a missing identifier placeholder
1866            return self.arena.add_identifier(
1867                SyntaxKind::Identifier as u16,
1868                start_pos,
1869                end_pos,
1870                IdentifierData {
1871                    atom: Atom::NONE,
1872                    escaped_text: String::new(),
1873                    original_text: None,
1874                    type_arguments: None,
1875                },
1876            );
1877        }
1878
1879        // Check if current token is an identifier or keyword that can be used as identifier
1880        // This allows contextual keywords (type, interface, package, etc.) to be used as identifiers
1881        // in appropriate contexts (e.g., type aliases, interface names)
1882        let (atom, text) = if self.is_identifier_or_keyword() {
1883            // OPTIMIZATION: Capture atom for O(1) comparison
1884            let atom = self.scanner.get_token_atom();
1885            // Use zero-copy accessor and clone only when storing
1886            let text = self.scanner.get_token_value_ref().to_string();
1887            self.next_token();
1888            (atom, text)
1889        } else {
1890            self.error_identifier_expected();
1891            (Atom::NONE, String::new())
1892        };
1893
1894        self.arena.add_identifier(
1895            SyntaxKind::Identifier as u16,
1896            start_pos,
1897            end_pos,
1898            IdentifierData {
1899                atom,
1900                escaped_text: text,
1901                original_text: None,
1902                type_arguments: None,
1903            },
1904        )
1905    }
1906
1907    // Parse identifier name - allows keywords to be used as identifiers
1908    // This is used in contexts where keywords are valid identifier names
1909    // (e.g., class names, property names, function names)
1910    pub(crate) fn parse_identifier_name(&mut self) -> NodeIndex {
1911        let start_pos = self.token_pos();
1912        // Capture end position BEFORE consuming the token
1913        let end_pos = self.token_end();
1914        let (atom, text) = if self.is_identifier_or_keyword() {
1915            // OPTIMIZATION: Capture atom for O(1) comparison
1916            let atom = self.scanner.get_token_atom();
1917            let text = self.scanner.get_token_value_ref().to_string();
1918            self.next_token();
1919            (atom, text)
1920        } else {
1921            self.error_identifier_expected();
1922            (Atom::NONE, String::new())
1923        };
1924
1925        self.arena.add_identifier(
1926            SyntaxKind::Identifier as u16,
1927            start_pos,
1928            end_pos,
1929            IdentifierData {
1930                atom,
1931                escaped_text: text,
1932                original_text: None,
1933                type_arguments: None,
1934            },
1935        )
1936    }
1937
1938    // Parse private identifier (#name)
1939    pub(crate) fn parse_private_identifier(&mut self) -> NodeIndex {
1940        let start_pos = self.token_pos();
1941        // Capture end position BEFORE consuming the token
1942        let end_pos = self.token_end();
1943        // OPTIMIZATION: Capture atom for O(1) comparison
1944        let atom = self.scanner.get_token_atom();
1945        let text = self.scanner.get_token_value_ref().to_string();
1946        self.parse_expected(SyntaxKind::PrivateIdentifier);
1947
1948        self.arena.add_identifier(
1949            SyntaxKind::PrivateIdentifier as u16,
1950            start_pos,
1951            end_pos,
1952            IdentifierData {
1953                atom,
1954                escaped_text: text,
1955                original_text: None,
1956                type_arguments: None,
1957            },
1958        )
1959    }
1960
1961    // Binding patterns, literals, array/object literals, property names,
1962    // new/member expressions → state_expressions_literals.rs
1963}