From 4b2e06485c6bf0984da131c944875984233886dc Mon Sep 17 00:00:00 2001 From: yp9522 Date: Thu, 21 Aug 2025 16:19:11 +0800 Subject: [PATCH] The lsp supports the arkts hybrid scenario Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICU2ME Signed-off-by: yp9522 --- ets2panda/lsp/src/api.cpp | 60 ++++++++++ .../unit/lsp/get_node_expression_test.cpp | 91 +++++++++++++++ .../test/unit/lsp/get_node_infos_test.cpp | 53 ++++++++- .../lsp/get_node_script_function_test.cpp | 105 ++++++++++++++++++ 4 files changed, 308 insertions(+), 1 deletion(-) create mode 100755 ets2panda/test/unit/lsp/get_node_script_function_test.cpp diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index e354781aea..3c241354dd 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -508,6 +508,66 @@ std::vector GetNodeInfosByDefinitionData(es2panda_Context *context, si result.emplace_back(std::string(ident->Name()), ir::AstNodeType::CLASS_DEFINITION); } break; + case ir::AstNodeType::CLASS_DECLARATION: + if (auto ident = node->AsClassDeclaration()->Definition()->Ident()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::CLASS_DECLARATION); + } + break; + case ir::AstNodeType::SCRIPT_FUNCTION: + if (auto ident = node->AsScriptFunction()->Id()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::SCRIPT_FUNCTION); + } + break; + case ir::AstNodeType::FUNCTION_DECLARATION: + if (auto ident = node->AsFunctionDeclaration()->Function()->Id()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::FUNCTION_DECLARATION); + } + break; + case ir::AstNodeType::METHOD_DEFINITION: + if (auto ident = node->AsMethodDefinition()->Function()->Id()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::METHOD_DEFINITION); + } + break; + case ir::AstNodeType::TS_ENUM_DECLARATION: + if (auto ident = node->AsTSEnumDeclaration()->Key()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::TS_ENUM_DECLARATION); + } + break; + case ir::AstNodeType::TS_ENUM_MEMBER: + result.emplace_back(std::string(node->AsTSEnumMember()->Name()), ir::AstNodeType::TS_ENUM_MEMBER); + break; + case ir::AstNodeType::MEMBER_EXPRESSION: + if (auto ident = node->AsMemberExpression()->Property()) { + result.emplace_back(ident->ToString(), ir::AstNodeType::MEMBER_EXPRESSION); + } + break; + case ir::AstNodeType::CALL_EXPRESSION: + if (node->AsCallExpression()->Callee()->IsMemberExpression()) { + result.emplace_back( + node->AsCallExpression()->Callee()->AsMemberExpression()->Property()->ToString(), + ir::AstNodeType::CALL_EXPRESSION); + } + if (node->AsCallExpression()->Callee()->IsIdentifier()) { + result.emplace_back(std::string(node->AsCallExpression()->Callee()->AsIdentifier()->Name()), + ir::AstNodeType::CALL_EXPRESSION); + } + if (node->AsCallExpression()->Callee()->IsSuperExpression()) { + result.emplace_back("super", ir::AstNodeType::CALL_EXPRESSION); + } + break; + case ir::AstNodeType::SUPER_EXPRESSION: + result.emplace_back("super", ir::AstNodeType::SUPER_EXPRESSION); + break; + case ir::AstNodeType::IMPORT_SPECIFIER: + if (auto ident = node->AsImportSpecifier()->Imported()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::IMPORT_SPECIFIER); + } + break; + case ir::AstNodeType::ANNOTATION_DECLARATION: + if (auto ident = node->AsAnnotationDeclaration()->GetBaseName()) { + result.emplace_back(std::string(ident->Name()), ir::AstNodeType::ANNOTATION_DECLARATION); + } + break; default: break; } diff --git a/ets2panda/test/unit/lsp/get_node_expression_test.cpp b/ets2panda/test/unit/lsp/get_node_expression_test.cpp index 3d4bed69a3..4d934e64d6 100644 --- a/ets2panda/test/unit/lsp/get_node_expression_test.cpp +++ b/ets2panda/test/unit/lsp/get_node_expression_test.cpp @@ -40,6 +40,28 @@ let obj: Record = { }; let propName = "prop"; obj[propName]; + +function a() { + return "hello"; +} +a(); + +class Parent { + name: string; + + constructor(name: string) { + this.name = name; + } +} + +class Child extends Parent { + age: number; + + constructor(name: string, age: number) { + super(name); + this.age = age; + } +} )"; GenerateContexts(*initializer_); } @@ -102,4 +124,73 @@ TEST_F(LspGetNodeExpressionTests, GetMemberExpression_NotFound) std::string extractedText(sourceCode_.substr(res.start, res.length)); ASSERT_EQ(extractedText.find(nodeName), std::string::npos); } + +TEST_F(LspGetNodeExpressionTests, GetMemberExpressionInfo_Error) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t errorOffset = 450; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, errorOffset); + ASSERT_TRUE(result.empty()); +} + +TEST_F(LspGetNodeExpressionTests, GetMemberExpressionInfo_PROPERTY) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 53; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"bar", ark::es2panda::ir::AstNodeType::CALL_EXPRESSION}, + {"bar", ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}, + {"bar", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} + +TEST_F(LspGetNodeExpressionTests, GetMemberExpressionInfo_ELEMENT) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 145; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"propName", ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}, + {"propName", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 2); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} + +TEST_F(LspGetNodeExpressionTests, GetSpuerExpressionInfo) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 387; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"Child", ark::es2panda::ir::AstNodeType::CLASS_DECLARATION}, + {"Child", ark::es2panda::ir::AstNodeType::CLASS_DEFINITION}, + {"constructor", ark::es2panda::ir::AstNodeType::METHOD_DEFINITION}, + {"constructor", ark::es2panda::ir::AstNodeType::SCRIPT_FUNCTION}, + {"super", ark::es2panda::ir::AstNodeType::CALL_EXPRESSION}, + {"super", ark::es2panda::ir::AstNodeType::SUPER_EXPRESSION}}; + ASSERT_EQ(result.size(), 6); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} + +TEST_F(LspGetNodeExpressionTests, GetCallExpression_IdentifierInfo) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 194; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"a", ark::es2panda::ir::AstNodeType::CALL_EXPRESSION}, + {"a", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 2); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} } // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_infos_test.cpp b/ets2panda/test/unit/lsp/get_node_infos_test.cpp index 86478e2aef..7e3a8d9ad7 100644 --- a/ets2panda/test/unit/lsp/get_node_infos_test.cpp +++ b/ets2panda/test/unit/lsp/get_node_infos_test.cpp @@ -43,6 +43,12 @@ protected: contexts_ = initializer.CreateContext("LspGetNodeInfosTests.ets", ES2PANDA_STATE_CHECKED, R"('use static' declare class Foo { foo(): void; + bar() {} + enum Color { + Red, + Green, + Blue + }; })"); } // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) @@ -61,7 +67,7 @@ TEST_F(LspGetNodeInfosTests, GetNodeInfosTests1) TEST_F(LspGetNodeInfosTests, GetNodeInfosTests2) { LSPAPI const *lspApi = GetImpl(); - const size_t errorOffset = 52; + const size_t errorOffset = 150; auto result = lspApi->getNodeInfosByDefinitionData(contexts_, errorOffset); ASSERT_TRUE(result.empty()); } @@ -81,4 +87,49 @@ TEST_F(LspGetNodeInfosTests, GetNodeInfosTests3) } } +TEST_F(LspGetNodeInfosTests, GetMethodDefinitionInfo) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 54; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"Foo", ark::es2panda::ir::AstNodeType::CLASS_DEFINITION}, + {"bar", ark::es2panda::ir::AstNodeType::METHOD_DEFINITION}, + {"bar", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} + +TEST_F(LspGetNodeInfosTests, GetTsEnumDeclarationInfo) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 72; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"Foo", ark::es2panda::ir::AstNodeType::CLASS_DEFINITION}, + {"Color", ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION}, + {"Color", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} + +TEST_F(LspGetNodeInfosTests, GetTsEnumMemberInfo) +{ + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 101; + auto result = lspApi->getNodeInfosByDefinitionData(contexts_, offset); + std::vector expectedResult = {{"Foo", ark::es2panda::ir::AstNodeType::CLASS_DEFINITION}, + {"Color", ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION}, + {"Green", ark::es2panda::ir::AstNodeType::TS_ENUM_MEMBER}, + {"Green", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 4); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } +} } // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_script_function_test.cpp b/ets2panda/test/unit/lsp/get_node_script_function_test.cpp new file mode 100755 index 0000000000..14aec4c6ec --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_script_function_test.cpp @@ -0,0 +1,105 @@ +/** + * 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 "ir/astNode.h" +#include "lsp/include/api.h" +#include "lsp_api_test.h" +#include "public/es2panda_lib.h" +#include "public/public.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetNodeScriptFunctionTests : public LSPAPITests {}; + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunctionInfo_Simple_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +function test() {} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunctionInfo.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + size_t offset = 20; + auto result = lspApi->getNodeInfosByDefinitionData(contexts, offset); + ASSERT_TRUE(result.empty()); + + offset = 0; + result = lspApi->getNodeInfosByDefinitionData(contexts, offset); + + std::vector expectedResult = {{"test", ark::es2panda::ir::AstNodeType::FUNCTION_DECLARATION}, + {"test", ark::es2panda::ir::AstNodeType::SCRIPT_FUNCTION}, + {"test", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } + + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunctionInfo_Arrow_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +function add(a: number, b: number): number { return a + b; } +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunctionInfo.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 10; + auto result = lspApi->getNodeInfosByDefinitionData(contexts, offset); + + std::vector expectedResult = {{"add", ark::es2panda::ir::AstNodeType::FUNCTION_DECLARATION}, + {"add", ark::es2panda::ir::AstNodeType::SCRIPT_FUNCTION}, + {"add", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } + + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunctionInfo_Async_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +async function fetchData(): Promise { + return "data"; +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunctionInfo.ets", ES2PANDA_STATE_PARSED, sourceCode.c_str()); + LSPAPI const *lspApi = GetImpl(); + const size_t offset = 16; + auto result = lspApi->getNodeInfosByDefinitionData(contexts, offset); + + std::vector expectedResult = {{"fetchData", ark::es2panda::ir::AstNodeType::FUNCTION_DECLARATION}, + {"fetchData", ark::es2panda::ir::AstNodeType::SCRIPT_FUNCTION}, + {"fetchData", ark::es2panda::ir::AstNodeType::IDENTIFIER}}; + ASSERT_EQ(result.size(), 3); + for (size_t i = 0; i < result.size(); i++) { + ASSERT_EQ(result[i].name, expectedResult[i].name); + ASSERT_EQ(result[i].kind, expectedResult[i].kind); + } + + initializer.DestroyContext(contexts); +} +} // namespace \ No newline at end of file -- Gitee