From bd3729588acca21dace1aaa1dd90ff67d6958767 Mon Sep 17 00:00:00 2001 From: yp9522 Date: Tue, 19 Aug 2025 09:39:17 +0800 Subject: [PATCH] Lsp supports the arkts hybrid scenario Issue: https://gitee.com/openharmony/arkcompiler_ets_frontend/issues/ICTJKL Signed-off-by: yp9522 --- ets2panda/bindings/native/src/lsp.cpp | 23 +- .../src/common/Es2pandaNativeModule.ts | 6 +- ets2panda/bindings/src/common/types.ts | 19 +- ets2panda/bindings/src/lsp/lspNode.ts | 4 +- ets2panda/bindings/src/lsp/lsp_helper.ts | 14 +- ets2panda/bindings/test/cases.ts | 118 ++++++- .../expected/getDefinitionAtPosition.json | 45 +++ .../getDefinitionAtPosition21.ets | 20 ++ .../getDefinitionAtPosition22.ets | 20 ++ .../getDefinitionAtPosition23.ets | 22 ++ .../getDefinitionAtPosition24.ets | 18 ++ .../getDefinitionAtPosition25.ets | 20 ++ .../getDefinitionAtPosition26.ets | 18 ++ .../getDefinitionAtPosition27.ets | 22 ++ .../getDefinitionAtPosition28.ets | 22 ++ .../getDefinitionAtPosition30.ets | 32 ++ .../getDefinitionAtPosition/index.ets | 18 ++ ets2panda/lsp/BUILD.gn | 1 + ets2panda/lsp/CMakeLists.txt | 1 + ets2panda/lsp/include/api.h | 2 +- ets2panda/lsp/include/node_matchers.h | 46 +++ ets2panda/lsp/src/api.cpp | 52 ++-- ets2panda/lsp/src/node_matchers.cpp | 219 +++++++++++++ ets2panda/test/unit/lsp/CMakeLists.txt | 22 +- .../lsp/get_definition_from_node_test.cpp | 16 +- .../test/unit/lsp/get_node_export_test.cpp | 84 +++++ .../unit/lsp/get_node_expression_test.cpp | 105 +++++++ .../test/unit/lsp/get_node_interface_test.cpp | 116 +++++++ ets2panda/test/unit/lsp/get_node_test.cpp | 287 +++++++++++++++++- .../lsp/get_node_ts_class_Implements_test.cpp | 202 ++++++++++++ .../unit/lsp/get_node_type_namespace_test.cpp | 130 ++++++++ 31 files changed, 1661 insertions(+), 63 deletions(-) create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets create mode 100644 ets2panda/bindings/test/testcases/getDefinitionAtPosition/index.ets create mode 100755 ets2panda/lsp/include/node_matchers.h create mode 100755 ets2panda/lsp/src/node_matchers.cpp create mode 100644 ets2panda/test/unit/lsp/get_node_export_test.cpp create mode 100644 ets2panda/test/unit/lsp/get_node_expression_test.cpp create mode 100644 ets2panda/test/unit/lsp/get_node_interface_test.cpp create mode 100644 ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp create mode 100644 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 fc95986732..9bca9f5a2c 100644 --- a/ets2panda/bindings/native/src/lsp.cpp +++ b/ets2panda/bindings/native/src/lsp.cpp @@ -1888,6 +1888,12 @@ KNativePointer impl_getNameByNodeInfo(KNativePointer nodeInfo) } TS_INTEROP_1(getNameByNodeInfo, KNativePointer, KNativePointer) +KNativePointer impl_CreateNodeInfoPtr(KStringPtr &nodeName, KInt nodeKind) +{ + return new NodeInfo(nodeName.Data(), ark::es2panda::ir::AstNodeType(nodeKind)); +} +TS_INTEROP_2(CreateNodeInfoPtr, KNativePointer, KStringPtr, KInt) + KNativePointer impl_getKindByNodeInfo(KNativePointer nodeInfo) { auto *info = reinterpret_cast(nodeInfo); @@ -1895,13 +1901,22 @@ KNativePointer impl_getKindByNodeInfo(KNativePointer nodeInfo) } TS_INTEROP_1(getKindByNodeInfo, KNativePointer, KNativePointer) -KNativePointer impl_getDefinitionDataFromNode(KNativePointer astNodePtr, KStringPtr &nodeNamePtr) +KNativePointer impl_getDefinitionDataFromNode(KNativePointer context, KStringArray pointerArrayPtr, KInt arraySize) { - auto ast = reinterpret_cast(astNodePtr); + auto pointerArray = ParsePointerArray(arraySize, pointerArrayPtr); + auto nodeInfos = std::vector {}; + nodeInfos.reserve(arraySize); + for (std::size_t i = 0; i < static_cast(arraySize); ++i) { + auto contextPtr = reinterpret_cast(pointerArray[i]); + if (contextPtr != nullptr) { + nodeInfos.push_back(contextPtr); + } + } + auto ctx = reinterpret_cast(context); LSPAPI const *impl = GetImpl(); - return new DefinitionInfo(impl->getDefinitionDataFromNode(ast, nodeNamePtr.Data())); + return new DefinitionInfo(impl->getDefinitionDataFromNode(ctx, nodeInfos)); } -TS_INTEROP_2(getDefinitionDataFromNode, KNativePointer, KNativePointer, KStringPtr) +TS_INTEROP_3(getDefinitionDataFromNode, KNativePointer, KNativePointer, KStringArray, KInt) KInt impl_getSourceLocationLine(KNativePointer locationPtr) { diff --git a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts index d6f4b53f4f..8695fd8e07 100644 --- a/ets2panda/bindings/src/common/Es2pandaNativeModule.ts +++ b/ets2panda/bindings/src/common/Es2pandaNativeModule.ts @@ -1026,6 +1026,10 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } + _CreateNodeInfoPtr(nodeName: String, nodeKind: KInt): KPtr { + throw new Error('Not implemented'); + } + _getProgramAst(context: KPtr): KPtr { throw new Error('Not implemented'); } @@ -1034,7 +1038,7 @@ export class Es2pandaNativeModule { throw new Error('Not implemented'); } - _getDefinitionDataFromNode(astNode: KPtr, nodeName: String): KPtr { + _getDefinitionDataFromNode(context: KPtr, nodeInfoPtrs: Uint8Array, arraySize: KInt): KPtr { throw new Error('Not implemented'); } diff --git a/ets2panda/bindings/src/common/types.ts b/ets2panda/bindings/src/common/types.ts index 501904a65a..fdfcd1d480 100644 --- a/ets2panda/bindings/src/common/types.ts +++ b/ets2panda/bindings/src/common/types.ts @@ -233,10 +233,25 @@ export interface TextDocumentChangeInfo { } export enum AstNodeType { + CLASS_DEFINITION = 14, + CLASS_PROPERTY = 17, + EXPORT_DEFAULT_DECLARATION = 27, + EXPORT_NAMED_DECLARATION = 28, + EXPORT_SPECIFIER = 29, + IDENTIFIER = 36, + MEMBER_EXPRESSION = 45, + METHOD_DEFINITION = 47, + PROPERTY = 56, + ETS_FUNCTION_TYPE = 69, + TS_ENUM_DECLARATION = 89, + TS_ENUM_MEMBER = 90, + TS_MODULE_DECLARATION = 125, + TS_TYPE_ALIAS_DECLARATION = 129, + TS_INTERFACE_DECLARATION = 133, + TS_CLASS_IMPLEMENTS = 141, UNKNOWN, - IDENTIFIER, - CLASS_DEFINITION } + export interface NodeInfo { name: string; kind: AstNodeType; diff --git a/ets2panda/bindings/src/lsp/lspNode.ts b/ets2panda/bindings/src/lsp/lspNode.ts index 80aedc2a74..32111b13d6 100644 --- a/ets2panda/bindings/src/lsp/lspNode.ts +++ b/ets2panda/bindings/src/lsp/lspNode.ts @@ -175,9 +175,9 @@ export class LspDiagsNode extends LspNode { } export class LspDefinitionData extends LspNode { - constructor(peer: KNativePointer) { + constructor(peer: KNativePointer, filePath?: string) { super(peer); - this.fileName = unpackString(global.es2panda._GetFileNameFromDef(peer)); + this.fileName = filePath ? filePath : unpackString(global.es2panda._GetFileNameFromDef(peer)); this.start = global.es2panda._GetStartFromDef(peer); this.length = global.es2panda._getLengthFromDef(peer); } diff --git a/ets2panda/bindings/src/lsp/lsp_helper.ts b/ets2panda/bindings/src/lsp/lsp_helper.ts index 5e6c3fba1b..e5fab55045 100644 --- a/ets2panda/bindings/src/lsp/lsp_helper.ts +++ b/ets2panda/bindings/src/lsp/lsp_helper.ts @@ -289,24 +289,18 @@ export class Lsp { private getDefinitionAtPositionByNodeInfos(declFilePath: String, nodeInfos: NodeInfo[]): LspDefinitionData { let ptr: KPointer; + let nodeInfoPtrs: KPointer[] = []; const sourceFilePath = this.declFileMap[declFilePath.valueOf()]; const [cfg, ctx] = this.createContext(sourceFilePath, false); - let astNode = global.es2panda._getProgramAst(ctx); - let currentNodeName: string = ''; 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); - } + nodeInfoPtrs.push(global.es2panda._CreateNodeInfoPtr(nodeInfo.name, nodeInfo.kind)); }); + ptr = global.es2panda._getDefinitionDataFromNode(ctx, passPointerArray(nodeInfoPtrs), nodeInfoPtrs.length); } finally { - ptr = global.es2panda._getDefinitionDataFromNode(astNode, currentNodeName); this.destroyContext(cfg, ctx); } - return new LspDefinitionData(ptr); + return new LspDefinitionData(ptr, sourceFilePath); } private getMergedCompileFiles(filename: String): string[] { diff --git a/ets2panda/bindings/test/cases.ts b/ets2panda/bindings/test/cases.ts index 3bd830dc25..3f733f48c0 100644 --- a/ets2panda/bindings/test/cases.ts +++ b/ets2panda/bindings/test/cases.ts @@ -62,7 +62,123 @@ export const basicCases: TestCases = { name: 'Foo' } ] as NodeInfo[] - ] + ], + '14': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition21.d.ets' + ), + 0, + [ + { + kind: AstNodeType.CLASS_DEFINITION, + name: 'Foo' + }, + { + kind: AstNodeType.METHOD_DEFINITION, + name: 'bar' + } + ] as NodeInfo[] + ], + '15': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition22.d.ets' + ), + 0, + [ + { + kind: AstNodeType.CLASS_DEFINITION, + name: 'Foo' + }, + { + kind: AstNodeType.CLASS_PROPERTY, + name: 'staticProperty' + } + ] as NodeInfo[] + ], + '16': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition23.d.ets' + ), + 0, + [ + { + kind: AstNodeType.MEMBER_EXPRESSION, + name: 'bar' + } + ] as NodeInfo[] + ], + '17': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition24.d.ets' + ), + 0, + [ + { + kind: AstNodeType.EXPORT_SPECIFIER, + name: 'PI' + } + ] as NodeInfo[] + ], + '18': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition25.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_INTERFACE_DECLARATION, + name: 'User' + } + ] as NodeInfo[] + ], + '19': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition26.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_TYPE_ALIAS_DECLARATION, + name: 'ID' + } + ] as NodeInfo[] + ], + '20': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition27.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_ENUM_DECLARATION, + name: 'Color' + } + ] as NodeInfo[] + ], + '21': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition28.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_ENUM_MEMBER, + name: 'Green' + } + ] as NodeInfo[] + ], + '22': [ + resolveTestPath( + 'test/testcases/.idea/.deveco/getDefinitionAtPosition/declgen/static/getDefinitionAtPosition30.d.ets' + ), + 0, + [ + { + kind: AstNodeType.TS_CLASS_IMPLEMENTS, + name: 'Printable' + } + ] as NodeInfo[] + ], }, getSemanticDiagnostics: { expectedFilePath: resolveTestPath('test/expected/getSemanticDiagnostics.json'), diff --git a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json index bf898a7beb..ace1dbc196 100644 --- a/ets2panda/bindings/test/expected/getDefinitionAtPosition.json +++ b/ets2panda/bindings/test/expected/getDefinitionAtPosition.json @@ -63,5 +63,50 @@ "fileName": "getDefinitionAtPosition20.ets", "start": 625, "length": 3 + }, + "14": { + "fileName": "getDefinitionAtPosition21.ets", + "start": 636, + "length": 3 + }, + "15": { + "fileName": "getDefinitionAtPosition22.ets", + "start": 643, + "length": 14 + }, + "16": { + "fileName": "getDefinitionAtPosition23.ets", + "start": 672, + "length": 3 + }, + "17": { + "fileName": "getDefinitionAtPosition24.ets", + "start": 629, + "length": 2 + }, + "18": { + "fileName": "getDefinitionAtPosition25.ets", + "start": 630, + "length": 4 + }, + "19": { + "fileName": "getDefinitionAtPosition26.ets", + "start": 625, + "length": 2 + }, + "20": { + "fileName": "getDefinitionAtPosition27.ets", + "start": 625, + "length": 5 + }, + "21": { + "fileName": "getDefinitionAtPosition28.ets", + "start": 642, + "length": 5 + }, + "22": { + "fileName": "getDefinitionAtPosition30.ets", + "start": 714, + "length": 9 } } diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets new file mode 100644 index 0000000000..aa771a995d --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition21.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +'use static'; + +class Foo { + bar() {} +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets new file mode 100644 index 0000000000..7133f4c681 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition22.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +'use static'; + +class Foo { + static staticProperty: number = 42; +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets new file mode 100644 index 0000000000..d935e42fd3 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition23.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +'use static'; + +class Foo { + bar() {} +} +let foo = new Foo(); +foo.bar(); diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets new file mode 100644 index 0000000000..6c3b998d16 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition24.ets @@ -0,0 +1,18 @@ +/* + * 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. + */ + +'use static'; + +export { PI } from "index.ets"; diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets new file mode 100644 index 0000000000..08edec23b4 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition25.ets @@ -0,0 +1,20 @@ +/* + * 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. + */ + +'use static'; + +interface User { + bar(): void; +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets new file mode 100644 index 0000000000..f81dfc2c6c --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition26.ets @@ -0,0 +1,18 @@ +/* + * 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. + */ + +'use static'; + +type ID = string | number; diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets new file mode 100644 index 0000000000..747a1cf485 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition27.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +'use static'; + +enum Color { + Red, + Green, + Blue +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets new file mode 100644 index 0000000000..747a1cf485 --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition28.ets @@ -0,0 +1,22 @@ +/* + * 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. + */ + +'use static'; + +enum Color { + Red, + Green, + Blue +} diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets new file mode 100644 index 0000000000..c6f842f27d --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/getDefinitionAtPosition30.ets @@ -0,0 +1,32 @@ +/* + * 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. + */ + +'use static'; + +interface Printable { + print(): void; + getTitle(): string; +} + +class Document implements Printable { + constructor() {} + + print(): void { + } + + getTitle(): string { + return ""; + } +} \ No newline at end of file diff --git a/ets2panda/bindings/test/testcases/getDefinitionAtPosition/index.ets b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/index.ets new file mode 100644 index 0000000000..467a1f191b --- /dev/null +++ b/ets2panda/bindings/test/testcases/getDefinitionAtPosition/index.ets @@ -0,0 +1,18 @@ +/* + * 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. + */ + +'use static'; + +const PI = 1; \ No newline at end of file diff --git a/ets2panda/lsp/BUILD.gn b/ets2panda/lsp/BUILD.gn index 601b93ad09..906d494c38 100644 --- a/ets2panda/lsp/BUILD.gn +++ b/ets2panda/lsp/BUILD.gn @@ -78,6 +78,7 @@ ohos_source_set("libes2panda_lsp_static") { "src/isolated_declaration.cpp", "src/line_column_offset.cpp", "src/navigate_to.cpp", + "src/node_matchers.cpp", "src/organize_imports.cpp", "src/quick_info.cpp", "src/refactor_provider.cpp", diff --git a/ets2panda/lsp/CMakeLists.txt b/ets2panda/lsp/CMakeLists.txt index e93fcc7acd..587f416137 100644 --- a/ets2panda/lsp/CMakeLists.txt +++ b/ets2panda/lsp/CMakeLists.txt @@ -135,6 +135,7 @@ set(ES2PANDA_LSP_SRC ./src/get_signature.cpp ./src/get_name_or_dotted_name_span.cpp ./src/get_node.cpp + ./src/node_matchers.cpp ) panda_frontend_add_library(${LSP_LIB} SHARED ${ES2PANDA_LSP_SRC}) diff --git a/ets2panda/lsp/include/api.h b/ets2panda/lsp/include/api.h index 8749f10b70..d4e723edff 100644 --- a/ets2panda/lsp/include/api.h +++ b/ets2panda/lsp/include/api.h @@ -567,7 +567,7 @@ 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); - DefinitionInfo (*getDefinitionDataFromNode)(es2panda_AstNode *astNode, const std::string &nodeName); + DefinitionInfo (*getDefinitionDataFromNode)(es2panda_Context *context, const std::vector nodeInfos); } LSPAPI; CAPI_EXPORT LSPAPI const *GetImpl(); // NOLINTEND diff --git a/ets2panda/lsp/include/node_matchers.h b/ets2panda/lsp/include/node_matchers.h new file mode 100755 index 0000000000..a57247b70c --- /dev/null +++ b/ets2panda/lsp/include/node_matchers.h @@ -0,0 +1,46 @@ +/** + * 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. + */ + +#ifndef NODE_MATCHERS_H +#define NODE_MATCHERS_H + +#include +#include +#include "ir/astNode.h" +#include "api.h" + +namespace ark::es2panda::lsp { +using NodeMatcher = std::function; +using NodeExtractor = ir::AstNode *(*)(ir::AstNode *, const NodeInfo *); + +bool MatchClassDefinition(ir::AstNode *childNode, const NodeInfo *info); +bool MatchIdentifier(ir::AstNode *childNode, const NodeInfo *info); +bool MatchClassProperty(ir::AstNode *childNode, const NodeInfo *info); +bool MatchProperty(ir::AstNode *childNode, const NodeInfo *info); +bool MatchMethodDefinition(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSEnumDeclaration(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSEnumMember(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSInterfaceDeclaration(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSTypeAliasDeclaration(ir::AstNode *childNode, const NodeInfo *info); +bool MatchExportSpecifier(ir::AstNode *childNode, const NodeInfo *info); +bool MatchMemberExpression(ir::AstNode *childNode, const NodeInfo *info); +bool MatchTSClassImplements(ir::AstNode *childNode, const NodeInfo *info); + +ir::AstNode *ExtractExportSpecifierIdentifier(ir::AstNode *node, const NodeInfo *info); +ir::AstNode *ExtractTSClassImplementsIdentifier(ir::AstNode *node, const NodeInfo *info); +ir::AstNode *ExtractIdentifierFromNode(ir::AstNode *node, const NodeInfo *info); +extern const std::unordered_map nodeMatchers; +} // namespace ark::es2panda::lsp +#endif // NODE_MATCHERS_H \ No newline at end of file diff --git a/ets2panda/lsp/src/api.cpp b/ets2panda/lsp/src/api.cpp index 155d760fac..1fd8be282c 100644 --- a/ets2panda/lsp/src/api.cpp +++ b/ets2panda/lsp/src/api.cpp @@ -42,6 +42,7 @@ #include "completions_details.h" #include "get_name_or_dotted_name_span.h" #include "get_signature.h" +#include "node_matchers.h" using ark::es2panda::lsp::details::GetCompletionEntryDetailsImpl; @@ -525,31 +526,44 @@ es2panda_AstNode *GetIdentifier(es2panda_AstNode *astNode, const std::string &no return GetIdentifierImpl(astNode, nodeName); } -DefinitionInfo GetDefinitionDataFromNode(es2panda_AstNode *astNode, const std::string &nodeName) +DefinitionInfo GetDefinitionDataFromNode(es2panda_Context *context, const std::vector nodeInfos) { - DefinitionInfo result; - if (astNode == nullptr) { + DefinitionInfo result {"", 0, 0}; + if (context == nullptr || nodeInfos.empty()) { return result; } - auto node = reinterpret_cast(astNode); - auto targetNode = node->IsIdentifier() ? node : node->FindChild([&nodeName](ir::AstNode *childNode) { - return childNode->IsIdentifier() && std::string(childNode->AsIdentifier()->Name()) == nodeName; - }); - std::string filePath; - while (node != nullptr) { - if (node->Range().start.Program() != nullptr) { - filePath = std::string(node->Range().start.Program()->SourceFile().GetAbsolutePath().Utf8()); - break; - } - if (node->IsETSModule()) { - filePath = std::string(node->AsETSModule()->Program()->SourceFilePath()); - break; + auto ctx = reinterpret_cast(context); + auto rootNode = reinterpret_cast(ctx->parserProgram->Ast()); + if (rootNode == nullptr) { + return result; + } + + ir::AstNode *lastFoundNode = nullptr; + NodeInfo *lastNodeInfo = nullptr; + for (auto info : nodeInfos) { + auto foundNode = rootNode->FindChild([info](ir::AstNode *childNode) -> bool { + auto it = nodeMatchers.find(info->kind); + if (it != nodeMatchers.end()) { + return it->second(childNode, info); + } + return false; + }); + if (foundNode == nullptr) { + return {"", 0, 0}; } - node = node->Parent(); + lastFoundNode = foundNode; + lastNodeInfo = info; } - if (targetNode != nullptr) { - result = {filePath, targetNode->Start().index, targetNode->End().index - targetNode->Start().index}; + + if (lastFoundNode != nullptr && lastNodeInfo != nullptr) { + ir::AstNode *identifierNode = ExtractIdentifierFromNode(lastFoundNode, lastNodeInfo); + if (identifierNode != nullptr) { + result = {"", identifierNode->Start().index, identifierNode->End().index - identifierNode->Start().index}; + } else { + result = {"", lastFoundNode->Start().index, lastFoundNode->End().index - lastFoundNode->Start().index}; + } } + return result; } diff --git a/ets2panda/lsp/src/node_matchers.cpp b/ets2panda/lsp/src/node_matchers.cpp new file mode 100755 index 0000000000..f50d62cd0e --- /dev/null +++ b/ets2panda/lsp/src/node_matchers.cpp @@ -0,0 +1,219 @@ +/* + * 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 "node_matchers.h" +#include +#include "public/es2panda_lib.h" +#include "public/public.h" +#include "ir/ets/etsReExportDeclaration.h" + +namespace ark::es2panda::lsp { + +bool MatchClassDefinition(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsClassDefinition() && std::string(childNode->AsClassDefinition()->Ident()->Name()) == info->name; +} + +bool MatchIdentifier(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsIdentifier() && std::string(childNode->AsIdentifier()->Name()) == info->name; +} + +bool MatchClassProperty(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsClassProperty() && std::string(childNode->AsClassProperty()->Id()->Name()) == info->name; +} + +bool MatchProperty(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsProperty() && std::string(childNode->AsProperty()->Key()->AsIdentifier()->Name()) == info->name; +} + +bool MatchMethodDefinition(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsMethodDefinition() && + std::string(childNode->AsMethodDefinition()->Function()->Id()->Name()) == info->name; +} + +bool MatchTSEnumDeclaration(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSEnumDeclaration() && + std::string(childNode->AsTSEnumDeclaration()->Key()->Name()) == info->name; +} + +bool MatchTSEnumMember(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSEnumMember() && std::string(childNode->AsTSEnumMember()->Name()) == info->name; +} + +bool MatchTSInterfaceDeclaration(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSInterfaceDeclaration() && + std::string(childNode->AsTSInterfaceDeclaration()->Id()->Name()) == info->name; +} + +bool MatchTSTypeAliasDeclaration(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsTSTypeAliasDeclaration() && + std::string(childNode->AsTSTypeAliasDeclaration()->Id()->Name()) == info->name; +} + +bool MatchExportSpecifier(ir::AstNode *childNode, const NodeInfo *info) +{ + if (!childNode->IsETSReExportDeclaration()) { + return false; + } + auto specifiers = childNode->AsETSReExportDeclaration()->GetETSImportDeclarations()->Specifiers(); + if (specifiers.empty()) { + return false; + } + for (auto *importSpecifier : specifiers) { + if (importSpecifier->IsImportSpecifier() && + importSpecifier->AsImportSpecifier()->Local()->Name().Mutf8() == info->name) { + return true; + } + } + return false; +} + +bool MatchMemberExpression(ir::AstNode *childNode, const NodeInfo *info) +{ + return childNode->IsMemberExpression() && childNode->AsMemberExpression()->Property()->ToString() == info->name; +} + +bool MatchTSClassImplements(ir::AstNode *childNode, const NodeInfo *info) +{ + if (!childNode->IsTSClassImplements()) { + return false; + } + auto dd = childNode->AsTSClassImplements()->Expr()->AsETSTypeReference()->Part(); + return std::string(dd->GetIdent()->Name()) == info->name; +} + +ir::AstNode *ExtractExportSpecifierIdentifier(ir::AstNode *node, const NodeInfo *info) +{ + if (!node->IsETSReExportDeclaration()) { + return node; + } + + auto specifiers = node->AsETSReExportDeclaration()->GetETSImportDeclarations()->Specifiers(); + if (specifiers.empty()) { + return node; + } + + for (auto *importSpecifier : specifiers) { + if (!importSpecifier->IsImportSpecifier()) { + continue; + } + + if (importSpecifier->AsImportSpecifier()->Local()->Name().Mutf8() == info->name) { + return importSpecifier->AsImportSpecifier()->Local(); + } + } + + return node; +} + +ir::AstNode *ExtractTSClassImplementsIdentifier(ir::AstNode *node, const NodeInfo *info) +{ + if (!node->IsTSClassImplements()) { + return node; + } + + auto expr = node->AsTSClassImplements()->Expr(); + if (!expr || !expr->IsETSTypeReference()) { + return node; + } + + auto part = expr->AsETSTypeReference()->Part(); + if (!part || !part->GetIdent()) { + return node; + } + + if (std::string(part->GetIdent()->Name()) == info->name) { + return part->GetIdent(); + } + + return node; +} + +const std::unordered_map nodeExtractors = { + {ir::AstNodeType::CLASS_DEFINITION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsClassDefinition() ? node->AsClassDefinition()->Ident() : node; + }}, + {ir::AstNodeType::IDENTIFIER, + [](ir::AstNode *node, const NodeInfo *) { return node->IsIdentifier() ? node->AsIdentifier() : node; }}, + {ir::AstNodeType::CLASS_PROPERTY, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsClassProperty() ? node->AsClassProperty()->Id() : node; + }}, + {ir::AstNodeType::PROPERTY, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsProperty() ? node->AsProperty()->Key()->AsIdentifier() : node; + }}, + {ir::AstNodeType::METHOD_DEFINITION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsMethodDefinition() ? node->AsMethodDefinition()->Function()->Id() : node; + }}, + {ir::AstNodeType::TS_ENUM_DECLARATION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsTSEnumDeclaration() ? node->AsTSEnumDeclaration()->Key() : node; + }}, + {ir::AstNodeType::TS_ENUM_MEMBER, + [](ir::AstNode *node, const NodeInfo *) { return node->IsTSEnumMember() ? node->AsTSEnumMember()->Key() : node; }}, + {ir::AstNodeType::TS_INTERFACE_DECLARATION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsTSInterfaceDeclaration() ? node->AsTSInterfaceDeclaration()->Id() : node; + }}, + {ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsTSTypeAliasDeclaration() ? node->AsTSTypeAliasDeclaration()->Id() : node; + }}, + {ir::AstNodeType::EXPORT_SPECIFIER, + [](ir::AstNode *node, const NodeInfo *info) { return ExtractExportSpecifierIdentifier(node, info); }}, + {ir::AstNodeType::MEMBER_EXPRESSION, + [](ir::AstNode *node, const NodeInfo *) { + return node->IsMemberExpression() ? node->AsMemberExpression()->Property()->AsIdentifier() : node; + }}, + {ir::AstNodeType::TS_CLASS_IMPLEMENTS, + [](ir::AstNode *node, const NodeInfo *info) { return ExtractTSClassImplementsIdentifier(node, info); }}}; + +ir::AstNode *ExtractIdentifierFromNode(ir::AstNode *node, const NodeInfo *info) +{ + if (!node) + return node; + + auto it = nodeExtractors.find(info->kind); + if (it != nodeExtractors.end()) { + return it->second(node, info); + } + return node; +} + +const std::unordered_map nodeMatchers = { + {ir::AstNodeType::CLASS_DEFINITION, MatchClassDefinition}, + {ir::AstNodeType::IDENTIFIER, MatchIdentifier}, + {ir::AstNodeType::CLASS_PROPERTY, MatchClassProperty}, + {ir::AstNodeType::PROPERTY, MatchProperty}, + {ir::AstNodeType::METHOD_DEFINITION, MatchMethodDefinition}, + {ir::AstNodeType::TS_ENUM_DECLARATION, MatchTSEnumDeclaration}, + {ir::AstNodeType::TS_ENUM_MEMBER, MatchTSEnumMember}, + {ir::AstNodeType::TS_INTERFACE_DECLARATION, MatchTSInterfaceDeclaration}, + {ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION, MatchTSTypeAliasDeclaration}, + {ir::AstNodeType::EXPORT_SPECIFIER, MatchExportSpecifier}, + {ir::AstNodeType::MEMBER_EXPRESSION, MatchMemberExpression}, + {ir::AstNodeType::TS_CLASS_IMPLEMENTS, MatchTSClassImplements}}; +} // 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 f76ffe2b87..681462e971 100644 --- a/ets2panda/test/unit/lsp/CMakeLists.txt +++ b/ets2panda/test/unit/lsp/CMakeLists.txt @@ -342,10 +342,30 @@ ets2panda_add_gtest(lsp_api_spelling_test CPP_SOURCES fix_spelling_test.cpp ) +ets2panda_add_gtest(lsp_api_get_node_expression_test CPP_SOURCES + get_node_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_test_add_name_to_nameless_parameter CPP_SOURCES add_name_to_nameless_parameter_test.cpp -) \ No newline at end of file +) + +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 +) + +ets2panda_add_gtest(lsp_api_get_node_export_test CPP_SOURCES + get_node_export_test.cpp +) + +ets2panda_add_gtest(lsp_api_get_node_ts_class_Implements_test CPP_SOURCES + get_node_ts_class_Implements_test.cpp +) diff --git a/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp b/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp index 82c2096871..8c07c30d86 100644 --- a/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp +++ b/ets2panda/test/unit/lsp/get_definition_from_node_test.cpp @@ -34,18 +34,18 @@ TEST_F(LspGetDefinitionFromNodeTest, GetDefinitionFromNode1) ASSERT_EQ(filePaths.size(), expectedFileCount); Initializer initializer = Initializer(); - auto context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_CHECKED); - - auto ctx = reinterpret_cast(context); - auto ast = ctx->parserProgram->Ast(); + auto context = initializer.CreateContext(filePaths[0].c_str(), ES2PANDA_STATE_PARSED); LSPAPI const *lspApi = GetImpl(); const std::string nodeName = "Foo"; - auto res = lspApi->getDefinitionDataFromNode(reinterpret_cast(ast), nodeName); + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(context, nodeInfoPtrs); initializer.DestroyContext(context); - std::string expectedFileName = filePaths[0]; - size_t const expectedStart = 6; + size_t const expectedStart = 16; size_t const expectedLength = 3; - ASSERT_EQ(res.fileName, expectedFileName); ASSERT_EQ(res.start, expectedStart); ASSERT_EQ(res.length, expectedLength); } diff --git a/ets2panda/test/unit/lsp/get_node_export_test.cpp b/ets2panda/test/unit/lsp/get_node_export_test.cpp new file mode 100644 index 0000000000..aa3fa2f63f --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_export_test.cpp @@ -0,0 +1,84 @@ +/** + * 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 "ir/ets/etsReExportDeclaration.h" +#include + +namespace { +using ark::es2panda::lsp::Initializer; + +class LspGetNodeExportTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"( +export { PI } from "std/math"; +export { E as CircleE } from "std/math"; +)"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeExportTest.ts", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeExportTests, GetExportSpecifierDeclarationTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string moduleName = "PI"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {moduleName, ark::es2panda::ir::AstNodeType::EXPORT_SPECIFIER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(moduleName), std::string::npos); +} + +TEST_F(LspGetNodeExportTests, GetExportSpecifierDeclarationAsNameTest1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string moduleName = "CircleE"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {moduleName, ark::es2panda::ir::AstNodeType::EXPORT_SPECIFIER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(moduleName), std::string::npos); +} +} // namespace \ 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 100644 index 0000000000..1f5d6a18da --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_expression_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 LspGetNodeExpressionTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"( +class Foo { + bar() {} +} +let foo = new Foo(); +foo.bar(); + +let obj: Record = { + prop: "value" +}; +let propName = "prop"; +obj[propName]; +)"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeExpresion.ts", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; +TEST_F(LspGetNodeExpressionTests, GetMemberExpression_PROPERTY_ACCESS) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "bar"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeExpressionTests, GetMemberExpression_ELEMENT_ACCESS) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "propName"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeExpressionTests, GetMemberExpression_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonexistent"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::MEMBER_EXPRESSION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} +} // namespace \ 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 100644 index 0000000000..3d066c747e --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_interface_test.cpp @@ -0,0 +1,116 @@ +/** + * 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(); + sourceCode_ = R"(export namespace a { + export interface User { + bar(): void; + } + export namespace b { + interface Client extends a.User {} + } +} +interface Worker extends a.User {})"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeInterfaceTest.ets", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationNonExistentTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "NonExistentInterface"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(interfaceName), std::string::npos); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "User"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(interfaceName), std::string::npos); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationExtendsTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "Client"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(interfaceName), std::string::npos); +} + +TEST_F(LspGetNodeInterfaceTests, GetTsInterfaceDeclarationExtendsTest1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string interfaceName = "Worker"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {interfaceName, ark::es2panda::ir::AstNodeType::TS_INTERFACE_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(interfaceName), std::string::npos); +} +} // 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..5b305f186f 100644 --- a/ets2panda/test/unit/lsp/get_node_test.cpp +++ b/ets2panda/test/unit/lsp/get_node_test.cpp @@ -28,6 +28,34 @@ protected: static void SetUpTestSuite() { initializer_ = new Initializer(); + sourceCode_ = R"(class Foo { + Foo = 1; + bar() {} + enum Color { + Red, + Green, + Blue + }; + + static staticProperty: number = 42; + optionalProperty?: boolean; + readonly readOnlyProperty: string = "read-only"; + protected protectedProperty: string = "protected"; +} + +const obj = { + prop: "value", + methodProp: function() { + return "method result"; + }, + arrowProp: () => { + console.log("arrow function property"); + }, + arrayProp: [1, 2, 3, 4, 5], +}; + +const myClassInstance = new Foo(); +})"; GenerateContexts(*initializer_); } @@ -36,16 +64,16 @@ protected: initializer_->DestroyContext(contexts_); delete initializer_; initializer_ = nullptr; + sourceCode_ = ""; } static void GenerateContexts(Initializer &initializer) { - contexts_ = initializer.CreateContext("GetNodeTest.ets", ES2PANDA_STATE_CHECKED, R"(class Foo { - Foo = 1; -})"); + contexts_ = initializer.CreateContext("GetNodeTest.ets", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); } // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) static inline es2panda_Context *contexts_ = nullptr; static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) }; @@ -60,25 +88,256 @@ TEST_F(LspGetNodeTests, GetProgramAst1) TEST_F(LspGetNodeTests, GetClassDefinition1) { - auto ctx = reinterpret_cast(contexts_); - auto ast = ctx->parserProgram->Ast(); LSPAPI const *lspApi = GetImpl(); const std::string nodeName = "Foo"; - auto res = lspApi->getClassDefinition(reinterpret_cast(ast), nodeName); - ASSERT_TRUE(reinterpret_cast(res)->IsClassDefinition()); - ASSERT_EQ(reinterpret_cast(res)->AsClassDefinition()->Ident()->Name(), - nodeName.data()); + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_DEFINITION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); } TEST_F(LspGetNodeTests, GetIdentifier1) { - auto ctx = reinterpret_cast(contexts_); - auto ast = ctx->parserProgram->Ast(); LSPAPI const *lspApi = GetImpl(); const std::string nodeName = "Foo"; - auto res = lspApi->getIdentifier(reinterpret_cast(ast), nodeName); - ASSERT_TRUE(reinterpret_cast(res)->IsIdentifier()); - ASSERT_EQ(reinterpret_cast(res)->AsIdentifier()->Name(), nodeName.data()); + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::IDENTIFIER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Foo"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty2) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "staticProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty3) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "optionalProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty4) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "readOnlyProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassProperty5) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "protectedProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetClassPropertyNotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonExistentProperty"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::CLASS_PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "prop"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty2) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "methodProp"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty3) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "arrowProp"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetProperty4) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "arrayProp"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::PROPERTY}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); } +TEST_F(LspGetNodeTests, GetMethodDefinition1) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "bar"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::METHOD_DEFINITION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetMethodDefinition_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "nonExistentMethod"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::METHOD_DEFINITION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumDeclaration) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string enumName = "Color"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {enumName, ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(enumName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumDeclaration_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string enumName = "nonExistentEnum"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {enumName, ark::es2panda::ir::AstNodeType::TS_ENUM_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(enumName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumMember) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Red"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_ENUM_MEMBER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetNodeTests, GetTsEnumMember_NotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "nonExistentEnumMember"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_ENUM_MEMBER}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(memberName), std::string::npos); +} } // namespace \ No newline at end of file diff --git a/ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp b/ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp new file mode 100644 index 0000000000..7b29548a1e --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_ts_class_Implements_test.cpp @@ -0,0 +1,202 @@ +/** + * 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 LspGetTsClassImplementsTests : public LSPAPITests { +protected: + static void SetUpTestSuite() + { + initializer_ = new Initializer(); + sourceCode_ = R"( +interface Printable { print(): void; getTitle(): string; } + +class Document implements Printable { + constructor() {} + print(): void {} + getTitle(): string { return ""; } +} +interface Drawable { draw(): void; } + +interface Serializable { serialize(): string; } + +class Circle implements Drawable, Serializable { + draw(): void {} + serialize(): string { return "{}"; } +} + +namespace Graphics { + interface Shape { area(): number; } + class Rectangle implements Shape { area(): number { return 0; } } +} + +class Base {} +interface Loggable { log(): void; } +class Derived extends Base implements Loggable { log(): void {} } + +interface Repository { save(item: T): void; } +class UserRepository implements Repository { save(item: User): void {} } +class User { + id: number; + name: string; + constructor(id: number, name: string) { this.id = id; this.name = name; } +} + +interface RealInterface { method(): void; } +type Alias = RealInterface; +class UsingAlias implements Alias { method(): void {} } + +interface MyInterface { method(): void; } +class MyClass implements MyInterface { method(): void {} } +)"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = + initializer.CreateContext("GetNodeTsClassImplements.ts", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetTsClassImplementsTests, GetTsClassImplements) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Printable"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetTsClassImplementsTests, MultipleInterfaces) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Drawable"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetTsClassImplementsTests, MultipleInterfaces2) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Serializable"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetTsClassImplementsTests, InterfaceNotFound) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "NonExistentInterface"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(memberName), std::string::npos); +} +TEST_F(LspGetTsClassImplementsTests, InNamespace) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Shape"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetTsClassImplementsTests, ExtendsAndImplements) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Loggable"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetTsClassImplementsTests, GenericInterface) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "Repository"; + + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} + +TEST_F(LspGetTsClassImplementsTests, CaseSensitivity) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string memberName = "MyInterface"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {memberName, ark::es2panda::ir::AstNodeType::TS_CLASS_IMPLEMENTS}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(memberName), std::string::npos); +} +} // 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 100644 index 0000000000..26d59e7f89 --- /dev/null +++ b/ets2panda/test/unit/lsp/get_node_type_namespace_test.cpp @@ -0,0 +1,130 @@ +/** + * 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(); + sourceCode_ = R"(type ID = string | number; +type Status = "active" | "inactive" | "pending"; +type List = T[]; +namespace Models { + namespace Utils { + type Formatter = (input: T) => string; + } +})"; + GenerateContexts(*initializer_); + } + + static void TearDownTestSuite() + { + initializer_->DestroyContext(contexts_); + delete initializer_; + initializer_ = nullptr; + sourceCode_ = ""; + } + static void GenerateContexts(Initializer &initializer) + { + contexts_ = initializer.CreateContext("GetNodeTypeNamespace.ets", ES2PANDA_STATE_PARSED, sourceCode_.c_str()); + } + // NOLINTBEGIN(fuchsia-statically-constructed-objects, cert-err58-cpp) + static inline es2panda_Context *contexts_ = nullptr; + static inline Initializer *initializer_ = nullptr; + static inline std::string sourceCode_ = ""; + // NOLINTEND(fuchsia-statically-constructed-objects, cert-err58-cpp) +}; + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationNonExistentTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "NonExistentTypeDeclaration"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_EQ(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationIDTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "ID"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationStatusTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Status"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + ASSERT_EQ(nodeInfoPtrs[0]->kind, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationGenericTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "List"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} + +TEST_F(LspGetNodeTypNamespaceTests, GetTsTypeDeclarationChildTest) +{ + LSPAPI const *lspApi = GetImpl(); + const std::string nodeName = "Formatter"; + std::vector nodeInfos; + nodeInfos.emplace_back(NodeInfo {nodeName, ark::es2panda::ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION}); + std::vector nodeInfoPtrs; + nodeInfoPtrs.push_back(&nodeInfos[0]); + + auto res = lspApi->getDefinitionDataFromNode(contexts_, nodeInfoPtrs); + std::string extractedText(sourceCode_.substr(res.start, res.length)); + ASSERT_NE(extractedText.find(nodeName), std::string::npos); +} +} // namespace \ No newline at end of file -- Gitee