From 37e57e5cd77c18e0f5b37a06c155304266dce2a3 Mon Sep 17 00:00:00 2001 From: abdulsamethaymana Date: Mon, 23 Jun 2025 14:21:53 +0300 Subject: [PATCH] fix: #26889 Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICH7XQ?from=project-issue Signed-off-by: abdulsamethaymana --- ets2panda/ir/ts/tsBooleanKeyword.cpp | 22 +++ ets2panda/ir/ts/tsBooleanKeyword.h | 1 + ets2panda/ir/ts/tsFunctionType.cpp | 38 +++++ ets2panda/ir/ts/tsFunctionType.h | 2 + ets2panda/ir/ts/tsIndexedAccessType.cpp | 29 ++++ ets2panda/ir/ts/tsIndexedAccessType.h | 2 + ets2panda/ir/ts/tsLiteralType.cpp | 33 +++++ ets2panda/ir/ts/tsLiteralType.h | 2 + ets2panda/ir/ts/tsMappedType.cpp | 33 +++++ ets2panda/ir/ts/tsMappedType.h | 2 + ets2panda/ir/ts/tsNumberKeyword.cpp | 22 +++ ets2panda/ir/ts/tsNumberKeyword.h | 2 + ets2panda/ir/ts/tsStringKeyword.cpp | 22 +++ ets2panda/ir/ts/tsStringKeyword.h | 2 + ets2panda/ir/ts/tsTupleType.cpp | 32 ++++ ets2panda/ir/ts/tsTupleType.h | 2 + ets2panda/ir/ts/tsTypeLiteral.cpp | 32 ++++ ets2panda/ir/ts/tsTypeLiteral.h | 2 + ets2panda/ir/ts/tsTypeOperator.cpp | 33 +++++ ets2panda/ir/ts/tsTypeOperator.h | 2 + ets2panda/ir/ts/tsTypeReference.cpp | 32 ++++ ets2panda/ir/ts/tsTypeReference.h | 2 + ets2panda/ir/ts/tsUnionType.cpp | 37 +++++ ets2panda/ir/ts/tsUnionType.h | 2 + ets2panda/ir/ts/tsVoidKeyword.cpp | 22 +++ ets2panda/ir/ts/tsVoidKeyword.h | 2 + .../ets/generic_type_alias_clone_fix.ets | 31 ++++ .../ets/type_node_clone_assertion_fix.ets | 35 +++++ .../parser/ets/typenode_clone_brokentype.ets | 59 ++++++++ .../ets/typenode_clone_comprehensive.ets | 140 ++++++++++++++++++ .../parser/ets/typenode_clone_primitives.ets | 71 +++++++++ 31 files changed, 748 insertions(+) create mode 100644 ets2panda/test/ast/parser/ets/generic_type_alias_clone_fix.ets create mode 100644 ets2panda/test/ast/parser/ets/type_node_clone_assertion_fix.ets create mode 100644 ets2panda/test/ast/parser/ets/typenode_clone_brokentype.ets create mode 100644 ets2panda/test/ast/parser/ets/typenode_clone_comprehensive.ets create mode 100644 ets2panda/test/ast/parser/ets/typenode_clone_primitives.ets diff --git a/ets2panda/ir/ts/tsBooleanKeyword.cpp b/ets2panda/ir/ts/tsBooleanKeyword.cpp index bae3ac4ae1..6c4e40ab63 100644 --- a/ets2panda/ir/ts/tsBooleanKeyword.cpp +++ b/ets2panda/ir/ts/tsBooleanKeyword.cpp @@ -72,4 +72,26 @@ checker::VerifiedType TSBooleanKeyword::Check(checker::ETSChecker *checker) { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSBooleanKeyword *TSBooleanKeyword::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clone = allocator->New(allocator); + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsBooleanKeyword.h b/ets2panda/ir/ts/tsBooleanKeyword.h index bf726daf82..94d597e445 100644 --- a/ets2panda/ir/ts/tsBooleanKeyword.h +++ b/ets2panda/ir/ts/tsBooleanKeyword.h @@ -32,6 +32,7 @@ public: checker::Type *Check(checker::TSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check(checker::ETSChecker *checker) override; + TSBooleanKeyword *Clone(ArenaAllocator *allocator, AstNode *parent) override; void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsFunctionType.cpp b/ets2panda/ir/ts/tsFunctionType.cpp index 89e4751aeb..29c10a2a26 100644 --- a/ets2panda/ir/ts/tsFunctionType.cpp +++ b/ets2panda/ir/ts/tsFunctionType.cpp @@ -84,4 +84,42 @@ checker::Type *TSFunctionType::GetType([[maybe_unused]] checker::ETSChecker *che { return nullptr; } + +TSFunctionType *TSFunctionType::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + // Clone the function signature + FunctionSignature clonedSignature = signature_.Clone(allocator); + + auto *clone = allocator->New(std::move(clonedSignature), allocator); + + // Set parent relationships for cloned signature components + if (clone->signature_.TypeParams() != nullptr) { + clone->signature_.TypeParams()->SetParent(clone); + } + for (auto *param : clone->signature_.Params()) { + param->SetParent(clone); + } + if (clone->signature_.ReturnType() != nullptr) { + clone->signature_.ReturnType()->SetParent(clone); + } + + clone->SetNullable(nullable_); + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsFunctionType.h b/ets2panda/ir/ts/tsFunctionType.h index b66474ef0e..dcf8375b4c 100644 --- a/ets2panda/ir/ts/tsFunctionType.h +++ b/ets2panda/ir/ts/tsFunctionType.h @@ -95,6 +95,8 @@ public: checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::ETSChecker *checker) override; + TSFunctionType *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsIndexedAccessType.cpp b/ets2panda/ir/ts/tsIndexedAccessType.cpp index e1e110231a..b13cd33e44 100644 --- a/ets2panda/ir/ts/tsIndexedAccessType.cpp +++ b/ets2panda/ir/ts/tsIndexedAccessType.cpp @@ -95,4 +95,33 @@ checker::VerifiedType TSIndexedAccessType::Check([[maybe_unused]] checker::ETSCh { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSIndexedAccessType *TSIndexedAccessType::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clonedObjectType = objectType_->Clone(allocator, nullptr)->AsTypeNode(); + auto *clonedIndexType = indexType_->Clone(allocator, nullptr)->AsTypeNode(); + + auto *clone = allocator->New(clonedObjectType, clonedIndexType, allocator); + + // Set parent relationships + clonedObjectType->SetParent(clone); + clonedIndexType->SetParent(clone); + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsIndexedAccessType.h b/ets2panda/ir/ts/tsIndexedAccessType.h index a50a4c24d6..769d134fdc 100644 --- a/ets2panda/ir/ts/tsIndexedAccessType.h +++ b/ets2panda/ir/ts/tsIndexedAccessType.h @@ -52,6 +52,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSIndexedAccessType *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsLiteralType.cpp b/ets2panda/ir/ts/tsLiteralType.cpp index a679912bf4..5bf04d111f 100644 --- a/ets2panda/ir/ts/tsLiteralType.cpp +++ b/ets2panda/ir/ts/tsLiteralType.cpp @@ -83,4 +83,37 @@ checker::VerifiedType TSLiteralType::Check([[maybe_unused]] checker::ETSChecker { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSLiteralType *TSLiteralType::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + // Clone the literal expression + Expression *clonedLiteral = nullptr; + if (literal_ != nullptr) { + clonedLiteral = literal_->Clone(allocator, nullptr)->AsExpression(); + } + + auto *clone = allocator->New(clonedLiteral, allocator); + + // Set parent for cloned literal + if (clonedLiteral != nullptr) { + clonedLiteral->SetParent(clone); + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsLiteralType.h b/ets2panda/ir/ts/tsLiteralType.h index 41c5390872..bc6695a68c 100644 --- a/ets2panda/ir/ts/tsLiteralType.h +++ b/ets2panda/ir/ts/tsLiteralType.h @@ -41,6 +41,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSLiteralType *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsMappedType.cpp b/ets2panda/ir/ts/tsMappedType.cpp index 61da853b75..2cd6624fbb 100644 --- a/ets2panda/ir/ts/tsMappedType.cpp +++ b/ets2panda/ir/ts/tsMappedType.cpp @@ -97,4 +97,37 @@ checker::VerifiedType TSMappedType::Check([[maybe_unused]] checker::ETSChecker * { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSMappedType *TSMappedType::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clonedTypeParameter = typeParameter_->Clone(allocator, nullptr)->AsTSTypeParameter(); + auto *clonedTypeAnnotation = + typeAnnotation_ != nullptr ? typeAnnotation_->Clone(allocator, nullptr)->AsTypeNode() : nullptr; + + auto *clone = + allocator->New(clonedTypeParameter, clonedTypeAnnotation, readonly_, optional_, allocator); + + // Set parent relationships + clonedTypeParameter->SetParent(clone); + if (clonedTypeAnnotation != nullptr) { + clonedTypeAnnotation->SetParent(clone); + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsMappedType.h b/ets2panda/ir/ts/tsMappedType.h index a452b880dc..0b2faa31d0 100644 --- a/ets2panda/ir/ts/tsMappedType.h +++ b/ets2panda/ir/ts/tsMappedType.h @@ -62,6 +62,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSMappedType *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsNumberKeyword.cpp b/ets2panda/ir/ts/tsNumberKeyword.cpp index afcee6e7b5..2110b59d50 100644 --- a/ets2panda/ir/ts/tsNumberKeyword.cpp +++ b/ets2panda/ir/ts/tsNumberKeyword.cpp @@ -72,4 +72,26 @@ checker::VerifiedType TSNumberKeyword::Check(checker::ETSChecker *checker) { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSNumberKeyword *TSNumberKeyword::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clone = allocator->New(allocator); + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsNumberKeyword.h b/ets2panda/ir/ts/tsNumberKeyword.h index b0b174c3f5..5762a5a801 100644 --- a/ets2panda/ir/ts/tsNumberKeyword.h +++ b/ets2panda/ir/ts/tsNumberKeyword.h @@ -33,6 +33,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check(checker::ETSChecker *checker) override; + TSNumberKeyword *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsStringKeyword.cpp b/ets2panda/ir/ts/tsStringKeyword.cpp index be62a34004..8f1eb75b0f 100644 --- a/ets2panda/ir/ts/tsStringKeyword.cpp +++ b/ets2panda/ir/ts/tsStringKeyword.cpp @@ -72,4 +72,26 @@ checker::VerifiedType TSStringKeyword::Check(checker::ETSChecker *checker) { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSStringKeyword *TSStringKeyword::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clone = allocator->New(allocator); + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsStringKeyword.h b/ets2panda/ir/ts/tsStringKeyword.h index ec264c1313..8bb71e94e7 100644 --- a/ets2panda/ir/ts/tsStringKeyword.h +++ b/ets2panda/ir/ts/tsStringKeyword.h @@ -33,6 +33,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check(checker::ETSChecker *checker) override; + TSStringKeyword *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsTupleType.cpp b/ets2panda/ir/ts/tsTupleType.cpp index d98cbd1622..a76a006e52 100644 --- a/ets2panda/ir/ts/tsTupleType.cpp +++ b/ets2panda/ir/ts/tsTupleType.cpp @@ -158,4 +158,36 @@ checker::VerifiedType TSTupleType::Check([[maybe_unused]] checker::ETSChecker *c { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSTupleType *TSTupleType::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + ArenaVector clonedElementTypes(allocator->Adapter()); + for (auto *elementType : elementTypes_) { + clonedElementTypes.push_back(elementType->Clone(allocator, nullptr)->AsTypeNode()); + } + + auto *clone = allocator->New(std::move(clonedElementTypes), allocator); + + // Set parent relationships for cloned element types + for (auto *elementType : clone->elementTypes_) { + elementType->SetParent(clone); + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsTupleType.h b/ets2panda/ir/ts/tsTupleType.h index fbf8b51c87..9d2527c1cd 100644 --- a/ets2panda/ir/ts/tsTupleType.h +++ b/ets2panda/ir/ts/tsTupleType.h @@ -43,6 +43,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSTupleType *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsTypeLiteral.cpp b/ets2panda/ir/ts/tsTypeLiteral.cpp index d13f7662cd..9ebb23b66d 100644 --- a/ets2panda/ir/ts/tsTypeLiteral.cpp +++ b/ets2panda/ir/ts/tsTypeLiteral.cpp @@ -95,4 +95,36 @@ checker::VerifiedType TSTypeLiteral::Check([[maybe_unused]] checker::ETSChecker { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSTypeLiteral *TSTypeLiteral::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + ArenaVector clonedMembers(allocator->Adapter()); + for (auto *member : members_) { + clonedMembers.push_back(member->Clone(allocator, nullptr)); + } + + auto *clone = allocator->New(std::move(clonedMembers), allocator); + + // Set parent relationships for cloned members + for (auto *member : clone->members_) { + member->SetParent(clone); + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsTypeLiteral.h b/ets2panda/ir/ts/tsTypeLiteral.h index 368d3585d3..a546f2af0d 100644 --- a/ets2panda/ir/ts/tsTypeLiteral.h +++ b/ets2panda/ir/ts/tsTypeLiteral.h @@ -41,6 +41,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSTypeLiteral *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsTypeOperator.cpp b/ets2panda/ir/ts/tsTypeOperator.cpp index 2fa2ad26ad..d2342545fd 100644 --- a/ets2panda/ir/ts/tsTypeOperator.cpp +++ b/ets2panda/ir/ts/tsTypeOperator.cpp @@ -79,4 +79,37 @@ checker::VerifiedType TSTypeOperator::Check([[maybe_unused]] checker::ETSChecker { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSTypeOperator *TSTypeOperator::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + // Clone the type annotation + TypeNode *clonedType = nullptr; + if (type_ != nullptr) { + clonedType = type_->Clone(allocator, nullptr)->AsTypeNode(); + } + + auto *clone = allocator->New(clonedType, operatorType_, allocator); + + // Set parent for cloned type + if (clonedType != nullptr) { + clonedType->SetParent(clone); + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsTypeOperator.h b/ets2panda/ir/ts/tsTypeOperator.h index 453206e1f0..1c8e52791c 100644 --- a/ets2panda/ir/ts/tsTypeOperator.h +++ b/ets2panda/ir/ts/tsTypeOperator.h @@ -56,6 +56,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSTypeOperator *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsTypeReference.cpp b/ets2panda/ir/ts/tsTypeReference.cpp index e6748d33c6..c84f392770 100644 --- a/ets2panda/ir/ts/tsTypeReference.cpp +++ b/ets2panda/ir/ts/tsTypeReference.cpp @@ -137,4 +137,36 @@ checker::VerifiedType TSTypeReference::Check([[maybe_unused]] checker::ETSChecke { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSTypeReference *TSTypeReference::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clonedTypeName = typeName_->Clone(allocator, nullptr)->AsExpression(); + auto *clonedTypeParams = + typeParams_ != nullptr ? typeParams_->Clone(allocator, nullptr)->AsTSTypeParameterInstantiation() : nullptr; + + auto *clone = allocator->New(clonedTypeName, clonedTypeParams, allocator); + + // Set parent relationships + clonedTypeName->SetParent(clone); + if (clonedTypeParams != nullptr) { + clonedTypeParams->SetParent(clone); + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsTypeReference.h b/ets2panda/ir/ts/tsTypeReference.h index 6391925d31..c5201c14fa 100644 --- a/ets2panda/ir/ts/tsTypeReference.h +++ b/ets2panda/ir/ts/tsTypeReference.h @@ -55,6 +55,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; + TSTypeReference *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsUnionType.cpp b/ets2panda/ir/ts/tsUnionType.cpp index aca8a8aa89..6841974f5c 100644 --- a/ets2panda/ir/ts/tsUnionType.cpp +++ b/ets2panda/ir/ts/tsUnionType.cpp @@ -92,4 +92,41 @@ checker::Type *TSUnionType::GetType(checker::TSChecker *checker) SetTsType(checker->CreateUnionType(std::move(types))); return TsType(); } + +TSUnionType *TSUnionType::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + // Clone all type nodes in the union + ArenaVector clonedTypes(allocator->Adapter()); + for (auto *type : types_) { + if (type != nullptr) { + clonedTypes.push_back(type->Clone(allocator, nullptr)->AsTypeNode()); + } + } + + auto *clone = allocator->New(std::move(clonedTypes), allocator); + + // Set parent for all cloned types + for (auto *clonedType : clone->Types()) { + if (clonedType != nullptr) { + clonedType->SetParent(clone); + } + } + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsUnionType.h b/ets2panda/ir/ts/tsUnionType.h index 4e51d62068..a788ee5f6b 100644 --- a/ets2panda/ir/ts/tsUnionType.h +++ b/ets2panda/ir/ts/tsUnionType.h @@ -41,6 +41,8 @@ public: checker::VerifiedType Check([[maybe_unused]] checker::ETSChecker *checker) override; checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; + TSUnionType *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/ir/ts/tsVoidKeyword.cpp b/ets2panda/ir/ts/tsVoidKeyword.cpp index ff09cc56d8..1e78510240 100644 --- a/ets2panda/ir/ts/tsVoidKeyword.cpp +++ b/ets2panda/ir/ts/tsVoidKeyword.cpp @@ -72,4 +72,26 @@ checker::VerifiedType TSVoidKeyword::Check(checker::ETSChecker *checker) { return {this, checker->GetAnalyzer()->Check(this)}; } + +TSVoidKeyword *TSVoidKeyword::Clone(ArenaAllocator *allocator, AstNode *parent) +{ + auto *clone = allocator->New(allocator); + + if (parent != nullptr) { + clone->SetParent(parent); + } + + clone->SetRange(Range()); + + // Clone annotations if any + if (!Annotations().empty()) { + ArenaVector annotationUsages {allocator->Adapter()}; + for (auto *annotationUsage : Annotations()) { + annotationUsages.push_back(annotationUsage->Clone(allocator, clone)->AsAnnotationUsage()); + } + clone->SetAnnotations(std::move(annotationUsages)); + } + + return clone; +} } // namespace ark::es2panda::ir diff --git a/ets2panda/ir/ts/tsVoidKeyword.h b/ets2panda/ir/ts/tsVoidKeyword.h index afcffd999d..9a131fe57a 100644 --- a/ets2panda/ir/ts/tsVoidKeyword.h +++ b/ets2panda/ir/ts/tsVoidKeyword.h @@ -33,6 +33,8 @@ public: checker::Type *GetType([[maybe_unused]] checker::TSChecker *checker) override; checker::VerifiedType Check(checker::ETSChecker *checker) override; + TSVoidKeyword *Clone(ArenaAllocator *allocator, AstNode *parent) override; + void Accept(ASTVisitorT *v) override { v->Accept(this); diff --git a/ets2panda/test/ast/parser/ets/generic_type_alias_clone_fix.ets b/ets2panda/test/ast/parser/ets/generic_type_alias_clone_fix.ets new file mode 100644 index 0000000000..661d448ab5 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/generic_type_alias_clone_fix.ets @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 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. + */ + +type MyMap = { + kind: K +}; + +type RecordMap = { n: number }; + +let myMap: MyMap<"n"> = { kind: "n" }; + +/* Expected errors - these should NOT cause assertion failures or crashes: */ +/* @@? 16:37 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 16:41 Error SyntaxError: Invalid Type. */ +/* @@? 17:11 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 17:11 Error TypeError: Unresolved reference K */ +/* @@? 20:18 Error SyntaxError: Invalid Type. */ +/* @@? 20:23 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 20:23 Error TypeError: Type name 'number' used in the wrong context */ \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/type_node_clone_assertion_fix.ets b/ets2panda/test/ast/parser/ets/type_node_clone_assertion_fix.ets new file mode 100644 index 0000000000..d28120b38d --- /dev/null +++ b/ets2panda/test/ast/parser/ets/type_node_clone_assertion_fix.ets @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 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. + */ + +// Minimal test case for TypeNode::Clone() fix +// Previously caused: ASSERTION FAILED: clonedNode != typeAliasNode->TypeAnnotation() +// in ValidateGenericTypeAliasForClonedNode at validateHelpers.cpp:242 + +// This specific pattern triggered the assertion failure because TypeNode::Clone() +// was returning 'this' instead of creating a proper clone when TsType() was nullptr +type MyMap = { + kind: K +}; + +type RecordMap = { n: number }; + +// Expected compilation errors for unsupported TypeScript features in ETS: +/* @@? 22:37 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 22:41 Error SyntaxError: Invalid Type. */ +/* @@? 23:11 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 23:11 Error TypeError: Unresolved reference K */ +/* @@? 26:18 Error SyntaxError: Invalid Type. */ +/* @@? 26:23 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 26:23 Error TypeError: Type name 'number' used in the wrong context */ \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/typenode_clone_brokentype.ets b/ets2panda/test/ast/parser/ets/typenode_clone_brokentype.ets new file mode 100644 index 0000000000..c3712f9c1f --- /dev/null +++ b/ets2panda/test/ast/parser/ets/typenode_clone_brokentype.ets @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025 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. + */ + +// Focused test case for BrokenTypeNode::Clone() implementation +// This test specifically exercises the BrokenTypeNode cloning that was causing +// the "ASSERTION FAILED: clonedNode != typeAliasNode->TypeAnnotation()" crash + +// This creates BrokenTypeNode instances during parsing due to invalid TypeScript syntax +type BrokenType = { + kind: K +}; + +// Multiple instances to test different patterns +type AnotherBroken = T; +type YetAnother = U; + +// Nested broken types +type NestedBroken = { + nested: K, + value: UndefinedType[K] +}; + +// Function with broken parameter types +type BrokenFunc = (param: T) => void; + +// Test instantiation to trigger ValidateGenericTypeAliasForClonedNode +declare const broken1: BrokenType; +declare const broken2: AnotherBroken; + +/* Expected errors - these should NOT cause assertion failures or crashes: */ +/* @@? 21:33 Error TypeError: Cannot find type 'UndefinedMap'. */ +/* @@? 21:45 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 21:49 Error SyntaxError: Invalid Type. */ +/* @@? 22:11 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 22:11 Error TypeError: Unresolved reference K */ +/* @@? 26:36 Error TypeError: Cannot find type 'NonExistent'. */ +/* @@? 26:47 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 27:27 Error TypeError: Cannot find type 'InvalidInterface'. */ +/* @@? 30:52 Error SyntaxError: Invalid Type. */ +/* @@? 31:13 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 31:14 Error SyntaxError: Unexpected token ','. */ +/* @@? 32:12 Error SyntaxError: Class cannot be used as object. */ +/* @@? 32:12 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 32:12 Error TypeError: Indexed signatures are not allowed. Use arrays instead! */ +/* @@? 36:27 Error TypeError: Cannot find type 'UndefinedInterface'. */ +/* @@? 39:35 Error TypeError: Cannot find type 'any'. */ +/* @@? 40:38 Error TypeError: Cannot find type 'any'. */ \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/typenode_clone_comprehensive.ets b/ets2panda/test/ast/parser/ets/typenode_clone_comprehensive.ets new file mode 100644 index 0000000000..e3f3d7459d --- /dev/null +++ b/ets2panda/test/ast/parser/ets/typenode_clone_comprehensive.ets @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2025 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. + */ + +// Comprehensive test case for TypeNode::Clone() implementations +// This test covers all TypeNode subclasses that were missing Clone implementations +// and caused assertion failures in ValidateGenericTypeAliasForClonedNode + +// Test 1: TSTypeLiteral Clone +type ObjectType = { n: number, s: string, b: boolean }; + +// Test 2: TSNumberKeyword, TSStringKeyword, TSBooleanKeyword Clone +type NumType = number; +type StrType = string; +type BoolType = boolean; + +// Test 3: TSVoidKeyword Clone (in function types) +type VoidFunc = () => void; + +// Test 4: TSTypeOperator Clone (keyof operations) +type KeyofType = T; + +// Test 5: TSIndexedAccessType Clone +type IndexedType = ObjectType[K]; + +// Test 6: TSTupleType Clone +type TupleType = [a: number, b: string]; + +// Test 7: TSMappedType Clone +type MappedType = { [K in keyof ObjectType]: string }; + +// Test 8: TSFunctionType Clone +type FuncType = (v: ObjectType[K]) => void; + +// Test 9: TSLiteralType Clone (string literals) +type LiteralType = K; + +// Test 10: TSUnionType Clone +type UnionType = string | number | boolean; + +// Test 11: BrokenTypeNode Clone (created for invalid syntax) +// This will create BrokenTypeNode instances that need proper cloning +type InvalidType = K; + +// Test 12: Complex nested type with multiple Clone implementations +type ComplexType = { + kind: K, + value: ObjectType[K], + handler: (v: ObjectType[K]) => void, + tuple: [K, ObjectType[K]], + mapped: { [P in K]: ObjectType[P] } +}; + +// Test instantiation to trigger ValidateGenericTypeAliasForClonedNode +declare const test1: ComplexType<'n'>; +declare const test2: ComplexType<'s'>; +declare const test3: ComplexType<'b'>; + +/* Expected errors (these are normal for ETS, but should not crash): */ +/* @@? 1:3 Error TypeError: Class 'UnionType' is already defined with different type. */ +/* @@? 1:3 Error TypeError: Class 'TupleType' is already defined with different type. */ +/* @@? 21:19 Error SyntaxError: Invalid Type. */ +/* @@? 21:24 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 21:24 Error TypeError: Type name 'number' used in the wrong context */ +/* @@? 21:30 Error SyntaxError: Unexpected token ','. */ +/* @@? 21:32 Error SyntaxError: Unexpected token 's'. */ +/* @@? 21:35 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 21:35 Error TypeError: Type name 'string' used in the wrong context */ +/* @@? 21:41 Error SyntaxError: Unexpected token ','. */ +/* @@? 21:43 Error SyntaxError: Unexpected token 'b'. */ +/* @@? 21:46 Error SyntaxError: Unexpected token 'boolean'. */ +/* @@? 21:46 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 32:42 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 35:44 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 35:59 Error SyntaxError: Unexpected token ']'. */ +/* @@? 35:59 Error SyntaxError: Unexpected token, expected ']'. */ +/* @@? 35:60 Error SyntaxError: Unexpected token ']'. */ +/* @@? 38:19 Error TypeError: Cannot find type 'a'. */ +/* @@? 38:20 Error SyntaxError: Unexpected token, expected ',' or ']'. */ +/* @@? 38:20 Error SyntaxError: Unexpected token ':'. */ +/* @@? 38:22 Error SyntaxError: Unexpected token 'number'. */ +/* @@? 38:22 Error TypeError: Type name 'number' used in the wrong context */ +/* @@? 38:28 Error SyntaxError: Unexpected token ','. */ +/* @@? 38:30 Error SyntaxError: Unexpected token 'b'. */ +/* @@? 38:33 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 38:33 Error TypeError: Type name 'string' used in the wrong context */ +/* @@? 38:39 Error SyntaxError: Unexpected token ']'. */ +/* @@? 41:19 Error SyntaxError: Invalid Type. */ +/* @@? 41:22 Error TypeError: Unresolved reference K */ +/* @@? 41:24 Error SyntaxError: Unexpected token, expected ',' or ']'. */ +/* @@? 41:24 Error SyntaxError: Unexpected token 'in'. */ +/* @@? 41:24 Error TypeError: Unresolved reference in */ +/* @@? 41:27 Error SyntaxError: Unexpected token 'keyof'. */ +/* @@? 41:27 Error TypeError: Unresolved reference keyof */ +/* @@? 41:33 Error SyntaxError: Unexpected token 'ObjectType'. */ +/* @@? 41:33 Error TypeError: Type name 'ObjectType' used in the wrong context */ +/* @@? 41:43 Error SyntaxError: Unexpected token ']'. */ +/* @@? 41:44 Error SyntaxError: Unexpected token ':'. */ +/* @@? 41:46 Error SyntaxError: Unexpected token 'string'. */ +/* @@? 41:46 Error TypeError: Type name 'string' used in the wrong context */ +/* @@? 44:41 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 44:60 Error SyntaxError: Unexpected token, expected ']'. */ +/* @@? 54:34 Error TypeError: Cannot find type 'InvalidMap'. */ +/* @@? 54:44 Error TypeError: The `keyof` keyword can only be used for class or interface type. */ +/* @@? 57:67 Error SyntaxError: Invalid Type. */ +/* @@? 58:11 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 58:12 Error SyntaxError: Unexpected token ','. */ +/* @@? 59:12 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 59:12 Error TypeError: Indexed access is not supported for such expression type. */ +/* @@? 59:12 Error TypeError: Type name 'ObjectType' used in the wrong context */ +/* @@? 59:25 Error SyntaxError: Unexpected token ','. */ +/* @@? 60:14 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 60:29 Error SyntaxError: Unexpected token, expected ']'. */ +/* @@? 60:36 Error SyntaxError: Unexpected token 'void'. */ +/* @@? 60:40 Error SyntaxError: Unexpected token ','. */ +/* @@? 61:12 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 61:16 Error TypeError: Type name 'ObjectType' used in the wrong context */ +/* @@? 61:16 Error TypeError: Indexed access is not supported for such expression type. */ +/* @@? 61:30 Error SyntaxError: Unexpected token ','. */ +/* @@? 62:13 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 62:16 Error TypeError: Unresolved reference P */ +/* @@? 62:18 Error SyntaxError: Unexpected token, expected ',' or ']'. */ +/* @@? 62:18 Error SyntaxError: Unexpected token 'in'. */ +/* @@? 62:21 Error SyntaxError: Unexpected token 'K'. */ +/* @@? 62:22 Error SyntaxError: Unexpected token ']'. */ +/* @@? 62:23 Error SyntaxError: Unexpected token ':'. */ +/* @@? 62:25 Error SyntaxError: Unexpected token 'ObjectType'. */ +/* @@? 62:25 Error TypeError: Type name 'ObjectType' used in the wrong context */ +/* @@? 62:25 Error TypeError: Indexed access is not supported for such expression type. */ \ No newline at end of file diff --git a/ets2panda/test/ast/parser/ets/typenode_clone_primitives.ets b/ets2panda/test/ast/parser/ets/typenode_clone_primitives.ets new file mode 100644 index 0000000000..045aaf46b1 --- /dev/null +++ b/ets2panda/test/ast/parser/ets/typenode_clone_primitives.ets @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2025 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. + */ + +// Focused test case for primitive TypeNode Clone implementations +// Tests TSNumberKeyword, TSStringKeyword, TSBooleanKeyword, TSVoidKeyword cloning + +// Simple primitive type aliases that exercise Clone +type NumAlias = T; +type StrAlias = T; +type BoolAlias = T; +type VoidAlias = T; + +// Generic constraints using primitives +type NumericConstraint = T; +type StringConstraint = T; +type BooleanConstraint = T; + +// Function types with primitive returns (exercises TSVoidKeyword) +type VoidFunction = (param: T) => void; +type NumberFunction = (param: T) => number; +type StringFunction = (param: T) => string; +type BooleanFunction = (param: T) => boolean; + +// Complex types mixing primitives +type MixedType = { + numField: N, + strField: S, + boolField: B, + voidMethod: () => void +}; + +// Test instantiation to trigger ValidateGenericTypeAliasForClonedNode +declare const num: NumAlias<42>; +declare const str: StrAlias<"test">; +declare const bool: BoolAlias; + +/* Expected syntax errors (normal for ETS): */ +/* @@? 37:73 Error SyntaxError: Invalid Type. */ +/* @@? 38:15 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 38:15 Error TypeError: Unresolved reference N */ +/* @@? 38:16 Error SyntaxError: Unexpected token ','. */ +/* @@? 39:15 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 39:15 Error TypeError: Unresolved reference S */ +/* @@? 39:16 Error SyntaxError: Unexpected token ','. */ +/* @@? 40:16 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 40:16 Error TypeError: Unresolved reference B */ +/* @@? 40:17 Error SyntaxError: Unexpected token ','. */ +/* @@? 41:17 Error SyntaxError: Label must be followed by a loop statement. */ +/* @@? 41:23 Error SyntaxError: Unexpected token 'void'. */ +/* @@? 45:20 Error TypeError: Type alias declaration is generic, but no type parameters were provided */ +/* @@? 45:29 Error SyntaxError: Unexpected token, expected '>'. */ +/* @@? 45:29 Error SyntaxError: Unexpected token '>'. */ +/* @@? 45:29 Error SyntaxError: Invalid Type. */ +/* @@? 45:32 Error SyntaxError: Unexpected token ';'. */ +/* @@? 47:21 Error TypeError: Type alias declaration is generic, but no type parameters were provided */ +/* @@? 47:31 Error SyntaxError: Invalid Type. */ +/* @@? 47:31 Error SyntaxError: Unexpected token, expected '>'. */ +/* @@? 47:31 Error SyntaxError: Unexpected token '>'. */ +/* @@? 47:36 Error SyntaxError: Unexpected token ';'. */ \ No newline at end of file -- Gitee