/**
 * Copyright (c) 2021-2026 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ETSparser.h"
#include <string_view>
#include "ETSNolintParser.h"
#include "compiler/metadata/deserialization.h"
#include "program/program.h"
#include "program/ImportCache.h"
#include "public/public.h"
#include "driver/dependency_analyzer/dep_analyzer.h"

#include "parser/parserStatusContext.h"
#include "util/es2pandaMacros.h"
#include "util/helpers.h"
#include "util/language.h"
#include "varbinder/varbinder.h"
#include "varbinder/ETSBinder.h"
#include "lexer/lexer.h"
#include "lexer/ETSLexer.h"
#include "ir/astNode.h"
#include "ir/base/decorator.h"
#include "ir/base/catchClause.h"
#include "ir/base/scriptFunction.h"
#include "ir/base/methodDefinition.h"
#include "ir/base/spreadElement.h"
#include "ir/expressions/identifier.h"
#include "ir/expressions/functionExpression.h"
#include "ir/expressions/dummyNode.h"
#include "ir/module/importDeclaration.h"
#include "ir/module/importDefaultSpecifier.h"
#include "ir/module/importSpecifier.h"
#include "ir/module/exportSpecifier.h"
#include "ir/module/exportNamedDeclaration.h"
#include "ir/ets/etsPrimitiveType.h"
#include "ir/ets/etsPackageDeclaration.h"
#include "ir/ets/etsReExportDeclaration.h"
#include "ir/ets/etsTuple.h"
#include "ir/ets/etsFunctionType.h"
#include "ir/ets/etsModule.h"
#include "ir/ets/etsTypeReference.h"
#include "ir/ets/etsTypeReferencePart.h"
#include "ir/ets/etsUnionType.h"
#include "ir/ets/etsImportDeclaration.h"
#include "ir/ets/etsStructDeclaration.h"
#include "ir/module/importNamespaceSpecifier.h"
#include "ir/ts/tsInterfaceDeclaration.h"
#include "ir/ts/tsTypeParameterInstantiation.h"
#include "ir/ts/tsInterfaceBody.h"
#include "ir/ts/tsImportEqualsDeclaration.h"
#include "ir/ts/tsArrayType.h"
#include "ir/ts/tsTypeReference.h"
#include "ir/ts/tsTypeParameter.h"
#include "ir/ts/tsInterfaceHeritage.h"
#include "ir/ts/tsFunctionType.h"
#include "ir/ts/tsTypeAliasDeclaration.h"
#include "ir/ts/tsTypeParameterDeclaration.h"
#include "generated/signatures.h"
#include "generated/diagnostic.h"

namespace ark::es2panda::parser {
class FunctionContext;

using namespace std::literals::string_literals;

ETSParser::ETSParser(public_lib::Context *context, ParserStatus status)
    : TypedParser(context, status), packageDeprecationWarned_(false)
{
}

Program *ETSParser::GetGlobalProgram() const
{
    return Context()->parserProgram;
}

bool ETSParser::IsValidIdentifierName(const lexer::Token &token) const noexcept
{
    return !token.IsPredefinedType() || util::Helpers::IsStdLib(GetProgram());
}

std::unique_ptr<lexer::Lexer> ETSParser::InitLexer()
{
    ES2PANDA_ASSERT(GetProgram() == GetContext().GetProgram());
    auto lexer = std::make_unique<lexer::ETSLexer>(&GetContext(), DiagnosticEngine());
    if (GetProgram()->Is<util::ModuleKind::ETSCACHE_DECL>()) {
        lexer->SetDefaultNextTokenFlags(lexer::NextTokenFlags::CHAR_PERCENT_ALLOWED);
    }
    SetLexer(lexer.get());
    return lexer;
}

static void DoSomethingSpecificToMainProgram(ETSParser *parser)
{
#ifndef ENABLE_ISOLATED_DECLGEN
    std::vector<parser::Program *> directImportsFromMainSource {};
    for (auto directImport : parser->GetImportPathManager()->GetParseQueue()) {
        if (directImport.program == parser->Context()->parserProgram) {
            continue;
        }

        directImportsFromMainSource.emplace_back(directImport.program);
    }
    parser->AddDirectImportsToDirectExternalDecls(directImportsFromMainSource);
#endif

    auto mainProg = parser->Context()->parserProgram;
    if (mainProg->ModuleInfo().kind == util::ModuleKind::PACKAGE) {
        // NOTE(dkofanov): import metadata of a fraction should "point" to the related package:
        auto *package = parser->GetImportPathManager()->SearchResolved(mainProg->GetImportInfo());

        if (package->Is<util::ModuleKind::PACKAGE>()) {
            ES2PANDA_ASSERT(package != mainProg);
            [[maybe_unused]] auto &fractions = package->As<util::ModuleKind::PACKAGE>()->GetUnmergedPackagePrograms();
            ES2PANDA_ASSERT(std::find(fractions.begin(), fractions.end(), mainProg) != fractions.end());

            package->PromoteToMainProgram(parser->Context());
        }
    }
}

void ETSParser::ParseGlobalImpl()
{
    ES2PANDA_ASSERT(GetProgram() != nullptr);
    ES2PANDA_ASSERT(GetProgram() == Context()->parserProgram);

    ParseSource(GetProgram());
    GetImportPathManager()->GetParseQueue().front().isParsed = true;

    ES2PANDA_ASSERT(GetImportPathManager()->GetParseQueue().front().program == GetProgram());

    // for incremental dep analyzer , we only need find the first level childs to update the full dep map
    if (GetContext().IsIncrementalDependencyAnalyzerMode()) {
        return;
    }

    DoSomethingSpecificToMainProgram(this);

    ParseSources();
}

ir::ExpressionStatement *ETSParser::ParseFileHeaderFlag()
{
    if (Lexer()->GetToken().KeywordType() != lexer::TokenType::LITERAL_STRING ||
        Lexer()->GetToken().String() != compiler::Signatures::STATIC_PROGRAM_FLAG) {
        return nullptr;
    }

    auto startLoc = Lexer()->GetToken().Start();
    ir::Expression *fileHeaderFlag = ParseStringLiteral();
    auto *exprStatementNode = AllocNode<ir::ExpressionStatement>(fileHeaderFlag);
    ES2PANDA_ASSERT(exprStatementNode != nullptr);
    exprStatementNode->SetRange({startLoc, fileHeaderFlag->End()});
    ConsumeSemicolon(exprStatementNode);
    return exprStatementNode;
}

ir::ETSModule *ETSParser::ParseETSGlobalScript(lexer::SourcePosition startLoc, ArenaVector<ir::Statement *> &statements)
{
    ETSNolintParser etsnolintParser(this);
    etsnolintParser.CollectETSNolints();

    auto imports = ParseImportDeclarations();
    statements.insert(statements.end(), imports.begin(), imports.end());

    auto topLevelStatements = ParseTopLevelDeclaration();
    statements.insert(statements.end(), topLevelStatements.begin(), topLevelStatements.end());

    etsnolintParser.ApplyETSNolintsToStatements(statements);

    auto ident = AllocNode<ir::Identifier>(compiler::Signatures::ETS_GLOBAL, Allocator());
    auto *etsModule = AllocNode<ir::ETSModule>(Allocator(), std::move(statements), ident, ir::ModuleFlag::ETSSCRIPT,
                                               GetContext().GetLanguage(), GetProgram());
    ES2PANDA_ASSERT(etsModule != nullptr);
    etsModule->SetRange({startLoc, Lexer()->GetToken().End()});
    for (auto topLevelStatement : topLevelStatements) {
        if (topLevelStatement->IsETSReExportDeclaration()) {
            topLevelStatement->AsETSReExportDeclaration()->GetETSImportDeclarations()->SetParent(etsModule);
        }
    }
    return etsModule;
}

ir::ETSModule *ETSParser::ParseImportsAndReExportOnly(lexer::SourcePosition startLoc,
                                                      ArenaVector<ir::Statement *> &statements)
{
    ETSNolintParser etsnolintParser(this);
    etsnolintParser.CollectETSNolints();

    auto imports = ParseImportDeclarations();
    statements.insert(statements.end(), imports.begin(), imports.end());
    etsnolintParser.ApplyETSNolintsToStatements(statements);

    auto ident = AllocNode<ir::Identifier>(compiler::Signatures::ETS_GLOBAL, Allocator());
    auto *etsModule = AllocNode<ir::ETSModule>(Allocator(), std::move(statements), ident, ir::ModuleFlag::ETSSCRIPT,
                                               GetContext().GetLanguage(), GetProgram());
    ES2PANDA_ASSERT(etsModule != nullptr);
    etsModule->SetRange({startLoc, Lexer()->GetToken().End()});
    return etsModule;
}

parser::Program *ETSParser::IntroduceStdlibImportProgram(std::string &&importSrc)
{
    auto *stdlibImportProgram = GetImportPathManager()->IntroduceStdlibImportProgram(std::move(importSrc));
    if (stdlibImportProgram->Ast() != nullptr) {
        return stdlibImportProgram;
    }
    ES2PANDA_ASSERT(GetImportPathManager()->GetParseQueue().back().program == stdlibImportProgram);
    ES2PANDA_ASSERT(!GetImportPathManager()->GetParseQueue().back().isParsed);

    GetContext().Status() |= ParserStatus::IN_DEFAULT_IMPORTS;
    ParseSources();
    GetContext().Status() &= ~ParserStatus::IN_DEFAULT_IMPORTS;

    return stdlibImportProgram;
}

void ETSParser::AddDirectImportsToDirectExternalDecls(
    const std::vector<parser::Program *> &directImportsFromMainSource) const
{
    // NOTE(dkofanov): For some reason, "directExternalPrograms" are populated only for main program.
    ES2PANDA_ASSERT(GetProgram() == GetGlobalProgram());
    auto &directExtSourcesHolder = GetGlobalProgram()->GetExternalDecls()->Direct();
    for (auto *prog : directImportsFromMainSource) {
        auto key = prog->GetImportInfo().ResolvedSource();
        directExtSourcesHolder.insert({ArenaString {key}, prog});
    }
}

void ETSParser::IncrementalParse()
{
    ES2PANDA_ASSERT(Context()->parserProgram != nullptr);
    SetProgram(Context()->parserProgram);
    GetContext().SetProgram(Context()->parserProgram);
    GetContext().SetLanguage(ToLanguage(Context()->parserProgram->Extension()));
    ES2PANDA_ASSERT(Context()->parserProgram == GetProgram());

    ES2PANDA_ASSERT(GetProgram() != nullptr);
    ParseSource(GetProgram());
    GetImportPathManager()->GetParseQueue().front().isParsed = true;
    ES2PANDA_ASSERT(GetImportPathManager()->GetParseQueue().front().program == GetProgram());

    DoSomethingSpecificToMainProgram(this);
    ParseSources();
}

void ETSParser::ParseInSimultMode()
{
    ES2PANDA_ASSERT(Context()->parserProgram == nullptr);
    ES2PANDA_ASSERT(GetImportPathManager()->GetParseQueue().empty());

    GetImportPathManager()->InitParseQueueForSimult();

    ES2PANDA_ASSERT(Context()->parserProgram != nullptr);

    SetProgram(Context()->parserProgram);
    GetContext().SetProgram(Context()->parserProgram);
    GetContext().SetLanguage(ToLanguage(Context()->parserProgram->Extension()));

    // NOTE(dkofanov): direct-sources to be removed.
    std::vector<parser::Program *> directImportsFromMainSource {};
    for (auto directImport : GetImportPathManager()->GetParseQueue()) {
        ES2PANDA_ASSERT(!directImport.isParsed);
        directImportsFromMainSource.emplace_back(directImport.program);
    }
    ParseSources();
    AddDirectImportsToDirectExternalDecls(directImportsFromMainSource);
}

void ETSParser::ParseSources()
{
    auto &parseQueue = GetImportPathManager()->GetParseQueue();
    // NOLINTNEXTLINE(modernize-loop-convert)
    for (size_t i = 0; i < parseQueue.size(); i++) {
        if (!parseQueue[i].isParsed) {
            ParseNotParsed(&parseQueue[i]);
        }
    }
}

void ETSParser::ParseNotParsed(util::ImportPathManager::ParseInfo *notParsedElement)
{
    ES2PANDA_ASSERT(!notParsedElement->isParsed);
    notParsedElement->isParsed = true;

    const auto &data = notParsedElement->program->GetImportInfo();
    ES2PANDA_ASSERT(notParsedElement->program != GetGlobalProgram());
    ES2PANDA_ASSERT(data.Lang() != Language::Id::COUNT);
    auto preservedLang = GetContext().SetLanguage(data.Lang());

    // Check if already visited in dep_analyzer
    if (GetContext().IsDependencyAnalyzerMode()) {
        const auto *depAnalyzer = Context()->depAnalyzer;
        ES2PANDA_ASSERT(depAnalyzer != nullptr);
        if (depAnalyzer->GetAlreadyProcessedFiles().count(std::string {data.ResolvedSource()}) > 0) {
            return;
        }
    }
    ParseSource(notParsedElement->program);

    GetContext().SetLanguage(preservedLang);
}

void ETSParser::ParseSource(parser::Program *program)
{
    auto esp = ExternalSourceParser(this, program);
    ES2PANDA_ASSERT(GetProgram() == program);
    ES2PANDA_ASSERT(GetProgram()->Ast() == nullptr);
    auto lexer = InitLexer();

    lexer::SourcePosition startLoc = Lexer()->GetToken().Start();
    Lexer()->NextToken();
    ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
    ir::Statement *header = ParseFileHeaderFlag();
    ir::Statement *packageDecl = ParsePackageDeclaration();
    for (auto st : {header, packageDecl}) {
        if (st != nullptr) {
            statements.push_back(st);
        }
    }
    ir::ETSModule *script = nullptr;
    if (GetContext().IsDependencyAnalyzerMode()) {
        script = ParseImportsAndReExportOnly(startLoc, statements);
    } else {
        script = ParseETSGlobalScript(startLoc, statements);
    }
    if ((GetContext().Status() & ParserStatus::IN_PACKAGE) != 0) {
        GetContext().Status() &= ~ParserStatus::IN_PACKAGE;
    }
    program->SetAst(script);
}

ir::Statement *ETSParser::ParseIdentKeyword()
{
    const auto token = Lexer()->GetToken();
    ES2PANDA_ASSERT(token.Type() == lexer::TokenType::LITERAL_IDENT);
    switch (token.KeywordType()) {
        case lexer::TokenType::KEYW_STRUCT: {
            return ParseTypeDeclaration(false);
        }
        case lexer::TokenType::KEYW_TYPE: {
            return ParseTypeAliasDeclaration();
        }
        default: {
            break;
        }
    }
    return nullptr;
}

ir::ScriptFunction *ETSParser::ParseFunction(ParserStatus newStatus)
{
    FunctionContext functionContext(this, newStatus | ParserStatus::FUNCTION);
    lexer::SourcePosition startLoc = Lexer()->GetToken().Start();
    auto [signature, throwMarker] = ParseFunctionSignature(newStatus);

    ir::AstNode *body = nullptr;
    lexer::SourcePosition endLoc = Lexer()->GetToken().Start();
    bool isOverload = false;
    bool isArrow = (newStatus & ParserStatus::ARROW_FUNCTION) != 0;

    if ((newStatus & ParserStatus::ASYNC_FUNCTION) != 0) {
        functionContext.AddFlag(ir::ScriptFunctionFlags::ASYNC);
    }

    if (isArrow) {
        ExpectToken(lexer::TokenType::PUNCTUATOR_ARROW);
        functionContext.AddFlag(ir::ScriptFunctionFlags::ARROW);
    }

    auto &contextStatus = GetContext().Status();
    contextStatus |= ParserStatus::ALLOW_SUPER;
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
        std::tie(std::ignore, body, endLoc, isOverload) =
            ParseFunctionBody(signature.Params(), newStatus, contextStatus);
    } else if (isArrow) {
        body = ParseExpression();
        endLoc = body->AsExpression()->End();
        functionContext.AddFlag(ir::ScriptFunctionFlags::EXPRESSION);
    }
    contextStatus ^= ParserStatus::ALLOW_SUPER;

    if ((GetContext().Status() & ParserStatus::FUNCTION_HAS_RETURN_STATEMENT) != 0) {
        functionContext.AddFlag(ir::ScriptFunctionFlags::HAS_RETURN);
        GetContext().Status() ^= ParserStatus::FUNCTION_HAS_RETURN_STATEMENT;
    }
    if ((GetContext().Status() & ParserStatus::FUNCTION_HAS_THROW_STATEMENT) != 0) {
        functionContext.AddFlag(ir::ScriptFunctionFlags::HAS_THROW);
        GetContext().Status() ^= ParserStatus::FUNCTION_HAS_THROW_STATEMENT;
    }

    functionContext.AddFlag(throwMarker);

    bool isDeclare = InAmbientContext();
    if (functionContext.IsAsync() && isDeclare) {
        LogError(diagnostic::ASYNC_IN_AMBIENT_CONTEXT);
    }

    if (isDeclare && signature.ReturnType() != nullptr) {
        // Note: if the return type annotation is null, the error handler will set later.
        endLoc = signature.ReturnType()->Range().end;
    }

    // clang-format off
    ir::ModifierFlags mFlags = isDeclare ? ir::ModifierFlags::DECLARE : ir::ModifierFlags::NONE;
    ir::ScriptFunctionFlags funcFlags =
        isDeclare ? (functionContext.Flags() | ir::ScriptFunctionFlags::EXTERNAL) : functionContext.Flags();
    auto *funcNode = AllocNode<ir::ScriptFunction>(
        Allocator(), ir::ScriptFunction::ScriptFunctionData {body, std::move(signature), funcFlags, mFlags,
                                                             GetContext().GetLanguage()});
    ES2PANDA_ASSERT(funcNode != nullptr);
    funcNode->SetRange({startLoc, endLoc});
    // clang-format on

    return funcNode;
}

std::tuple<bool, ir::BlockStatement *, lexer::SourcePosition, bool> ETSParser::ParseFunctionBody(
    // CC-OFFNXT(G.FMT.06-CPP) project code style
    [[maybe_unused]] const ArenaVector<ir::Expression *> &params, [[maybe_unused]] ParserStatus newStatus,
    [[maybe_unused]] ParserStatus contextStatus)
{
    if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
        LogError(diagnostic::EXPECTED_PARAM_GOT_PARAM, {"{", TokenToString(Lexer()->GetToken().Type())});
        return {false, nullptr, Lexer()->GetToken().End(), false};
    }

    if (InAmbientContext()) {
        LogError(diagnostic::IMPLEMENTATION_IN_AMBIENT_CONTEXT, {}, Lexer()->GetToken().Start());
    }

    ir::BlockStatement *body = ParseBlockStatement();
    ES2PANDA_ASSERT(body != nullptr);

    return {true, body, body->End(), false};
}

ir::AstNode *ETSParser::ParseInnerTypeDeclaration(ir::ModifierFlags memberModifiers, lexer::LexerPosition savedPos,
                                                  bool isStepToken, bool seenStatic)
{
    if ((GetContext().Status() & ParserStatus::IN_NAMESPACE) == 0) {
        LogError(diagnostic::IMPROPER_NESTING_CLASS);
    }

    // remove saved_pos nolint
    Lexer()->Rewind(savedPos);
    if (isStepToken) {
        Lexer()->NextToken();
    }

    Lexer()->GetToken().SetTokenType(Lexer()->GetToken().KeywordType());
    ir::AstNode *typeDecl = ParseTypeDeclaration(true);
    if (typeDecl == nullptr) {
        return nullptr;
    }
    memberModifiers &= (ir::ModifierFlags::PUBLIC | ir::ModifierFlags::PROTECTED | ir::ModifierFlags::PRIVATE);
    typeDecl->AddModifier(memberModifiers);

    if (!seenStatic) {
        if (typeDecl->IsClassDeclaration()) {
            typeDecl->AsClassDeclaration()->Definition()->AsClassDefinition()->SetInnerModifier();
        } else if (typeDecl->IsETSStructDeclaration()) {
            typeDecl->AsETSStructDeclaration()->Definition()->AsClassDefinition()->SetInnerModifier();
        }
    }

    return typeDecl;
}

ir::AstNode *ETSParser::ParseInnerConstructorDeclaration(ir::ModifierFlags memberModifiers,
                                                         const lexer::SourcePosition &startLoc, bool isDefault)
{
    if ((memberModifiers & (~(ir::ModifierFlags::ACCESS | ir::ModifierFlags::DECLARE | ir::ModifierFlags::NATIVE))) !=
        0) {
        LogError(diagnostic::INVALID_DECORATOR_CONSTRUCTOR);
    }

    lexer::Token constructorToken = Lexer()->GetToken();
    Lexer()->TryEatTokenType(lexer::TokenType::KEYW_CONSTRUCTOR);
    memberModifiers |= ir::ModifierFlags::CONSTRUCTOR;

    ir::Identifier *memberName = nullptr;
    if (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_IDENT) {
        memberName = ExpectIdentifier(false, true);
    } else {
        memberName = AllocNode<ir::Identifier>(constructorToken.Ident(), Allocator());
        memberName->SetRange(constructorToken.Loc());
    }

    auto *classMethod = ParseClassMethodDefinition(memberName, memberModifiers, isDefault);
    ES2PANDA_ASSERT(classMethod != nullptr);
    classMethod->SetStart(startLoc);

    return classMethod;
}

ir::Identifier *ETSParser::CreateInvokeIdentifier()
{
    util::StringView tokenName = util::StringView {compiler::Signatures::STATIC_INVOKE_METHOD};
    auto ident = AllocNode<ir::Identifier>(tokenName, Allocator());
    ES2PANDA_ASSERT(ident != nullptr);
    ident->SetRange({Lexer()->GetToken().Start(), Lexer()->GetToken().End()});
    return ident;
}

bool ETSParser::CheckAccessorDeclaration(ir::ModifierFlags memberModifiers)
{
    if (Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_GET &&
        Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_SET) {
        return false;
    }

    ir::ModifierFlags methodModifiersNotAccessorModifiers = ir::ModifierFlags::ASYNC;
    if ((memberModifiers & methodModifiersNotAccessorModifiers) != 0) {
        LogError(diagnostic::MODIFIERS_OF_GET_SET_LIMITED);
    }

    auto pos = Lexer()->Save();
    Lexer()->NextToken();
    if (Lexer()->TryEatTokenType(lexer::TokenType::LITERAL_IDENT) ||
        Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_FORMAT)) {
        Lexer()->Rewind(pos);
        return true;
    }
    Lexer()->Rewind(pos);

    return false;
}

ir::AstNode *ETSParser::ParseInnerRest(const ArenaVector<ir::AstNode *> &properties,
                                       ir::ClassDefinitionModifiers modifiers, ir::ModifierFlags memberModifiers,
                                       const lexer::SourcePosition &startLoc, bool isDefault)
{
    if (CheckAccessorDeclaration(memberModifiers)) {
        return ParseClassGetterSetterMethod(properties, modifiers, memberModifiers, isDefault);
    }

    auto parseClassMethod = [&memberModifiers, isDefault, this](ir::Identifier *methodName) {
        auto *classMethod = ParseClassMethodDefinition(methodName, memberModifiers, isDefault);
        ES2PANDA_ASSERT(classMethod != nullptr);
        classMethod->SetStart(methodName->Start());
        return classMethod;
    };

    if (InAmbientContext()) {
        if (auto *property = HandleAmbientDeclaration(memberModifiers, parseClassMethod); property != nullptr) {
            return property;
        }
    }

    if (Lexer()->TryEatTokenFromKeywordType(lexer::TokenType::KEYW_OVERLOAD)) {
        auto *classOverload = ParseClassOverloadDeclaration(memberModifiers);
        classOverload->SetStart(startLoc);
        return classOverload;
    }

    auto *memberName = ExpectIdentifier(false, false, TypeAnnotationParsingOptions::NO_OPTS);  // don't report error
    if (memberName == nullptr) {
        auto tokenType = Lexer()->GetToken().Type();
        if (Lexer()->TryEatTokenType(lexer::TokenType::KEYW_NEW) ||
            Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS) {
            LogError(diagnostic::CALL_SIG_IN_OBJECT,
                     {tokenType == lexer::TokenType::KEYW_NEW ? "Constructor" : "Call"});
            auto *ident = AllocNode<ir::Identifier>("dummy", Allocator());
            parseClassMethod(ident);
            return AllocBrokenStatement(Lexer()->GetToken().Loc());
        }  // log error here
        LogUnexpectedToken(Lexer()->GetToken());
        const auto &rangeToken = Lexer()->GetToken().Loc();
        Lexer()->NextToken();
        return AllocBrokenStatement(rangeToken);
    }
    if (memberName->IsErrorPlaceHolder()) {
        return AllocBrokenStatement(startLoc);
    }
    ThrowOptionalMethodErrorIfNeeded();

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS ||
        Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LESS_THAN) {
        return parseClassMethod(memberName);
    }

    ArenaVector<ir::AstNode *> fieldDeclarations(Allocator()->Adapter());
    auto *placeholder = AllocNode<ir::TSInterfaceBody>(std::move(fieldDeclarations));
    ES2PANDA_ASSERT(placeholder != nullptr);
    ParseClassFieldDefinition(memberName, memberModifiers, placeholder->BodyPtr(), isDefault);
    return placeholder;
}

ir::Statement *ETSParser::ParseTypeDeclarationAbstractFinal(bool allowStatic, ir::ClassDefinitionModifiers modifiers)
{
    auto flags = ParseClassModifiers();
    if (allowStatic && (flags & ir::ModifierFlags::STATIC) == 0U) {
        modifiers |= ir::ClassDefinitionModifiers::INNER;
    }

    if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS) {
        return ParseClassDeclaration(modifiers, flags);
    }

    if (IsStructKeyword()) {
        return ParseStructDeclaration(modifiers, flags);
    }

    LogUnexpectedToken(Lexer()->GetToken());
    return AllocBrokenStatement(Lexer()->GetToken().Loc());
}

ir::Statement *ETSParser::ParseTypeDeclaration(bool allowStatic)
{
    auto savedPos = Lexer()->Save();

    auto modifiers = ir::ClassDefinitionModifiers::ID_REQUIRED | ir::ClassDefinitionModifiers::CLASS_DECL;

    auto tokenType = Lexer()->GetToken().Type();
    switch (tokenType) {
        case lexer::TokenType::KEYW_STATIC:
            if (!allowStatic) {
                LogUnexpectedToken(Lexer()->GetToken());
            }

            Lexer()->NextToken();

            if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_INTERFACE) {
                return ParseInterfaceDeclaration(true);
            }

            if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
                Lexer()->Rewind(savedPos);
                return ParseClassStaticBlock();
            }

            Lexer()->Rewind(savedPos);
            [[fallthrough]];
        case lexer::TokenType::KEYW_ABSTRACT:
        case lexer::TokenType::KEYW_FINAL:
            return ParseTypeDeclarationAbstractFinal(allowStatic, modifiers);
        case lexer::TokenType::KEYW_ENUM:
            return ParseEnumDeclaration(false);
        case lexer::TokenType::KEYW_INTERFACE:
            return ParseInterfaceDeclaration(false);
        case lexer::TokenType::KEYW_CLASS:
            return ParseClassDeclaration(modifiers);
        case lexer::TokenType::KEYW_STRUCT:
            return ParseStructDeclaration(modifiers);
        case lexer::TokenType::LITERAL_IDENT:
            if (Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_STRUCT) {
                return ParseStructDeclaration(modifiers);
            }
            [[fallthrough]];
        default:
            const auto &tokenNow = Lexer()->GetToken();
            LogUnexpectedToken(tokenNow);
            return AllocBrokenStatement(tokenNow.Loc());
    }
}

ir::TSTypeAliasDeclaration *ETSParser::ParseTypeAliasDeclaration()
{
    ES2PANDA_ASSERT(Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_TYPE);

    const auto start = Lexer()->Save();
    lexer::SourcePosition typeStart = Lexer()->GetToken().Start();
    Lexer()->NextToken();  // eat type keyword

    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        Lexer()->Rewind(start);
        return nullptr;
    }

    if (Lexer()->GetToken().IsReservedTypeName() && !util::Helpers::IsStdLib(GetProgram())) {
        LogError(diagnostic::TYPE_ALIAS_INVALID_NAME, {TokenToString(Lexer()->GetToken().KeywordType())});
    }

    ir::Identifier *id = ExpectIdentifier();

    auto *typeAliasDecl = AllocNode<ir::TSTypeAliasDeclaration>(Allocator(), id);
    ES2PANDA_ASSERT(typeAliasDecl != nullptr);

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LESS_THAN) {
        auto options =
            TypeAnnotationParsingOptions::REPORT_ERROR | TypeAnnotationParsingOptions::ALLOW_DECLARATION_SITE_VARIANCE;
        ir::TSTypeParameterDeclaration *params = ParseTypeParameterDeclaration(&options);
        typeAliasDecl->SetTypeParameters(params);
        params->SetParent(typeAliasDecl);
    }

    ExpectToken(lexer::TokenType::PUNCTUATOR_SUBSTITUTION);

    if (Lexer()->TryEatTokenFromKeywordType(lexer::TokenType::KEYW_NEW)) {
        LogError(diagnostic::CONSTRUCTOR_FUNC_TYPE_NOT_SUPPORTED, {}, typeStart);
        typeAliasDecl->SetTsTypeAnnotation(AllocBrokenType(typeStart));
    }

    TypeAnnotationParsingOptions options =
        TypeAnnotationParsingOptions::REPORT_ERROR | TypeAnnotationParsingOptions::TYPE_ALIAS_CONTEXT;
    ir::TypeNode *typeAnnotation = ParseTypeAnnotation(&options);
    if (typeAnnotation == nullptr) {
        return nullptr;
    }
    if (typeAliasDecl->TypeAnnotation() == nullptr) {
        typeAliasDecl->SetTsTypeAnnotation(typeAnnotation);
    }
    typeAnnotation->SetParent(typeAliasDecl);
    typeAliasDecl->SetRange({typeStart, typeAnnotation->End()});
    return typeAliasDecl;
}

std::string ETSParser::PrimitiveTypeToName(ir::PrimitiveType type) const
{
    switch (type) {
        case ir::PrimitiveType::BYTE:
            return "byte";
        case ir::PrimitiveType::INT:
            return "int";
        case ir::PrimitiveType::LONG:
            return "long";
        case ir::PrimitiveType::SHORT:
            return "short";
        case ir::PrimitiveType::FLOAT:
            return "float";
        case ir::PrimitiveType::DOUBLE:
            return "double";
        case ir::PrimitiveType::BOOLEAN:
            return "boolean";
        case ir::PrimitiveType::CHAR:
            return "char";
        case ir::PrimitiveType::VOID:
            return "void";
        default:
            ES2PANDA_UNREACHABLE();
    }
}

std::string ETSParser::GetNameForETSUnionType(const ir::TypeNode *typeAnnotation) const
{
    ES2PANDA_ASSERT(typeAnnotation->IsETSUnionType());
    std::string newstr;
    for (size_t i = 0; i < typeAnnotation->AsETSUnionType()->Types().size(); i++) {
        auto type = typeAnnotation->AsETSUnionType()->Types()[i];
        std::string str = GetNameForTypeNode(type);
        newstr += str;
        if (i != typeAnnotation->AsETSUnionType()->Types().size() - 1) {
            newstr += "|";
        }
    }
    return newstr;
}

std::string ETSParser::GetNameForTypeNode(const ir::TypeNode *typeAnnotation) const
{
    if (typeAnnotation->IsETSUnionType()) {
        return GetNameForETSUnionType(typeAnnotation);
    }
    if (typeAnnotation->IsETSPrimitiveType()) {
        return PrimitiveTypeToName(typeAnnotation->AsETSPrimitiveType()->GetPrimitiveType());
    }

    if (typeAnnotation->IsETSTypeReference()) {
        std::string typeParamNames;
        auto typeParam = typeAnnotation->AsETSTypeReference()->Part()->TypeParams();
        if (typeParam != nullptr && typeParam->IsTSTypeParameterInstantiation()) {
            typeParamNames = "<";
            auto paramList = typeParam->Params();
            for (auto param : paramList) {
                std::string typeParamName = GetNameForTypeNode(param);
                typeParamNames += typeParamName + ",";
            }
            typeParamNames.pop_back();
            typeParamNames += ">";
        }
        return typeAnnotation->AsETSTypeReference()->Part()->GetIdent()->Name().Mutf8() + typeParamNames;
    }

    if (typeAnnotation->IsETSFunctionType()) {
        std::string lambdaParams = " ";

        for (const auto *const param : typeAnnotation->AsETSFunctionType()->Params()) {
            lambdaParams += param->AsETSParameterExpression()->Name().Mutf8();
            lambdaParams += ":";
            lambdaParams += GetNameForTypeNode(param->AsETSParameterExpression()->TypeAnnotation());
            lambdaParams += ",";
        }

        lambdaParams.pop_back();
        const std::string returnTypeName = GetNameForTypeNode(typeAnnotation->AsETSFunctionType()->ReturnType());

        return "((" + lambdaParams + ") => " + returnTypeName + ")";
    }

    if (typeAnnotation->IsTSArrayType()) {
        // Note! array is required for the rest parameter.
        return GetNameForTypeNode(typeAnnotation->AsTSArrayType()->ElementType()) + "[]";
    }

    if (typeAnnotation->IsETSNullType()) {
        return "null";
    }

    if (typeAnnotation->IsETSUndefinedType()) {
        return "undefined";
    }

    ES2PANDA_UNREACHABLE();
}

void ETSParser::ValidateRestParameter(ir::Expression *param)
{
    if (!param->IsETSParameterExpression()) {
        return;
    }
    if (!param->AsETSParameterExpression()->IsRestParameter()) {
        return;
    }
    GetContext().Status() |= ParserStatus::HAS_COMPLEX_PARAM;
    if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) {
        // rest_parameter_04.ets
        LogError(diagnostic::REST_PARAM_NOT_LAST);
        const auto pos = Lexer()->Save();
        Lexer()->NextToken();
        if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) {
            // ...a: int, b: int)
            Lexer()->Rewind(pos);
            Lexer()->GetToken().SetTokenType(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS);
        }
        // typo happened, just skip the token
        // ...a: int,)
    }
}

bool ETSParser::ValidateBreakLabel([[maybe_unused]] util::StringView label)
{
    // For ETS validate labels in checker via variables
    return true;
}

bool ETSParser::ValidateContinueLabel([[maybe_unused]] util::StringView label)
{
    // For ETS validate labels in checker via variables
    return true;
}

std::tuple<ir::Expression *, ir::TSTypeParameterInstantiation *> ETSParser::ParseTypeReferencePart(
    TypeAnnotationParsingOptions *options)
{
    ExpressionParseFlags flags = ExpressionParseFlags::NO_OPTS;

    if (((*options) & TypeAnnotationParsingOptions::POTENTIAL_CLASS_LITERAL) != 0) {
        flags |= ExpressionParseFlags::POTENTIAL_CLASS_LITERAL;
    }

    if (((*options) & TypeAnnotationParsingOptions::POTENTIAL_NEW_ARRAY) != 0) {
        flags |= ExpressionParseFlags::POTENTIAL_NEW_ARRAY;
    }

    auto *typeName = ParseQualifiedName(flags);
    if (((*options) & TypeAnnotationParsingOptions::POTENTIAL_CLASS_LITERAL) != 0 &&
        (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS || IsStructKeyword())) {
        return {typeName, nullptr};
    }

    ir::TSTypeParameterInstantiation *typeParamInst = nullptr;
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT ||
        (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LESS_THAN && Lexer()->HasMatchingGreaterThan())) {
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT) {
            Lexer()->BackwardToken(lexer::TokenType::PUNCTUATOR_LESS_THAN, 1);
        }
        *options |= TypeAnnotationParsingOptions::ALLOW_WILDCARD;
        typeParamInst = ParseTypeParameterInstantiation(options);
        *options &= ~(TypeAnnotationParsingOptions::ALLOW_WILDCARD);
    }

    return {typeName, typeParamInst};
}

ir::TypeNode *ETSParser::ParseTypeReference(TypeAnnotationParsingOptions *options)
{
    auto startPos = Lexer()->GetToken().Start();
    ir::ETSTypeReferencePart *typeRefPart = nullptr;

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_FORMAT) {
        return ParseTypeFormatPlaceholder();
    }

    while (true) {
        auto partPos = Lexer()->GetToken().Start();
        auto [typeName, typeParams] = ParseTypeReferencePart(options);
        if (typeName == nullptr) {
            typeName = AllocBrokenExpression(partPos);
        }

        typeRefPart = AllocNode<ir::ETSTypeReferencePart>(typeName, typeParams, typeRefPart, Allocator());
        ES2PANDA_ASSERT(typeRefPart != nullptr);
        auto endPos = typeParams == nullptr ? typeName->End() : typeParams->End();
        typeRefPart->SetRange({partPos, endPos});

        if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_PERIOD)) {
            if (Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_IS) {
                auto isPos = Lexer()->GetToken().Start();
                Lexer()->NextToken();  // eat 'is'
                Lexer()->TryEatTokenType(lexer::TokenType::LITERAL_IDENT);
                LogError(diagnostic::ERROR_ARKTS_NO_IS_OPERATOR, {}, isPos);
                return AllocBrokenType({partPos, Lexer()->GetToken().End()});
            }
            break;
        }

        if (((*options) & TypeAnnotationParsingOptions::POTENTIAL_CLASS_LITERAL) != 0 &&
            (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_CLASS || IsStructKeyword())) {
            break;
        }
    }

    auto *typeReference = AllocNode<ir::ETSTypeReference>(typeRefPart, Allocator());
    ES2PANDA_ASSERT(typeReference != nullptr);
    typeReference->SetRange({startPos, typeRefPart->End()});
    return typeReference;
}

ir::TypeNode *ETSParser::ParseBaseTypeReference(TypeAnnotationParsingOptions *options)
{
    switch (Lexer()->GetToken().KeywordType()) {
        case lexer::TokenType::KEYW_BOOLEAN:
            return ParsePrimitiveType(options, ir::PrimitiveType::BOOLEAN);
        case lexer::TokenType::KEYW_BYTE:
            return ParsePrimitiveType(options, ir::PrimitiveType::BYTE);
        case lexer::TokenType::KEYW_CHAR:
            return ParsePrimitiveType(options, ir::PrimitiveType::CHAR);
        case lexer::TokenType::KEYW_DOUBLE:
            return ParsePrimitiveType(options, ir::PrimitiveType::DOUBLE);
        case lexer::TokenType::KEYW_FLOAT:
            return ParsePrimitiveType(options, ir::PrimitiveType::FLOAT);
        case lexer::TokenType::KEYW_INT:
            return ParsePrimitiveType(options, ir::PrimitiveType::INT);
        case lexer::TokenType::KEYW_LONG:
            return ParsePrimitiveType(options, ir::PrimitiveType::LONG);
        case lexer::TokenType::KEYW_SHORT:
            return ParsePrimitiveType(options, ir::PrimitiveType::SHORT);
        default:
            return nullptr;
    }
}

ir::TypeNode *ETSParser::ParseLiteralIdent(TypeAnnotationParsingOptions *options)
{
    if (Lexer()->GetToken().IsPredefinedType()) {
        return GetTypeAnnotationOfPrimitiveType(Lexer()->GetToken().KeywordType(), options);
    }

    if ((((*options) & TypeAnnotationParsingOptions::IGNORE_KEYW_KEYOF) == 0) &&
        Lexer()->TryEatTokenFromKeywordType(lexer::TokenType::KEYW_KEYOF)) {
        auto keyofOptions = *options | TypeAnnotationParsingOptions::REPORT_ERROR;
        auto *typeAnnotation = ParseTypeAnnotationNoPreferParam(&keyofOptions);
        ES2PANDA_ASSERT(typeAnnotation != nullptr);
        typeAnnotation = AllocNode<ir::ETSKeyofType>(typeAnnotation, Allocator());
        typeAnnotation->SetRange(Lexer()->GetToken().Loc());
        return typeAnnotation;
    }

    return ParseTypeReference(options);
}

void ETSParser::ParseRightParenthesis(TypeAnnotationParsingOptions *options, ir::TypeNode *&typeAnnotation,
                                      lexer::LexerPosition savedPos)
{
    if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS)) {
        if (((*options) & TypeAnnotationParsingOptions::REPORT_ERROR) != 0) {
            LogExpectedToken(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS);
            Lexer()->NextToken();  // eat ')'
            return;
        }

        Lexer()->Rewind(savedPos);
        typeAnnotation = nullptr;
    }
}

void ETSParser::ReportIfVarDeclaration(VariableParsingFlags flags)
{
    if ((flags & VariableParsingFlags::VAR) != 0) {
        LogError(diagnostic::ERROR_ARKTS_NO_VAR);
    }
}

ir::Statement *ETSParser::CreateReExportDeclarationNode(ir::ETSImportDeclaration *reExportDeclaration,
                                                        const lexer::SourcePosition &startLoc,
                                                        const ir::ModifierFlags &modifiers)
{
    if (GetProgram()->AbsoluteName().Is(reExportDeclaration->ResolvedSource())) {
        LogError(diagnostic::RE_EXPORTING_LOCAL_BINDINGS_IS_NOT_ALLOWED, {}, startLoc);
        return AllocBrokenStatement(startLoc);
    }
    auto reExport = AllocNode<ir::ETSReExportDeclaration>(reExportDeclaration, std::vector<std::string>(),
                                                          GetProgram()->AbsoluteName(), Allocator());
    ES2PANDA_ASSERT(reExport != nullptr);
    reExport->AddModifier(modifiers);
    reExport->SetRange(reExportDeclaration->Range());
    return reExport;
}

ir::Statement *ETSParser::ParseDefaultIfSingleExport(ir::ModifierFlags modifiers)
{
    auto tokenType = Lexer()->GetToken().Type();
    if (tokenType != lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
        return ParseSingleExport(modifiers);
    }
    auto savePos = Lexer()->Save();
    Lexer()->NextToken();
    auto isSelectiveExport = Lexer()->TryEatTokenType(lexer::TokenType::LITERAL_IDENT) &&
                             (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA ||
                              Lexer()->GetToken().Type() == lexer::TokenType::KEYW_AS ||
                              Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_BRACE);
    Lexer()->Rewind(savePos);
    return !isSelectiveExport ? ParseSingleExport(modifiers) : nullptr;
}

ir::ExportNamedDeclaration *ETSParser::CreateExportNamedDeclaration(const SpecifiersInfo &specs,
                                                                    const ir::ModifierFlags &modifiers,
                                                                    const lexer::SourcePosition &startLoc)
{
    const size_t exportDefaultMaxSize = 1;
    ArenaVector<ir::ExportSpecifier *> exports(Allocator()->Adapter());
    auto endLoc = startLoc;
    for (auto spec : specs.result) {
        auto exportSpec = AllocNode<ir::ExportSpecifier>(spec->Local(), spec->Imported());
        exportSpec->SetRange(spec->Range());
        exports.emplace_back(exportSpec);
        endLoc = endLoc.index < spec->End().index ? spec->End() : endLoc;
    }

    if (specs.resultExportDefault.size() > exportDefaultMaxSize) {
        LogError(diagnostic::EXPORT_DEFAULT_WITH_MUPLTIPLE_SPECIFIER, {}, startLoc);
    }
    for (auto spec : specs.resultExportDefault) {
        exports.emplace_back(spec);
        endLoc = endLoc.index < spec->End().index ? spec->End() : endLoc;
    }

    auto result = AllocNode<ir::ExportNamedDeclaration>(Allocator(), static_cast<ir::StringLiteral *>(nullptr),
                                                        std::move(exports));
    ES2PANDA_ASSERT(result != nullptr);
    result->AddModifier(modifiers);
    result->SetRange({startLoc, endLoc});
    return result;
}

ir::Statement *ETSParser::ParseExport(lexer::SourcePosition startLoc, ir::ModifierFlags modifiers)
{
    // export a constant variable anonymously, as export default new A()
    if ((modifiers & ir::ModifierFlags::DEFAULT_EXPORT) != 0U &&
        Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_MULTIPLY &&
        Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        auto exportedExpression = ParseDefaultIfSingleExport(modifiers);
        if (exportedExpression != nullptr) {
            return exportedExpression;
        }
    }

    ArenaVector<ir::AstNode *> specifiers(Allocator()->Adapter());

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_MULTIPLY) {
        ParseNameSpaceSpecifier(&specifiers, true);
        // export * as xxx from yyy, the xxx should have export flag to pass the following check.
        specifiers[0]->AddModifier(ir::ModifierFlags::EXPORT);
    } else if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
        ir::ExportKinds exportKind =
            (modifiers & ir::ModifierFlags::EXPORT_TYPE) != 0U ? ir::ExportKinds::TYPES : ir::ExportKinds::ALL;
        auto specs = ParseExportNamedSpecifiers(exportKind);

        if (Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_FROM) {
            for (auto *spec : specs.resultExportDefault) {
                LogError(diagnostic::EXPECTED_PARAM_GOT_PARAM, {"identifier", "default"}, spec->Start());
            }
            specifiers = util::Helpers::ConvertVector<ir::AstNode>(specs.result);
            for (auto *spec : specs.resultExportDefault) {
                auto *imported = AllocNode<ir::Identifier>(spec->Exported()->Name(), Allocator());
                ES2PANDA_ASSERT(imported != nullptr);
                imported->SetRange(spec->Exported()->Range());

                auto *local =
                    AllocNode<ir::Identifier>(compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY, Allocator());
                ES2PANDA_ASSERT(local != nullptr);
                local->SetRange(spec->Range());

                auto *importSpecifier = AllocNode<ir::ImportSpecifier>(imported, local);
                ES2PANDA_ASSERT(importSpecifier != nullptr);
                importSpecifier->SetRange(spec->Range());
                specifiers.emplace_back(importSpecifier);
            }
        } else {
            return CreateExportNamedDeclaration(specs, modifiers, startLoc);
        }
    } else {
        return ParseSingleExport(modifiers);
    }
    if ((modifiers & ir::ModifierFlags::DEFAULT_EXPORT) != 0) {
        LogError(diagnostic::EXPORT_DEFAULT_NO_REEXPORT);
    }
    // re-export directive
    auto *reExportDeclaration = ParseImportPathBuildImport(std::move(specifiers), true, startLoc, ir::ImportKinds::ALL);
    return CreateReExportDeclarationNode(reExportDeclaration, startLoc, modifiers);
}

ir::ETSPackageDeclaration *ETSParser::ParsePackageDeclaration()
{
    auto startLoc = Lexer()->GetToken().Start();

    if (Lexer()->TryEatTokenType(lexer::TokenType::KEYW_PACKAGE)) {
        RaisePackageDeprecatedMessage();

        ir::Expression *packageNameNode = ParseQualifiedName();
        auto *packageDeclaration = AllocNode<ir::ETSPackageDeclaration>(packageNameNode);
        ES2PANDA_ASSERT(packageDeclaration != nullptr);
        packageDeclaration->SetRange({startLoc, Lexer()->GetToken().End()});
        ConsumeSemicolon(packageDeclaration);

        // If we found a package declaration, then add all files with the same package to the package parse list
        EnsureContainingPackageIsRegistered(packageDeclaration);
        GetContext().Status() |= ParserStatus::IN_PACKAGE;
        return packageDeclaration;
    }

    return nullptr;
}

ir::ETSImportDeclaration *ETSParser::ParseImportPathBuildImport(ArenaVector<ir::AstNode *> &&specifiers,
                                                                bool requireFrom, lexer::SourcePosition startLoc,
                                                                ir::ImportKinds importKind)
{
    util::StringView str = ERROR_LITERAL;
    if (Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_FROM && requireFrom) {
        LogExpectedToken(lexer::TokenType::KEYW_FROM);
        if (Lexer()->GetToken().KeywordType() == lexer::TokenType::LITERAL_IDENT) {
            str = Lexer()->GetToken().Ident();
        } else if (Lexer()->GetToken().KeywordType() == lexer::TokenType::EOS) {
            str = "";
        }
    }
    Lexer()->NextToken();  // eat `from`

    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_STRING) {
        LogExpectedToken(lexer::TokenType::LITERAL_STRING);
        // Try to create DUMMY import source as error placeholder
        auto errorLiteral = AllocNode<ir::StringLiteral>(str);
        ES2PANDA_ASSERT(errorLiteral != nullptr);
        errorLiteral->SetRange(Lexer()->GetToken().Loc());
        auto *const importDeclaration =
            AllocNode<ir::ETSImportDeclaration>(errorLiteral, util::ImportInfo {}, std::move(specifiers), importKind);
        ES2PANDA_ASSERT(importDeclaration != nullptr);
        importDeclaration->SetRange({startLoc, errorLiteral->End()});
        return importDeclaration;
    }

    ES2PANDA_ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_STRING);
    auto pathToResolve = Lexer()->GetToken().Ident();
    auto *importPathStringLiteral = AllocNode<ir::StringLiteral>(pathToResolve);
    ES2PANDA_ASSERT(importPathStringLiteral != nullptr);
    importPathStringLiteral->SetRange(Lexer()->GetToken().Loc());
    Lexer()->NextToken();
    auto *const importDeclaration = BuildImportDeclaration(importKind, std::move(specifiers), importPathStringLiteral,
                                                           const_cast<parser::Program *>(GetContext().GetProgram()));
    ES2PANDA_ASSERT(importDeclaration != nullptr);
    importDeclaration->SetRange({startLoc, importPathStringLiteral->End()});
    if (Lexer()->GetToken().Ident().Is("assert")) {
        LogError(diagnostic::ERROR_ARKTS_NO_IMPORT_ASSERTIONS);
        return importDeclaration;
    }
    ConsumeSemicolon(importDeclaration);
    return importDeclaration;
}

ir::ETSImportDeclaration *ETSParser::BuildImportDeclaration(ir::ImportKinds importKind,
                                                            ArenaVector<ir::AstNode *> &&specifiers,
                                                            ir::StringLiteral *pathToResolve, parser::Program *program)
{
    auto importedProg = GetImportPathManager()->GatherImportInfo(program, pathToResolve);
    if (importedProg != nullptr) {
        return AllocNode<ir::ETSImportDeclaration>(pathToResolve, importedProg->GetImportInfo(), std::move(specifiers),
                                                   importKind);
    }
    return AllocNode<ir::ETSImportDeclaration>(pathToResolve, std::move(specifiers), importKind);
}

ArenaVector<ir::ETSImportDeclaration *> ETSParser::ParseImportDeclarations()
{
    std::vector<std::string> userPaths;
    ArenaVector<ir::ETSImportDeclaration *> statements(Allocator()->Adapter());

    while (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_IMPORT) {
        auto startLoc = Lexer()->GetToken().Start();
        Lexer()->NextToken();  // eat import

        ir::ImportKinds importKind = TryEatTypeKeyword() ? ir::ImportKinds::TYPES : ir::ImportKinds::ALL;

        ArenaVector<ir::AstNode *> specifiers(Allocator()->Adapter());
        ArenaVector<ir::AstNode *> defaultSpecifiers(Allocator()->Adapter());

        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_MULTIPLY) {
            ParseNameSpaceSpecifier(&specifiers);
        } else if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
            auto saveLoc = Lexer()->GetToken().Start();
            auto specs = ParseNamedSpecifiers(importKind);
            specifiers = util::Helpers::ConvertVector<ir::AstNode>(specs.result);
            defaultSpecifiers = util::Helpers::ConvertVector<ir::AstNode>(specs.resultDefault);
            if (specifiers.empty() && defaultSpecifiers.empty()) {
                specifiers.push_back(AllocBrokenExpression({saveLoc, specs.rightBackPos}));
                LogError(diagnostic::EMPTY_IMPORT_SPECIFIER_LIST);
            }
        } else {
            ParseImportDefaultSpecifier(&specifiers);
        }
        auto pos = Lexer()->Save();
        if (!specifiers.empty()) {
            auto *const importDecl = ParseImportPathBuildImport(std::move(specifiers), true, startLoc, importKind);
            ES2PANDA_ASSERT(importDecl != nullptr);
            statements.push_back(importDecl->AsETSImportDeclaration());
        }

        if (!defaultSpecifiers.empty()) {
            Lexer()->Rewind(pos);
            auto *const importDeclDefault =
                ParseImportPathBuildImport(std::move(defaultSpecifiers), true, startLoc, importKind);
            ES2PANDA_ASSERT(importDeclDefault != nullptr);
            if (!importDeclDefault->IsBrokenStatement()) {
                util::Helpers::CheckDefaultImport(statements);
                statements.push_back(importDeclDefault->AsETSImportDeclaration());
            }
        }
    }

    std::sort(statements.begin(), statements.end(), [](const auto *s1, const auto *s2) -> bool {
        return s1->Specifiers()[0]->IsImportNamespaceSpecifier() && !s2->Specifiers()[0]->IsImportNamespaceSpecifier();
    });
    return statements;
}

ir::ExportNamedDeclaration *ETSParser::ParseSingleExportForAnonymousConst(ir::ModifierFlags modifiers)
{
    ir::Expression *constantExpression = ParseExpression();

    auto *exported = AllocNode<ir::Identifier>(compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY, Allocator());
    ES2PANDA_ASSERT(exported != nullptr);
    exported->SetRange(Lexer()->GetToken().Loc());

    ArenaVector<ir::ExportSpecifier *> exports(Allocator()->Adapter());
    auto *exportSpecifier = AllocNode<ir::ExportSpecifier>(exported, exported->Clone(Allocator(), nullptr));
    exportSpecifier->SetConstantExpression(constantExpression);
    exportSpecifier->SetDefault();
    exports.push_back(exportSpecifier);

    auto result = AllocNode<ir::ExportNamedDeclaration>(Allocator(), static_cast<ir::StringLiteral *>(nullptr),
                                                        std::move(exports));
    ES2PANDA_ASSERT(result != nullptr);
    result->AddModifier(modifiers);
    ConsumeSemicolon(result);

    return result;
}

ir::ExportNamedDeclaration *ETSParser::ParseSingleExport(ir::ModifierFlags modifiers)
{
    lexer::Token token = Lexer()->GetToken();
    if (((modifiers & ir::ModifierFlags::DEFAULT_EXPORT) != 0) && token.Type() != lexer::TokenType::LITERAL_IDENT) {
        return ParseSingleExportForAnonymousConst(modifiers);
    }

    if (token.KeywordType() == lexer::TokenType::KEYW_AS) {
        LogError(diagnostic::ERROR_ARKTS_NO_UMD, {}, token.Start());
        return nullptr;
    }
    if (token.Type() != lexer::TokenType::LITERAL_IDENT) {
        LogError(diagnostic::EXPORT_NON_DECLARATION, {}, token.Start());
        return nullptr;
    }
    auto *exported = AllocNode<ir::Identifier>(token.Ident(), Allocator());
    exported->SetRange(Lexer()->GetToken().Loc());

    Lexer()->NextToken();  // eat exported variable name

    auto *specifier = AllocNode<ir::ExportSpecifier>(exported, ParseNamedExport(&token));
    specifier->SetRange(exported->Range());
    specifier->AddModifier(modifiers);

    ArenaVector<ir::ExportSpecifier *> exports(Allocator()->Adapter());
    exports.emplace_back(specifier);

    auto result = AllocNode<ir::ExportNamedDeclaration>(Allocator(), static_cast<ir::StringLiteral *>(nullptr),
                                                        std::move(exports));
    result->SetRange(exported->Range());
    result->AddModifier(modifiers);
    ConsumeSemicolon(result);

    return result;
}

bool ETSParser::IsDefaultImport()
{
    if (Lexer()->TryEatTokenKeyword(lexer::TokenType::KEYW_DEFAULT)) {
        if (Lexer()->TryEatTokenKeyword(lexer::TokenType::KEYW_AS)) {
            return true;
        }
        LogError(diagnostic::UNEXPECTED_TOKEN_AS);
    }
    return false;
}

bool ETSParser::IsDefaultExport()
{
    if (Lexer()->TryEatTokenKeyword(lexer::TokenType::KEYW_DEFAULT)) {
        Lexer()->TryEatTokenKeyword(lexer::TokenType::KEYW_AS);
        return true;
    }
    return false;
}

bool ETSParser::IsPrimitiveType(const lexer::TokenType &tokenType)
{
    switch (tokenType) {
        case lexer::TokenType::KEYW_BIGINT:
        case lexer::TokenType::KEYW_BOOLEAN:
        case lexer::TokenType::KEYW_BYTE:
        case lexer::TokenType::KEYW_CHAR:
        case lexer::TokenType::KEYW_DOUBLE:
        case lexer::TokenType::KEYW_FLOAT:
        case lexer::TokenType::KEYW_INT:
        case lexer::TokenType::KEYW_LONG:
        case lexer::TokenType::KEYW_SHORT:
        case lexer::TokenType::KEYW_VOID:
        case lexer::TokenType::KEYW_ANY:
            return true;
        default:
            return false;
    }
}

void ETSParser::ParseNamedSpecifiesDefaultImport(ArenaVector<ir::ImportDefaultSpecifier *> *resultDefault,
                                                 const std::string &fileName)
{
    auto *imported = AllocNode<ir::Identifier>(Lexer()->GetToken().Ident(), Allocator());
    ES2PANDA_ASSERT(imported != nullptr);
    imported->SetRange(Lexer()->GetToken().Loc());
    Lexer()->NextToken();
    auto *specifier = AllocNode<ir::ImportDefaultSpecifier>(imported);
    ES2PANDA_ASSERT(specifier != nullptr);
    specifier->SetRange({imported->Start(), imported->End()});

    util::Helpers::CheckDefaultImportedName(*resultDefault, specifier, fileName);

    resultDefault->emplace_back(specifier);
}

void ETSParser::ParseNamedSpecifiersDefaultExport(ArenaVector<ir::ImportSpecifier *> *result,
                                                  ArenaVector<ir::ImportDefaultSpecifier *> *resultDefault,
                                                  const std::string &fileName)
{
    auto *local = AllocNode<ir::Identifier>(Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_BRACE
                                                ? compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY
                                                : Lexer()->GetToken().Ident(),
                                            Allocator());
    ES2PANDA_ASSERT(local != nullptr);
    local->SetRange(Lexer()->GetToken().Loc());
    if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) {
        Lexer()->NextToken();
    }
    auto *specifier = AllocNode<ir::ImportDefaultSpecifier>(local);
    ES2PANDA_ASSERT(specifier != nullptr);
    specifier->SetRange({local->Start(), local->End()});

    util::Helpers::CheckDefaultImportedName(*resultDefault, specifier, fileName);

    resultDefault->emplace_back(specifier);

    auto *imported = AllocNode<ir::Identifier>(compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY, Allocator());
    auto *specifierResult = AllocNode<ir::ImportSpecifier>(imported, local);
    specifierResult->SetRange({local->Start(), local->End()});
    result->emplace_back(specifierResult);
}

bool ETSParser::ParseNamedSpecifiesImport(ArenaVector<ir::ImportSpecifier *> *result,
                                          ArenaVector<ir::ExportSpecifier *> *resultExportDefault)
{
    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        ir::Expression *constantExpression = ParseUnaryOrPrefixUpdateExpression();
        auto *exported = AllocNode<ir::Identifier>(compiler::Signatures::REEXPORT_DEFAULT_ANONYMOUSLY, Allocator());
        ES2PANDA_ASSERT(exported != nullptr);
        exported->SetRange(Lexer()->GetToken().Loc());
        auto *exportedAnonyConst = AllocNode<ir::ExportSpecifier>(exported, exported->Clone(Allocator(), nullptr));
        ES2PANDA_ASSERT(exportedAnonyConst != nullptr);
        exportedAnonyConst->SetConstantExpression(constantExpression);
        exportedAnonyConst->SetDefault();
        resultExportDefault->emplace_back(exportedAnonyConst);
        return Lexer()->TryEatTokenType(lexer::TokenType::KEYW_AS) &&
               Lexer()->TryEatTokenType(lexer::TokenType::KEYW_DEFAULT);
    }

    lexer::Token importedToken = Lexer()->GetToken();
    auto *imported = ExpectIdentifier();

    ir::Identifier *local = nullptr;
    CheckModuleAsModifier();
    if (Lexer()->TryEatTokenType(lexer::TokenType::KEYW_AS)) {
        if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_DEFAULT) {
            auto *exportedAnonyConst = AllocNode<ir::ExportSpecifier>(imported, imported->Clone(Allocator(), nullptr));
            exportedAnonyConst->SetDefault();
            exportedAnonyConst->SetRange({Lexer()->GetToken().Start(), Lexer()->GetToken().End()});
            Lexer()->NextToken();
            resultExportDefault->emplace_back(exportedAnonyConst);
            return true;
        }
        local = ParseNamedImport(&Lexer()->GetToken());
        Lexer()->NextToken();
    } else {
        local = ParseNamedImport(&importedToken);
    }

    auto *specifier = AllocNode<ir::ImportSpecifier>(imported, local);
    ES2PANDA_ASSERT(specifier != nullptr);
    specifier->SetRange({imported->Start(), local->End()});

    util::Helpers::CheckImportedName(*result, specifier, DiagnosticEngine());

    result->emplace_back(specifier);
    return true;
}

SpecifiersInfo ETSParser::ParseNamedSpecifiers(const ir::ImportKinds importKind)
{
    // NOTE(user): handle qualifiedName in file bindings: qualifiedName '.' '*'
    if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_LEFT_BRACE)) {
        // For now, this function is called only after checking that
        // current token is lexer::TokenType::PUNCTUATOR_LEFT_BRACE
        // So it is impossible to create a test now.
        LogExpectedToken(lexer::TokenType::PUNCTUATOR_LEFT_BRACE);
        Lexer()->NextToken();
    }

    auto fileName = GetProgram()->SourceFilePath().Mutf8();

    ArenaVector<ir::ImportSpecifier *> result(Allocator()->Adapter());
    ArenaVector<ir::ImportDefaultSpecifier *> resultDefault(Allocator()->Adapter());
    ArenaVector<ir::ExportSpecifier *> resultExportDefault(Allocator()->Adapter());
    lexer::SourcePosition sourceEnd;

    auto token = Lexer()->GetToken();
    if (token.Ident() == (lexer::TokenToString(lexer::TokenType::KEYW_IMPORT)) &&
        token.Type() == lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) {
        LogError(diagnostic::ERROR_ARKTS_NO_SIDE_EFFECT_IMPORT);
    }

    ParseList(
        lexer::TokenType::PUNCTUATOR_RIGHT_BRACE, lexer::NextTokenFlags::KEYWORD_TO_IDENT,
        [this, &result, &resultDefault, &resultExportDefault, &fileName, &importKind](bool &typeKeywordOnSpecifier) {
            if (typeKeywordOnSpecifier && importKind == ir::ImportKinds::TYPES) {
                LogError(diagnostic::REPEATED_TYPE_KEYWORD);
            }
            if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_MULTIPLY) {
                LogError(diagnostic::ASTERIKS_NOT_ALLOWED_IN_SELECTIVE_BINDING);
            }

            if (!IsDefaultImport()) {
                typeKeywordOnSpecifier = false;
                return ParseNamedSpecifiesImport(&result, &resultExportDefault);
            }
            ParseNamedSpecifiesDefaultImport(&resultDefault, fileName);
            typeKeywordOnSpecifier = false;
            return true;
        },
        &sourceEnd, ParseListOptions::ALLOW_TRAILING_SEP | ParseListOptions::ALLOW_TYPE_KEYWORD);
    return {result, resultDefault, resultExportDefault, sourceEnd};
}

SpecifiersInfo ETSParser::ParseExportNamedSpecifiers(const ir::ExportKinds exportKind)
{
    if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_LEFT_BRACE)) {
        LogExpectedToken(lexer::TokenType::PUNCTUATOR_LEFT_BRACE);
        Lexer()->NextToken();
    }

    auto fileName = GetProgram()->SourceFilePath().Mutf8();

    ArenaVector<ir::ImportSpecifier *> result(Allocator()->Adapter());
    ArenaVector<ir::ImportDefaultSpecifier *> resultDefault(Allocator()->Adapter());
    ArenaVector<ir::ExportSpecifier *> resultExportDefault(Allocator()->Adapter());

    ParseList(
        lexer::TokenType::PUNCTUATOR_RIGHT_BRACE, lexer::NextTokenFlags::KEYWORD_TO_IDENT,
        [this, &result, &resultDefault, &resultExportDefault, &fileName, &exportKind](bool &typeKeywordOnSpecifier) {
            if (typeKeywordOnSpecifier && exportKind == ir::ExportKinds::TYPES) {
                LogError(diagnostic::REPEATED_TYPE_KEYWORD);
            }
            if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_MULTIPLY) {
                LogError(diagnostic::ASTERIKS_NOT_ALLOWED_IN_SELECTIVE_BINDING);
            }

            if (!IsDefaultExport()) {
                typeKeywordOnSpecifier = false;
                return ParseNamedSpecifiesImport(&result, &resultExportDefault);
            }
            ParseNamedSpecifiersDefaultExport(&result, &resultDefault, fileName);
            typeKeywordOnSpecifier = false;
            return true;
        },
        nullptr, ParseListOptions::ALLOW_TRAILING_SEP | ParseListOptions::ALLOW_TYPE_KEYWORD);
    return {result, resultDefault, resultExportDefault};
}

void ETSParser::ParseNameSpaceSpecifier(ArenaVector<ir::AstNode *> *specifiers, bool isReExport)
{
    lexer::SourcePosition namespaceStart = Lexer()->GetToken().Start();
    Lexer()->NextToken();  // eat `*` character

    CheckModuleAsModifier();

    // Note (oeotvos) As a temporary solution we allow the stdlib to use namespace import without an alias, but this
    // should be handled at some point.
    if (Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_FROM && !isReExport &&
        (GetContext().Status() & ParserStatus::IN_DEFAULT_IMPORTS) == 0) {
        LogExpectedToken(lexer::TokenType::KEYW_AS);  // invalid_namespace_import.ets
    }

    auto *local = AllocNode<ir::Identifier>(util::StringView(""), Allocator());
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA ||
        Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_FROM) {
        auto *specifier = AllocNode<ir::ImportNamespaceSpecifier>(local);
        ES2PANDA_ASSERT(specifier != nullptr);
        specifier->SetRange({namespaceStart, Lexer()->GetToken().End()});
        specifiers->push_back(specifier);
        return;
    }

    ExpectToken(lexer::TokenType::KEYW_AS, true);  // eat `as` literal
    local = ParseNamedImport(&Lexer()->GetToken());

    auto *specifier = AllocNode<ir::ImportNamespaceSpecifier>(local);
    ES2PANDA_ASSERT(specifier != nullptr);
    specifier->SetRange({namespaceStart, Lexer()->GetToken().End()});
    specifiers->push_back(specifier);

    Lexer()->NextToken();  // eat local name
}

ir::AstNode *ETSParser::ParseImportDefaultSpecifier(ArenaVector<ir::AstNode *> *specifiers)
{
    if (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_STRING) {
        LogError(diagnostic::ERROR_ARKTS_NO_SIDE_EFFECT_IMPORT);
        return nullptr;
    }
    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        LogExpectedToken(lexer::TokenType::LITERAL_IDENT);
    }

    auto *imported = AllocNode<ir::Identifier>(Lexer()->GetToken().Ident(), Allocator());
    ES2PANDA_ASSERT(imported != nullptr);
    imported->SetRange(Lexer()->GetToken().Loc());
    Lexer()->NextToken();  // Eat import specifier.

    // Support is needed at present.
    // 1.import defaultExport,allBinding from importPath
    // 2.import defaultExport,selectiveBindings from importPath
    if (Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_COMMA)) {
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_MULTIPLY) {
            ParseNameSpaceSpecifier(specifiers);
        } else if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
            const ir::ImportKinds importKind = Lexer()->TryEatTokenKeyword(lexer::TokenType::KEYW_TYPE)
                                                   ? ir::ImportKinds::TYPES
                                                   : ir::ImportKinds::ALL;
            auto specs = ParseNamedSpecifiers(importKind);
            auto importSpecifiers = util::Helpers::ConvertVector<ir::AstNode>(specs.result);
            specifiers->insert(specifiers->end(), importSpecifiers.begin(), importSpecifiers.end());
        }
    }

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
        LogError(diagnostic::ERROR_ARKTS_NO_REQUIRE);
        return nullptr;
    }
    if (Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_FROM) {
        LogExpectedToken(lexer::TokenType::KEYW_FROM);
        Lexer()->NextToken();  // eat 'from'
    }

    auto *specifier = AllocNode<ir::ImportDefaultSpecifier>(imported);
    ES2PANDA_ASSERT(specifier != nullptr);
    specifier->SetRange({imported->Start(), imported->End()});
    specifiers->push_back(specifier);

    return nullptr;
}

void ETSParser::CheckModuleAsModifier()
{
    if ((Lexer()->GetToken().Flags() & lexer::TokenFlags::HAS_ESCAPE) != 0U) {
        LogError(diagnostic::ESCAPE_SEQUENCES_IN_AS);
    }
}

ir::AnnotatedExpression *ETSParser::GetAnnotatedExpressionFromParam()
{
    ir::AnnotatedExpression *parameter = nullptr;

    switch (Lexer()->GetToken().Type()) {
        case lexer::TokenType::LITERAL_IDENT: {
            parameter = AllocNode<ir::Identifier>(Lexer()->GetToken().Ident(), Allocator());
            ES2PANDA_ASSERT(parameter != nullptr);

            parameter->SetRange(Lexer()->GetToken().Loc());

            break;
        }

        case lexer::TokenType::PUNCTUATOR_PERIOD_PERIOD_PERIOD: {
            const auto startLoc = Lexer()->GetToken().Start();
            Lexer()->NextToken();

            if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
                LogExpectedToken(lexer::TokenType::LITERAL_IDENT);
            }

            auto *const restIdent = AllocNode<ir::Identifier>(Lexer()->GetToken().Ident(), Allocator());
            ES2PANDA_ASSERT(restIdent != nullptr);
            restIdent->SetRange(Lexer()->GetToken().Loc());

            parameter = AllocNode<ir::SpreadElement>(ir::AstNodeType::REST_ELEMENT, Allocator(), restIdent);
            ES2PANDA_ASSERT(parameter != nullptr);
            parameter->SetRange({startLoc, Lexer()->GetToken().End()});
            break;
        }

        default: {
            LogError(diagnostic::UNEXPECTED_TOKEN_ID);
            return AllocBrokenExpression(Lexer()->GetToken().Loc());
        }
    }

    Lexer()->NextToken();
    return parameter;
}

ir::Expression *ETSParser::ParseFunctionParameterAnnotations()
{
    EatLeadingAtForAnnotation();

    auto annotations = ParseAnnotations(false);
    auto savePos = Lexer()->GetToken().Start();
    ir::Expression *result = ParseFunctionParameter();
    if (result != nullptr && !result->IsBrokenExpression()) {
        ApplyAnnotationsToNode(result, std::move(annotations), savePos);
    }
    return result;
}

ir::Expression *ETSParser::ParseFunctionReceiver()
{
    ES2PANDA_ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::KEYW_THIS);
    auto thisLoc = Lexer()->GetToken().Start();
    Lexer()->NextToken();  // eat 'this';
    if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_COLON)) {
        LogError(diagnostic::FUN_PARAM_THIS_MISSING_TYPE);
        return AllocBrokenExpression(Lexer()->GetToken().Loc());
    }

    TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR;
    ir::TypeNode *typeAnnotation = ParseTypeAnnotation(&options);

    if (!GetContext().AllowReceiver()) {
        LogError(diagnostic::UNEXPECTED_THIS, {}, thisLoc);
        return AllocBrokenExpression(thisLoc);
    }

    if (typeAnnotation->IsBrokenTypeNode()) {
        return AllocBrokenExpression(thisLoc);
    }

    return CreateParameterThis(typeAnnotation);
}

void ETSParser::SkipInvalidType() const
{
    int openParenthesisCount = 1;
    int openBraceCount = 0;
    while (Lexer()->GetToken().Type() != lexer::TokenType::EOS &&
           !(Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COMMA && openBraceCount == 0)) {
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
            openBraceCount++;
        }
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) {
            openBraceCount--;
        }
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS) {
            openParenthesisCount++;
        }
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) {
            openParenthesisCount--;
        }
        if (openParenthesisCount == 0) {
            break;
        }
        Lexer()->NextToken();
    }
}

bool ETSParser::IsFixedArrayTypeNode(ir::AstNode *node)
{
    return node->IsETSTypeReference() &&
           node->AsETSTypeReference()->BaseName()->Name() == compiler::Signatures::FIXED_ARRAY_TYPE_NAME;
}

ir::ThisExpression *ETSParser::ParseThisExpression()
{
    ES2PANDA_ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::KEYW_THIS);

    auto *thisExprNode = AllocNode<ir::ThisExpression>();
    ES2PANDA_ASSERT(thisExprNode != nullptr);
    thisExprNode->SetRange(Lexer()->GetToken().Loc());

    Lexer()->NextToken();
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_QUESTION_DOT) {
        LogError(diagnostic::THIS_OPTIONAL_CHAIN_NOT_SUPPORTED);
    }
    return thisExprNode;
}

ir::Expression *ETSParser::ParseFunctionParameter()
{
    switch (Lexer()->GetToken().Type()) {
        case lexer::TokenType::KEYW_PRIVATE:
        case lexer::TokenType::KEYW_PUBLIC:
        case lexer::TokenType::KEYW_PROTECTED:
            LogError(diagnostic::FIELD_IN_PARAM);
            Lexer()->NextToken();
            [[fallthrough]];
        default:
            break;
    }

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_AT) {
        return ParseFunctionParameterAnnotations();
    }

    if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_THIS) {
        return ParseFunctionReceiver();
    }

    auto *const paramIdent = GetAnnotatedExpressionFromParam();

    bool isOptional = false;
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_QUESTION_MARK) {
        isOptional = true;
        if (paramIdent->IsRestElement()) {
            LogError(diagnostic::NO_DEFAULT_FOR_REST);
        }
        Lexer()->NextToken();  // eat '?'
    }

    const bool isArrow = (GetContext().Status() & ParserStatus::ARROW_FUNCTION) != 0;

    if (Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_COLON)) {
        TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR;
        ir::TypeNode *typeAnnotation = ParseTypeAnnotation(&options);
        ES2PANDA_ASSERT(typeAnnotation != nullptr);
        if (typeAnnotation->IsBrokenTypeNode()) {
            // the compiler can't process "declare class A { static foo(x: {key: string}[]):void; }" correctly
            // and resolve "{key: string}" as function body, so skip invalid types
            SkipInvalidType();
        }
        paramIdent->SetTypeAnnotation(typeAnnotation);
        paramIdent->SetEnd(typeAnnotation->End());
    }

    if ((!isArrow || isOptional) && paramIdent->TypeAnnotation() == nullptr) {
        LogError(diagnostic::EXPLICIT_PARAM_TYPE);
        paramIdent->SetTsTypeAnnotation(AllocBrokenType(Lexer()->GetToken().Loc()));
    }

    return ParseFunctionParameterExpression(paramIdent, isOptional);
}

ir::Expression *ETSParser::CreateParameterThis(ir::TypeNode *typeAnnotation)
{
    auto *paramIdent = AllocNode<ir::Identifier>(varbinder::TypedBinder::MANDATORY_PARAM_THIS, Allocator());
    ES2PANDA_ASSERT(paramIdent != nullptr);
    paramIdent->SetRange(Lexer()->GetToken().Loc());

    paramIdent->SetTypeAnnotation(typeAnnotation);

    auto *paramExpression = AllocNode<ir::ETSParameterExpression>(paramIdent, false, Allocator());
    ES2PANDA_ASSERT(paramExpression != nullptr);
    paramExpression->SetRange({paramIdent->Start(), paramIdent->End()});

    return paramExpression;
}

ir::Expression *ETSParser::ParseVariableDeclaratorKey(VariableParsingFlags flags)
{
    ir::Expression *init = nullptr;
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_SQUARE_BRACKET) {
        init = ParseArrayOrDestructuringExpression(ExpressionParseFlags::MUST_BE_PATTERN);
    } else {
        init = ExpectIdentifier();
    }
    ES2PANDA_ASSERT(init != nullptr);
    ir::TypeNode *typeAnnotation = nullptr;
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_QUESTION_MARK) {
        Lexer()->NextToken();  // eat '?'
        if ((flags & VariableParsingFlags::FOR_OF) != 0U) {
            LogError(diagnostic::OPTIONAL_VAR_IN_FOR_OF);
        } else {
            init->AddModifier(ir::ModifierFlags::OPTIONAL);
            LogError(diagnostic::OPTIONAL_VARIABLE);
        }
    }

    if (auto const tokenType = Lexer()->GetToken().Type(); tokenType == lexer::TokenType::PUNCTUATOR_COLON) {
        Lexer()->NextToken();  // eat ':'
        TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR;
        typeAnnotation = ParseTypeAnnotation(&options);
    } else if (InAmbientContext()) {
        LogError(diagnostic::MISSING_TYPE_ANNOTATION, {init->AsIdentifier()->Name()}, init->Start());
    }

    if (init->IsAnnotatedExpression() && typeAnnotation != nullptr) {
        init->AsAnnotatedExpression()->SetTypeAnnotation(typeAnnotation);
    }
    return init;
}

ir::VariableDeclarator *ETSParser::ParseVariableDeclaratorInitializer(ir::Expression *init, VariableParsingFlags flags,
                                                                      const lexer::SourcePosition &startLoc)
{
    if ((flags & VariableParsingFlags::DISALLOW_INIT) != 0) {
        LogError(diagnostic::FOR_AWAIT_OF_VAR_NOT_INIT);
    }

    Lexer()->NextToken();

    ir::Expression *initializer = ParseExpression();
    lexer::SourcePosition endLoc = initializer->End();

    if (InAmbientContext()) {
        LogError(diagnostic::INITIALIZERS_IN_AMBIENT_CONTEXTS, {init->AsIdentifier()->Name()}, initializer->Start());
    }

    auto *declarator = AllocNode<ir::VariableDeclarator>(GetFlag(flags), init, initializer);
    declarator->SetRange({startLoc, endLoc});

    return declarator;
}

ir::VariableDeclarator *ETSParser::ParseVariableDeclarator(ir::Expression *init, lexer::SourcePosition startLoc,
                                                           VariableParsingFlags flags)
{
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
        return ParseVariableDeclaratorInitializer(init, flags, startLoc);
    }

    if (init->IsETSDestructuring()) {
        LogError(diagnostic::MISSING_INIT_IN_DEST_DEC);
    } else if (!InAmbientContext() && init->AsIdentifier()->TypeAnnotation() == nullptr &&
               (flags & VariableParsingFlags::FOR_OF) == 0U) {
        LogError(diagnostic::MISSING_INIT_OR_TYPE);
    } else if (!IsExternal() && (flags & VariableParsingFlags::CONST) != 0U && !InAmbientContext() &&
               (flags & (VariableParsingFlags::FOR_OF | VariableParsingFlags::IN_FOR)) == 0U) {
        LogError(diagnostic::CONST_WITHOUT_INIT, {}, startLoc);
    }

    lexer::SourcePosition endLoc = init->End();
    auto declarator = AllocNode<ir::VariableDeclarator>(GetFlag(flags), init);
    declarator->SetRange({startLoc, endLoc});

    // NOTE (psiket)  Transfer the OPTIONAL flag from the init to the declarator?
    return declarator;
}

ir::Expression *ETSParser::ParseCatchParam()
{
    ExpectToken(lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS);
    ir::AnnotatedExpression *param = nullptr;

    bool checkRestrictedBinding = true;
    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        // tryCatchMissingParam.ets
        LogError(diagnostic::UNEXPECTED_TOKEN_ID);
        if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) {
            checkRestrictedBinding = false;
        } else {
            Lexer()->GetToken().SetTokenType(lexer::TokenType::LITERAL_IDENT);
            Lexer()->GetToken().SetTokenStr(ERROR_LITERAL);
        }
    }
    if (!checkRestrictedBinding) {
        ExpectToken(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS);
        return nullptr;
    }

    CheckRestrictedBinding();
    param = ExpectIdentifier();
    ParseCatchParamTypeAnnotation(param);

    ExpectToken(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS);
    return param;
}

void ETSParser::ParseCatchParamTypeAnnotation([[maybe_unused]] ir::AnnotatedExpression *param)
{
    if (Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_COLON)) {
        LogError(diagnostic::MULTI_CATCH_DEPRECATED);
        TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR;
        if (auto *typeAnnotation = ParseTypeAnnotation(&options); typeAnnotation != nullptr) {
            ES2PANDA_ASSERT(param != nullptr);
            param->SetTypeAnnotation(typeAnnotation);
        }
    }

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
        LogError(diagnostic::CATCH_CLAUSE_VAR_HAS_INIT);
    }
}

ir::Statement *ETSParser::ParseImportDeclaration([[maybe_unused]] StatementParsingFlags flags)
{
    bool isError = false;
    if ((flags & StatementParsingFlags::GLOBAL) == 0) {
        LogError(diagnostic::IMPORT_TOP_LEVEL);
        isError = true;
    }

    char32_t nextChar = Lexer()->Lookahead();
    if (nextChar == lexer::LEX_CHAR_LEFT_PAREN || nextChar == lexer::LEX_CHAR_DOT) {
        return ParseExpressionStatement();
    }

    lexer::SourcePosition startLoc = Lexer()->GetToken().Start();
    Lexer()->NextToken();  // eat import

    ArenaVector<ir::AstNode *> specifiers(Allocator()->Adapter());

    ir::ETSImportDeclaration *importDeclaration {};
    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_STRING) {
        ir::AstNode *astNode = ParseImportSpecifiers(&specifiers);
        if (astNode != nullptr) {
            ES2PANDA_ASSERT(astNode->IsTSImportEqualsDeclaration());
            astNode->SetRange({startLoc, Lexer()->GetToken().End()});
            ConsumeSemicolon(astNode->AsTSImportEqualsDeclaration());
            return astNode->AsTSImportEqualsDeclaration();
        }
        importDeclaration = ParseImportPathBuildImport(std::move(specifiers), true, startLoc, ir::ImportKinds::ALL);
    } else {
        importDeclaration = ParseImportPathBuildImport(std::move(specifiers), false, startLoc, ir::ImportKinds::ALL);
    }

    return isError ? AllocBrokenStatement(startLoc) : importDeclaration;
}

ir::Statement *ETSParser::ParseExportDeclaration([[maybe_unused]] StatementParsingFlags flags)
{
    LogUnexpectedToken(lexer::TokenType::KEYW_EXPORT);
    return AllocBrokenStatement(Lexer()->GetToken().Loc());
}

ir::Expression *ETSParser::ParseExpressionOrTypeAnnotation(lexer::TokenType type,
                                                           [[maybe_unused]] ExpressionParseFlags flags)
{
    if (type == lexer::TokenType::KEYW_INSTANCEOF) {
        TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR |
                                               TypeAnnotationParsingOptions::ANNOTATION_NOT_ALLOW |
                                               TypeAnnotationParsingOptions::INSTANCEOF;

        if (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_NULL) {
            auto *typeAnnotation = AllocNode<ir::NullLiteral>();
            ES2PANDA_ASSERT(typeAnnotation != nullptr);
            typeAnnotation->SetRange(Lexer()->GetToken().Loc());
            Lexer()->NextToken();

            return typeAnnotation;
        }

        return ParseTypeAnnotation(&options);
    }

    return ParseExpression(ExpressionParseFlags::DISALLOW_YIELD);
}

bool ETSParser::ParsePotentialGenericFunctionCall(ir::Expression *primaryExpr, ir::Expression **returnExpression,
                                                  [[maybe_unused]] const lexer::SourcePosition &startLoc,
                                                  bool ignoreCallExpression)
{
    if (Lexer()->Lookahead() == lexer::LEX_CHAR_LESS_THAN ||
        (!primaryExpr->IsIdentifier() && !primaryExpr->IsMemberExpression())) {
        return true;
    }

    const auto savedPos = Lexer()->Save();

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_SHIFT) {
        Lexer()->BackwardToken(lexer::TokenType::PUNCTUATOR_LESS_THAN, 1);
    }

    TypeAnnotationParsingOptions options =
        TypeAnnotationParsingOptions::ALLOW_WILDCARD | TypeAnnotationParsingOptions::IGNORE_FUNCTION_TYPE;
    ir::TSTypeParameterInstantiation *typeParams = ParseTypeParameterInstantiation(&options);

    if (typeParams == nullptr) {
        Lexer()->Rewind(savedPos);
        return true;
    }

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_PERIOD) {
        Lexer()->Rewind(savedPos);
        return true;
    }

    // unexpected_token_49,ets, 50, 51
    if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS) {
        *returnExpression = AllocNode<ir::ETSGenericInstantiatedNode>(primaryExpr, typeParams);
        // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage)
        (*returnExpression)->SetRange({startLoc, Lexer()->GetToken().Start()});
        return false;
    }

    if (!ignoreCallExpression) {
        *returnExpression = ParseCallExpression(*returnExpression, false, false);
        (*returnExpression)->AsCallExpression()->SetTypeParams(typeParams);
        return false;
    }

    Lexer()->Rewind(savedPos);
    return true;
}

ir::ModifierFlags ETSParser::ParseTypeVarianceModifier(TypeAnnotationParsingOptions *const options)
{
    if ((*options & TypeAnnotationParsingOptions::ALLOW_WILDCARD) == 0 &&
        (*options & TypeAnnotationParsingOptions::ALLOW_DECLARATION_SITE_VARIANCE) == 0) {
        LogError(diagnostic::VARIANCE_NOD_ALLOWED);
    }

    switch (Lexer()->GetToken().KeywordType()) {
        case lexer::TokenType::KEYW_IN: {
            Lexer()->NextToken();
            return ir::ModifierFlags::IN;
        }
        case lexer::TokenType::KEYW_OUT: {
            Lexer()->NextToken();
            return ir::ModifierFlags::OUT;
        }
        default: {
            return ir::ModifierFlags::NONE;
        }
    }
}

ir::AstNode *ETSParser::ParseAmbientSignature(const lexer::SourcePosition &startPos)
{
    auto const indexName = ParseIndexName();
    auto [typeAnno, returnType] = ParseIndexTypeAndReturnType();

    if (returnType->IsBrokenTypeNode()) {
        LogError(diagnostic::INDEX_MISSING_IDENTIFIER);
        return AllocBrokenStatement({startPos, Lexer()->GetToken().End()});
    }

    auto dummyNode = AllocNode<ir::DummyNode>(compiler::Signatures::AMBIENT_INDEXER, indexName, returnType,
                                              ir::DummyNodeFlag::INDEXER, typeAnno);
    ES2PANDA_ASSERT(dummyNode != nullptr);
    dummyNode->SetRange({startPos, Lexer()->GetToken().End()});
    return dummyNode;
}

util::StringView ETSParser::ParseIndexName()
{
    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        // ambient_indexer_9.ets
        LogUnexpectedToken(Lexer()->GetToken());
        auto pos = Lexer()->Save();
        Lexer()->NextToken();
        while (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
            // just skip usls tokens, we have an identifier after
            Lexer()->Rewind(pos);
            Lexer()->NextToken();
            pos = Lexer()->Save();
        }
        if (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_IDENT) {
            // if next token is not an ident, so current token should be an identifier
            // and we set it as literal ident
            Lexer()->GetToken().SetTokenType(lexer::TokenType::LITERAL_IDENT);
            Lexer()->GetToken().SetTokenStr(ERROR_LITERAL);
        }
    }
    auto const indexName = Lexer()->GetToken().Ident();

    if (Lexer()->NextToken(); Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_COLON) {
        // ambient_indexer_8.ets
        LogError(diagnostic::INDEX_TYPE_EXPECTED);
        Lexer()->GetToken().SetTokenType(lexer::TokenType::PUNCTUATOR_COLON);
    }

    return indexName;
}

std::pair<ir::TypeNode *, ir::TypeNode *> ETSParser::ParseIndexTypeAndReturnType()
{
    // eat ":"
    if (Lexer()->NextToken(); Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_NUMBER &&
                              Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_STRING &&
                              Lexer()->GetToken().KeywordType() != lexer::TokenType::KEYW_INT) {
        // ambient_indexer_3.ets
        LogError(diagnostic::INDEX_TYPE_NOT_NUMBER, {TokenToString(Lexer()->GetToken().Type())});
        Lexer()->GetToken().SetTokenType(lexer::TokenType::KEYW_NUMBER);
    }

    TypeAnnotationParsingOptions typAnotationOptions = TypeAnnotationParsingOptions::NO_OPTS;
    auto typeAnno = ParseTypeAnnotation(&typAnotationOptions);

    // eat indexType
    if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_SQUARE_BRACKET) {
        // ambient_indexer_7.ets
        LogError(diagnostic::EXPECTED_BRACKETS_IN_INDEX);

        Lexer()->GetToken().SetTokenType(lexer::TokenType::PUNCTUATOR_RIGHT_SQUARE_BRACKET);
    }

    // eat "]"
    if (Lexer()->NextToken(); Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_COLON) {
        // ambient_indexer_4.ets
        LogError(diagnostic::INDEX_MISSING_TYPE);
    }

    if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_COLON)) {
        LogError(diagnostic::EXPECTED_PARAM_GOT_PARAM, {":", TokenToString(Lexer()->GetToken().Type())});
    }

    TypeAnnotationParsingOptions options =
        TypeAnnotationParsingOptions::RETURN_TYPE | TypeAnnotationParsingOptions::REPORT_ERROR;
    auto *returnType = ParseTypeAnnotation(&options);

    return {typeAnno, returnType};
}

ir::TSTypeParameter *ETSParser::ParseTypeParameter([[maybe_unused]] TypeAnnotationParsingOptions *options)
{
    lexer::SourcePosition startLoc = Lexer()->GetToken().Start();
    ArenaVector<ir::AnnotationUsage *> annotations {Allocator()->Adapter()};
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_AT) {
        EatLeadingAtForAnnotation();
        annotations = ParseAnnotations(false);
    }

    const auto varianceModifier = [this, options] {
        switch (Lexer()->GetToken().KeywordType()) {
            case lexer::TokenType::KEYW_IN:
            case lexer::TokenType::KEYW_OUT:
                return ParseTypeVarianceModifier(options);
            default:
                return ir::ModifierFlags::NONE;
        }
    }();

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_AT) {
        EatLeadingAtForAnnotation();
        auto moreAnnos = ParseAnnotations(false);
        for (auto *anno : moreAnnos) {
            annotations.push_back(anno);
        }
    }
    auto saveLoc = Lexer()->GetToken().Start();
    auto *paramIdent = ExpectIdentifier(false, false, *options | TypeAnnotationParsingOptions::REPORT_ERROR);

    ir::TypeNode *constraint = nullptr;
    if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_EXTENDS) {
        Lexer()->NextToken();
        TypeAnnotationParsingOptions newOptions = TypeAnnotationParsingOptions::REPORT_ERROR;
        constraint = ParseTypeAnnotation(&newOptions);
    }

    ir::TypeNode *defaultType = nullptr;

    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
        Lexer()->NextToken();  // eat '='
        defaultType = ParseTypeAnnotation(options);
    }

    auto *typeParam =
        AllocNode<ir::TSTypeParameter>(paramIdent, constraint, defaultType, varianceModifier, Allocator());

    ES2PANDA_ASSERT(typeParam != nullptr);
    ApplyAnnotationsToNode(typeParam, std::move(annotations), saveLoc);
    typeParam->SetRange({startLoc, Lexer()->GetToken().End()});
    return typeParam;
}

ir::Identifier *ETSParser::ParseClassIdent([[maybe_unused]] ir::ClassDefinitionModifiers modifiers)
{
    return ExpectIdentifier(false, true);
}

bool ETSParser::IsStructKeyword() const
{
    return (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_IDENT &&
            Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_STRUCT);
}

void ETSParser::ParseTrailingBlock(ir::CallExpression *callExpr)
{
    if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
        SavedParserContext svCtx(this, ParserStatus::PARSE_TRAILING_BLOCK);
        callExpr->SetIsTrailingBlockInNewLine(Lexer()->GetToken().NewLine());
        callExpr->SetTrailingBlock(ParseBlockStatement());
    }
}

void ETSParser::CheckDeclare()
{
    ES2PANDA_ASSERT(Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_DECLARE);

    if (InAmbientContext()) {
        LogError(diagnostic::DECALRE_IN_AMBIENT_CONTEXT);
    }

    GetContext().Status() |= ParserStatus::IN_AMBIENT_CONTEXT;

    Lexer()->NextToken();  // eat 'declare'

    if (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_IDENT && Lexer()->GetToken().Ident().Is("module")) {
        LogError(diagnostic::IMPLEMENTATION_IN_AMBIENT_CONTEXT);
        return;
    }

    switch (Lexer()->GetToken().KeywordType()) {
        case lexer::TokenType::KEYW_LET:
        case lexer::TokenType::KEYW_CONST:
        case lexer::TokenType::KEYW_FUNCTION:
        case lexer::TokenType::KEYW_CLASS:
        case lexer::TokenType::KEYW_NAMESPACE:
        case lexer::TokenType::KEYW_ENUM:
        case lexer::TokenType::KEYW_ABSTRACT:
        case lexer::TokenType::KEYW_FINAL:
        case lexer::TokenType::KEYW_INTERFACE:
        case lexer::TokenType::KEYW_OVERLOAD:
        case lexer::TokenType::KEYW_TYPE:
        case lexer::TokenType::KEYW_ASYNC:
        case lexer::TokenType::KEYW_STRUCT:
        case lexer::TokenType::KEYW_GET:
        case lexer::TokenType::KEYW_SET: {
            return;
        }
        default: {
            if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_AT) {
                return;
            }
            LogUnexpectedToken(Lexer()->GetToken());
        }
    }
}

void ETSParser::WarnPackageDeprecated()
{
    if (!packageDeprecationWarned_) {
        LogError(diagnostic::PACKAGE_DEPRECATED_WARNING);
        packageDeprecationWarned_ = true;
    }
}

void ETSParser::RaisePackageDeprecatedMessage()
{
    if (util::Helpers::IsStdLib(GetProgram())) {
        WarnPackageDeprecated();
    } else {
        LogError(diagnostic::PACKAGE_DEPRECATED);
    }
}

void ETSParser::ValidateOverloadDeclarationModifiers(ir::ModifierFlags modifiers)
{
    ir::ModifierFlags allowModifiers = ir::ModifierFlags::STATIC | ir::ModifierFlags::ASYNC |
                                       ir::ModifierFlags::ACCESS | ir::ModifierFlags::EXPORTED |
                                       ir::ModifierFlags::DECLARE;
    if ((modifiers & ~allowModifiers) != 0) {
        LogError(diagnostic::OVERLOAD_MODIFIERS);
    }
}

bool ETSParser::ParseOverloadListElement(ArenaVector<ir::Expression *> &overloads,
                                         ir::OverloadDeclaration *overloadDecl)
{
    if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
        LogExpectedToken(lexer::TokenType::LITERAL_IDENT);
    }
    ir::Expression *qualifiedName = AllocNode<ir::Identifier>(Lexer()->GetToken().Ident(), Allocator());
    qualifiedName->SetRange(Lexer()->GetToken().Loc());
    Lexer()->NextToken();
    while (Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_PERIOD)) {
        if (Lexer()->GetToken().Type() != lexer::TokenType::LITERAL_IDENT) {
            LogExpectedToken(lexer::TokenType::LITERAL_IDENT);
            return false;
        }
        auto *identNode = AllocNode<ir::Identifier>(Lexer()->GetToken().Ident(), Allocator());
        identNode->SetRange(Lexer()->GetToken().Loc());
        auto start = qualifiedName->Start();
        qualifiedName = AllocNode<ir::MemberExpression>(qualifiedName, identNode,
                                                        ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
        qualifiedName->SetRange({start, identNode->End()});
        Lexer()->NextToken();
    }

    qualifiedName->SetParent(overloadDecl);
    overloads.push_back(qualifiedName);
    return true;
}

ir::OverloadDeclaration *ETSParser::ParseOverloadDeclaration(ir::ModifierFlags modifiers)
{
    ValidateOverloadDeclarationModifiers(modifiers);
    Lexer()->TryEatTokenKeyword(lexer::TokenType::KEYW_OVERLOAD);
    auto *overloadName = ExpectIdentifier(false, false, TypeAnnotationParsingOptions::REPORT_ERROR);
    auto *overloadDef = AllocNode<ir::OverloadDeclaration>(overloadName->Clone(Allocator(), nullptr)->AsExpression(),
                                                           modifiers, Allocator());
    overloadDef->AddOverloadDeclFlag(ir::OverloadDeclFlags::FUNCTION);

    auto startLoc = overloadName->Start();
    if (!Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_LEFT_BRACE)) {
        LogExpectedToken(lexer::TokenType::PUNCTUATOR_LEFT_BRACE);
    }
    ArenaVector<ir::Expression *> overloads(Allocator()->Adapter());
    lexer::SourcePosition endLoc;

    ParseList(
        lexer::TokenType::PUNCTUATOR_RIGHT_BRACE, lexer::NextTokenFlags::NONE,
        [this, &overloads, overloadDef](bool &) { return ParseOverloadListElement(overloads, overloadDef); }, &endLoc,
        ParseListOptions::ALLOW_TRAILING_SEP);
    overloadDef->SetOverloadedList(std::move(overloads));
    overloadDef->SetRange({startLoc, endLoc});
    for (ir::Expression *overloadedName : overloadDef->OverloadedList()) {
        std::function<bool(ir::Expression *)> checkQualifiedName = [&checkQualifiedName](ir::Expression *expr) -> bool {
            if (expr->IsIdentifier()) {
                return true;
            }
            if (expr->IsMemberExpression()) {
                return expr->AsMemberExpression()->Property()->IsIdentifier() &&
                       checkQualifiedName(expr->AsMemberExpression()->Object());
            }
            return false;
        };
        if (!checkQualifiedName(overloadedName)) {
            LogError(diagnostic::FUNCTION_OVERLOADED_NAME_MUST_QUALIFIED_NAME, {}, overloadedName->Start());
        }
    }
    return overloadDef;
}

ir::FunctionDeclaration *ETSParser::ParseFunctionDeclaration(bool canBeAnonymous, ir::ModifierFlags modifiers)
{
    lexer::SourcePosition startLoc = Lexer()->GetToken().Start();

    ES2PANDA_ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::KEYW_FUNCTION);
    Lexer()->NextToken();
    auto newStatus = ParserStatus::NEED_RETURN_TYPE | ParserStatus::ALLOW_SUPER;

    if ((modifiers & ir::ModifierFlags::ASYNC) != 0) {
        newStatus |= ParserStatus::ASYNC_FUNCTION;
    }

    if (Lexer()->TryEatTokenType(lexer::TokenType::PUNCTUATOR_MULTIPLY)) {
        LogError(diagnostic::GENERATOR_FUNCTION);
        newStatus |= ParserStatus::GENERATOR_FUNCTION;
    }

    ir::Identifier *funcIdentNode = nullptr;

    if (Lexer()->GetToken().Type() == lexer::TokenType::LITERAL_IDENT) {
        // Parse regular function. Example: `function foo() {}`:
        funcIdentNode = ExpectIdentifier();
    } else if (!canBeAnonymous) {
        LogError(diagnostic::UNEXPECTED_TOKEN_ID);
        funcIdentNode = AllocBrokenExpression(Lexer()->GetToken().Loc());
    }

    if (funcIdentNode != nullptr) {
        CheckRestrictedBinding(funcIdentNode->Name(), funcIdentNode->Start());
    }

    ir::ScriptFunction *func =
        ParseFunction(newStatus | ParserStatus::FUNCTION_DECLARATION | ParserStatus::ALLOW_RECEIVER);
    ES2PANDA_ASSERT(func != nullptr);
    if (funcIdentNode != nullptr) {  // Error processing.
        func->SetIdent(funcIdentNode);
    }

    auto *funcDecl = AllocNode<ir::FunctionDeclaration>(Allocator(), func);
    if (func->IsOverload() && Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SEMI_COLON) {
        Lexer()->NextToken();
    }

    ES2PANDA_ASSERT(funcDecl != nullptr);
    funcDecl->SetRange(func->Range());
    func->AddModifier(modifiers);
    func->SetStart(startLoc);
    return funcDecl;
}

// NOLINTNEXTLINE(google-default-arguments)
void ETSParser::ValidateGetterSetter(ir::MethodDefinitionKind methodDefinition, const ir::ScriptFunction *func,
                                     bool hasReceiver)
{
    const auto &params = func->Params();
    const size_t receiverOffset = hasReceiver ? 1 : 0;
    if (methodDefinition == ir::MethodDefinitionKind::SET) {
        if (func->ReturnTypeAnnotation() != nullptr) {
            LogError(diagnostic::SETTER_NO_RETURN_TYPE, {}, func->Start());
        }
        if (params.size() != 1 + receiverOffset) {
            LogError(hasReceiver ? diagnostic::EXTENSION_SETTER_WRONG_PARAM : diagnostic::SETTER_FORMAL_PARAMS, {},
                     func->Start());
            return;
        }
        // For ETS, the param type must be ETSParameterExpression.
        // Otherwise it has been logged error before, no need continue check.
        if (!params[0 + receiverOffset]->IsETSParameterExpression()) {
            ES2PANDA_ASSERT(DiagnosticEngine().IsAnyError());
            return;
        }
        auto setterParam = params[0 + receiverOffset]->AsETSParameterExpression();
        if (setterParam->IsOptional()) {
            LogError(diagnostic::SETTER_OPTIONAL_PARAM, {}, params[0 + receiverOffset]->Start());
        } else if (setterParam->Spread() != nullptr) {
            LogError(diagnostic::SETTER_REST_PARAM, {}, params[0 + receiverOffset]->Start());
        }
    } else if (methodDefinition == ir::MethodDefinitionKind::GET) {
        if (params.size() != receiverOffset) {
            LogError(hasReceiver ? diagnostic::EXTENSION_GETTER_WRONG_PARAM : diagnostic::GETTER_FORMAL_PARAMS, {},
                     func->Start());
        }
    }
}

ir::FunctionDeclaration *ETSParser::ParseTopLevelAccessor(ir::ModifierFlags modifiers)
{
    ES2PANDA_ASSERT(Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_GET ||
                    Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_SET);

    bool isGetter = Lexer()->GetToken().KeywordType() == lexer::TokenType::KEYW_GET;
    lexer::SourcePosition startLoc = Lexer()->GetToken().Start();

    Lexer()->NextToken();
    auto newStatus = ParserStatus::ALLOW_SUPER | ParserStatus::FUNCTION_DECLARATION | ParserStatus::ALLOW_RECEIVER;

    ir::Identifier *funcIdentNode = ExpectIdentifier();
    ES2PANDA_ASSERT(funcIdentNode != nullptr);
    CheckRestrictedBinding(funcIdentNode->Name(), funcIdentNode->Start());

    ir::ScriptFunction *func =
        isGetter ? ParseFunction(newStatus | ParserStatus::NEED_RETURN_TYPE) : ParseFunction(newStatus);
    ES2PANDA_ASSERT(func != nullptr);
    func->SetStart(startLoc);

    auto params = func->Params();
    bool hasReceiver = !params.empty() && params[0]->IsETSParameterExpression() &&
                       params[0]->AsETSParameterExpression()->Ident()->IsReceiver();

    if (isGetter) {
        func->AddFlag(ir::ScriptFunctionFlags::GETTER);
    } else {
        func->AddFlag(ir::ScriptFunctionFlags::SETTER);
    }

    ValidateGetterSetter(isGetter ? ir::MethodDefinitionKind::GET : ir::MethodDefinitionKind::SET, func, hasReceiver);

    func->SetIdent(funcIdentNode);

    auto *funcDecl = AllocNode<ir::FunctionDeclaration>(Allocator(), func);
    ES2PANDA_ASSERT(funcDecl != nullptr);
    funcDecl->SetRange(func->Range());
    func->AddModifier(modifiers);
    return funcDecl;
}

void ETSParser::EnsureContainingPackageIsRegistered(ir::ETSPackageDeclaration *packageDecl)
{
    ES2PANDA_ASSERT(packageDecl->Range().start.Program() == GetProgram());
    auto *packageFraction = GetProgram();
    [[maybe_unused]] auto packageProg =
        GetImportPathManager()->EnsurePackageIsRegisteredByPackageFraction(packageFraction, packageDecl);
    ES2PANDA_ASSERT(packageFraction->GetModuleKind() == util::ModuleKind::MODULE);
    ES2PANDA_ASSERT(packageProg->ModuleName() == packageFraction->ModuleName());
}

//================================================================================================//
//  ExternalSourceParser class
//================================================================================================//

ExternalSourceParser::ExternalSourceParser(ETSParser *parser, Program *newProgram)
    : parser_(parser),
      savedProgram_(parser_->GetProgram()),
      savedLexer_(parser_->Lexer()),
      savedTopScope_(parser_->GetProgram()->VarBinder()->TopScope())
{
    parser_->SetProgram(newProgram);
    parser_->GetContext().SetProgram(newProgram);
}

ExternalSourceParser::~ExternalSourceParser()
{
    parser_->SetLexer(savedLexer_);
    parser_->SetProgram(savedProgram_);
    parser_->GetContext().SetProgram(savedProgram_);
    parser_->GetProgram()->VarBinder()->ResetTopScope(savedTopScope_);
}

//================================================================================================//
//  InnerSourceParser class
//================================================================================================//

InnerSourceParser::InnerSourceParser(ETSParser *parser) : parser_(parser), savedLexer_(parser_->Lexer()) {}

InnerSourceParser::~InnerSourceParser()
{
    parser_->SetLexer(savedLexer_);
}

}  // namespace ark::es2panda::parser