/*
 * Copyright (c) 2026-present, the Ladybird developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

//! Declaration parsing: variables, functions, classes, imports, exports.

use crate::fast_hash::{HashMap, HashSet};

use crate::ast::*;
use crate::lexer::ch;
use crate::parser::{
    Associativity, DeclarationKind, ForbiddenTokens, FunctionKind, MethodKind, PRECEDENCE_ASSIGNMENT, ParamInfo,
    ParsedParameters, Parser, Position, ProgramType, PropertyKey,
};
use crate::token::TokenType;

fn expression_into_identifier(expression: Expression) -> IdentifierId {
    match expression.inner {
        ExpressionKind::Identifier(id) => id,
        _ => unreachable!("expected Identifier expression"),
    }
}

/// Extract bound names from a declaration for export statements.
fn get_declaration_export_names(statement: &Statement, arena: &AstArena) -> Vec<Utf16String> {
    match &statement.inner {
        StatementKind::VariableDeclaration(vd) => {
            let mut names = Vec::new();
            for declaration in &vd.declarations {
                collect_declarator_names(&declaration.target, &mut names, arena);
            }
            names
        }
        StatementKind::UsingDeclaration(declarations) => {
            let mut names = Vec::new();
            for declaration in declarations.iter() {
                if let VariableDeclaratorTarget::Identifier(id) = &declaration.target {
                    names.push(arena.name_of(*id).clone());
                }
            }
            names
        }
        StatementKind::FunctionDeclaration(fd) => {
            if let Some(name) = fd.name {
                vec![arena.name_of(name).clone()]
            } else {
                Vec::new()
            }
        }
        StatementKind::ClassDeclaration(class) => {
            if let Some(name) = class.name {
                vec![arena.name_of(name).clone()]
            } else {
                Vec::new()
            }
        }
        _ => Vec::new(),
    }
}

fn collect_declarator_names(target: &VariableDeclaratorTarget, names: &mut Vec<Utf16String>, arena: &AstArena) {
    match target {
        VariableDeclaratorTarget::Identifier(id) => names.push(arena.name_of(*id).clone()),
        VariableDeclaratorTarget::BindingPattern(pat) => collect_pattern_names(pat, names, arena),
    }
}

fn collect_pattern_names(pat: &BindingPattern, names: &mut Vec<Utf16String>, arena: &AstArena) {
    for entry in &pat.entries {
        match &entry.alias {
            Some(BindingEntryAlias::Identifier(id)) => names.push(arena.name_of(*id).clone()),
            Some(BindingEntryAlias::BindingPattern(nested)) => collect_pattern_names(nested, names, arena),
            _ => {}
        }
        if entry.alias.is_none()
            && let Some(BindingEntryName::Identifier(id)) = &entry.name
        {
            names.push(arena.name_of(*id).clone());
        }
    }
}

impl Parser<'_> {
    pub(crate) fn parse_declaration(&mut self) -> Statement {
        if self.match_token(TokenType::Async) {
            let next = self.next_token();
            if next.token_type == TokenType::Function && !next.trivia_has_line_terminator {
                return self.parse_function_declaration();
            }
        }

        match self.current_token_type() {
            TokenType::Function => self.parse_function_declaration(),
            TokenType::Class => self.parse_class_declaration(),
            TokenType::Let | TokenType::Const => self.parse_variable_declaration(false),
            TokenType::Identifier if self.token_value(&self.current_token) == utf16!("using") => {
                if !self.scope_collector.can_have_using_declaration() {
                    self.syntax_error("'using' not allowed outside of block, for loop or function");
                }
                self.parse_using_declaration(false)
            }
            _ => {
                self.expected("declaration");
                let start = self.position();
                self.consume();
                self.statement(start, StatementKind::Empty)
            }
        }
    }

    // https://tc39.es/ecma262/#sec-variable-statement
    // https://tc39.es/ecma262/#sec-let-and-const-declarations
    // VariableStatement : `var` VariableDeclarationList `;`
    // LexicalDeclaration : LetOrConst BindingList `;`
    // NB: `var` declarations are hoisted to the enclosing function/script scope,
    // while `let`/`const` are block-scoped (sec-declarations-and-the-variable-statement).
    pub(crate) fn parse_variable_declaration(&mut self, is_for_loop: bool) -> Statement {
        let start = self.position();
        let declaration_line = self.current_token().line_number;
        let declaration_column = self.current_token().line_column;

        let kind = match self.current_token_type() {
            TokenType::Var => DeclarationKind::Var,
            TokenType::Let => DeclarationKind::Let,
            TokenType::Const => DeclarationKind::Const,
            _ => {
                self.expected("variable declaration keyword");
                DeclarationKind::Var
            }
        };
        self.consume();

        let mut declarators: Vec<VariableDeclarator> = Vec::new();
        let mut any_init = false;

        loop {
            let declaration_start = self.position();

            let target = if self.match_identifier() {
                let token = self.consume();
                let name = self.token_identifier_name(&token);
                let name_str = self.arena.strings[name].clone();
                self.check_identifier_name_for_assignment_validity(name_str.as_slice(), false);
                if kind != DeclarationKind::Var && name_str.as_slice() == utf16!("let") {
                    self.syntax_error("Lexical binding may not be called 'let'");
                }
                let id = self.make_identifier(declaration_start, name);

                if kind == DeclarationKind::Var {
                    let Self {
                        scope_collector, arena, ..
                    } = self;
                    scope_collector.add_var_declaration(
                        &[(name_str.as_slice(), Some(id))],
                        declaration_line,
                        declaration_column,
                        Some(DeclarationKind::Var),
                        &mut arena.identifiers,
                        &arena.strings,
                        &mut arena.scopes,
                    );
                } else {
                    self.scope_collector.add_lexical_declaration(
                        &[name_str.as_slice()],
                        declaration_line,
                        declaration_column,
                    );
                    let Self {
                        scope_collector, arena, ..
                    } = self;
                    scope_collector.register_identifier(
                        id,
                        Some(kind),
                        &mut arena.identifiers,
                        &arena.strings,
                        &mut arena.scopes,
                    );
                }

                VariableDeclaratorTarget::Identifier(id)
            } else if self.match_token(TokenType::CurlyOpen) || self.match_token(TokenType::BracketOpen) {
                let pat = self.parse_binding_pattern();
                let bound_names = std::mem::take(&mut self.pattern_bound_names);
                // Materialize slices for scope-collector calls (which key on Utf16String).
                let name_strs: Vec<Utf16String> = bound_names
                    .iter()
                    .map(|(n, _)| self.arena.strings[*n].clone())
                    .collect();

                for name in &name_strs {
                    self.check_identifier_name_for_assignment_validity(name.as_slice(), false);
                    if kind != DeclarationKind::Var && name.as_slice() == utf16!("let") {
                        self.syntax_error("Lexical binding may not be called 'let'");
                    }
                }

                if kind != DeclarationKind::Var {
                    let mut seen: HashSet<&[u16]> = HashSet::default();
                    for name in &name_strs {
                        if !seen.insert(name.as_slice()) {
                            self.syntax_error("Duplicate parameter names in bindings");
                        }
                    }
                }

                // Register bound names with scope collector.
                if kind == DeclarationKind::Var {
                    let entries: Vec<(&[u16], Option<IdentifierId>)> = name_strs
                        .iter()
                        .zip(bound_names.iter())
                        .map(|(n, (_, id))| (n.as_slice(), Some(*id)))
                        .collect();
                    let Self {
                        scope_collector, arena, ..
                    } = self;
                    scope_collector.add_var_declaration(
                        &entries,
                        declaration_line,
                        declaration_column,
                        Some(DeclarationKind::Var),
                        &mut arena.identifiers,
                        &arena.strings,
                        &mut arena.scopes,
                    );
                } else {
                    let refs: Vec<&[u16]> = name_strs.iter().map(|n| n.as_slice()).collect();
                    self.scope_collector
                        .add_lexical_declaration(&refs, declaration_line, declaration_column);
                    let Self {
                        scope_collector, arena, ..
                    } = self;
                    for (_name, id) in &bound_names {
                        scope_collector.register_identifier(
                            *id,
                            Some(kind),
                            &mut arena.identifiers,
                            &arena.strings,
                            &mut arena.scopes,
                        );
                    }
                }

                VariableDeclaratorTarget::BindingPattern(pat)
            } else {
                self.expected("identifier or a binding pattern");
                self.consume();
                let empty = self.arena.strings.intern(&[]);
                let id = self.make_identifier(declaration_start, empty);
                VariableDeclaratorTarget::Identifier(id)
            };

            let init = if self.match_token(TokenType::Equals) {
                self.consume();
                any_init = true;
                let forbidden = if is_for_loop {
                    ForbiddenTokens::with_in()
                } else {
                    ForbiddenTokens::none()
                };
                Some(self.parse_expression(PRECEDENCE_ASSIGNMENT, Associativity::Right, forbidden))
            } else {
                None
            };

            if kind == DeclarationKind::Const && init.is_none() && !is_for_loop {
                self.syntax_error("Missing initializer in const declaration");
            }

            declarators.push(VariableDeclarator {
                range: self.range_from(start),
                target,
                init,
            });

            if !self.match_token(TokenType::Comma) {
                break;
            }
            self.consume();
        }

        if !is_for_loop {
            self.consume_or_insert_semicolon();
        }

        if is_for_loop {
            self.for_loop_declaration_count = declarators.len();
            self.for_loop_declaration_has_init = any_init;
            self.for_loop_declaration_is_var = kind == DeclarationKind::Var;
            self.for_loop_declaration_is_pattern = declarators
                .iter()
                .any(|d| matches!(d.target, VariableDeclaratorTarget::BindingPattern(_)));
        }

        self.statement(
            start,
            StatementKind::VariableDeclaration(Box::new(VariableDeclarationData {
                kind,
                declarations: declarators,
            })),
        )
    }

    // https://tc39.es/proposal-explicit-resource-management/
    // UsingDeclaration : `using` BindingList `;`
    // NB: `using` declarations have lexical scoping like `const` and invoke
    // the Symbol.dispose method when the enclosing scope exits.
    pub(crate) fn parse_using_declaration(&mut self, is_for_loop: bool) -> Statement {
        let start = self.position();
        let declaration_line = self.current_token().line_number;
        let declaration_column = self.current_token().line_column;
        self.consume(); // consume 'using'

        let mut declarators: Vec<VariableDeclarator> = Vec::new();

        loop {
            let declaration_start = self.position();

            if !self.match_identifier() {
                self.expected("identifier");
                break;
            }
            let token = self.consume();
            let name = self.token_identifier_name(&token);
            let name_str = self.arena.strings[name].clone();

            self.check_identifier_name_for_assignment_validity(name_str.as_slice(), false);
            if name_str.as_slice() == utf16!("let") {
                self.syntax_error("Lexical binding may not be called 'let'");
            }

            let id = self.make_identifier(declaration_start, name);

            self.scope_collector
                .add_lexical_declaration(&[name_str.as_slice()], declaration_line, declaration_column);
            // C++ calls parse_lexical_binding() without declaration_kind for using,
            // so we pass None to match.
            let Self {
                scope_collector, arena, ..
            } = self;
            scope_collector.register_identifier(id, None, &mut arena.identifiers, &arena.strings, &mut arena.scopes);

            let init = if self.match_token(TokenType::Equals) {
                self.consume();
                if is_for_loop {
                    Some(self.parse_expression(PRECEDENCE_ASSIGNMENT, Associativity::Right, ForbiddenTokens::with_in()))
                } else {
                    Some(self.parse_assignment_expression())
                }
            } else if !is_for_loop {
                self.consume_token(TokenType::Equals);
                None
            } else {
                None
            };

            // C++ uses rule_start (using keyword position) for all VariableDeclarators.
            declarators.push(VariableDeclarator {
                range: self.range_from(start),
                target: VariableDeclaratorTarget::Identifier(id),
                init,
            });

            if self.match_token(TokenType::Comma) {
                self.consume();
                continue;
            }
            break;
        }

        if !is_for_loop {
            self.consume_or_insert_semicolon();
        }

        if is_for_loop {
            let any_init = declarators.iter().any(|d| d.init.is_some());
            self.for_loop_declaration_count = declarators.len();
            self.for_loop_declaration_has_init = any_init;
        }

        self.statement(start, StatementKind::UsingDeclaration(Box::new(declarators)))
    }

    // https://tc39.es/ecma262/#sec-function-definitions
    // FunctionDeclaration : `function` BindingIdentifier `(` FormalParameters `)` `{` FunctionBody `}`
    //                     | [+Default] `function` `(` FormalParameters `)` `{` FunctionBody `}`
    // NB: The second form (without name) is only valid in `export default` context.
    pub(crate) fn parse_function_declaration(&mut self) -> Statement {
        let start = self.position();
        let declaration_line = self.current_token().line_number;
        let declaration_column = self.current_token().line_column;

        let saved_might_need_arguments = self.flags.function_might_need_arguments_object;
        self.flags.function_might_need_arguments_object = false;

        let is_async = self.eat(TokenType::Async);
        self.consume_token(TokenType::Function);
        let is_generator = self.eat(TokenType::Asterisk);
        let kind = FunctionKind::from_async_generator(is_async, is_generator);

        // Parse function name.
        let (name, fn_name) = if self.has_default_export_name && !self.match_identifier() {
            let default_name = Utf16String::from(utf16!("*default*"));
            self.last_function_name = default_name.clone();
            (
                Some(self.make_identifier_from_slice(start, default_name.as_slice())),
                default_name,
            )
        } else if self.match_identifier() {
            let token = self.consume();
            let value = Utf16String::from(self.token_value(&token));
            self.last_function_name = value.clone();
            (Some(self.make_identifier_from_slice(start, value.as_slice())), value)
        } else {
            self.last_function_name.0.clear();
            (None, Utf16String::default())
        };
        self.last_function_kind = kind;

        // Register function declaration in parent scope (before opening function scope).
        let strict = self.flags.strict_mode;
        let Self {
            scope_collector, arena, ..
        } = self;
        scope_collector.add_function_declaration(
            &fn_name,
            name,
            kind,
            strict,
            declaration_line,
            declaration_column,
            &mut arena.identifiers,
            &arena.strings,
            &mut arena.scopes,
        );

        let fn_name_for_scope = if fn_name.is_empty() {
            None
        } else {
            Some(fn_name.as_slice())
        };
        self.scope_collector.open_function_scope(fn_name_for_scope);
        self.scope_collector.set_is_function_declaration();

        let fd = self.parse_function_common(
            name,
            &fn_name,
            kind,
            is_async,
            is_generator,
            start,
            saved_might_need_arguments,
        );
        let decl_name = fd.name;
        let decl_kind = fd.kind;
        let function_id = self.insert_function_data(fd);
        self.statement(
            start,
            StatementKind::FunctionDeclaration(Box::new(FunctionDeclarationData {
                function_id,
                name: decl_name,
                kind: decl_kind,
                is_hoisted: false,
            })),
        )
    }

    // https://tc39.es/ecma262/#sec-function-definitions
    // FunctionExpression : `function` BindingIdentifier? `(` FormalParameters `)` `{` FunctionBody `}`
    // NB: The function name, if present, is bound within the function's own scope
    // (not the enclosing scope), allowing recursive self-reference.
    pub(crate) fn parse_function_expression(&mut self) -> Expression {
        let start = self.position();

        let saved_might_need_arguments = self.flags.function_might_need_arguments_object;
        self.flags.function_might_need_arguments_object = false;

        let is_async = self.eat(TokenType::Async);
        self.consume_token(TokenType::Function);
        let is_generator = self.eat(TokenType::Asterisk);
        let kind = FunctionKind::from_async_generator(is_async, is_generator);

        let mut fn_name_value = Utf16String::default();
        let name = if self.match_identifier() {
            let token = self.consume();
            fn_name_value = Utf16String::from(self.token_value(&token));
            Some(self.make_identifier_from_slice(start, fn_name_value.as_slice()))
        } else if self.match_token(TokenType::Yield) || self.match_token(TokenType::Await) {
            // C++ explicitly allows yield/await as function expression names
            // even inside generator/async contexts, then validates after.
            let token = self.consume();
            fn_name_value = Utf16String::from(self.token_value(&token));
            Some(self.make_identifier_from_slice(start, fn_name_value.as_slice()))
        } else {
            None
        };

        // https://tc39.es/ecma262/#prod-GeneratorExpression
        // GeneratorExpression : `function` `*` BindingIdentifier[+Yield, ~Await]? `(` FormalParameters[+Yield, ~Await] `)`
        // `{` GeneratorBody `}`
        if kind == FunctionKind::Generator && fn_name_value.as_slice() == utf16!("yield") {
            self.syntax_error("'yield' is not allowed as a generator function expression name");
        }

        // Register the function expression name in the outer scope, matching C++.
        // This must happen before open_function_scope so that the identifier group
        // exists with declaration_kind=None, preventing later var declarations
        // with the same name from setting a spurious declaration_kind.
        if let Some(id) = name {
            let Self {
                scope_collector, arena, ..
            } = self;
            scope_collector.register_identifier(id, None, &mut arena.identifiers, &arena.strings, &mut arena.scopes);
        }

        // Open function scope (function expression name is bound within its own scope).
        let fn_name_for_scope = if fn_name_value.is_empty() {
            None
        } else {
            Some(fn_name_value.as_slice())
        };
        self.scope_collector.open_function_scope(fn_name_for_scope);

        let fd = self.parse_function_common(
            name,
            &fn_name_value,
            kind,
            is_async,
            is_generator,
            start,
            saved_might_need_arguments,
        );
        let function_id = self.insert_function_data(fd);
        self.expression(start, ExpressionKind::Function(function_id))
    }

    /// Shared logic for parsing formal parameters, function body, and constructing
    /// FunctionData. Called after the function scope has been opened.
    #[allow(clippy::too_many_arguments)]
    fn parse_function_common(
        &mut self,
        name: Option<IdentifierId>,
        fn_name: &[u16],
        kind: FunctionKind,
        is_async: bool,
        is_generator: bool,
        start: Position,
        saved_might_need_arguments: bool,
    ) -> FunctionData {
        // Validate name against async generator and class static init restrictions.
        if name.is_some() {
            if kind == FunctionKind::AsyncGenerator && (fn_name == utf16!("await") || fn_name == utf16!("yield")) {
                let name_str = String::from_utf16_lossy(fn_name);
                self.syntax_error(&format!(
                    "async generator function is not allowed to be called '{name_str}'"
                ));
            }
            if self.flags.in_class_static_init_block && is_async && fn_name == utf16!("await") {
                self.syntax_error("'await' is a reserved word");
            }
        }

        let in_generator_before = self.flags.in_generator_function_context;
        let await_before = self.flags.await_expression_is_valid;
        let saved_static_init = self.flags.in_class_static_init_block;
        let saved_field_init = self.flags.in_class_field_initializer;
        let saved_allow_super_call = self.flags.allow_super_constructor_call;
        let saved_allow_super_lookup = self.flags.allow_super_property_lookup;
        let saved_new_target = self.flags.new_target_is_valid;
        self.flags.in_generator_function_context = is_generator;
        self.flags.await_expression_is_valid = is_async;
        self.flags.in_class_static_init_block = false;
        self.flags.in_class_field_initializer = false;
        self.flags.allow_super_constructor_call = false;
        self.flags.allow_super_property_lookup = false;
        self.flags.new_target_is_valid = true;

        // Save pattern_bound_names so that destructuring patterns in the
        // function body don't steal names from an outer binding context.
        let saved_pattern_bound_names = std::mem::take(&mut self.pattern_bound_names);

        self.push_function_context();
        let parsed = self.parse_formal_parameters();
        self.register_function_parameters_with_scope(&parsed.parameters, &parsed.parameter_info);

        self.flags.in_generator_function_context = in_generator_before;
        self.flags.await_expression_is_valid = await_before;

        let (body, has_use_strict, mut insights) = self.parse_function_body(is_async, is_generator, parsed.is_simple);
        self.flags.allow_super_constructor_call = saved_allow_super_call;
        self.flags.allow_super_property_lookup = saved_allow_super_lookup;

        self.scope_collector.close_scope();
        self.pattern_bound_names = saved_pattern_bound_names;

        if name.is_some() {
            self.check_identifier_name_for_assignment_validity(fn_name, has_use_strict);
        }
        let parameters_are_strict = self.flags.strict_mode || has_use_strict;
        if parameters_are_strict || kind != FunctionKind::Normal {
            self.check_parameters_post_body(&parsed.parameter_info, parameters_are_strict, kind);
        }
        self.flags.in_class_static_init_block = saved_static_init;
        self.flags.in_class_field_initializer = saved_field_init;
        self.flags.new_target_is_valid = saved_new_target;

        insights.might_need_arguments_object = self.flags.function_might_need_arguments_object;
        self.flags.function_might_need_arguments_object = saved_might_need_arguments;
        let nested_function_ids = self.pop_function_context();

        FunctionData {
            name,
            source_text_start: start.offset,
            source_text_end: self.source_text_end_offset(),
            body: Box::new(body),
            parameters: parsed.parameters,
            function_length: parsed.function_length,
            kind,
            is_strict_mode: self.flags.strict_mode || has_use_strict,
            is_arrow_function: false,
            parsing_insights: insights,
            nested_function_ids: Some(nested_function_ids),
        }
    }

    // https://tc39.es/ecma262/#sec-class-definitions
    // ClassDeclaration : `class` BindingIdentifier ClassTail
    // ClassExpression  : `class` BindingIdentifier? ClassTail
    // ClassTail        : ClassHeritage? `{` ClassBody `}`
    // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
    // NB: All code within a ClassBody is in strict mode.
    pub(crate) fn parse_class_expression(&mut self, expect_name: bool) -> Expression {
        let start = self.position();

        let strict_before = self.flags.strict_mode;
        self.flags.strict_mode = true;

        self.consume_token(TokenType::Class);

        let (name_id, name_value) = if expect_name || self.match_identifier() {
            if self.match_identifier() {
                let token = self.consume();
                let value = Utf16String::from(self.token_value(&token));
                self.last_class_name = value.clone();
                (Some(self.make_identifier_from_slice(start, value.as_slice())), value)
            } else if expect_name {
                self.expected("class name");
                self.last_class_name.0.clear();
                (None, Utf16String::default())
            } else {
                self.last_class_name.0.clear();
                (None, Utf16String::default())
            }
        } else {
            self.last_class_name.0.clear();
            (None, Utf16String::default())
        };

        let saved_class_name = self.last_class_name.clone();

        let class_name_for_scope = if name_value.is_empty() {
            None
        } else {
            Some(name_value.as_slice())
        };
        self.scope_collector.open_class_declaration_scope(class_name_for_scope);

        if name_id.is_some() {
            self.check_identifier_name_for_assignment_validity(&name_value, true);
            if self.flags.in_class_static_init_block && name_value == utf16!("await") {
                self.syntax_error("Identifier must not be a reserved word in modules ('await')");
            }
        }

        let super_class = if self.match_token(TokenType::Extends) {
            self.consume();
            let heritage_start = self.position();
            let expression = self.parse_expression_any();
            // ClassHeritage[Yield, Await] : `extends` LeftHandSideExpression[?Yield, ?Await]
            if expression.range.start.offset == heritage_start.offset
                && let ExpressionKind::Function(function_id) = &expression.inner
                && self.function_table.get(*function_id).is_arrow_function
            {
                self.syntax_error("Arrow function is not allowed in class heritage");
            }
            Some(Box::new(expression))
        } else {
            None
        };

        self.consume_token(TokenType::CurlyOpen);
        let mut elements: Vec<Node<ClassElement>> = Vec::new();
        let mut constructor: Option<Expression> = None;
        let mut found_private_names: HashMap<Utf16String, (Option<ClassMethodKind>, bool)> = HashMap::default();

        self.referenced_private_names_stack.push(HashSet::default());

        let saved_class_has_super = self.class_has_super_class;
        self.class_has_super_class = super_class.is_some();
        self.class_scope_depth += 1;

        while !self.match_token(TokenType::CurlyClose) && !self.done() {
            if self.match_token(TokenType::Semicolon) {
                self.consume();
                continue;
            }

            let (element, maybe_ctor) = self.parse_class_element(start, &mut found_private_names);
            if let Some(ctor) = maybe_ctor {
                // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
                // It is a Syntax Error if PrototypePropertyNameList of ClassElementList
                // contains more than one occurrence of "constructor".
                if constructor.is_some() {
                    self.syntax_error("Classes may not have more than one constructor");
                }
                constructor = Some(ctor);
            } else if let Some(element) = element {
                elements.push(element);
            }
        }

        self.consume_token(TokenType::CurlyClose);
        self.class_scope_depth -= 1;
        self.class_has_super_class = saved_class_has_super;

        // AllPrivateNamesValid: check that all referenced private names were declared.
        let referenced = self.referenced_private_names_stack.pop().unwrap_or_default();
        for name in referenced {
            if found_private_names.contains_key(&name) {
                continue;
            }
            // Bubble up to outer class, or error if no outer class.
            if let Some(outer) = self.referenced_private_names_stack.last_mut() {
                outer.insert(name);
            } else if self.initiated_by_eval {
                self.register_eval_referenced_private_name(&name);
            } else {
                let name_str = String::from_utf16_lossy(&name);
                self.syntax_error(&format!("Reference to undeclared private field or method '{name_str}'"));
            }
        }
        self.flags.strict_mode = strict_before;

        self.scope_collector.close_scope();

        if constructor.is_none() {
            constructor = Some(self.synthesize_default_constructor(start, &name_value, super_class.is_some()));
        }

        self.last_class_name = saved_class_name;

        self.expression(
            start,
            ExpressionKind::Class(Box::new(ClassData {
                name: name_id,
                source_text_start: start.offset,
                source_text_end: self.source_text_end_offset(),
                constructor: constructor.map(Box::new),
                super_class,
                elements,
            })),
        )
    }

    pub(crate) fn parse_class_declaration(&mut self) -> Statement {
        let start = self.position();
        let class_expression = self.parse_class_expression(true);
        // Convert the class expression into a class declaration by extracting ClassData.
        match class_expression.inner {
            ExpressionKind::Class(data) => {
                // Register class name as lexical declaration in the outer scope.
                // The inner class scope (opened/closed inside parse_class_expression)
                // binds the name for self-reference. The outer scope needs the name
                // registered as a lexical declaration so it's visible to sibling code.
                if let Some(name_ident) = data.name {
                    let name_slice = self.arena.name_of(name_ident).clone();
                    self.scope_collector
                        .add_lexical_declaration(&[name_slice.as_slice()], start.line, start.column);
                    let Self {
                        scope_collector, arena, ..
                    } = self;
                    scope_collector.register_identifier(
                        name_ident,
                        None,
                        &mut arena.identifiers,
                        &arena.strings,
                        &mut arena.scopes,
                    );
                }
                self.statement(start, StatementKind::ClassDeclaration(data))
            }
            _ => unreachable!("parse_class_expression must return ExpressionKind::Class"),
        }
    }

    // https://tc39.es/ecma262/#sec-runtime-semantics-classdefinitionevaluation
    // If no constructor is present in the ClassBody:
    //   - Base class: constructor() {}
    //   - Derived class: constructor(...arguments) { super(...arguments); }
    fn synthesize_default_constructor(&mut self, start: Position, class_name: &[u16], has_super: bool) -> Expression {
        let ctor_name = if !class_name.is_empty() {
            Some(self.make_identifier_from_slice(start, class_name))
        } else {
            None
        };

        // Note: No scope collector calls here. The synthesized constructor AST
        // is stored in the SFD and compiled lazily — scope analysis runs at that point.

        if has_super {
            let arguments_name_id = self.arena.strings.intern(utf16!("args"));
            let range = self.range_from(start);

            let arguments_ref = self.arena.identifiers.insert(Identifier::new(range, arguments_name_id));
            let arguments_expression = self.expression(start, ExpressionKind::Identifier(arguments_ref));

            let super_call = self.expression(
                start,
                ExpressionKind::SuperCall(Box::new(SuperCallData {
                    arguments: vec![CallArgument {
                        value: arguments_expression,
                        is_spread: true,
                    }],
                    is_synthetic: true,
                })),
            );
            let return_statement = self.statement(start, StatementKind::Return(Some(Box::new(super_call))));
            let body_scope = self.make_scope(vec![return_statement]);
            let body = self.statement(start, StatementKind::Block(body_scope));

            let arguments_binding = self
                .arena
                .identifiers
                .insert(Identifier::new(self.range_from(start), arguments_name_id));
            let parameters = vec![FunctionParameter {
                binding: FunctionParameterBinding::Identifier(arguments_binding),
                default_value: None,
                is_rest: true,
            }];

            let function_id = self.insert_function_data(FunctionData {
                name: ctor_name,
                source_text_start: start.offset,
                source_text_end: self.source_text_end_offset(),
                body: Box::new(body),
                parameters,
                function_length: 0,
                kind: FunctionKind::Normal,
                is_strict_mode: true,
                is_arrow_function: false,
                parsing_insights: FunctionParsingInsights {
                    uses_this: true,
                    uses_this_from_environment: true,
                    ..FunctionParsingInsights::default()
                },
                nested_function_ids: Some(Vec::new()),
            });
            self.expression(start, ExpressionKind::Function(function_id))
        } else {
            let body_scope = self.make_scope(Vec::new());
            let body = self.statement(start, StatementKind::Block(body_scope));

            let function_id = self.insert_function_data(FunctionData {
                name: ctor_name,
                source_text_start: start.offset,
                source_text_end: self.source_text_end_offset(),
                body: Box::new(body),
                parameters: Vec::new(),
                function_length: 0,
                kind: FunctionKind::Normal,
                is_strict_mode: true,
                is_arrow_function: false,
                parsing_insights: FunctionParsingInsights {
                    uses_this: true,
                    uses_this_from_environment: true,
                    ..FunctionParsingInsights::default()
                },
                nested_function_ids: Some(Vec::new()),
            });
            self.expression(start, ExpressionKind::Function(function_id))
        }
    }

    // https://tc39.es/ecma262/#sec-class-definitions
    // ClassElement : MethodDefinition
    //             | `static` MethodDefinition
    //             | FieldDefinition `;`
    //             | `static` FieldDefinition `;`
    //             | ClassStaticBlock
    //             | `;`
    fn parse_class_element(
        &mut self,
        class_start: Position,
        found_private_names: &mut HashMap<Utf16String, (Option<ClassMethodKind>, bool)>,
    ) -> (Option<Node<ClassElement>>, Option<Expression>) {
        // C++ lexes "static" as Identifier and checks original_value() == "static".
        let mut is_static =
            if self.match_identifier() && self.token_original_value(&self.current_token) == utf16!("static") {
                self.consume();
                // https://tc39.es/ecma262/#sec-class-static-initialization-blocks
                // ClassStaticBlock : `static` `{` ClassStaticBlockBody `}`
                if self.match_token(TokenType::CurlyOpen) {
                    // C++ captures static_start (push_start) before consuming '{'.
                    let static_start = self.position();
                    self.consume(); // consume '{'
                    let saved_flags = self.flags;
                    self.flags.in_break_context = false;
                    self.flags.in_continue_context = false;
                    self.flags.in_function_context = false;
                    self.flags.in_generator_function_context = false;
                    self.flags.await_expression_is_valid = false;
                    self.flags.in_class_field_initializer = true;
                    self.flags.in_class_static_init_block = true;
                    self.flags.allow_super_property_lookup = true;
                    self.flags.new_target_is_valid = true;
                    self.scope_collector.open_static_init_scope(None);
                    let children = self.parse_statement_list(false);
                    self.flags = saved_flags;
                    self.consume_token(TokenType::CurlyClose);
                    let scope = self.make_scope(children);
                    self.scope_collector.set_scope_node(scope);
                    self.scope_collector.close_scope();
                    // C++ uses rule_start (class start) for FunctionBody position.
                    let body = self.statement(
                        class_start,
                        StatementKind::FunctionBody {
                            scope,
                            in_strict_mode: self.flags.strict_mode,
                        },
                    );
                    // C++ uses static_start (after '{') for StaticInitializer position.
                    return (
                        Some(Node::new(
                            self.range_from(static_start),
                            ClassElement::StaticInitializer { body: Box::new(body) },
                        )),
                        None,
                    );
                }
                true
            } else {
                false
            };

        let mut is_async = false;
        let mut is_generator = false;
        let mut is_getter = false;
        let mut is_setter = false;
        let function_start = self.position();

        // Check modifiers (must not contain escape sequences).
        if self.match_identifier_name() {
            let value = self.token_original_value(&self.current_token).to_vec();
            if value == utf16!("get") && self.match_property_key_ahead() {
                is_getter = true;
                self.consume();
            } else if value == utf16!("set") && self.match_property_key_ahead() {
                is_setter = true;
                self.consume();
            } else if value == utf16!("async") {
                let next = self.next_token();
                if !next.trivia_has_line_terminator
                    && next.token_type != TokenType::ParenOpen
                    && next.token_type != TokenType::Colon
                    && next.token_type != TokenType::Comma
                    && next.token_type != TokenType::CurlyClose
                    && next.token_type != TokenType::Semicolon
                    && next.token_type != TokenType::Equals
                {
                    is_async = true;
                    self.consume();
                }
            }
        }

        if self.match_token(TokenType::Asterisk) {
            is_generator = true;
            self.consume();
        }

        // If we consumed a modifier keyword (static/async/get/set) but the next token
        // is one that can't start a property key (`;`, `=`, `(`, `}`), the keyword was
        // actually the field/method name, not a modifier.
        let PropertyKey {
            expression: key,
            name: key_value,
            ..
        } = if (is_static || is_async || is_getter || is_setter)
            && (self.match_token(TokenType::Semicolon)
                || self.match_token(TokenType::Equals)
                || self.match_token(TokenType::ParenOpen)
                || self.match_token(TokenType::CurlyClose))
        {
            let name: &[u16] = if is_async {
                is_async = false;
                utf16!("async")
            } else if is_getter {
                is_getter = false;
                utf16!("get")
            } else if is_setter {
                is_setter = false;
                utf16!("set")
            } else {
                is_static = false;
                utf16!("static")
            };
            let expression = self.expression(
                class_start,
                ExpressionKind::StringLiteral(Box::new(Utf16String(name.to_vec()))),
            );
            PropertyKey {
                expression,
                name: Some(Utf16String::from(name)),
                is_proto: false,
                is_computed: false,
                is_identifier: false,
            }
        } else {
            self.parse_property_key()
        };

        // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
        // It is a Syntax Error if PropName of ClassElement is "prototype"
        // and ClassElement is `static` MethodDefinition or `static` FieldDefinition.
        if is_static && key_value.as_deref() == Some(utf16!("prototype")) {
            self.syntax_error("Classes may not have a static property named 'prototype'");
        }

        // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
        // It is a Syntax Error if PrivateBoundIdentifiers of ClassElementList contains
        // any duplicate entries, unless the name is used once for a getter and once for
        // a setter and in no other entries, and they are either both static or both non-static.
        let is_private = key_value.as_ref().is_some_and(|v| v.first() == Some(&ch(b'#')));
        if is_private {
            let name = key_value.as_ref().unwrap();
            let current_kind = if is_getter {
                Some(ClassMethodKind::Getter)
            } else if is_setter {
                Some(ClassMethodKind::Setter)
            } else {
                None
            };
            let is_accessor = is_getter || is_setter;
            if is_accessor {
                // Getter or setter: check against existing private names
                if let Some(&(existing_kind, existing_static)) = found_private_names.get(name) {
                    let is_error = match existing_kind {
                        // Existing is not a method (field/plain method) → error
                        None => true,
                        // Existing is a getter/setter
                        Some(ek) => {
                            // Different staticness → error
                            existing_static != is_static
                            // Same kind (getter+getter or setter+setter) → error
                            // Plain method → error
                            || ek == ClassMethodKind::Method
                            || ek == current_kind.unwrap()
                        }
                    };
                    if is_error {
                        let name_str = String::from_utf16_lossy(name);
                        self.syntax_error(&format!("Duplicate private field or method named '{name_str}'"));
                    }
                }
                found_private_names.insert(name.clone(), (current_kind, is_static));
            } else if found_private_names
                .insert(name.clone(), (current_kind, is_static))
                .is_some()
            {
                let name_str = String::from_utf16_lossy(name);
                self.syntax_error(&format!("Duplicate private field or method named '{name_str}'"));
            }
        }

        if self.match_token(TokenType::ParenOpen) {
            let ctor_name = utf16!("constructor");
            let is_constructor = !is_static && !is_getter && !is_setter && key_value.as_deref() == Some(ctor_name);

            // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
            // It is a Syntax Error if SpecialMethod of MethodDefinition is true
            // and PropName of MethodDefinition is "constructor".
            if is_constructor {
                if is_getter || is_setter {
                    self.syntax_error("Class constructor may not be an accessor");
                }
                if is_generator {
                    self.syntax_error("Class constructor may not be a generator");
                }
                if is_async {
                    self.syntax_error("Class constructor may not be async");
                }
            }

            // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
            // It is a Syntax Error if PropName of MethodDefinition is "constructor"
            // and SpecialMethod of MethodDefinition is true (getter/setter).
            if !is_static && (is_getter || is_setter) && key_value.as_deref() == Some(ctor_name) {
                self.syntax_error("Class constructor may not be an accessor");
            }

            let method_kind = if is_constructor {
                MethodKind::Constructor
            } else if is_getter {
                MethodKind::Getter
            } else if is_setter {
                MethodKind::Setter
            } else {
                MethodKind::Normal
            };
            let function = self.parse_method_definition(is_async, is_generator, method_kind, function_start);
            let class_method_kind = if is_getter {
                ClassMethodKind::Getter
            } else if is_setter {
                ClassMethodKind::Setter
            } else {
                ClassMethodKind::Method
            };

            if is_constructor {
                return (None, Some(function));
            }

            return (
                Some(Node::new(
                    self.range_from(class_start),
                    ClassElement::Method {
                        key: Box::new(key),
                        function: Box::new(function),
                        kind: class_method_kind,
                        is_static,
                    },
                )),
                None,
            );
        }

        // https://tc39.es/ecma262/#sec-class-definitions-static-semantics-early-errors
        // It is a Syntax Error if PropName of ClassElement is "constructor"
        // and ClassElement is FieldDefinition.
        if key_value.as_deref() == Some(utf16!("constructor")) {
            self.syntax_error("Class cannot have field named 'constructor'");
        }

        let init = if self.match_token(TokenType::Equals) {
            self.consume();
            let saved_await = self.flags.await_expression_is_valid;
            let saved_field_init = self.flags.in_class_field_initializer;
            let saved_super_lookup = self.flags.allow_super_property_lookup;
            self.flags.await_expression_is_valid = false;
            self.flags.in_class_field_initializer = true;
            self.flags.allow_super_property_lookup = true;
            self.scope_collector.open_class_field_scope(None);
            let expression = self.parse_assignment_expression();
            self.scope_collector.close_scope();
            self.flags.await_expression_is_valid = saved_await;
            self.flags.in_class_field_initializer = saved_field_init;
            self.flags.allow_super_property_lookup = saved_super_lookup;
            Some(Box::new(expression))
        } else {
            None
        };

        self.consume_or_insert_semicolon();
        (
            Some(Node::new(
                self.range_from(class_start),
                ClassElement::Field {
                    key: Box::new(key),
                    initializer: init,
                    is_static,
                },
            )),
            None,
        )
    }

    // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
    // It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
    // and IsSimpleParameterList of FormalParameters is false.
    pub(crate) fn parse_function_body(
        &mut self,
        is_async: bool,
        is_generator: bool,
        is_simple: bool,
    ) -> (Statement, bool, FunctionParsingInsights) {
        self.consume_token(TokenType::CurlyOpen);
        // C++ captures FunctionBody position AFTER consuming `{`.
        let start = self.position();

        let in_function_before = self.flags.in_function_context;
        let in_generator_before = self.flags.in_generator_function_context;
        let await_before = self.flags.await_expression_is_valid;
        let formal_parameter_before = self.flags.in_formal_parameter_context;
        let old_labels = std::mem::take(&mut self.labels_in_scope);
        self.flags.in_function_context = true;
        self.flags.in_generator_function_context = is_generator;
        self.flags.await_expression_is_valid = is_async;
        self.flags.in_formal_parameter_context = false;

        let (has_use_strict, mut children) = self.parse_directive();
        let body_is_strict = has_use_strict || self.flags.strict_mode;

        let strict_before = self.flags.strict_mode;
        if has_use_strict {
            self.flags.strict_mode = true;
            if !is_simple {
                self.syntax_error("Illegal 'use strict' directive in function with non-simple parameter list");
            }
        }

        // https://tc39.es/ecma262/#sec-labelled-function-declarations
        // That rule is then modified to suppress the Syntax Error in non-strict code if the host supports this feature.
        children.extend(self.parse_statement_list(true));

        self.flags.strict_mode = strict_before;
        self.flags.in_function_context = in_function_before;
        self.flags.in_generator_function_context = in_generator_before;
        self.flags.await_expression_is_valid = await_before;
        self.flags.in_formal_parameter_context = formal_parameter_before;
        self.labels_in_scope = old_labels;

        // Read scope analysis flags before the function scope is closed.
        let insights = FunctionParsingInsights {
            contains_direct_call_to_eval: self.scope_collector.contains_direct_call_to_eval(),
            uses_this: self.scope_collector.uses_this(),
            uses_this_from_environment: self.scope_collector.uses_this_from_environment(),
            ..FunctionParsingInsights::default()
        };

        self.consume_token(TokenType::CurlyClose);

        let scope = self.make_scope(children);
        self.scope_collector.set_scope_node(scope);

        let body = self.statement(
            start,
            StatementKind::FunctionBody {
                scope,
                in_strict_mode: body_is_strict,
            },
        );

        (body, has_use_strict, insights)
    }

    // https://tc39.es/ecma262/#sec-function-definitions
    // FormalParameters : [empty]
    //                  | FunctionRestParameter
    //                  | FormalParameterList
    //                  | FormalParameterList `,`
    //                  | FormalParameterList `,` FunctionRestParameter
    pub(crate) fn parse_formal_parameters(&mut self) -> ParsedParameters {
        self.consume_token(TokenType::ParenOpen);
        let result = self.parse_formal_parameters_impl(false);
        self.consume_token(TokenType::ParenClose);
        result
    }

    pub(crate) fn parse_formal_parameters_impl(&mut self, is_arrow: bool) -> ParsedParameters {
        let saved_formal_parameter_ctx = self.flags.in_formal_parameter_context;
        self.flags.in_formal_parameter_context = true;

        // Save and clear pattern_bound_names so that nested function parsing
        // (e.g. arrow functions in default values) doesn't steal binding names
        // accumulated by an outer binding pattern context.
        let saved_pattern_bound_names = std::mem::take(&mut self.pattern_bound_names);

        if self.match_token(TokenType::ParenClose) {
            self.flags.in_formal_parameter_context = saved_formal_parameter_ctx;
            self.pattern_bound_names = saved_pattern_bound_names;
            return ParsedParameters {
                parameters: Vec::new(),
                function_length: 0,
                parameter_info: Vec::new(),
                is_simple: true,
            };
        }

        let mut parameters: Vec<FunctionParameter> = Vec::new();
        let mut function_length: i32 = 0;
        let mut has_seen_default = false;
        let mut has_seen_rest = false;
        let mut parameter_info: Vec<ParamInfo> = Vec::new();

        // C++ uses the position at the start of parse_formal_parameters for all
        // parameter identifiers (i.e., the position of the first parameter).
        let formal_parameters_start = self.position();

        loop {
            let parameter_start = self.position();
            let rest = self.eat(TokenType::TripleDot);
            if rest {
                has_seen_rest = true;
            }

            let binding = if self.match_identifier()
                || self.match_token(TokenType::Await)
                || self.match_token(TokenType::Yield)
            {
                // Emit errors for await/yield used as parameter names in
                // contexts where they are reserved.
                if self.current_token_type() == TokenType::Await
                    && (self.program_type == ProgramType::Module
                        || self.flags.await_expression_is_valid
                        || self.flags.in_class_static_init_block)
                {
                    self.syntax_error("'await' is not allowed as an identifier in this context");
                }
                if self.current_token_type() == TokenType::Yield
                    && (self.flags.strict_mode || self.flags.in_generator_function_context)
                {
                    self.syntax_error("'yield' is not allowed as an identifier in this context");
                }
                let token = self.consume();
                let value = Utf16String::from(self.token_value(&token));
                // Skip validity check for arrow functions; arrow parameter
                // parsing is speculative and errors would abort the parse.
                // The check runs after the arrow is confirmed instead.
                if !is_arrow {
                    self.check_identifier_name_for_assignment_validity(&value, false);
                }
                let name = self.token_identifier_name(&token);
                let id = self
                    .arena
                    .identifiers
                    .insert(Identifier::new(self.range_from(formal_parameters_start), name));
                parameter_info.push(ParamInfo {
                    name: value,
                    is_rest: rest,
                    is_from_pattern: false,
                    identifier: Some(id),
                });
                FunctionParameterBinding::Identifier(id)
            } else if self.match_token(TokenType::CurlyOpen) || self.match_token(TokenType::BracketOpen) {
                let pat = self.parse_binding_pattern();
                let bound_names = std::mem::take(&mut self.pattern_bound_names);
                for (n, id) in bound_names {
                    let name = self.arena.strings[n].clone();
                    parameter_info.push(ParamInfo {
                        name,
                        is_rest: rest,
                        is_from_pattern: true,
                        identifier: Some(id),
                    });
                }
                FunctionParameterBinding::BindingPattern(pat)
            } else {
                self.expected("parameter name");
                self.consume();
                let empty = self.arena.strings.intern(&[]);
                let id = self
                    .arena
                    .identifiers
                    .insert(Identifier::new(self.range_from(parameter_start), empty));
                FunctionParameterBinding::Identifier(id)
            };

            let default_value = if !rest && self.match_token(TokenType::Equals) {
                self.consume();
                has_seen_default = true;
                let saved_in_function = self.flags.in_function_context;
                self.flags.in_function_context = true;
                let expr =
                    self.parse_expression(PRECEDENCE_ASSIGNMENT, Associativity::Right, ForbiddenTokens::with_in());
                self.flags.in_function_context = saved_in_function;
                Some(expr)
            } else {
                None
            };

            if !rest && !has_seen_default && default_value.is_none() {
                function_length += 1;
            }

            parameters.push(FunctionParameter {
                binding,
                default_value,
                is_rest: rest,
            });

            if rest || !self.match_token(TokenType::Comma) {
                break;
            }
            self.consume();

            if self.match_token(TokenType::ParenClose) {
                break;
            }
        }

        let is_simple = !has_seen_default
            && !has_seen_rest
            && !parameters
                .iter()
                .any(|p| matches!(&p.binding, FunctionParameterBinding::BindingPattern(_)));

        // Validate duplicates after parsing so we can use borrowed name slices
        // from `parameter_info` without cloning each entry into the HashSet.
        let mut seen_parameter_names: HashSet<&[u16]> = HashSet::default();
        for info in &parameter_info {
            if info.name.is_empty() {
                continue;
            }
            let is_duplicate = seen_parameter_names.contains(info.name.as_slice());
            // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors
            // Duplicate names are tolerated only for sloppy, non-arrow functions with a fully simple parameter list.
            if is_duplicate && !is_arrow && (self.flags.strict_mode || !is_simple) {
                let name_str = String::from_utf16_lossy(&info.name);
                self.syntax_error(&format!("Duplicate parameter '{name_str}' not allowed"));
            }
            seen_parameter_names.insert(info.name.as_slice());
        }

        self.flags.in_formal_parameter_context = saved_formal_parameter_ctx;
        self.pattern_bound_names = saved_pattern_bound_names;

        ParsedParameters {
            parameters,
            function_length,
            parameter_info,
            is_simple,
        }
    }

    // https://tc39.es/ecma262/#sec-destructuring-binding-patterns
    // BindingPattern : ObjectBindingPattern | ArrayBindingPattern
    // ObjectBindingPattern : `{` `}`
    //                      | `{` BindingRestProperty `}`
    //                      | `{` BindingPropertyList `}`
    //                      | `{` BindingPropertyList `,` BindingRestProperty? `}`
    // ArrayBindingPattern  : `[` Elision? BindingRestElement? `]`
    //                      | `[` BindingElementList `]`
    //                      | `[` BindingElementList `,` Elision? BindingRestElement? `]`
    pub(crate) fn parse_binding_pattern(&mut self) -> BindingPattern {
        let is_object = self.match_token(TokenType::CurlyOpen);
        let is_array = self.match_token(TokenType::BracketOpen);
        if !is_object && !is_array {
            return BindingPattern {
                kind: BindingPatternKind::Object,
                entries: Vec::new(),
            };
        }
        self.consume();

        let kind = if is_object {
            BindingPatternKind::Object
        } else {
            BindingPatternKind::Array
        };
        let closing_token = if is_object {
            TokenType::CurlyClose
        } else {
            TokenType::BracketClose
        };
        let mut entries: Vec<BindingEntry> = Vec::new();

        while !self.match_token(closing_token) && !self.done() {
            // Array elision: bare comma.
            if !is_object && self.match_token(TokenType::Comma) {
                self.consume();
                entries.push(BindingEntry {
                    name: None,
                    alias: None,
                    initializer: None,
                    is_rest: false,
                });
                continue;
            }

            let is_rest = self.eat(TokenType::TripleDot);

            let mut entry_name = None;
            let mut entry_alias = None;

            if is_object {
                if self.allow_member_expressions && is_rest {
                    // Destructuring assignment: rest target can be MemberExpression or Identifier.
                    let expression = self.parse_expression(
                        PRECEDENCE_ASSIGNMENT,
                        Associativity::Right,
                        ForbiddenTokens::none().forbid(&[TokenType::Equals]),
                    );
                    if Self::is_member_expression(&expression) {
                        entry_alias = Some(BindingEntryAlias::MemberExpression(Box::new(expression)));
                    } else if Self::is_identifier(&expression) {
                        entry_name = Some(BindingEntryName::Identifier(expression_into_identifier(expression)));
                    } else {
                        self.syntax_error("Invalid destructuring assignment target");
                        break;
                    }
                } else {
                    let mut needs_alias = false;
                    let mut entry_name_value: Option<StringId> = None;
                    let mut entry_is_keyword = false;

                    if self.match_identifier_name()
                        || self.match_token(TokenType::StringLiteral)
                        || self.match_token(TokenType::NumericLiteral)
                        || self.match_token(TokenType::BigIntLiteral)
                    {
                        let entry_start = self.position();

                        if self.match_token(TokenType::StringLiteral) || self.match_token(TokenType::NumericLiteral) {
                            needs_alias = true;
                        }

                        entry_is_keyword =
                            self.current_token.token_type.is_identifier_name() && !self.match_identifier();

                        // Property keys are name tokens, not identifier references,
                        // so don't run the eval/arguments check on them.
                        if self.match_token(TokenType::StringLiteral) {
                            let token = self.consume_property_key_token();
                            let (value, _has_octal) = self.parse_string_value(&token);
                            let id = self.make_identifier_from_slice(entry_start, value.as_slice());
                            let Self {
                                scope_collector, arena, ..
                            } = self;
                            scope_collector.register_identifier(
                                id,
                                None,
                                &mut arena.identifiers,
                                &arena.strings,
                                &mut arena.scopes,
                            );
                            entry_name = Some(BindingEntryName::Identifier(id));
                        } else if self.match_token(TokenType::BigIntLiteral) {
                            let token = self.consume_property_key_token();
                            let value = self.token_value(&token);
                            let name_value: Vec<u16> = if value.last() == Some(&ch(b'n')) {
                                value[..value.len() - 1].to_vec()
                            } else {
                                value.to_vec()
                            };
                            let id = self.make_identifier_from_slice(entry_start, &name_value);
                            let Self {
                                scope_collector, arena, ..
                            } = self;
                            scope_collector.register_identifier(
                                id,
                                None,
                                &mut arena.identifiers,
                                &arena.strings,
                                &mut arena.scopes,
                            );
                            entry_name = Some(BindingEntryName::Identifier(id));
                        } else {
                            let token = self.consume_property_key_token();
                            let name = self.token_identifier_name(&token);
                            entry_name_value = Some(name);
                            let id = self.make_identifier(entry_start, name);
                            let Self {
                                scope_collector, arena, ..
                            } = self;
                            scope_collector.register_identifier(
                                id,
                                None,
                                &mut arena.identifiers,
                                &arena.strings,
                                &mut arena.scopes,
                            );
                            entry_name = Some(BindingEntryName::Identifier(id));
                        }
                    } else if self.match_token(TokenType::BracketOpen) {
                        self.consume();
                        let expression = self.parse_expression_any();
                        entry_name = Some(BindingEntryName::Expression(Box::new(expression)));
                        self.consume_token(TokenType::BracketClose);
                    } else {
                        self.expected("identifier or computed property name");
                        break;
                    }

                    if !is_rest && self.match_token(TokenType::Colon) {
                        self.consume();
                        if self.allow_member_expressions {
                            let expression_start = self.position();
                            let expression = self.parse_expression(
                                PRECEDENCE_ASSIGNMENT,
                                Associativity::Right,
                                ForbiddenTokens::none().forbid(&[TokenType::Equals]),
                            );
                            if Self::is_parenthesized_object_or_array_expression(&expression, expression_start) {
                                // https://tc39.es/ecma262/#sec-destructuring-assignment-static-semantics-early-errors
                                // If |LeftHandSideExpression| is either an |ObjectLiteral| or an |ArrayLiteral|,
                                // |LeftHandSideExpression| must cover an |AssignmentPattern|.
                                self.syntax_error("Invalid destructuring assignment target");
                                break;
                            } else if Self::is_object_or_array_expression(&expression) {
                                let pattern = self.synthesize_binding_pattern(expression.range.start);
                                entry_alias = Some(BindingEntryAlias::BindingPattern(Box::new(pattern)));
                            } else if Self::is_member_expression(&expression) {
                                entry_alias = Some(BindingEntryAlias::MemberExpression(Box::new(expression)));
                            } else if Self::is_identifier(&expression) {
                                entry_alias =
                                    Some(BindingEntryAlias::Identifier(expression_into_identifier(expression)));
                            } else {
                                self.syntax_error("Invalid destructuring assignment target");
                                break;
                            }
                        } else if self.match_token(TokenType::CurlyOpen) || self.match_token(TokenType::BracketOpen) {
                            let nested = self.parse_binding_pattern();
                            entry_alias = Some(BindingEntryAlias::BindingPattern(Box::new(nested)));
                        } else if self.match_identifier_name() {
                            let alias_start = self.position();
                            let token = self.consume();
                            let name = self.token_identifier_name(&token);
                            let id = self.make_identifier(alias_start, name);
                            self.pattern_bound_names.push((name, id));
                            entry_alias = Some(BindingEntryAlias::Identifier(id));
                        } else {
                            self.expected("identifier or binding pattern");
                            break;
                        }
                    } else if needs_alias {
                        self.expected("alias for string or numeric literal name");
                        break;
                    } else if let Some(name_id) = entry_name_value {
                        // Shorthand: name is the bound identifier.
                        if entry_is_keyword {
                            self.syntax_error("Binding pattern target may not be a reserved word");
                        }
                        if let Some(BindingEntryName::Identifier(id)) = entry_name {
                            self.pattern_bound_names.push((name_id, id));
                        }
                    }
                }
            } else if self.allow_member_expressions {
                let expression_start = self.position();
                let expression = self.parse_expression(
                    PRECEDENCE_ASSIGNMENT,
                    Associativity::Right,
                    ForbiddenTokens::none().forbid(&[TokenType::Equals]),
                );
                if Self::is_parenthesized_object_or_array_expression(&expression, expression_start) {
                    // https://tc39.es/ecma262/#sec-destructuring-assignment-static-semantics-early-errors
                    // If |LeftHandSideExpression| is either an |ObjectLiteral| or an |ArrayLiteral|,
                    // |LeftHandSideExpression| must cover an |AssignmentPattern|.
                    self.syntax_error("Invalid destructuring assignment target");
                    break;
                } else if Self::is_object_or_array_expression(&expression) {
                    let pattern = self.synthesize_binding_pattern(expression.range.start);
                    entry_alias = Some(BindingEntryAlias::BindingPattern(Box::new(pattern)));
                } else if Self::is_member_expression(&expression) {
                    entry_alias = Some(BindingEntryAlias::MemberExpression(Box::new(expression)));
                } else if Self::is_identifier(&expression) {
                    let id = expression_into_identifier(expression);
                    let name = self.arena.identifiers[id].name;
                    self.pattern_bound_names.push((name, id));
                    entry_alias = Some(BindingEntryAlias::Identifier(id));
                } else {
                    self.syntax_error("Invalid destructuring assignment target");
                    break;
                }
            } else if self.match_token(TokenType::CurlyOpen) || self.match_token(TokenType::BracketOpen) {
                let nested = self.parse_binding_pattern();
                entry_alias = Some(BindingEntryAlias::BindingPattern(Box::new(nested)));
            } else if self.match_identifier_name() {
                let alias_start = self.position();
                let token = self.consume();
                let name = self.token_identifier_name(&token);
                let id = self.make_identifier(alias_start, name);
                self.pattern_bound_names.push((name, id));
                entry_alias = Some(BindingEntryAlias::Identifier(id));
            } else {
                self.expected("identifier or binding pattern");
                break;
            }

            let initializer = if self.match_token(TokenType::Equals) {
                if is_rest {
                    self.syntax_error("Unexpected initializer after rest element");
                }
                self.consume();
                Some(self.parse_assignment_expression())
            } else {
                None
            };

            entries.push(BindingEntry {
                name: entry_name,
                alias: entry_alias,
                initializer,
                is_rest,
            });

            if is_rest {
                if self.match_token(TokenType::Comma) {
                    self.syntax_error("Rest element may not be followed by a comma");
                    self.consume();
                }
                break;
            }

            if self.match_token(TokenType::Comma) {
                self.consume();
            } else if is_object && !self.match_token(closing_token) {
                self.consume_token(TokenType::Comma);
            }
        }

        // Consume trailing commas for arrays.
        if !is_object {
            while self.match_token(TokenType::Comma) {
                self.consume();
            }
        }

        self.consume_token(closing_token);
        BindingPattern { kind, entries }
    }

    // https://tc39.es/ecma262/#sec-imports
    // ImportDeclaration : `import` ImportClause FromClause `;`
    //                   | `import` ModuleSpecifier `;`
    // ImportClause : ImportedDefaultBinding
    //             | NameSpaceImport
    //             | NamedImports
    //             | ImportedDefaultBinding `,` NameSpaceImport
    //             | ImportedDefaultBinding `,` NamedImports
    pub(crate) fn parse_import_statement(&mut self) -> Statement {
        let start = self.position();
        self.consume_token(TokenType::Import);

        if self.program_type != ProgramType::Module {
            self.syntax_error("Cannot use 'import' outside a module");
        }

        if self.match_token(TokenType::StringLiteral) {
            let module_specifier = self.consume_module_specifier();
            let attributes = self.parse_with_clause();
            self.consume_or_insert_semicolon();
            return self.statement(
                start,
                StatementKind::Import(Box::new(ImportStatementData {
                    module_request: ModuleRequest {
                        module_specifier,
                        attributes,
                    },
                    entries: Vec::new(),
                })),
            );
        }

        let mut entries: Vec<ImportEntry> = Vec::new();
        let mut continue_parsing = true;

        if self.match_imported_binding() {
            let token = self.consume();
            let local_name: Utf16String = self.token_value(&token).into();
            entries.push(ImportEntry {
                import_name: Some(utf16!("default").into()),
                local_name,
            });
            if self.match_token(TokenType::Comma) {
                self.consume();
            } else {
                continue_parsing = false;
            }
        }

        if continue_parsing {
            if self.match_token(TokenType::Asterisk) {
                // NameSpaceImport: * as ImportedBinding
                self.consume();
                if !self.match_as() {
                    self.expected("'as'");
                }
                self.consume(); // consume 'as'
                if self.match_imported_binding() {
                    let token = self.consume();
                    let namespace_name: Utf16String = self.token_value(&token).into();
                    entries.push(ImportEntry {
                        import_name: None,
                        local_name: namespace_name,
                    });
                } else {
                    self.expected("identifier");
                }
            } else if self.match_token(TokenType::CurlyOpen) {
                // NamedImports: { ImportSpecifier, ... }
                self.consume();
                while !self.done() && !self.match_token(TokenType::CurlyClose) {
                    if self.match_identifier_name() {
                        let require_as = !self.match_imported_binding();
                        let name_pos = self.position();
                        let token = self.consume();
                        let name = self.token_value(&token).to_vec();

                        if self.match_as() {
                            self.consume(); // consume 'as'
                            let alias_token = self.consume_identifier();
                            let alias = self.token_value(&alias_token).to_vec();
                            self.check_identifier_name_for_assignment_validity(&alias, false);
                            entries.push(ImportEntry {
                                import_name: Some(name.into()),
                                local_name: alias.into(),
                            });
                        } else if require_as {
                            self.syntax_error_at_position(
                                &format!("Unexpected reserved word '{}'", String::from_utf16_lossy(&name)),
                                name_pos,
                            );
                        } else {
                            self.check_identifier_name_for_assignment_validity(&name, false);
                            let name: Utf16String = name.into();
                            entries.push(ImportEntry {
                                import_name: Some(name.clone()),
                                local_name: name,
                            });
                        }
                    } else if self.match_token(TokenType::StringLiteral) {
                        let token = self.consume();
                        let (name, _) = self.parse_string_value(&token);
                        if let Some(&last) = name.last()
                            && (0xD800..=0xDBFF).contains(&last)
                        {
                            self.syntax_error("StringValue ending with unpaired high surrogate");
                        }

                        if !self.match_as() {
                            self.expected("'as'");
                        }
                        self.consume(); // consume 'as'

                        let alias_token = self.consume_identifier();
                        let alias = self.token_value(&alias_token).to_vec();
                        self.check_identifier_name_for_assignment_validity(&alias, false);
                        entries.push(ImportEntry {
                            import_name: Some(name),
                            local_name: alias.into(),
                        });
                    } else {
                        self.expected("identifier");
                        break;
                    }

                    if !self.match_token(TokenType::Comma) {
                        break;
                    }
                    self.consume();
                }
                self.consume_token(TokenType::CurlyClose);
            } else {
                self.expected("import clauses");
            }
        }

        if !self.match_from() {
            self.expected("'from'");
        }
        self.consume(); // consume 'from'

        let module_specifier = self.consume_module_specifier();
        let attributes = self.parse_with_clause();
        self.consume_or_insert_semicolon();

        self.statement(
            start,
            StatementKind::Import(Box::new(ImportStatementData {
                module_request: ModuleRequest {
                    module_specifier,
                    attributes,
                },
                entries,
            })),
        )
    }

    // https://tc39.es/ecma262/#sec-exports
    // ExportDeclaration : `export` ExportFromClause FromClause `;`
    //                   | `export` NamedExports `;`
    //                   | `export` VariableStatement
    //                   | `export` Declaration
    //                   | `export` `default` HoistableDeclaration
    //                   | `export` `default` ClassDeclaration
    //                   | `export` `default` AssignmentExpression `;`
    pub(crate) fn parse_export_statement(&mut self) -> Statement {
        let start = self.position();
        self.consume_token(TokenType::Export);

        if self.program_type != ProgramType::Module {
            self.syntax_error("Cannot use 'export' outside a module");
        }

        let mut entries: Vec<ExportEntry> = Vec::new();
        let mut statement: Option<Box<Statement>> = None;
        let mut is_default = false;
        let mut from_specifier: Option<Utf16String> = None;

        if self.match_token(TokenType::Default) {
            is_default = true;
            self.consume();

            let mut local_name: Option<Utf16String> = None;

            let matches_function = self.match_function_declaration_for_export();

            if matches_function != MatchesFunctionDeclaration::No {
                let has_default_name = matches_function == MatchesFunctionDeclaration::WithoutName;
                let declaration = self.parse_function_declaration_for_export(has_default_name);
                if !has_default_name
                    && let StatementKind::FunctionDeclaration(ref fd) = declaration.inner
                    && let Some(name_id) = fd.name
                {
                    local_name = Some(self.arena.name_of(name_id).clone());
                }
                statement = Some(Box::new(declaration));
            } else if self.match_token(TokenType::Class) {
                let next = self.next_token();
                if next.token_type != TokenType::CurlyOpen && next.token_type != TokenType::Extends {
                    let declaration = self.parse_class_declaration();
                    if let StatementKind::ClassDeclaration(ref class) = declaration.inner
                        && let Some(name_id) = class.name
                    {
                        local_name = Some(self.arena.name_of(name_id).clone());
                    }
                    statement = Some(Box::new(declaration));
                } else {
                    // Unnamed class declaration - don't consume semicolon,
                    // matching the C++ parser's special_case_declaration_without_name.
                    let expression = self.parse_assignment_expression();
                    let expression_range = expression.range;
                    statement = Some(Box::new(Statement::new(
                        expression_range,
                        StatementKind::Expression(Box::new(expression)),
                    )));
                }
            } else if self.match_expression() {
                // Check if this is an unnamed function/class declaration that
                // should NOT consume a trailing semicolon.
                let special_case_declaration_without_name = self.match_token(TokenType::Class)
                    || self.match_token(TokenType::Function)
                    || (self.match_token(TokenType::Async) && {
                        let next = self.next_token();
                        next.token_type == TokenType::Function && !next.trivia_has_line_terminator
                    });
                let expression = self.parse_assignment_expression();
                if !special_case_declaration_without_name {
                    self.consume_or_insert_semicolon();
                }
                let expression_range = expression.range;
                statement = Some(Box::new(Statement::new(
                    expression_range,
                    StatementKind::Expression(Box::new(expression)),
                )));
            } else {
                self.expected("declaration or assignment expression");
            }

            if local_name.is_none() {
                local_name = Some(utf16!("*default*").into());
            }

            entries.push(ExportEntry {
                kind: ExportEntryKind::NamedExport,
                export_name: Some(utf16!("default").into()),
                local_or_import_name: local_name,
            });
        } else {
            #[derive(Clone, Copy, Debug, PartialEq, Eq)]
            enum FromSpecifier {
                NotAllowed,
                Optional,
                Required,
            }
            let mut check_for_from = FromSpecifier::NotAllowed;

            if self.match_token(TokenType::Asterisk) {
                self.consume();
                if self.match_as() {
                    self.consume(); // consume 'as'
                    let (exported_name, _) = self.parse_module_export_name();
                    entries.push(ExportEntry {
                        kind: ExportEntryKind::ModuleRequestAll,
                        export_name: Some(exported_name),
                        local_or_import_name: None,
                    });
                } else {
                    entries.push(ExportEntry {
                        kind: ExportEntryKind::ModuleRequestAllButDefault,
                        export_name: None,
                        local_or_import_name: None,
                    });
                }
                check_for_from = FromSpecifier::Required;
            } else if self.match_declaration() {
                let declaration = self.parse_declaration();
                let names = get_declaration_export_names(&declaration, &self.arena);
                for name in &names {
                    entries.push(ExportEntry {
                        kind: ExportEntryKind::NamedExport,
                        export_name: Some(name.clone()),
                        local_or_import_name: Some(name.clone()),
                    });
                }
                statement = Some(Box::new(declaration));
            } else if self.match_token(TokenType::Var) {
                let var_declaration = self.parse_variable_declaration(false);
                let names = get_declaration_export_names(&var_declaration, &self.arena);
                for name in &names {
                    entries.push(ExportEntry {
                        kind: ExportEntryKind::NamedExport,
                        export_name: Some(name.clone()),
                        local_or_import_name: Some(name.clone()),
                    });
                }
                statement = Some(Box::new(var_declaration));
            } else if self.match_token(TokenType::CurlyOpen) {
                self.consume();
                check_for_from = FromSpecifier::Optional;

                while !self.done() && !self.match_token(TokenType::CurlyClose) {
                    let (identifier, was_string) = self.parse_module_export_name();
                    if was_string {
                        check_for_from = FromSpecifier::Required;
                    }

                    if self.match_as() {
                        self.consume(); // consume 'as'
                        let (export_name, _) = self.parse_module_export_name();
                        entries.push(ExportEntry {
                            kind: ExportEntryKind::NamedExport,
                            export_name: Some(export_name),
                            local_or_import_name: Some(identifier),
                        });
                    } else {
                        entries.push(ExportEntry {
                            kind: ExportEntryKind::NamedExport,
                            export_name: Some(identifier.clone()),
                            local_or_import_name: Some(identifier),
                        });
                    }

                    if !self.match_token(TokenType::Comma) {
                        break;
                    }
                    self.consume();
                }

                if entries.is_empty() {
                    entries.push(ExportEntry {
                        kind: ExportEntryKind::EmptyNamedExport,
                        export_name: None,
                        local_or_import_name: None,
                    });
                }

                self.consume_token(TokenType::CurlyClose);
            } else {
                self.syntax_error("Unexpected token 'export'");
            }

            if check_for_from != FromSpecifier::NotAllowed && self.match_from() {
                self.consume(); // consume 'from'
                from_specifier = Some(self.consume_module_specifier());
            } else if check_for_from == FromSpecifier::Required {
                self.expected("'from'");
            }

            if from_specifier.is_none() && check_for_from != FromSpecifier::NotAllowed {
                self.consume_or_insert_semicolon();
            }
        }

        let module_request = if let Some(specifier) = from_specifier {
            let attributes = self.parse_with_clause();
            self.consume_or_insert_semicolon();
            Some(ModuleRequest {
                module_specifier: specifier,
                attributes,
            })
        } else {
            None
        };

        // Check for duplicate exported names.
        for entry in &entries {
            if let Some(ref name) = entry.export_name
                && !self.exported_names.insert(name.clone())
            {
                self.syntax_error_at_position(
                    &format!(
                        "Duplicate export with name: '{}'",
                        String::from_utf16_lossy(name.as_slice())
                    ),
                    start,
                );
            }
        }

        self.statement(
            start,
            StatementKind::Export(Box::new(ExportStatementData {
                statement,
                entries,
                is_default_export: is_default,
                module_request,
            })),
        )
    }

    fn match_imported_binding(&self) -> bool {
        self.match_identifier() || self.match_token(TokenType::Yield) || self.match_token(TokenType::Await)
    }

    fn match_as(&self) -> bool {
        self.match_token(TokenType::Identifier) && self.token_original_value(&self.current_token) == utf16!("as")
    }

    fn match_from(&self) -> bool {
        self.match_token(TokenType::Identifier) && self.token_original_value(&self.current_token) == utf16!("from")
    }

    fn consume_module_specifier(&mut self) -> Utf16String {
        if !self.match_token(TokenType::StringLiteral) {
            self.expected("module specifier (string)");
            return utf16!("!!invalid!!").into();
        }
        let token = self.consume();
        let (value, _) = self.parse_string_value(&token);
        value
    }

    fn parse_module_export_name(&mut self) -> (Utf16String, bool) {
        if self.match_identifier_name() {
            let token = self.consume();
            (self.token_value(&token).into(), false)
        } else if self.match_token(TokenType::StringLiteral) {
            let token = self.consume();
            let (value, _) = self.parse_string_value(&token);
            // https://tc39.es/ecma262/#sec-module-semantics-static-semantics-early-errors
            // It is a Syntax Error if IsStringWellFormedUnicode of the StringValue
            // of StringLiteral is false.
            if let Some(&last) = value.last()
                && (0xD800..=0xDBFF).contains(&last)
            {
                self.syntax_error("StringValue ending with unpaired high surrogate");
            }
            (value, true)
        } else {
            self.expected("export specifier (string or identifier)");
            (Utf16String::default(), false)
        }
    }

    // https://tc39.es/ecma262/#sec-imports
    // WithClause : `with` `{` WithEntries `}`
    // WithEntries : AttributeKey `:` StringLiteral
    fn parse_with_clause(&mut self) -> Vec<ImportAttribute> {
        if !self.match_token(TokenType::With) {
            return Vec::new();
        }
        self.consume();
        self.consume_token(TokenType::CurlyOpen);

        let mut attributes = Vec::new();
        while !self.done() && !self.match_token(TokenType::CurlyClose) {
            let key: Utf16String = if self.match_token(TokenType::StringLiteral) {
                let token = self.consume();
                let (value, _) = self.parse_string_value(&token);
                value
            } else if self.match_identifier_name() {
                let token = self.consume();
                self.token_value(&token).into()
            } else {
                self.expected("identifier or string as attribute key");
                self.consume();
                continue;
            };

            self.consume_token(TokenType::Colon);

            if self.match_token(TokenType::StringLiteral) {
                let token = self.consume();
                let (value, _) = self.parse_string_value(&token);
                attributes.push(ImportAttribute { key, value });
            } else {
                self.expected("string as attribute value");
                self.consume();
            }

            if self.match_token(TokenType::Comma) {
                self.consume();
            } else {
                break;
            }
        }
        self.consume_token(TokenType::CurlyClose);
        attributes
    }

    fn match_function_declaration_for_export(&mut self) -> MatchesFunctionDeclaration {
        if self.match_token(TokenType::Function) {
            let next = self.next_token();
            if next.token_type == TokenType::Asterisk {
                self.save_state();
                self.consume(); // function
                self.consume(); // *
                let result = if self.match_token(TokenType::ParenOpen) {
                    MatchesFunctionDeclaration::WithoutName
                } else {
                    MatchesFunctionDeclaration::Yes
                };
                self.load_state();
                return result;
            }
            return if next.token_type == TokenType::ParenOpen {
                MatchesFunctionDeclaration::WithoutName
            } else {
                MatchesFunctionDeclaration::Yes
            };
        }

        if self.match_token(TokenType::Async) {
            let next = self.next_token();
            if next.token_type != TokenType::Function || next.trivia_has_line_terminator {
                return MatchesFunctionDeclaration::No;
            }
            self.save_state();
            self.consume(); // async
            self.consume(); // function
            if self.match_token(TokenType::Asterisk) {
                self.consume(); // *
            }
            let result = if self.match_token(TokenType::ParenOpen) {
                MatchesFunctionDeclaration::WithoutName
            } else {
                MatchesFunctionDeclaration::Yes
            };
            self.load_state();
            return result;
        }

        MatchesFunctionDeclaration::No
    }

    fn parse_function_declaration_for_export(&mut self, has_default_name: bool) -> Statement {
        if has_default_name {
            self.has_default_export_name = true;
            let result = self.parse_function_declaration();
            self.has_default_export_name = false;
            result
        } else {
            self.parse_function_declaration()
        }
    }
}

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum MatchesFunctionDeclaration {
    No,
    Yes,
    WithoutName,
}
