1 /**
2 * Copyright (c) 2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "lsp/include/signature_help_items.h"
17 #include "lsp/include/create_type_help_items.h"
18 #include "lsp_api_test.h"
19 #include <gtest/gtest.h>
20 #include "lsp/include/internal_api.h"
21 #include "test/unit/lsp/lsp_api_test.h"
22 #include "checker/types/signature.h"
23 #include "checker/checker.h"
24 #include "lexer/token/sourceLocation.h"
25 #include "public/es2panda_lib.h"
26 #include "ir/astNode.h"
27 #include "ir/expressions/functionExpression.h"
28 #include "ir/statements/functionDeclaration.h"
29 #include <iostream>
30 #include <optional>
31 #include <ostream>
32 #include <vector>
33 #include "lsp/include/signature_help.h"
34
35 namespace {
36
37 class LSPSignatureHelpItemsTests : public LSPAPITests {};
38
FindTokenOnLeftOfPosition(es2panda_Context * context,size_t position)39 ark::es2panda::ir::AstNode *FindTokenOnLeftOfPosition(es2panda_Context *context, size_t position)
40 {
41 auto const tokenAtPosition = ark::es2panda::lsp::GetTouchingToken(context, position, false);
42 if (tokenAtPosition->Start().index < position && tokenAtPosition->End().index > position) {
43 return tokenAtPosition;
44 }
45 const auto ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
46 return ark::es2panda::lsp::FindPrecedingToken(position, ctx->parserProgram->Ast(), ctx->allocator);
47 }
48
TEST_F(LSPSignatureHelpItemsTests,GetSignatureHelpItemsTest)49 TEST_F(LSPSignatureHelpItemsTests, GetSignatureHelpItemsTest)
50 {
51 const auto fileName = "getSignatureHelpItemsTest.ets";
52 const auto fileText = R"(
53 function test(a: number, b: string): void {
54 console.log(a);
55 }
56 test(1, "test");
57 )";
58 const size_t index0 = 0;
59 const size_t index1 = 1;
60 const size_t index2 = 2;
61 const size_t position = 10;
62 const size_t argumentIndex1 = 19;
63 const size_t argumentIndex2 = 30;
64 std::vector<std::string> files = {fileName};
65 std::vector<std::string> texts = {fileText};
66 auto filePaths = CreateTempFile(files, texts);
67 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
68 es2panda_Context *ctx =
69 initializer.CreateContext(files.at(index0).c_str(), ES2PANDA_STATE_CHECKED, texts.at(index0).c_str());
70 auto startingToken = FindTokenOnLeftOfPosition(ctx, position);
71 if (startingToken == nullptr) {
72 return;
73 }
74 std::vector<ark::es2panda::lsp::ArgumentListInfo> argumentInfo;
75 GetArgumentOrParameterListAndIndex(startingToken, argumentInfo);
76
77 EXPECT_EQ(argumentInfo.size(), index2);
78 EXPECT_EQ(argumentInfo[index0].GetArgumentIndex(), argumentIndex1);
79 EXPECT_EQ(argumentInfo[index1].GetArgumentIndex(), argumentIndex2);
80 initializer.DestroyContext(ctx);
81 }
TEST_F(LSPSignatureHelpItemsTests,GetResolvedSignatureForSignatureHelp)82 TEST_F(LSPSignatureHelpItemsTests, GetResolvedSignatureForSignatureHelp)
83 {
84 const auto fileName = "testSignature.ets";
85 const auto fileText = R"(
86 function add(x: number, y: number): number {
87 return x + y;
88 }
89 let result = add(1, 2);
90 )";
91 const size_t index0 = 0;
92 const size_t index2 = 3;
93 const size_t position = 77;
94 std::vector<std::string> files = {fileName};
95 std::vector<std::string> texts = {fileText};
96 auto filePaths = CreateTempFile(files, texts);
97
98 ark::es2panda::lsp::Initializer initializer;
99 es2panda_Context *ctx = initializer.CreateContext(files.at(index0).c_str(), ES2PANDA_STATE_CHECKED, fileText);
100
101 auto callToken = FindTokenOnLeftOfPosition(ctx, position);
102 const auto callExpr = callToken->Parent();
103 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
104 auto astNode = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
105 ASSERT_NE(callExpr, nullptr);
106 ASSERT_NE(callExpr, nullptr);
107 ASSERT_TRUE(callExpr->IsCallExpression());
108 std::vector<ark::es2panda::checker::Signature *> candidates;
109 auto *sig = ark::es2panda::lsp::GetResolvedSignatureForSignatureHelp(callExpr, astNode, candidates);
110
111 ASSERT_NE(sig, nullptr);
112
113 ASSERT_EQ(candidates.size(), index2);
114
115 initializer.DestroyContext(ctx);
116 }
TEST_F(LSPSignatureHelpItemsTests,GetCandidateOrTypeInfo)117 TEST_F(LSPSignatureHelpItemsTests, GetCandidateOrTypeInfo)
118 {
119 const auto fileName = "candidateOrTypeInfo.ets";
120 const auto fileText = R"(
121 function multiply(a: number, b: number): number {
122 return a * b;
123 }
124 let result = multiply(10, 20);
125 )";
126 const size_t index0 = 0;
127 const size_t position = 82;
128
129 std::vector<std::string> files = {fileName};
130 std::vector<std::string> texts = {fileText};
131 auto filePaths = CreateTempFile(files, texts);
132
133 ark::es2panda::lsp::Initializer initializer;
134 es2panda_Context *ctx = initializer.CreateContext(files.at(index0).c_str(), ES2PANDA_STATE_CHECKED, fileText);
135
136 auto callToken = ark::es2panda::lsp::FindTokenOnLeftOfPosition(ctx, position);
137 ASSERT_NE(callToken, nullptr);
138 const auto callee = callToken->Parent();
139 std::vector<ark::es2panda::lsp::ArgumentListInfo> argumentInfoVec;
140 ark::es2panda::lsp::GetArgumentOrParameterListAndIndex(callee, argumentInfoVec);
141 auto context = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
142 auto astNode = reinterpret_cast<ark::es2panda::ir::AstNode *>(context->parserProgram->Ast());
143
144 ASSERT_FALSE(argumentInfoVec.empty());
145 ark::es2panda::lsp::ArgumentListInfo info = argumentInfoVec[index0];
146 auto result = ark::es2panda::lsp::GetCandidateOrTypeInfo(info, astNode, false);
147 ASSERT_TRUE(result.has_value());
148 ASSERT_TRUE(std::holds_alternative<ark::es2panda::lsp::CandidateInfo>(*result));
149 auto candidateInfo = std::get<ark::es2panda::lsp::CandidateInfo>(*result);
150 EXPECT_EQ(candidateInfo.GetKind(), ark::es2panda::lsp::CandidateOrTypeKind::CANDIDATE);
151 EXPECT_FALSE(candidateInfo.GetSignatures().empty());
152 EXPECT_NE(candidateInfo.GetResolvedSignature(), nullptr);
153
154 initializer.DestroyContext(ctx);
155 }
156
TEST_F(LSPSignatureHelpItemsTests,CreateSignatureHelpItemTest)157 TEST_F(LSPSignatureHelpItemsTests, CreateSignatureHelpItemTest)
158 {
159 const auto fileName = "createSignatureHelpItemTest.ets";
160 const auto fileText = R"(
161 function testFunction<T, U>(param1: T, param2: U): number {
162 return 0;
163 }
164 testFunction<number, string>(130, "test");
165 )";
166 const size_t index0 = 0;
167 const size_t index1 = 1;
168 const size_t position = 83;
169 std::vector<std::string> files = {fileName};
170 std::vector<std::string> texts = {fileText};
171 auto filePaths = CreateTempFile(files, texts);
172 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
173 es2panda_Context *ctx = initializer.CreateContext(files.at(index0).c_str(), ES2PANDA_STATE_CHECKED, fileText);
174 auto callToken = FindTokenOnLeftOfPosition(ctx, position);
175 const auto callNode = callToken->Parent();
176 ASSERT_NE(callNode, nullptr);
177
178 ASSERT_TRUE(callNode->IsCallExpression());
179 ark::es2panda::ir::CallExpression *callExpr = nullptr;
180 if (callNode->IsCallExpression()) {
181 callExpr = callNode->AsCallExpression();
182 }
183 ark::es2panda::checker::Signature *signature = callExpr->Signature();
184 SignatureHelpItem item = ark::es2panda::lsp::CreateSignatureHelpItem(*signature);
185 bool found1 = false;
186 bool found2 = false;
187 EXPECT_FALSE(item.GetPrefixDisplayParts().empty());
188 for (const auto &displayPart : item.GetPrefixDisplayParts()) {
189 if (displayPart.GetText() == "<") {
190 found1 = true;
191 } else if (displayPart.GetText() == ">") {
192 found2 = true;
193 }
194 }
195 EXPECT_EQ(found1, true);
196 EXPECT_EQ(found2, true);
197 EXPECT_FALSE(item.GetSeparatorDisplayParts().empty());
198 EXPECT_EQ(item.GetSeparatorDisplayParts().front().GetText(), ",");
199 EXPECT_EQ(item.GetParameters().size(), 2U);
200 EXPECT_EQ(item.GetParameters()[index0].GetName(), "param1");
201 EXPECT_EQ(item.GetParameters()[index1].GetName(), "param2");
202 item.Clear();
203 initializer.DestroyContext(ctx);
204 }
205
TEST_F(LSPSignatureHelpItemsTests,CreateSignatureHelpItemParamsTest)206 TEST_F(LSPSignatureHelpItemsTests, CreateSignatureHelpItemParamsTest)
207 {
208 const auto fileName = "CreateSignatureHelpItemParamsTest.ets";
209 const auto fileText = R"(
210 function testFunction<T, U>(param1: T, param2: U): number {
211 return 0;
212 }
213 testFunction<number, string>(130, "test");
214 )";
215 const size_t index0 = 0;
216 const size_t position = 83;
217 const size_t position1 = 13;
218 std::vector<std::string> files = {fileName};
219 std::vector<std::string> texts = {fileText};
220 auto filePaths = CreateTempFile(files, texts);
221 ark::es2panda::lsp::Initializer initializer = ark::es2panda::lsp::Initializer();
222 es2panda_Context *ctx = initializer.CreateContext(files.at(index0).c_str(), ES2PANDA_STATE_CHECKED, fileText);
223 auto callToken = FindTokenOnLeftOfPosition(ctx, position);
224 const auto callNode = callToken->Parent();
225 ASSERT_NE(callNode, nullptr);
226 ASSERT_TRUE(callNode->IsCallExpression());
227 ark::es2panda::ir::CallExpression *callExpr = nullptr;
228 if (callNode->IsCallExpression()) {
229 callExpr = callNode->AsCallExpression();
230 }
231 auto funcNode = FindTokenOnLeftOfPosition(ctx, position1);
232 funcNode = funcNode->Parent();
233 ASSERT_NE(funcNode, nullptr);
234 ASSERT_TRUE(funcNode->IsMethodDefinition());
235 ark::es2panda::ir::MethodDefinition *funcDecl = nullptr;
236 if (funcNode->IsMethodDefinition()) {
237 funcDecl = funcNode->AsMethodDefinition();
238 }
239 std::vector<ark::es2panda::checker::Signature *> signatures;
240 signatures.push_back(callExpr->Signature());
241 signatures.push_back(funcDecl->Function()->Signature());
242 ark::es2panda::lsp::ArgumentListInfo argumentListInfo;
243 const size_t argumentCount = 2;
244 const size_t argumentIndex = 1;
245 argumentListInfo.SetArgumentCount(argumentCount);
246 argumentListInfo.SetArgumentIndex(argumentIndex);
247 auto signatureHelpItems =
248 ark::es2panda::lsp::CreateSignatureHelpItems(signatures, callExpr->Signature(), argumentListInfo);
249 EXPECT_EQ(signatureHelpItems.GetSelectedItemIndex(), index0);
250 signatureHelpItems.Clear();
251 initializer.DestroyContext(ctx);
252 }
253
254 } // namespace