From 7595d4b1de24fbb52d1015a886e91d3421d215b2 Mon Sep 17 00:00:00 2001 From: yp9522 Date: Tue, 5 Aug 2025 14:09:16 +0800 Subject: [PATCH] Lsp supports arkts mixed scene jumping Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICR7HR Signed-off-by: yp9522 --- ets2panda/bindings/native/src/lsp.cpp | 72 ++++++++++ .../src/common/Es2pandaNativeModule.ts | 104 ++++++++++++++ ets2panda/bindings/src/common/types.ts | 32 ++++- ets2panda/bindings/src/lsp/lsp_helper.ts | 45 +++++- ets2panda/lsp/include/api.h | 9 ++ ets2panda/lsp/include/get_node.h | 9 ++ ets2panda/lsp/src/api.cpp | 54 +++++++ ets2panda/lsp/src/get_node.cpp | 136 ++++++++++++++++++ ets2panda/test/unit/lsp/CMakeLists.txt | 11 ++ .../test/unit/lsp/get_node_interface_test.cpp | 114 +++++++++++++++ .../lsp/get_node_member_expression_test.cpp | 92 ++++++++++++ ets2panda/test/unit/lsp/get_node_test.cpp | 64 +++++++++ .../unit/lsp/get_node_type_namespace_test.cpp | 129 +++++++++++++++++ 13 files changed, 866 insertions(+), 5 deletions(-) create mode 100755 ets2panda/test/unit/lsp/get_node_interface_test.cpp create mode 100755 ets2panda/test/unit/lsp/get_node_member_expression_test.cpp create mode 100755 ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp diff --git a/ets2panda/bindings/native/src/lsp.cpp b/ets2panda/bindings/native/src/lsp.cpp index f4f29b2440..ceb4f19f7a 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1816,6 +1816,22 @@ KNativePointer impl_getClassDefinition(KNativePointer astNodePtr, KStringPtr &no } TS_INTEROP_2(getClassDefinition, KNativePointer, KNativePointer, KStringPtr) +KNativePointer impl_getClassProperty(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getClassProperty(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getClassProperty, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getProperty(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getProperty(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getProperty, KNativePointer, KNativePointer, KStringPtr) + KNativePointer impl_getIdentifier(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) { auto ast = reinterpret_cast(astNodePtr); @@ -1832,6 +1848,38 @@ KNativePointer impl_getProgramAst(KNativePointer contextPtr) } TS_INTEROP_1(getProgramAst, KNativePointer, KNativePointer) +KNativePointer impl_getMethodDefinition(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getMethodDefinition(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getMethodDefinition, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getTsEnumDeclaration(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTsEnumDeclaration(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getTsEnumDeclaration, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getTsEnumMember(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTsEnumMember(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getTsEnumMember, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getMemberExpression(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getMemberExpression(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getMemberExpression, KNativePointer, KNativePointer, KStringPtr) + KNativePointer impl_getDefinitionDataFromNode(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) { auto ast = reinterpret_cast(astNodePtr); @@ -1860,3 +1908,27 @@ KNativePointer impl_getColAndLineByOffset(KStringPtr &sourceCodePtr, KInt offset return new std::pair(impl->getColAndLineByOffset(sourceCodePtr.data(), offset)); } TS_INTEROP_2(getColAndLineByOffset, KNativePointer, KStringPtr, KInt) + +KNativePointer impl_getTsInterfaceDeclaration(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTsInterfaceDeclaration(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getTsInterfaceDeclaration, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getTsModuleDeclaration(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTsModuleDeclaration(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getTsModuleDeclaration, KNativePointer, KNativePointer, KStringPtr) + +KNativePointer impl_getTsTypeAliasDeclaration(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +{ + auto ast = reinterpret_cast(astNodePtr); + LSPAPI const *impl = GetImpl(); + return impl->getTsTypeAliasDeclaration(ast, nodeNamePtr.data()); +} +TS_INTEROP_2(getTsTypeAliasDeclaration, KNativePointer, KNativePointer, KStringPtr) diff --git a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts index e667da211d..a455c4df31 100644 --- a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts @@ -1013,6 +1013,110 @@ export class Es2pandaNativeModule { _getIdentifier(astNode: KPtr, nodeName: String): KPtr { throw new Error('Not implemented'); } + + _getMemberExpression(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getCallExpression(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getNewExpression(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getSuperExpression(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsTypeReference(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getFunctionDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getVariableDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getVariableDeclarator(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getMethodDefinition(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getClassProperty(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getProperty(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getImportDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getImportSpecifier(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getImportDefaultSpecifier(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getImportNamespaceSpecifier(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getExportAllDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getExportDefaultDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getExportNamedDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getExportSpecifier(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsModuleDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsInterfaceDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsTypeAliasDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsEnumDeclaration(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsEnumMember(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsFunctionType(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } + + _getTsClassImplements(astNode: KPtr, nodeName: String): KPtr { + throw new Error('Not implemented'); + } } export function initEs2panda(): Es2pandaNativeModule { diff --git a/ets2panda/bindings/src/common/types.ts b/ets2panda/bindings/src/common/types.ts index 918cc29bd0..ed594efe8f 100644 --- a/ets2panda/bindings/src/common/types.ts +++ b/ets2panda/bindings/src/common/types.ts @@ -230,8 +230,38 @@ export interface TextDocumentChangeInfo { } export enum AstNodeType { + // 表达式 IDENTIFIER, - CLASS_DEFINITION + MEMBER_EXPRESSION, + CALL_EXPRESSION, + NEW_EXPRESSION, + SUPER_EXPRESSION, + TS_TYPE_REFERENCE, + // 语句 + CLASS_DEFINITION, + FUNCTION_DECLARATION, + VARIABLE_DECLARATION, + VARIABLE_DECLARATOR, + METHOD_DEFINITION, + CLASS_PROPERTY, + PROPERTY, + // 导入导出(import/export)待确认是否需要? + IMPORT_DECLARATION, + IMPORT_SPECIFIER, + IMPORT_DEFAULT_SPECIFIER, + IMPORT_NAMESPACE_SPECIFIER, + EXPORT_ALL_DECLARATION, + EXPORT_DEFAULT_DECLARATION, + EXPORT_NAMED_DECLARATION, + EXPORT_SPECIFIER, + TS_MODULE_DECLARATION, + // 类型定义 + TS_INTERFACE_DECLARATION, + TS_TYPE_ALIAS_DECLARATION, + TS_ENUM_DECLARATION, + TS_ENUM_MEMBER, + TS_FUNCTION_TYPE, + TS_CLASS_IMPLEMENTS, } export interface NodeInfo { name: string; diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts index c257ac60e0..f47acf7e31 100644 --- a/ets2panda/bindings/src/lsp/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -108,6 +108,7 @@ export class Lsp { private pathConfig: PathConfig; private lspDriverHelper = new LspDriverHelper(); private declFileMap: Record = {}; // Map + private typeAliasMap: Partial KPointer>> = {}; constructor(pathConfig: PathConfig, getContentCallback?: (filePath: string) => string, modules?: ModuleDescriptor[]) { initBuildEnv(); @@ -128,6 +129,8 @@ export class Lsp { this.pathConfig = pathConfig; PluginDriver.getInstance().initPlugins(Object.values(this.buildConfigs)[0]); this.initDeclFile(); + // 创建节点类型映射 + this.initTypeAliasMap(); } // Partially update for new file @@ -234,6 +237,39 @@ export class Lsp { } } + private initTypeAliasMap(): void { + this.typeAliasMap = { + [AstNodeType.IDENTIFIER]: global.es2panda._getIdentifier, + [AstNodeType.MEMBER_EXPRESSION]: global.es2panda._getMemberExpression, + [AstNodeType.CALL_EXPRESSION]: global.es2panda._getCallExpression, + [AstNodeType.NEW_EXPRESSION]: global.es2panda._getNewExpression, + [AstNodeType.SUPER_EXPRESSION]: global.es2panda._getSuperExpression, + [AstNodeType.TS_TYPE_REFERENCE]: global.es2panda._getTsTypeReference, + [AstNodeType.CLASS_DEFINITION]: global.es2panda._getClassDefinition, + [AstNodeType.FUNCTION_DECLARATION]: global.es2panda._getFunctionDeclaration, + [AstNodeType.VARIABLE_DECLARATION]: global.es2panda._getVariableDeclaration, + [AstNodeType.VARIABLE_DECLARATOR]: global.es2panda._getVariableDeclarator, + [AstNodeType.METHOD_DEFINITION]: global.es2panda._getMethodDefinition, + [AstNodeType.CLASS_PROPERTY]: global.es2panda._getClassProperty, + [AstNodeType.PROPERTY]: global.es2panda._getProperty, + [AstNodeType.IMPORT_DECLARATION]: global.es2panda._getImportDeclaration, + [AstNodeType.IMPORT_SPECIFIER]: global.es2panda._getImportSpecifier, + [AstNodeType.IMPORT_DEFAULT_SPECIFIER]: global.es2panda._getImportDefaultSpecifier, + [AstNodeType.IMPORT_NAMESPACE_SPECIFIER]: global.es2panda._getImportNamespaceSpecifier, + [AstNodeType.EXPORT_ALL_DECLARATION]: global.es2panda._getExportAllDeclaration, + [AstNodeType.EXPORT_DEFAULT_DECLARATION]: global.es2panda._getExportDefaultDeclaration, + [AstNodeType.EXPORT_NAMED_DECLARATION]: global.es2panda._getExportNamedDeclaration, + [AstNodeType.EXPORT_SPECIFIER]: global.es2panda._getExportSpecifier, + [AstNodeType.TS_MODULE_DECLARATION]: global.es2panda._getTsModuleDeclaration, + [AstNodeType.TS_INTERFACE_DECLARATION]: global.es2panda._getTsInterfaceDeclaration, + [AstNodeType.TS_TYPE_ALIAS_DECLARATION]: global.es2panda._getTsTypeAliasDeclaration, + [AstNodeType.TS_ENUM_DECLARATION]: global.es2panda._getTsEnumDeclaration, + [AstNodeType.TS_ENUM_MEMBER]: global.es2panda._getTsEnumMember, + [AstNodeType.TS_FUNCTION_TYPE]: global.es2panda._getTsFunctionType, + [AstNodeType.TS_CLASS_IMPLEMENTS]: global.es2panda._getTsClassImplements, + }; + } + updateDeclFile(filePath: string): void { if (!this.moduleInfos.hasOwnProperty(filePath)) { return; @@ -274,10 +310,11 @@ export class Lsp { try { nodeInfos.forEach((nodeInfo) => { currentNodeName = nodeInfo.name; - if (nodeInfo.kind === AstNodeType.CLASS_DEFINITION) { - astNode = global.es2panda._getClassDefinition(astNode, currentNodeName); - } else if (nodeInfo.kind === AstNodeType.IDENTIFIER) { - astNode = global.es2panda._getIdentifier(astNode, currentNodeName); + const handler = this.typeAliasMap[nodeInfo.kind]; + if (handler) { + astNode = handler(astNode, currentNodeName); + } else { + console.warn(`No handler found for node type: ${nodeInfo.kind}`); } }); } finally { diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 8feb3be682..448bbe8f24 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -555,7 +555,16 @@ typedef struct LSPAPI { TextSpan *(*GetNameOrDottedNameSpan)(es2panda_Context *context, int startPos); es2panda_AstNode *(*getProgramAst)(es2panda_Context *context); es2panda_AstNode *(*getClassDefinition)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getClassProperty)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getProperty)(es2panda_AstNode *astNode, const std::string &nodeName); es2panda_AstNode *(*getIdentifier)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getMethodDefinition)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getTsEnumDeclaration)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getTsEnumMember)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getTsInterfaceDeclaration)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getTsTypeAliasDeclaration)(es2panda_AstNode *astNode, const std::string &nodeType); + es2panda_AstNode *(*getTsModuleDeclaration)(es2panda_AstNode *astNode, const std::string &nodeName); + es2panda_AstNode *(*getMemberExpression)(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..e3d6a1a237 100644 --- a/ets2panda/lsp/include/get_node.h +++ b/ets2panda/lsp/include/get_node.h @@ -22,6 +22,15 @@ 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 *GetMethodDefinitionImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetTsEnumDeclarationImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetTsEnumMemberImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetTsInterfaceDeclarationImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetTsTypeAliasDeclarationImpl(es2panda_AstNode *astNode, const std::string &nodeType); +es2panda_AstNode *GetTsModuleDeclarationImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetMemberExpressionImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetClassPropertyImpl(es2panda_AstNode *astNode, const std::string &nodeName); +es2panda_AstNode *GetPropertyImpl(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 8f942b3daa..8b293afd8e 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -478,11 +478,56 @@ es2panda_AstNode *GetClassDefinition(es2panda_AstNode *astNode, const std::strin return GetClassDefinitionImpl(astNode, nodeName); } +es2panda_AstNode *GetClassProperty(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetClassPropertyImpl(astNode, nodeName); +} + +es2panda_AstNode *GetProperty(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetPropertyImpl(astNode, nodeName); +} + es2panda_AstNode *GetIdentifier(es2panda_AstNode *astNode, const std::string &nodeName) { return GetIdentifierImpl(astNode, nodeName); } +es2panda_AstNode *GetMethodDefinition(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetMethodDefinitionImpl(astNode, nodeName); +} + +es2panda_AstNode *GetTsEnumDeclaration(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetTsEnumDeclarationImpl(astNode, nodeName); +} + +es2panda_AstNode *GetTsEnumMember(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetTsEnumMemberImpl(astNode, nodeName); +} + +es2panda_AstNode *GetTsInterfaceDeclaration(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetTsInterfaceDeclarationImpl(astNode, nodeName); +} + +es2panda_AstNode *GetTsTypeAliasDeclaration(es2panda_AstNode *astNode, const std::string &nodeType) +{ + return GetTsTypeAliasDeclarationImpl(astNode, nodeType); +} + +es2panda_AstNode *GetTsModuleDeclaration(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetTsModuleDeclarationImpl(astNode, nodeName); +} + +es2panda_AstNode *GetMemberExpression(es2panda_AstNode *astNode, const std::string &nodeName) +{ + return GetMemberExpressionImpl(astNode, nodeName); +} + DefinitionInfo GetDefinitionDataFromNode(es2panda_AstNode *astNode, const std::string &nodeName) { DefinitionInfo result; @@ -555,7 +600,16 @@ LSPAPI g_lspImpl = {GetDefinitionAtPosition, GetNameOrDottedNameSpan, GetProgramAst, GetClassDefinition, + GetClassProperty, + GetProperty, GetIdentifier, + GetMethodDefinition, + GetTsEnumDeclaration, + GetTsEnumMember, + GetTsInterfaceDeclaration, + GetTsTypeAliasDeclaration, + GetTsModuleDeclaration, + GetMemberExpression, GetDefinitionDataFromNode}; } // namespace ark::es2panda::lsp diff --git a/ets2panda/lsp/src/get_node.cpp b/ets2panda/lsp/src/get_node.cpp index fa9ea5fea6..d00c70fc5d 100644 --- a/ets2panda/lsp/src/get_node.cpp +++ b/ets2panda/lsp/src/get_node.cpp @@ -40,6 +40,31 @@ es2panda_AstNode *GetClassDefinitionImpl(es2panda_AstNode *astNode, const std::s return reinterpret_cast(targetNode); } +es2panda_AstNode *GetClassPropertyImpl(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) { + return childNode->IsClassProperty() && std::string(childNode->AsClassProperty()->Id()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetPropertyImpl(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) { + return childNode->IsProperty() && + std::string(childNode->AsProperty()->Key()->AsIdentifier()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} + es2panda_AstNode *GetIdentifierImpl(es2panda_AstNode *astNode, const std::string &nodeName) { if (astNode == nullptr) { @@ -52,4 +77,115 @@ es2panda_AstNode *GetIdentifierImpl(es2panda_AstNode *astNode, const std::string return reinterpret_cast(targetNode); } +es2panda_AstNode *GetMethodDefinitionImpl(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) { + return childNode->IsMethodDefinition() && + std::string(childNode->AsMethodDefinition()->Function()->Id()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetTsEnumDeclarationImpl(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) { + return childNode->IsTSEnumDeclaration() && + std::string(childNode->AsTSEnumDeclaration()->Key()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetTsEnumMemberImpl(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) { + return childNode->IsTSEnumMember() && + std::string(childNode->AsTSEnumMember()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetTsInterfaceDeclarationImpl(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) { + return childNode->IsTSInterfaceDeclaration() && + std::string(childNode->AsTSInterfaceDeclaration()->Id()->Name()) == nodeName; + }); + + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetTsTypeAliasDeclarationImpl(es2panda_AstNode *astNode, const std::string &typeName) +{ + if (astNode == nullptr) { + return nullptr; + } + auto ast = reinterpret_cast(astNode); + auto targetNode = ast->FindChild([&typeName](ir::AstNode *childNode) { + return childNode->IsTSTypeAliasDeclaration() && + std::string(childNode->AsTSTypeAliasDeclaration()->Id()->Name()) == typeName; + }); + return reinterpret_cast(targetNode); +} + +es2panda_AstNode *GetTsModuleDeclarationImpl(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) { + return childNode->IsTSModuleDeclaration() && + std::string(childNode->AsTSModuleDeclaration()->AsIdentifier()->Name()) == nodeName; + }); + return reinterpret_cast(targetNode); +} + +bool findMenberExpression(ir::AstNode *childNode, const std::string &nodeName) +{ + if (childNode->IsMemberExpression()) { + auto memberExpr = childNode->AsMemberExpression(); + + std::string propertyName = memberExpr->Property()->ToString(); + if (propertyName != nodeName) { + return false; + } + if (memberExpr->HasMemberKind(ir::MemberExpressionKind::PROPERTY_ACCESS)) { + return true; + } + if (memberExpr->HasMemberKind(ir::MemberExpressionKind::ELEMENT_ACCESS)) { + return true; + } + return false; + } + return false; +} + +es2panda_AstNode *GetMemberExpressionImpl(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) { return findMenberExpression(childNode, 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 5792c19b5e..6561089c53 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -313,7 +313,18 @@ ets2panda_add_gtest(lsp_api_get_definition_from_node_test CPP_SOURCES get_definition_from_node_test.cpp ) +ets2panda_add_gtest(lsp_api_get_node_member_expression_test CPP_SOURCES + get_node_member_expression_test.cpp +) ets2panda_add_gtest(lsp_api_test_constructor_for_derived_need_super_call CPP_SOURCES constructor_for_derived_need_super_call_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_interface_test CPP_SOURCES + get_node_interface_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_type_namespace_test CPP_SOURCES + get_node_type_namespace_test.cpp ) \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_interface_test.cpp b/ets2panda/test/unit/lsp/get_node_interface_test.cpp new file mode 100755 index 0000000000..373dbe9327 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_interface_test.cpp @@ -0,0 +1,114 @@ +/** + * 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 LspGetNodeInterfaceTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeInterfaceTest.ets", ES2PANDA_STATE_CHECKED, R"(namespace a { + interface User { + bar(): void; + } + namespace b { + interface Client extends a.User {} + } +} +interface Worker extends a.User {})"); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationNonExistentTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "NonExistentInterface"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsInterfaceDeclaration(node, interfaceName); + ASSERT_TRUE(res == nullptr); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "User"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsInterfaceDeclaration(node, interfaceName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSInterfaceDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSInterfaceDeclaration()->Id()->Name(), + interfaceName.data()); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationExtendsTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "Client"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsInterfaceDeclaration(node, interfaceName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSInterfaceDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSInterfaceDeclaration()->Id()->Name(), + interfaceName.data()); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationExtendsTest1) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "Worker"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsInterfaceDeclaration(node, interfaceName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSInterfaceDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSInterfaceDeclaration()->Id()->Name(), + interfaceName.data()); +} +} // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_member_expression_test.cpp b/ets2panda/test/unit/lsp/get_node_member_expression_test.cpp new file mode 100755 index 0000000000..43ece39de3 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_member_expression_test.cpp @@ -0,0 +1,92 @@ +/** + * 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 LspGetNodeMemberExpressionTests : public LSPAPITests {}; + +TEST_F(LspGetNodeMemberExpressionTests, GetMemberExpression_PROPERTY_ACCESS) +{ + ark::es2panda::lsp::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->getMemberExpression(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsMemberExpression()); + ASSERT_EQ(reinterpret_cast(res)->AsMemberExpression()->Property()->ToString(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeMemberExpressionTests, GetMemberExpression_ELEMENT_ACCESS) +{ + ark::es2panda::lsp::Initializer initializer; + const std::string sourceCode = R"( +let obj: Record = { + prop: "value" +}; +let propName = "prop"; +obj[propName]; +)"; + 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 = "propName"; + auto res = lspApi->getMemberExpression(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsMemberExpression()); + ASSERT_EQ(reinterpret_cast(res)->AsMemberExpression()->Property()->ToString(), + nodeName.data()); + initializer.DestroyContext(contexts); +} + +TEST_F(LspGetNodeMemberExpressionTests, GetMemberExpression_NotFound) +{ + ark::es2panda::lsp::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 = "nonexistent"; + auto res = lspApi->getMemberExpression(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_test.cpp b/ets2panda/test/unit/lsp/get_node_test.cpp index 140f44ff22..cfddce5ec2 100644 --- a/ets2panda/test/unit/lsp/get_node_test.cpp +++ b/ets2panda/test/unit/lsp/get_node_test.cpp @@ -41,6 +41,12 @@ protected: { contexts_ = initializer.CreateContext("GetNodeTest.ets", ES2PANDA_STATE_CHECKED, R"(class Foo { Foo = 1; + bar() {} + enum Color { + Red, + Green, + Blue + }; })"); } // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) @@ -70,6 +76,29 @@ TEST_F(LspGetNodeTests, GetClassDefinition1) nodeName.data()); } +TEST_F(LspGetNodeTests, GetClassProperty1) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Foo"; + auto res = lspApi->getClassProperty(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsClassProperty()); + ASSERT_EQ(reinterpret_cast(res)->AsClassProperty()->Id()->Name(), nodeName.data()); +} + +TEST_F(LspGetNodeTests, GetProperty) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "prop"; + auto res = lspApi->getProperty(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsProperty()); + ASSERT_EQ(reinterpret_cast(res)->AsProperty()->Key()->AsIdentifier()->Name(), + nodeName.data()); +} + TEST_F(LspGetNodeTests, GetIdentifier1) { auto ctx = reinterpret_cast(contexts_); @@ -81,4 +110,39 @@ TEST_F(LspGetNodeTests, GetIdentifier1) ASSERT_EQ(reinterpret_cast(res)->AsIdentifier()->Name(), nodeName.data()); } +TEST_F(LspGetNodeTests, GetMethodDefinition1) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "bar"; + auto res = lspApi->getMethodDefinition(reinterpret_cast(ast), nodeName); + ASSERT_TRUE(reinterpret_cast(res)->IsMethodDefinition()); + ASSERT_EQ(reinterpret_cast(res)->AsMethodDefinition()->Function()->Id()->Name(), + nodeName.data()); +} + +TEST_F(LspGetNodeTests, GetTsEnumDeclaration) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string enumName = "Color"; + auto res = lspApi->getTsEnumDeclaration(reinterpret_cast(ast), enumName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSEnumDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSEnumDeclaration()->Key()->Name(), + enumName.data()); +} + +TEST_F(LspGetNodeTests, GetTsEnumMember) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Red"; + auto res = lspApi->getTsEnumMember(reinterpret_cast(ast), memberName); + ASSERT_TRUE(reinterpret_cast(res)->IsTSEnumMember()); + ASSERT_EQ(reinterpret_cast(res)->AsTSEnumMember()->Name(), memberName.data()); +} } // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp b/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp new file mode 100755 index 0000000000..2851cbf693 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp @@ -0,0 +1,129 @@ +/** + * 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 LspGetNodeTypNamespaceTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeInterfaceTest.ets", ES2PANDA_STATE_CHECKED, R"(type ID = string | number; +type Status = "active" | "inactive" | "pending"; +type List = T[]; +namespace Models { + namespace Utils { + type Formatter = (input: T) => string; + } +})"); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationNonExistentTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "NonExistentTypeDeclaration"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsTypeAliasDeclaration(node, nodeName); + ASSERT_TRUE(res == nullptr); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationIDTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "ID"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsTypeAliasDeclaration(node, nodeName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSTypeAliasDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSTypeAliasDeclaration()->Id()->Name(), + nodeName.data()); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationStatusTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Status"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsTypeAliasDeclaration(node, nodeName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSTypeAliasDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSTypeAliasDeclaration()->Id()->Name(), + nodeName.data()); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationGenericTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "List"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsTypeAliasDeclaration(node, nodeName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSTypeAliasDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSTypeAliasDeclaration()->Id()->Name(), + nodeName.data()); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationChildTest) +{ + auto ctx = reinterpret_cast(contexts_); + auto ast = ctx->parserProgram->Ast(); + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Formatter"; + auto node = reinterpret_cast(ast); + ASSERT_TRUE(node != nullptr); + auto res = lspApi->getTsTypeAliasDeclaration(node, nodeName); + ASSERT_TRUE(res != nullptr); + ASSERT_TRUE(reinterpret_cast(res)->IsTSTypeAliasDeclaration()); + ASSERT_EQ(reinterpret_cast(res)->AsTSTypeAliasDeclaration()->Id()->Name(), + nodeName.data()); +} + +} // namespace \ No newline at end of file -- Gitee