From 8b7c6a120ad4d0a960e2411f00658e9f5d2bb5b1 Mon Sep 17 00:00:00 2001 From: yp9522 Date: Tue, 12 Aug 2025 14:14:38 +0800 Subject: [PATCH] Lsp supports arkts mixed scene jumping Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICSJCM Signed-off-by: yp9522 --- ets2panda/bindings/native/src/lsp.cpp | 32 +++ ets2panda/lsp/include/api.h | 4 + ets2panda/lsp/include/get_node.h | 3 + ets2panda/lsp/src/api.cpp | 24 ++ ets2panda/lsp/src/get_node.cpp | 63 +++++ ets2panda/test/unit/lsp/CMakeLists.txt | 13 +- .../unit/lsp/get_node_expression_test.cpp | 146 +++++++++++ .../lsp/get_node_script_function_test.cpp | 229 ++++++++++++++++ .../lsp/get_node_ts_type_reference_test.cpp | 246 ++++++++++++++++++ 9 files changed, 759 insertions(+), 1 deletion(-) create mode 100755 ets2panda/test/unit/lsp/get_node_expression_test.cpp create mode 100755 ets2panda/test/unit/lsp/get_node_script_function_test.cpp create mode 100755 ets2panda/test/unit/lsp/get_node_ts_type_reference_test.cpp diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index f1b1468bba..41bce657d8 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1859,6 +1859,38 @@ KNativePointer impl_getKindByNodeInfo(KNativePointer nodeInfo) } TS_INTEROP_1(getKindByNodeInfo, KNativePointer, KNativePointer) +KNativePointer impl_getCallExpression(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getCallExpression(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getCallExpression, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getSuperExpression(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getSuperExpression(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getSuperExpression, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getTSTypeReference(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTSTypeReference(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getTSTypeReference, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getScriptFunction(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getScriptFunction(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getScriptFunction, KNativePointer, KNativePointer, KStringPtr) + KNativePointer impl_getDefinitionDataFromNode(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) { auto ast = reinterpret_cast(astNodePtr); diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index ae40a5db5b..263a1bf8dc 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -564,6 +564,10 @@ typedef struct LSPAPI { std::vector (*getNodeInfosByDefinitionData)(es2panda_Context *context, size_t position); es2panda_AstNode *(*getClassDefinition)(es2panda_AstNode *astNode, const std::string &nodeName); es2panda_AstNode *(*getIdentifier)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getCallExpression)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getSuperExpression)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getTSTypeReference)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getScriptFunction)(es2panda_AstNode *astNode, const std::string &nodeName); DefinitionInfo (*getDefinitionDataFromNode)(es2panda_AstNode *astNode, const std::string &nodeName); } LSPAPI; CAPI_EXPORT LSPAPI const *GetImpl(); diff --git a/ets2panda/lsp/include/get_node.h b/ets2panda/lsp/include/get_node.h index b2c63afacb..eab6b9240e 100644 --- a/ets2panda/lsp/include/get_node.h +++ b/ets2panda/lsp/include/get_node.h @@ -22,6 +22,9 @@ namespace ark::es2panda::lsp { es2panda_AstNode *GetProgramAstImpl(es2panda_Context *context); es2panda_AstNode *GetClassDefinitionImpl(es2panda_AstNode *astNode, const std::string &nodeName); es2panda_AstNode *GetIdentifierImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetInvocationExpressionImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetTSTypeReferenceImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetScriptFunctionImpl(es2panda_AstNode *astNode, const std::string &nodeName); } // namespace ark::es2panda::lsp #endif \ No newline at end of file diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index 12f2d1905a..3c41a643ef 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -513,6 +513,26 @@ es2panda_AstNode *GetIdentifier(es2panda_AstNode *astNode, const std::string &no return GetIdentifierImpl(astNode, nodeName); } +es2panda_AstNode *GetCallExpression(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetInvocationExpressionImpl(astNode, nodeName); +} + +es2panda_AstNode *GetSpuerExpression(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetInvocationExpressionImpl(astNode, nodeName); +} + +es2panda_AstNode *GetTSTypeReference(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetTSTypeReferenceImpl(astNode, nodeName); +} + +es2panda_AstNode *GetScriptFunction(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetScriptFunctionImpl(astNode, nodeName); +} + DefinitionInfo GetDefinitionDataFromNode(es2panda_AstNode *astNode, const std::string &nodeName) { DefinitionInfo result; @@ -587,6 +607,10 @@ LSPAPI g_lspImpl = {GetDefinitionAtPosition, GetNodeInfosByDefinitionData, GetClassDefinition, GetIdentifier, + GetCallExpression, + GetSuperExpression, + GetTSTypeReference, + GetScriptFunction, GetDefinitionDataFromNode}; } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/src/get_node.cpp b/ets2panda/lsp/src/get_node.cpp index fa9ea5fea6..77d6dfaa75 100644 --- a/ets2panda/lsp/src/get_node.cpp +++ b/ets2panda/lsp/src/get_node.cpp @@ -52,4 +52,67 @@ es2panda_AstNode *GetIdentifierImpl(es2panda_AstNode *astNode, const std::string return reinterpret_cast(targetNode); } +es2panda_AstNode *GetInvocationExpressionImpl(es2panda_AstNode *astNode, const std::string &nodeName) +{ + if (astNode == nullptr) { + return nullptr; + } + auto ast = reinterpret_cast(astNode); + auto targetNode = ast->FindChild([&nodeName](ir::AstNode *childNode) { + if (childNode->IsCallExpression()) { + auto callExpr = childNode->AsCallExpression(); + auto callee = callExpr->Callee(); + + if (callee->IsIdentifier()) { + return std::string(callee->AsIdentifier()->Name()) == nodeName; + } + + if (callee->IsSuperExpression() && nodeName == "super") { + return true; + } + + if (callee->IsMemberExpression()) { + return callee->AsMemberExpression()->Property()->ToString() == nodeName; + } + } + return false; + }); + + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetTSTypeReferenceImpl(es2panda_AstNode *astNode, const std::string &nodeName) +{ + if (astNode == nullptr) { + return nullptr; + } + auto ast = reinterpret_cast(astNode); + auto targetNode = ast->FindChild([&nodeName](ir::AstNode *childNode) { + if (childNode->IsETSTypeReference()) { + auto typeRef = childNode->AsETSTypeReference(); + auto part = typeRef->Part(); + if (part != nullptr && part->Name()->IsIdentifier()) { + auto identifier = part->Name()->AsIdentifier(); + if (std::string(identifier->Name()) == nodeName) { + return true; + } + } + } + return false; + }); + + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetScriptFunctionImpl(es2panda_AstNode *astNode, const std::string &nodeName) +{ + if (astNode == nullptr) { + return nullptr; + } + auto ast = reinterpret_cast(astNode); + auto targetNode = ast->FindChild([&nodeName](ir::AstNode *childNode) -> bool { + return childNode->IsScriptFunction() && std::string(childNode->AsScriptFunction()->Id()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} } // namespace ark::es2panda::lsp \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/CMakeLists.txt b/ets2panda/test/unit/lsp/CMakeLists.txt index f17ee96897..0f561bee98 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -323,11 +323,22 @@ ets2panda_add_gtest(lsp_api_get_node_infos_test CPP_SOURCES get_node_infos_test.cpp ) +ets2panda_add_gtest(lsp_api_get_node_expression_test CPP_SOURCES + get_node_expression_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_ts_type_reference_test CPP_SOURCES + get_node_ts_type_reference_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_script_function_test CPP_SOURCES + get_node_script_function_test.cpp +) + ets2panda_add_gtest(lsp_api_get_definition_from_node_test CPP_SOURCES get_definition_from_node_test.cpp ) - ets2panda_add_gtest(lsp_api_test_constructor_for_derived_need_super_call CPP_SOURCES constructor_for_derived_need_super_call_test.cpp ) \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_expression_test.cpp b/ets2panda/test/unit/lsp/get_node_expression_test.cpp new file mode 100755 index 0000000000..ce1366ffb3 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_expression_test.cpp @@ -0,0 +1,146 @@ +/** + * 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 LspGetNodeExpressionTests : public LSPAPITests {}; +TEST_F(LspGetNodeExpressionTests, GetCallExpression_Identifier) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +function a() { + return "hello"; +} +a(); +)"; + es2panda_Context *contexts = + initializer.CreateContext("GetExpression.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "a"; + auto res = lspApi->getCallExpression(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeExpressionTests, GetCallExpression_SpuerExpression) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +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; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("GetExpression.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "super"; + auto res = lspApi->getCallExpression(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsCallExpression()); + ASSERT_TRUE(reinterpret_cast(res)->AsCallExpression()->Callee()->IsSuperExpression()); + + res = lspApi->getSuperExpression(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsCallExpression()); + ASSERT_TRUE(reinterpret_cast(res)->AsCallExpression()->Callee()->IsSuperExpression()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeExpressionTests, GetCallExpression_MemberExpression) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class Foo { + bar() {} +} +let foo = new Foo(); +foo.bar(); +)"; + es2panda_Context *contexts = + initializer.CreateContext("GetExpression.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "bar"; + auto res = lspApi->getCallExpression(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsCallExpression()); + ASSERT_TRUE( + reinterpret_cast(res)->AsCallExpression()->Callee()->IsMemberExpression()); + ASSERT_EQ(reinterpret_cast(res) + ->AsCallExpression() + ->Callee() + ->AsMemberExpression() + ->Property() + ->ToString(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeExpressionTests, GetCallExpression_NotFound) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +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; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("GetExpression.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonexistent"; + auto res = lspApi->getCallExpression(reinterpret_cast(ast), nodeName); + ASSERT_EQ(res, nullptr); + initializer.DestroyContext(contexts); +} +} // 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..7ce2710c38 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_script_function_test.cpp @@ -0,0 +1,229 @@ +/** + * 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, GetScriptFunction_Simple_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +function test(){} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "test"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_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("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "add"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_Async_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +async function fetchData(): Promise { + return "data"; +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "fetchData"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_TRUE(reinterpret_cast(res)->AsScriptFunction()->IsAsyncFunc()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_ClassMethod_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class MyClass { + myMethod(): void { + console.log("method"); + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "myMethod"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_Constructor_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class Person { + name: string; + constructor(name: string) { + this.name = name; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "constructor"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_TRUE(reinterpret_cast(res)->AsScriptFunction()->IsConstructor()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_Getter_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class MyClass { + get value(): number { + return 42; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "value"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_TRUE(reinterpret_cast(res)->AsScriptFunction()->IsGetter()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_Setter_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class MyClass { + private _value: number = 0; + + set value(v: number) { + this._value = v; + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "value"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_TRUE(reinterpret_cast(res)->AsScriptFunction()->IsSetter()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_StaticMethod_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class MyClass { + static staticMethod(): void { + console.log("static method"); + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "staticMethod"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsScriptFunction()); + ASSERT_EQ(reinterpret_cast(res)->AsScriptFunction()->Id()->Name(), nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeScriptFunctionTests, GetScriptFunction_NotFound_TEST) +{ + Initializer initializer = Initializer(); + const std::string sourceCode = R"( +class MyClass { + static staticMethod(): void { + console.log("static method"); + } +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("ScriptFunction.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonexistent"; + auto res = lspApi->getScriptFunction(reinterpret_cast(ast), nodeName); + ASSERT_EQ(res, nullptr); + initializer.DestroyContext(contexts); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_ts_type_reference_test.cpp b/ets2panda/test/unit/lsp/get_node_ts_type_reference_test.cpp new file mode 100755 index 0000000000..9d87d88403 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_ts_type_reference_test.cpp @@ -0,0 +1,246 @@ +/** + * 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 LspGetNodeTSTypeReferenceTests : public LSPAPITests {}; + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST1) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +interface Animal { + name: string; +} + +let animal: Animal = { name: "Generic Animal" }; +)"; + es2panda_Context *contexts = + initializer.CreateContext("GetExpression.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Animal"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST2) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +let name: string = "test"; +)"; + es2panda_Context *contexts = + initializer.CreateContext("GetExpression.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "string"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); +} + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST3) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +interface Animal { + name: string; +} + +interface Dog extends Animal { + breed: string; +} + +let dog: Dog = { name: "Buddy", breed: "Golden Retriever" }; +)"; + es2panda_Context *contexts = + initializer.CreateContext("InterfaceInheritance.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Dog"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST4) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +interface Animal { + name: string; +} + +let animals: Animal[] = [{ name: "Cat" }, { name: "Dog" }]; +)"; + es2panda_Context *contexts = + initializer.CreateContext("ArrayTypeReference.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Animal"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST5) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +interface Animal { + name: string; +} + +function feedAnimal(animal: Animal): void { + console.log("Feeding " + animal.name); +} +)"; + es2panda_Context *contexts = + initializer.CreateContext("FunctionParameter.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Animal"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST6) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +interface Animal { + name: T; +} + +let animal: Animal = { name: "Generic Animal" }; +)"; + es2panda_Context *contexts = + initializer.CreateContext("GenericType.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Animal"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeTSTypeReferenceTests, GetTSTypeReference_TEST7) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +interface Animal { + name: string; +} + +type Pet = Animal; + +let pet: Pet = { name: "My Pet" }; +)"; + es2panda_Context *contexts = initializer.CreateContext("TypeAlias.ets", ES2PANDA_STATE_CHECKED, sourceCode.c_str()); + auto ctx = reinterpret_cast(contexts); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Pet"; + auto res = lspApi->getTSTypeReference(reinterpret_cast(ast), nodeName); + ASSERT_NE(res, nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsETSTypeReference()); + ASSERT_TRUE( + reinterpret_cast(res)->AsETSTypeReference()->Part()->Name()->IsIdentifier()); + ASSERT_EQ(reinterpret_cast(res) + ->AsETSTypeReference() + ->Part() + ->Name() + ->AsIdentifier() + ->Name(), + nodeName.data()); + initializer.DestroyContext(contexts); +} +} // namespace \ No newline at end of file -- Gitee