• 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 #include <cstddef>
16 #include <string>
17 #include <vector>
18 #include <lexer/lexer.h>
19 #include <lexer/token/letters.h>
20 #include <lexer/token/sourceLocation.h>
21 #include "lsp_api_test.h"
22 #include "lsp/include/internal_api.h"
23 #include "lsp/include/inlay_hints.h"
24 #include "public/es2panda_lib.h"
25 
26 class LSPInlayHintsTests : public LSPAPITests {};
27 
TEST_F(LSPInlayHintsTests,VisitCallOrNewExpressionTest)28 TEST_F(LSPInlayHintsTests, VisitCallOrNewExpressionTest)
29 {
30     std::vector<std::string> files = {"call_file_test.ets"};
31 
32     std::vector<std::string> fileContent = {R"(
33         function foo(param1: number, param2: number) {
34         }
35 
36         function bar() {
37             foo(10, 20); // Call expression
38         }
39     )"};
40 
41     auto filePath = CreateTempFile(files, fileContent);
42 
43     using ark::es2panda::lsp::UserPreferences;
44     UserPreferences preferences = UserPreferences::GetDefaultUserPreferences();
45 
46     preferences.SetQuotePreference(UserPreferences::QuotePreference::DOUBLE);
47     preferences.SetIncludePackageJsonAutoImports(UserPreferences::IncludePackageJsonAutoImports::ON);
48     preferences.SetDisableSuggestions(true);
49     preferences.SetIncludeInlayParameterNameHints(UserPreferences::IncludeInlayParameterNameHints::ALL);
50 
51     InlayHintList result = {};
52 
53     ark::es2panda::lsp::Initializer initializer;
54     const auto ctx = initializer.CreateContext(filePath[0].c_str(), ES2PANDA_STATE_CHECKED);
55     auto astContext = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
56     ASSERT_NE(astContext, nullptr);
57     auto *parent = reinterpret_cast<ark::es2panda::ir::AstNode *>(astContext->parserProgram->Ast());
58     ASSERT_NE(parent, nullptr);
59 
60     ark::es2panda::ir::AstNode *callExprNode = nullptr;
61     parent->FindChild([&callExprNode, parent, &result](ark::es2panda::ir::AstNode *childNode) {
62         if (childNode->IsCallExpression()) {
63             callExprNode = childNode;
64             ark::es2panda::lsp::GetCallExpTypeForHints(callExprNode, parent, &result);
65         }
66         return false;
67     });
68     const size_t num1 = 108;
69     const size_t num2 = 112;
70     const std::string p1 = "param1";
71     const std::string p2 = "param2";
72     ASSERT_NE(callExprNode, nullptr);
73     std::vector<InlayHint> expectedHints = {{p1, num1, InlayHintKind::PARAMETER, false, true},
74                                             {p2, num2, InlayHintKind::PARAMETER, false, true}};
75 
76     ASSERT_EQ(result.hints.size(), expectedHints.size());
77     for (size_t i = 0; i < expectedHints.size(); i++) {
78         ASSERT_EQ(result.hints[i].text, expectedHints[i].text);
79         ASSERT_EQ(result.hints[i].number, expectedHints[i].number);
80         ASSERT_EQ(result.hints[i].kind, expectedHints[i].kind);
81         ASSERT_EQ(result.hints[i].whitespaceBefore, expectedHints[i].whitespaceBefore);
82         ASSERT_EQ(result.hints[i].whitespaceAfter, expectedHints[i].whitespaceAfter);
83     }
84     initializer.DestroyContext(ctx);
85 }
86 
TEST_F(LSPInlayHintsTests,VisitEnumMemberTest)87 TEST_F(LSPInlayHintsTests, VisitEnumMemberTest)
88 {
89     const std::vector<std::string> files = {"enum_file.ets"};
90 
91     const std::vector<std::string> fileContent = {R"(
92 enum Color {
93     RED = 1,
94     GREEN = 2,
95     BLUE = 3
96 };
97 enum text {
98     name = "test",
99     className = "class",
100     color = "blue"
101 };
102 
103 let myColor = Color.GREEN;
104 let myColor1 = Color.BLUE;
105 let name1 = text.name;
106 let classText = text.className;
107 
108 )"};
109 
110     const auto filePath = CreateTempFile(files, fileContent);
111 
112     InlayHintList result = {};
113     const size_t size = 4;
114     const std::string enum2 = "2";
115     const std::string enum3 = "3";
116     const std::string enumtest = "test";
117     const std::string enumclass = "class";
118     const size_t i0 = 0;
119     const size_t i1 = 1;
120     const size_t i2 = 2;
121     const size_t i3 = 3;
122 
123     ark::es2panda::lsp::Initializer initializer;
124     const auto ctx = initializer.CreateContext(filePath[i0].c_str(), ES2PANDA_STATE_CHECKED);
125     const auto astContext = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
126     ASSERT_NE(astContext, nullptr);
127     const auto *parent = reinterpret_cast<ark::es2panda::ir::AstNode *>(astContext->parserProgram->Ast());
128     ASSERT_NE(parent, nullptr);
129 
130     parent->FindChild([parent, &result](ark::es2panda::ir::AstNode *childNode) {
131         if (childNode->IsAssignmentExpression()) {
132             ark::es2panda::lsp::GetEnumTypeForHints(childNode, parent, &result);
133         }
134         return false;
135     });
136     initializer.DestroyContext(ctx);
137     ASSERT_EQ(result.hints.size(), size);
138     ASSERT_EQ(result.hints[i0].text, enum2);
139     ASSERT_EQ(result.hints[i1].text, enum3);
140     ASSERT_EQ(result.hints[i2].text, enumtest);
141     ASSERT_EQ(result.hints[i3].text, enumclass);
142 }
143 
TEST_F(LSPInlayHintsTests,VisitFunctionLikeForParameterTypeTest)144 TEST_F(LSPInlayHintsTests, VisitFunctionLikeForParameterTypeTest)
145 {
146     const std::vector<std::string> files = {"function_test.ets"};
147     const std::vector<std::string> fileContent = {R"(
148         function add(a: number, b: number): number {
149             return a + b;
150         }
151 
152         function greet(name: string, age: number): string {
153             return `Hello, ${name}! You are ${age} years old.`;
154         }
155 
156         let sum = add(5, 10);
157         let message = greet("Alice", 30);
158     )"};
159     const std::string voidString = "void";
160     const std::string numberString = "number";
161     const std::string stdString = "string";
162     const size_t index1 = 32;
163     const size_t index2 = 43;
164     const size_t index3 = 127;
165     const size_t index4 = 140;
166     const size_t i0 = 0;
167     const size_t i1 = 1;
168     const size_t i2 = 2;
169     const size_t i3 = 3;
170     const auto filePaths = CreateTempFile(files, fileContent);
171     ASSERT_EQ(filePaths.size(), i1);
172     InlayHintList result = {};
173     ark::es2panda::lsp::Initializer initializer;
174 
175     const auto ctx = initializer.CreateContext(filePaths[i0].c_str(), ES2PANDA_STATE_CHECKED);
176     ASSERT_NE(ctx, nullptr);
177 
178     const auto astContext = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
179     ASSERT_NE(astContext, nullptr);
180 
181     const auto *parent = reinterpret_cast<ark::es2panda::ir::AstNode *>(astContext->parserProgram->Ast());
182     ASSERT_NE(parent, nullptr);
183 
184     parent->FindChild([&result](ark::es2panda::ir::AstNode *childNode) {
185         if (childNode->IsMethodDefinition()) {
186             ark::es2panda::lsp::GetFunctionParameterTypeForHints(childNode, &result);
187         }
188         return false;
189     });
190 
191     ASSERT_EQ(result.hints[i0].text, numberString);
192     ASSERT_EQ(result.hints[i0].number, index1);
193     ASSERT_EQ(result.hints[i1].text, numberString);
194     ASSERT_EQ(result.hints[i1].number, index2);
195     ASSERT_EQ(result.hints[i2].text, stdString);
196     ASSERT_EQ(result.hints[i2].number, index3);
197     ASSERT_EQ(result.hints[i3].text, numberString);
198     ASSERT_EQ(result.hints[i3].number, index4);
199 
200     initializer.DestroyContext(ctx);
201 }
202 
TEST_F(LSPInlayHintsTests,VisitFunctionDeclarationLikeForReturnTypeTest1)203 TEST_F(LSPInlayHintsTests, VisitFunctionDeclarationLikeForReturnTypeTest1)
204 {
205     const std::vector<std::string> files = {"function_return_type_test1.ets"};
206     const std::vector<std::string> fileContent = {R"(
207         function add(a: number, b: number): number {
208             return a + b;
209         }
210         const multiply = (a: number, b: number): number => {
211             return a * b;
212         };
213     )"};
214 
215     const std::string doubleString = "double";
216     const size_t addIndex = 89;
217     const size_t multiplyIndex = 186;
218     const size_t i0 = 0;
219     const size_t i1 = 1;
220     const size_t i2 = 2;
221     const size_t i3 = 3;
222     const auto filePaths = CreateTempFile(files, fileContent);
223     ASSERT_EQ(filePaths.size(), i1);
224     InlayHintList result;
225     ark::es2panda::lsp::Initializer initializer;
226     const auto ctx = initializer.CreateContext(filePaths[i0].c_str(), ES2PANDA_STATE_CHECKED);
227     ASSERT_NE(ctx, nullptr);
228     const auto astContext = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
229     ASSERT_NE(astContext, nullptr);
230     const auto *parent = reinterpret_cast<ark::es2panda::ir::AstNode *>(astContext->parserProgram->Ast());
231     ASSERT_NE(parent, nullptr);
232     parent->FindChild([&result](ark::es2panda::ir::AstNode *childNode) {
233         if (ark::es2panda::lsp::IsSignatureSupportingReturnAnnotation(childNode)) {
234             ark::es2panda::lsp::GetFunctionReturnTypeForHints(childNode, &result);
235         }
236         return false;
237     });
238     ASSERT_EQ(result.hints[i2].text, doubleString);
239     ASSERT_EQ(result.hints[i2].number, addIndex);
240     ASSERT_EQ(result.hints[i3].text, doubleString);
241     ASSERT_EQ(result.hints[i3].number, multiplyIndex);
242     initializer.DestroyContext(ctx);
243 }
244 
TEST_F(LSPInlayHintsTests,VisitFunctionDeclarationLikeForReturnTypeTest2)245 TEST_F(LSPInlayHintsTests, VisitFunctionDeclarationLikeForReturnTypeTest2)
246 {
247     const std::vector<std::string> files = {"function_return_type_test.ets"};
248     const std::vector<std::string> fileContent = {R"(
249         function greet(name: string): string {
250             return `Hello, ${name}!`;
251         }
252         const sayHello = (): void => {
253             console.log("Hello!");
254         };
255 
256     )"};
257     const std::string voidString = "void";
258     const std::string stdString = "String";
259 
260     const size_t greetIndex = 95;
261     const size_t sayHelloIndex = 179;
262     const size_t i0 = 0;
263     const size_t i1 = 1;
264     const size_t i2 = 2;
265     const size_t i3 = 3;
266 
267     const auto filePaths = CreateTempFile(files, fileContent);
268     ASSERT_EQ(filePaths.size(), i1);
269     InlayHintList result;
270     ark::es2panda::lsp::Initializer initializer;
271     const auto ctx = initializer.CreateContext(filePaths[i0].c_str(), ES2PANDA_STATE_CHECKED);
272     ASSERT_NE(ctx, nullptr);
273     const auto astContext = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
274     ASSERT_NE(astContext, nullptr);
275     const auto *parent = reinterpret_cast<ark::es2panda::ir::AstNode *>(astContext->parserProgram->Ast());
276     ASSERT_NE(parent, nullptr);
277     parent->FindChild([&result](ark::es2panda::ir::AstNode *childNode) {
278         if (ark::es2panda::lsp::IsSignatureSupportingReturnAnnotation(childNode)) {
279             ark::es2panda::lsp::GetFunctionReturnTypeForHints(childNode, &result);
280         }
281         return false;
282     });
283     ASSERT_EQ(result.hints[i2].text, stdString);
284     ASSERT_EQ(result.hints[i2].number, greetIndex);
285     ASSERT_EQ(result.hints[i3].text, voidString);
286     ASSERT_EQ(result.hints[i3].number, sayHelloIndex);
287     initializer.DestroyContext(ctx);
288 }
289 
TEST_F(LSPInlayHintsTests,VisitVariableLikeDeclarationTest)290 TEST_F(LSPInlayHintsTests, VisitVariableLikeDeclarationTest)
291 {
292     const std::vector<std::string> files = {"variable_test.ets"};
293     const std::vector<std::string> fileContent = {R"(
294         let x: number = 10;
295         let y = "Hello";
296         let z = true;
297         let obj = { a: 1, b: 2 };
298         let arr = [1, 2, 3];
299     )"};
300     const std::string numberString = "number";
301     const std::string stdString = "string";
302     const std::string boolString = "boolean";
303     const size_t index1 = 27;
304     const size_t index2 = 52;
305     const size_t index3 = 74;
306     const size_t i0 = 0;
307     const size_t i1 = 1;
308     const size_t i2 = 2;
309     const auto filePaths = CreateTempFile(files, fileContent);
310     ASSERT_EQ(filePaths.size(), i1);
311     InlayHintList result;
312     ark::es2panda::lsp::Initializer initializer;
313     const auto ctx = initializer.CreateContext(filePaths[i0].c_str(), ES2PANDA_STATE_CHECKED);
314     ASSERT_NE(ctx, nullptr);
315     const auto astContext = reinterpret_cast<ark::es2panda::public_lib::Context *>(ctx);
316     ASSERT_NE(astContext, nullptr);
317     const auto *parent = reinterpret_cast<ark::es2panda::ir::AstNode *>(astContext->parserProgram->Ast());
318     ASSERT_NE(parent, nullptr);
319     ark::es2panda::lsp::UserPreferences preferences;
320     preferences.SetIncludeInlayVariableTypeHintsWhenTypeMatchesName(false);
321     parent->FindChild([&result](ark::es2panda::ir::AstNode *childNode) {
322         if (childNode->IsAssignmentExpression()) {
323             ark::es2panda::lsp::GetVariableDeclarationTypeForHints(childNode, &result);
324         }
325         if (result.hints.empty()) {
326         }
327         return false;
328     });
329     ASSERT_EQ(result.hints[i0].text, numberString);
330     ASSERT_EQ(result.hints[i0].number, index1);
331     ASSERT_EQ(result.hints[i1].text, stdString);
332     ASSERT_EQ(result.hints[i1].number, index2);
333     ASSERT_EQ(result.hints[i2].text, boolString);
334     ASSERT_EQ(result.hints[i2].number, index3);
335     initializer.DestroyContext(ctx);
336 }
337