diff --git a/ets2panda/checker/ETSAnalyzer.cpp b/ets2panda/checker/ETSAnalyzer.cpp index 74631ae9763e2b213ee071ef4526bb980238433b..937b599668e2abb3d2af80d11b886cd1d8ba01d1 100644 --- a/ets2panda/checker/ETSAnalyzer.cpp +++ b/ets2panda/checker/ETSAnalyzer.cpp @@ -111,7 +111,6 @@ checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const } ES2PANDA_ASSERT(st->Id()->Variable() != nullptr); - checker->CheckAnnotations(st->Annotations()); if (st->TypeAnnotation() != nullptr) { st->TypeAnnotation()->Check(checker); @@ -130,6 +129,9 @@ checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const propertyType = propertyType != nullptr ? propertyType : checker->GlobalTypeError(); st->SetTsType(propertyType); + if (st->Id()->Name().Is("c") || st->Id()->Name().Is("d")) { + std::cout << "xsx" << std::endl; + } if (st->IsDefinite() && st->TsType()->PossiblyETSNullish()) { checker->LogError(diagnostic::LATE_INITIALIZATION_FIELD_HAS_INVALID_TYPE, st->TypeAnnotation()->Start()); } diff --git a/ets2panda/compiler/lowering/util.cpp b/ets2panda/compiler/lowering/util.cpp index bed24872a8f353c670ed4adc26e21d6ad623a8e3..9e5628dd4046f1db76ebb6df6ee11c1871b98758 100644 --- a/ets2panda/compiler/lowering/util.cpp +++ b/ets2panda/compiler/lowering/util.cpp @@ -337,6 +337,52 @@ ir::AstNode *DeclarationFromIdentifier(const ir::Identifier *node) return decl->Node(); } +using UAlloc = util::NodeAllocator; +static ir::TypeNode *CreateTypeNodeFromETSObjectType(ThreadSafeArenaAllocator *allocator, + const checker::ETSObjectType *tsType) +{ + ir::TSTypeParameterInstantiation *typeParam = nullptr; + if (!tsType->TypeArguments().empty()) { + ArenaVector typeNodes(allocator->Adapter()); + for (auto const *tp : tsType->AsETSObjectType()->TypeArguments()) { + typeNodes.emplace_back(CreateTypeNodeFromTsType(allocator, tp)); + } + typeParam = UAlloc::ForceSetParent(allocator, std::move(typeNodes)); + } + + auto typeName = tsType->AsETSObjectType()->Name(); + auto typeId = UAlloc::ForceSetParent(allocator, typeName, allocator); + auto typeRefPart = + UAlloc::ForceSetParent(allocator, typeId, typeParam, nullptr, allocator); + return UAlloc::ForceSetParent(allocator, typeRefPart, allocator); +} + +static ir::TypeNode *CreateTypeNodeFromETSFunctionType(ThreadSafeArenaAllocator *allocator, + const checker::ETSFunctionType *tsType) +{ + + return nullptr; +} + +ir::TypeNode *CreateTypeNodeFromTsType(ThreadSafeArenaAllocator *allocator, const checker::Type *tsType) +{ + if (tsType == nullptr) { + std::cout << "[[xsx stub]]: null tsType input!" << std::endl; + return nullptr; + } + + if (tsType->IsETSObjectType()) { + return CreateTypeNodeFromETSObjectType(allocator, tsType->AsETSObjectType()); + } + + if (tsType->IsETSFunctionType()) { + return CreateTypeNodeFromETSFunctionType(allocator, tsType->AsETSFunctionType()); + } + + std::cout << "[[xsx stub]]: such a type is in implementation" << std::endl; + return nullptr; +} + // NOTE: used to get the license string from the input root node. util::StringView GetLicenseFromRootNode(const ir::AstNode *node) { diff --git a/ets2panda/compiler/lowering/util.h b/ets2panda/compiler/lowering/util.h index 4e84d9429881c81ff5cf01391ee6e9d0918ed193..02650fd5d731ee06470a3ff8e5a43decd44bc0f9 100644 --- a/ets2panda/compiler/lowering/util.h +++ b/ets2panda/compiler/lowering/util.h @@ -43,6 +43,7 @@ void Recheck(PhaseManager *phaseManager, varbinder::ETSBinder *varBinder, checke // NOTE: used to get the declaration from identifier in Plugin API and LSP ir::AstNode *DeclarationFromIdentifier(const ir::Identifier *node); +ir::TypeNode *CreateTypeNodeFromTsType(ThreadSafeArenaAllocator *allocator, const checker::Type *tsType); // NOTE: used to get the declaration name in Plugin API and LSP std::optional GetNameOfDeclaration(const ir::AstNode *node); // NOTE: used to get the license string from the input root node. diff --git a/ets2panda/public/es2panda_lib.cpp b/ets2panda/public/es2panda_lib.cpp index e8ec8a9a3f2064b513128cfb3a121bd5fba9b241..0e1b629631d09819fef7af75f4e0f4d8f9cedc48 100644 --- a/ets2panda/public/es2panda_lib.cpp +++ b/ets2panda/public/es2panda_lib.cpp @@ -1124,6 +1124,15 @@ extern "C" bool IsImportTypeKind([[maybe_unused]] es2panda_Context *context, es2 return false; } +extern "C" es2panda_AstNode *CreateTypeNodeFromTsType(es2panda_Context *context, es2panda_Type *tsType) +{ + auto *typeE2p = reinterpret_cast(tsType); + auto *ctx = reinterpret_cast(context); + auto *astNode = compiler::CreateTypeNodeFromTsType(ctx->allocator, typeE2p); + astNode->AddAstNodeFlags(ir::AstNodeFlags::NOCLEANUP); + return reinterpret_cast(astNode); +} + extern "C" char *GetLicenseFromRootNode(es2panda_Context *ctx, es2panda_AstNode *node) { auto E2pNode = reinterpret_cast(node); @@ -1404,6 +1413,7 @@ es2panda_Impl g_impl = { Es2pandaEnumToString, DeclarationFromIdentifier, IsImportTypeKind, + CreateTypeNodeFromTsType, JsdocStringFromDeclaration, GetLicenseFromRootNode, FirstDeclarationByNameFromNode, diff --git a/ets2panda/public/es2panda_lib.h b/ets2panda/public/es2panda_lib.h index 4e287ec06a770ea8c2d74f9971a720bf064dae4a..20f3bdce310ea1de198989d1b2a56a90f41a1775 100644 --- a/ets2panda/public/es2panda_lib.h +++ b/ets2panda/public/es2panda_lib.h @@ -257,6 +257,7 @@ struct CAPI_EXPORT es2panda_Impl { char *(*Es2pandaEnumToString)(es2panda_Context *ctx, Es2pandaEnum id); es2panda_AstNode *(*DeclarationFromIdentifier)(es2panda_Context *ctx, es2panda_AstNode *node); bool (*IsImportTypeKind)(es2panda_Context *ctx, es2panda_AstNode *node); + es2panda_AstNode *(*CreateTypeNodeFromTsType)(es2panda_Context *context, es2panda_Type *tsType); char *(*JsdocStringFromDeclaration)(es2panda_Context *ctx, es2panda_AstNode *node); char *(*GetLicenseFromRootNode)(es2panda_Context *ctx, es2panda_AstNode *node); es2panda_AstNode *(*FirstDeclarationByNameFromNode)(es2panda_Context *ctx, const es2panda_AstNode *node, diff --git a/ets2panda/public/es2panda_lib.idl.erb b/ets2panda/public/es2panda_lib.idl.erb index 88794e5282e81aec1a1b655f8cf25d2df30d4d17..adf09a44fff77f02bdfbd000120b0c2b89bd3688 100644 --- a/ets2panda/public/es2panda_lib.idl.erb +++ b/ets2panda/public/es2panda_lib.idl.erb @@ -218,6 +218,7 @@ interface es2panda_Impl { String Es2pandaEnumToString(es2panda_Context ctx, Es2pandaEnum id); ir.AstNode DeclarationFromIdentifier(es2panda_Context ctx, ir.Identifier node); boolean IsImportTypeKind(es2panda_Context ctx, ir.AstNode node); + ir.AstNode CreateTypeNodeFromTsType(es2panda_Context context, es2panda_Type tsType); String JsdocStringFromDeclaration(es2panda_Context ctx, ir.AstNode node); String GetLicenseFromRootNode(es2panda_Context ctx, ir.AstNode node); ir.AstNode FirstDeclarationByNameFromNode(es2panda_Context ctx, ir.AstNode node, String name); diff --git a/ets2panda/test/unit/plugin/CMakeLists.txt b/ets2panda/test/unit/plugin/CMakeLists.txt index 477c1a61e2c1530d3e2c1fbc77eafc36ad98a3b4..e376aadce19f512d49dbbb6c08cd6bc3ba22dc2d 100644 --- a/ets2panda/test/unit/plugin/CMakeLists.txt +++ b/ets2panda/test/unit/plugin/CMakeLists.txt @@ -107,6 +107,7 @@ set(PLUGIN_TESTS "plugin_proceed_to_state_log_diagnostic_with_suggestion compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "use_plugin_to_test_column_number compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_check_jsdoc compile.ets ${EXPECTED_MODE} cpp ${EXECUTABLE_PLUGIN}" + "plugin_proceed_to_state_check_recheck_create_opaque_type compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_check_recheck_trailinglambda compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_test_global_func_call_dump compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" "plugin_proceed_to_state_test_interface_duplicate_setter compile.ets ${COMPILE_MODE} cpp ${EXECUTABLE_PLUGIN}" diff --git a/ets2panda/test/unit/plugin/plugin_proceed_to_state_check_recheck_create_opaque_type.cpp b/ets2panda/test/unit/plugin/plugin_proceed_to_state_check_recheck_create_opaque_type.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a0889a93f2c8120784ce8098a532a24019af3c18 --- /dev/null +++ b/ets2panda/test/unit/plugin/plugin_proceed_to_state_check_recheck_create_opaque_type.cpp @@ -0,0 +1,152 @@ +/** + * 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. + */ + +#include +#include +#include +#include "public/es2panda_lib.h" +#include "util.h" + +// NOLINTBEGIN +static std::string source = R"( +class A {} + +namespace NS { + export class A {} + export namespace MS { + export class B {} + } +} + +function foo(): A { + return new A(); +} + +function goo(): string { + return ""; +} + +function zoo(): NS.MS.B { + return new NS.MS.B(); +} +let a: Array = new Array(); +let b = new Array(); + +function forEach(arr: ()=>Array) {} +forEach(() => {return new Array}) + +//let c: (p1: Number, p2: A) => A = (p1:Number, p2: A):A => {return new A()} +//let d = c +let c: (p1: T1, p2: T2) => void = (p1: T1, p2: T2): void => {} +let d: (p1: number, p2: A) => void = c +)"; + +static es2panda_Impl *impl = nullptr; +es2panda_Context *context = nullptr; +es2panda_AstNode *propA = nullptr; +es2panda_AstNode *propB = nullptr; +es2panda_AstNode *funcCall = nullptr; +es2panda_AstNode *lambdaExpression = nullptr; +es2panda_AstNode *scriptFuncOfLambda = nullptr; + +static void FindTargetAst(es2panda_AstNode *ast, [[maybe_unused]] void *ctx) +{ + if (!impl->IsClassProperty(ast)) { + return; + } + + auto id = impl->ClassElementId(context, ast); + if (id == nullptr) { + return; + } + + auto name = std::string(impl->IdentifierName(context, id)); + if (name == "a") { + propA = ast; + } + + if (name == "b") { + propB = ast; + } +} + +static void FindTargetFunctionCallAst(es2panda_AstNode *ast, [[maybe_unused]] void *ctx) +{ + if (!impl->IsCallExpression(ast)) { + return; + } + + auto callee = impl->CallExpressionCallee(context, ast); + if (callee == nullptr || !impl->IsIdentifier(callee)) { + return; + } + + auto name = std::string(impl->IdentifierName(context, callee)); + if (name != "forEach") { + return; + } + size_t len = 0; + funcCall = ast; + es2panda_AstNode **arguments = impl->CallExpressionArguments(context, funcCall, &len); + lambdaExpression = arguments[0]; +} + +static void FindAndSetTypeAnnotation(es2panda_AstNode *ast, [[maybe_unused]] void *ctx) +{ + impl->AstNodeForEach(ast, FindTargetAst, context); + impl->AstNodeForEach(ast, FindTargetFunctionCallAst, context); + ASSERT(propA != nullptr && propB != nullptr); + auto tsType = impl->TypedTsType(context, propA); + ASSERT(tsType != nullptr); + auto typeAnno = impl->CreateTypeNodeFromTsType(context, tsType); + impl->ClassPropertySetTypeAnnotation(context, propB, typeAnno); + + auto typeAnno2 = impl->CreateTypeNodeFromTsType(context, tsType); + scriptFuncOfLambda = impl->ArrowFunctionExpressionFunction(context, lambdaExpression); + impl->ScriptFunctionSetReturnTypeAnnotation(context, scriptFuncOfLambda, typeAnno2); +} + +int main(int argc, char **argv) +{ + if (argc < MIN_ARGC) { + return INVALID_ARGC_ERROR_CODE; + } + + if (GetImpl() == nullptr) { + return NULLPTR_IMPL_ERROR_CODE; + } + impl = GetImpl(); + + const char **args = const_cast(&(argv[1])); + auto config = impl->CreateConfig(argc - 1, args); + context = impl->CreateContextFromString(config, source.data(), argv[argc - 1]); + if (context == nullptr) { + return NULLPTR_CONTEXT_ERROR_CODE; + } + impl->ProceedToState(context, ES2PANDA_STATE_CHECKED); + CheckForErrors("CHECKED", context); + + auto *program = impl->ContextProgram(context); + es2panda_AstNode *programNode = impl->ProgramAst(context, program); + FindAndSetTypeAnnotation(programNode, context); + + impl->AstNodeRecheck(context, programNode); + CheckForErrors("RECHECKED", context); + + impl->DestroyConfig(config); + return 0; +} + +// NOLINTEND \ No newline at end of file