* 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 "generated/diagnostic.h"
#include "lexer/lexer.h"
#include "ir/ets/etsTuple.h"
#include "ir/ets/etsUnionType.h"
#include "ir/statements/annotationDeclaration.h"
#include "ir/brokenTypeNode.h"
namespace ark::es2panda::parser {
void ETSParser::ReportGapAfterAtIfAny(lexer::SourcePosition atExclusiveEnd)
{
if (Lexer()->GetToken().Type() == lexer::TokenType::EOS) {
return;
}
if (Lexer()->GetToken().Start().index != atExclusiveEnd.index) {
LogError(diagnostic::ANNOTATION_AT_NAME_GAP, {}, Lexer()->GetToken().Start());
}
}
void ETSParser::EatLeadingAtForAnnotation()
{
ES2PANDA_ASSERT(Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_AT);
const lexer::SourcePosition atEnd = Lexer()->GetToken().End();
Lexer()->NextToken();
ReportGapAfterAtIfAny(atEnd);
}
ir::Statement *ETSParser::ParseTopLevelAnnotation(ir::ModifierFlags memberModifiers)
{
ir::Statement *result = nullptr;
EatLeadingAtForAnnotation();
if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_INTERFACE) {
result = ParseAnnotationDeclaration(memberModifiers);
} else {
auto annotations = ParseAnnotations(true);
auto savePos = Lexer()->GetToken().Start();
result = ParseTopLevelStatement();
if (result != nullptr) {
ApplyAnnotationsToNode(result, std::move(annotations), savePos);
} else {
LogError(diagnostic::ANNOTATION_NOT_ALLOWED);
}
}
return result;
}
template <bool IS_USAGE>
ir::Expression *ETSParser::ParseAnnotationName()
{
ir::Expression *expr = nullptr;
auto setAnnotation = [](ir::Identifier *ident) {
if constexpr (IS_USAGE) {
ident->SetAnnotationUsage();
} else {
ident->SetAnnotationDecl();
}
};
auto save = Lexer()->Save();
ir::Identifier *ident = nullptr;
Lexer()->NextToken();
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_PERIOD_PERIOD_PERIOD) {
Lexer()->Rewind(save);
expr = ExpectIdentifier();
ES2PANDA_ASSERT(expr != nullptr);
ident = expr->AsIdentifier();
} else {
Lexer()->Rewind(save);
if (IS_USAGE && Lexer()->Lookahead() == '.') {
auto opt = TypeAnnotationParsingOptions::NO_OPTS;
expr = ParseTypeReference(&opt);
ES2PANDA_ASSERT(expr != nullptr);
ident = expr->AsETSTypeReference()->Part()->GetIdent();
} else {
expr = ExpectIdentifier();
ES2PANDA_ASSERT(expr != nullptr);
ident = expr->AsIdentifier();
}
}
if (ident->IsBrokenExpression()) {
LogError(diagnostic::INVALID_ANNOTATION_NAME, {}, expr->Start());
}
setAnnotation(ident);
return expr;
}
ir::AnnotationDeclaration *ETSParser::ParseAnnotationDeclaration(ir::ModifierFlags flags)
{
const lexer::SourcePosition startLoc = Lexer()->GetToken().Start();
flags |= ir::ModifierFlags::ABSTRACT | ir::ModifierFlags::PUBLIC | ir::ModifierFlags::ANNOTATION_DECLARATION;
flags &= ~ir::ModifierFlags::STATIC;
if (InAmbientContext()) {
flags |= ir::ModifierFlags::DECLARE;
}
Lexer()->NextToken();
ir::Expression *expr = ParseAnnotationName<false>();
ExpectToken(lexer::TokenType::PUNCTUATOR_LEFT_BRACE, false);
auto properties = ParseAnnotationProperties(flags);
lexer::SourcePosition endLoc = Lexer()->GetToken().End();
auto *annotationDecl = AllocNode<ir::AnnotationDeclaration>(expr, std::move(properties), Allocator());
ES2PANDA_ASSERT(annotationDecl != nullptr);
annotationDecl->SetRange({startLoc, endLoc});
annotationDecl->AddModifier(flags);
return annotationDecl;
}
static bool IsMemberAccessModifiers(lexer::TokenType type)
{
return type == lexer::TokenType::KEYW_STATIC || type == lexer::TokenType::KEYW_ASYNC ||
type == lexer::TokenType::KEYW_PUBLIC || type == lexer::TokenType::KEYW_PROTECTED ||
type == lexer::TokenType::KEYW_PRIVATE || type == lexer::TokenType::KEYW_DECLARE ||
type == lexer::TokenType::KEYW_READONLY || type == lexer::TokenType::KEYW_ABSTRACT ||
type == lexer::TokenType::KEYW_CONST || type == lexer::TokenType::KEYW_FINAL ||
type == lexer::TokenType::KEYW_NATIVE;
}
ArenaVector<ir::AstNode *> ETSParser::ParseAnnotationProperties(ir::ModifierFlags memberModifiers)
{
Lexer()->NextToken(lexer::NextTokenFlags::KEYWORD_TO_IDENT);
ArenaVector<ir::AstNode *> properties(Allocator()->Adapter());
while (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_BRACE &&
Lexer()->GetToken().Type() != lexer::TokenType::EOS) {
if ((memberModifiers & ir::ModifierFlags::ANNOTATION_DECLARATION) != 0U &&
Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SEMI_COLON) {
Lexer()->NextToken();
continue;
}
while (IsMemberAccessModifiers(Lexer()->GetToken().Type())) {
LogError(diagnostic::ANNOTATION_PROPERTY_ACCESS_MODIFIERS, {}, Lexer()->GetToken().Start());
Lexer()->NextToken();
}
auto *fieldName = ExpectIdentifier();
ES2PANDA_ASSERT(fieldName != nullptr);
if (fieldName->IsErrorPlaceHolder()) {
auto const pos = Lexer()->Save();
auto *fieldName1 = ExpectIdentifier();
ES2PANDA_ASSERT(fieldName1 != nullptr);
if (fieldName1->IsErrorPlaceHolder()) {
Lexer()->Rewind(pos);
} else {
fieldName = fieldName1;
}
}
bool needTypeAnnotation = (memberModifiers & ir::ModifierFlags::ANNOTATION_USAGE) == 0U;
properties.emplace_back(ParseAnnotationProperty(fieldName, memberModifiers, needTypeAnnotation));
if ((memberModifiers & ir::ModifierFlags::ANNOTATION_USAGE) != 0U &&
Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_BRACE) {
ExpectToken(lexer::TokenType::PUNCTUATOR_COMMA);
}
}
Lexer()->NextToken();
return properties;
}
bool ETSParser::ValidAnnotationValue(ir::Expression *initializer)
{
if (initializer->IsArrayExpression()) {
for (auto *element : initializer->AsArrayExpression()->Elements()) {
if (!ValidAnnotationValue(element)) {
return false;
}
}
return true;
}
return initializer->IsStringLiteral() || initializer->IsNumberLiteral() || initializer->IsMemberExpression() ||
initializer->IsBooleanLiteral() || initializer->IsBinaryExpression() || initializer->IsUnaryExpression() ||
initializer->IsConditionalExpression() || initializer->IsIdentifier() || initializer->IsTSAsExpression();
}
ir::AstNode *ETSParser::ParseAnnotationProperty(ir::Identifier *fieldName, ir::ModifierFlags memberModifiers,
bool needTypeAnnotation)
{
lexer::SourcePosition endLoc = fieldName->End();
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS ||
Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LESS_THAN) {
LogError(diagnostic::ANNOTATION_METHOD_AS_PROP, {}, Lexer()->GetToken().Start());
}
ir::TypeNode *typeAnnotation = nullptr;
TypeAnnotationParsingOptions options = TypeAnnotationParsingOptions::REPORT_ERROR;
if (needTypeAnnotation && Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COLON) {
Lexer()->NextToken();
typeAnnotation = ParseTypeAnnotation(&options);
}
if (typeAnnotation == nullptr && (memberModifiers & ir::ModifierFlags::ANNOTATION_DECLARATION) != 0 &&
!fieldName->IsErrorPlaceHolder()) {
LogError(diagnostic::MISSING_TYPE_ANNOTATION, {fieldName->Name().Mutf8()}, Lexer()->GetToken().Start());
typeAnnotation = AllocNode<ir::BrokenTypeNode>(Allocator());
typeAnnotation->SetRange({endLoc, endLoc});
}
if (typeAnnotation != nullptr) {
endLoc = typeAnnotation->End();
}
ir::Expression *initializer = nullptr;
lexer::SourcePosition savePos;
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION ||
(Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_COLON)) {
Lexer()->NextToken();
savePos = Lexer()->GetToken().Start();
initializer = ParseExpression();
}
if (initializer == nullptr && (memberModifiers & ir::ModifierFlags::ANNOTATION_USAGE) != 0 &&
!fieldName->IsErrorPlaceHolder()) {
LogError(diagnostic::INVALID_ARGUMENT_PASSED, {fieldName->Name().Mutf8()}, Lexer()->GetToken().Start());
}
if (initializer != nullptr && !ValidAnnotationValue(initializer)) {
LogError(diagnostic::INVALID_VAL_ANNOTATION_FIELD, {}, savePos);
initializer = AllocBrokenExpression(savePos);
}
memberModifiers |= ir::ModifierFlags::PUBLIC;
memberModifiers |= ir::ModifierFlags::ABSTRACT;
auto *field =
AllocNode<ir::ClassProperty>(fieldName, initializer, typeAnnotation, memberModifiers, Allocator(), false);
ES2PANDA_ASSERT(field != nullptr);
field->SetRange({fieldName->Start(), initializer != nullptr ? initializer->End() : endLoc});
return field;
}
ArenaVector<ir::AnnotationUsage *> ETSParser::ParseAnnotations(bool isTopLevelSt)
{
ArenaVector<ir::AnnotationUsage *> annotations(Allocator()->Adapter());
bool hasMoreAnnotations = true;
auto save = Lexer()->Save();
while (hasMoreAnnotations) {
if (Lexer()->GetToken().Type() == lexer::TokenType::KEYW_INTERFACE) {
if (!isTopLevelSt) {
LogError(diagnostic::ANNOTATION_ONLY_TOP_LEVEL);
}
Lexer()->Rewind(save);
return annotations;
}
annotations.emplace_back(ParseAnnotationUsage());
if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_AT) {
hasMoreAnnotations = false;
} else {
save = Lexer()->Save();
EatLeadingAtForAnnotation();
}
}
return annotations;
}
static bool ApplyAnnotationsToNamespace(ir::ETSModule *ns, ArenaVector<ir::AnnotationUsage *> &annotations)
{
if (ns->IsNamespaceChainLastNode()) {
ns->SetAnnotations(std::move(annotations));
return true;
}
for (auto *node : ns->Statements()) {
if (node->IsETSModule()) {
if (ApplyAnnotationsToNamespace(node->AsETSModule(), annotations)) {
return true;
}
}
}
return false;
}
void ETSParser::ApplyAnnotationsToNode(ir::AstNode *node, ArenaVector<ir::AnnotationUsage *> &&annotations,
lexer::SourcePosition pos, TypeAnnotationParsingOptions options)
{
if (node == nullptr || annotations.empty()) {
return;
}
if ((options & TypeAnnotationParsingOptions::ANNOTATION_NOT_ALLOW) != 0) {
LogError(diagnostic::ANNOTATION_WRONG_DEC, {}, pos);
return;
}
if (node->IsExpressionStatement()) {
ApplyAnnotationsToNode(node->AsExpressionStatement()->GetExpression(), std::move(annotations), pos);
return;
}
ApplyAnnotationsToSpecificNodeType(node, std::move(annotations), pos);
}
void ETSParser::ApplyAnnotationsToSpecificNodeType(ir::AstNode *node, ArenaVector<ir::AnnotationUsage *> &&annotations,
lexer::SourcePosition pos)
{
switch (node->Type()) {
case ir::AstNodeType::METHOD_DEFINITION: {
auto *func = node->AsMethodDefinition()->Function();
ES2PANDA_ASSERT(func != nullptr);
func->SetAnnotations(std::move(annotations));
break;
}
case ir::AstNodeType::CLASS_DECLARATION:
node->AsClassDeclaration()->Definition()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::STRUCT_DECLARATION:
node->AsETSStructDeclaration()->Definition()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::FUNCTION_DECLARATION:
node->AsFunctionDeclaration()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::TS_INTERFACE_DECLARATION:
node->AsTSInterfaceDeclaration()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::CLASS_PROPERTY:
node->AsClassProperty()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::VARIABLE_DECLARATION:
node->AsVariableDeclaration()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
node->AsTSTypeAliasDeclaration()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_PARAMETER_EXPRESSION:
node->AsETSParameterExpression()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ARROW_FUNCTION_EXPRESSION:
node->AsArrowFunctionExpression()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_MODULE:
ApplyAnnotationsToNamespace(node->AsETSModule(), annotations);
break;
case ir::AstNodeType::ETS_TYPE_REFERENCE:
node->AsETSTypeReference()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::TS_ARRAY_TYPE:
node->AsTSArrayType()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_TUPLE:
node->AsETSTuple()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_UNION_TYPE:
node->AsETSUnionType()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_FUNCTION_TYPE:
node->AsETSFunctionType()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_STRING_LITERAL_TYPE:
node->AsETSStringLiteralType()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_NULL_TYPE:
node->AsETSNullType()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ETS_UNDEFINED_TYPE:
node->AsETSUndefinedType()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::TS_TYPE_PARAMETER:
node->AsTSTypeParameter()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::ANNOTATION_DECLARATION:
node->AsAnnotationDeclaration()->SetAnnotations(std::move(annotations));
break;
case ir::AstNodeType::TS_ENUM_DECLARATION:
node->AsTSEnumDeclaration()->SetAnnotations(std::move(annotations));
break;
default:
LogError(diagnostic::ANNOTATION_WRONG_DEC, {}, pos);
}
}
static lexer::SourcePosition GetAnnotationExpressionEndLoc(ir::Expression *expr)
{
ES2PANDA_ASSERT(expr != nullptr);
if (expr->IsIdentifier()) {
return expr->AsIdentifier()->End();
}
return expr->AsETSTypeReference()->Part()->GetIdent()->End();
}
ir::AnnotationUsage *ETSParser::ParseAnnotationUsage()
{
const lexer::SourcePosition startLoc = Lexer()->GetToken().Start();
ir::Expression *expr = ParseAnnotationName<true>();
auto flags = ir::ModifierFlags::ANNOTATION_USAGE;
ArenaVector<ir::AstNode *> properties(Allocator()->Adapter());
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS &&
!IsArrowFunctionExpressionStart() && IsAnnotationUsageStart(GetAnnotationExpressionEndLoc(expr))) {
Lexer()->NextToken();
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_BRACE) {
properties = ParseAnnotationProperties(flags);
} else if (Lexer()->GetToken().Type() != lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) {
auto *singleParamName = AllocNode<ir::Identifier>(compiler::Signatures::ANNOTATION_KEY_VALUE, Allocator());
ES2PANDA_ASSERT(singleParamName != nullptr);
singleParamName->SetRange({Lexer()->GetToken().Start(), Lexer()->GetToken().End()});
const auto savePos = Lexer()->GetToken().Start();
auto *initializer = ParseExpression();
if (initializer != nullptr && !ValidAnnotationValue(initializer)) {
LogError(diagnostic::INVALID_VAL_ANNOTATION_FIELD, {}, savePos);
}
auto *singleParam = AllocNode<ir::ClassProperty>(singleParamName, initializer, nullptr,
ir::ModifierFlags::ANNOTATION_USAGE, Allocator(), false);
ES2PANDA_ASSERT(singleParam != nullptr);
singleParam->SetRange(
{singleParamName->Start(), initializer != nullptr ? initializer->End() : singleParamName->End()});
properties.push_back(singleParam);
}
auto *annotationUsage = AllocNode<ir::AnnotationUsage>(expr, std::move(properties));
annotationUsage->AddModifier(flags);
annotationUsage->SetRange({startLoc, Lexer()->GetToken().End()});
annotationUsage->SetHasParameterParen(true);
ExpectToken(lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS, true);
return annotationUsage;
}
auto *annotationUsage = AllocNode<ir::AnnotationUsage>(expr, std::move(properties));
ES2PANDA_ASSERT(annotationUsage != nullptr);
annotationUsage->AddModifier(flags);
annotationUsage->SetRange({startLoc, GetAnnotationExpressionEndLoc(expr)});
return annotationUsage;
}
bool ETSParser::TryParseAnnotations()
{
std::vector<ir::AnnotationUsage *> annotations {};
while (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_AT) {
EatLeadingAtForAnnotation();
ir::Expression *ident = ParseAnnotationName<true>();
ArenaVector<ir::AstNode *> properties(Allocator()->Adapter());
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS) {
TryParseAnnotationsParams();
}
auto *annotation = AllocNode<ir::AnnotationUsage>(ident, std::move(properties));
annotations.push_back(annotation);
}
return !annotations.empty();
}
void ETSParser::TryParseAnnotationsParams()
{
Lexer()->NextToken();
int parenthesesCount = 1;
while (parenthesesCount > 0) {
if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_LEFT_PARENTHESIS) {
parenthesesCount++;
} else if (Lexer()->GetToken().Type() == lexer::TokenType::PUNCTUATOR_RIGHT_PARENTHESIS) {
parenthesesCount--;
}
Lexer()->NextToken();
}
}
bool ETSParser::IsAnnotationUsageStart(lexer::SourcePosition lastTokenEndIndex)
{
auto result = true;
if (Lexer()->GetToken().End().index - lastTokenEndIndex.index != 1) {
result = false;
}
return result;
}
}