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