• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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