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/suggestion_diagnostics.h"
17 #include <gtest/gtest.h>
18 #include "lsp/include/internal_api.h"
19 #include "lsp/include/classifier.h"
20 #include "lsp_api_test.h"
21 #include "public/es2panda_lib.h"
22 namespace {
23 using ark::es2panda::lsp::Initializer;
24
25 class LspSuggestionTests : public LSPAPITests {};
26
TEST_F(LspSuggestionTests,canBeConvertedToAsync)27 TEST_F(LspSuggestionTests, canBeConvertedToAsync)
28 {
29 const char *source = "function add(x: number, y: number): number\n {return x + y;};";
30 Initializer initializer = Initializer();
31 es2panda_Context *ctx = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
32 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
33 auto node = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
34 return childNode->Type() == ark::es2panda::ir::AstNodeType::FUNCTION_EXPRESSION;
35 });
36 EXPECT_TRUE(ark::es2panda::lsp::CanBeConvertedToAsync(node));
37 initializer.DestroyContext(ctx);
38 }
39
TEST_F(LspSuggestionTests,isFixablePromiseArgument)40 TEST_F(LspSuggestionTests, isFixablePromiseArgument)
41 {
42 const char *source =
43 "function add(x: number, y: number): number\n {return x + y;}"
44 "\nconsole.log(add(1,2));";
45 Initializer initializer = Initializer();
46 es2panda_Context *ctx = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
47 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
48 std::unordered_map<std::string, bool> visitedNestedConvertibleFunctions;
49 auto node = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
50 return childNode->Type() == ark::es2panda::ir::AstNodeType::FUNCTION_EXPRESSION;
51 });
52 EXPECT_TRUE(ark::es2panda::lsp::IsFixablePromiseArgument(node, visitedNestedConvertibleFunctions));
53 initializer.DestroyContext(ctx);
54 }
TEST_F(LspSuggestionTests,hasSupportedNumberOfArguments)55 TEST_F(LspSuggestionTests, hasSupportedNumberOfArguments)
56 {
57 const char *source =
58 "function fetchData():Promise<string>{\n"
59 "\nreturn Promise.resolve(\"Success\");\n}\nfetchData().then();";
60 Initializer initializer = Initializer();
61 es2panda_Context *ctx = initializer.CreateContext("token-pos-literal.ets", ES2PANDA_STATE_CHECKED, source);
62 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
63 std::vector<ark::es2panda::ir::AstNode *> callExprs;
64 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
65 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
66 callExprs.push_back(node);
67 }
68 return false;
69 });
70 EXPECT_TRUE(ark::es2panda::lsp::HasSupportedNumberOfArguments(callExprs.at(0)));
71 initializer.DestroyContext(ctx);
72 }
TEST_F(LspSuggestionTests,hasSupportedNumberOfArguments2)73 TEST_F(LspSuggestionTests, hasSupportedNumberOfArguments2)
74 {
75 const char *source =
76 "function fetchData():Promise<string>{\n"
77 "\nreturn Promise.reject(\"Error\");\n}\nfetchData().catch();";
78 Initializer initializer = Initializer();
79 es2panda_Context *ctx = initializer.CreateContext("token-pos-literal.ets", ES2PANDA_STATE_CHECKED, source);
80 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
81 std::vector<ark::es2panda::ir::AstNode *> callExprs;
82 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
83 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
84 callExprs.push_back(node);
85 }
86
87 return false;
88 });
89 EXPECT_TRUE(ark::es2panda::lsp::HasSupportedNumberOfArguments(callExprs.at(0)));
90 initializer.DestroyContext(ctx);
91 }
TEST_F(LspSuggestionTests,hasSupportedNumberOfArguments3)92 TEST_F(LspSuggestionTests, hasSupportedNumberOfArguments3)
93 {
94 const char *source =
95 "function fetchData():Promise<string>{\nreturn Promise.resolve(\"Success\");\n}"
96 "\nfetchData().finally();";
97 Initializer initializer = Initializer();
98 es2panda_Context *ctx = initializer.CreateContext("token-pos-literal.ets", ES2PANDA_STATE_CHECKED, source);
99 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
100 std::vector<ark::es2panda::ir::AstNode *> callExprs;
101 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
102 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
103 callExprs.push_back(node);
104 }
105 return false;
106 });
107 EXPECT_TRUE(ark::es2panda::lsp::HasSupportedNumberOfArguments(callExprs.at(0)));
108 initializer.DestroyContext(ctx);
109 }
TEST_F(LspSuggestionTests,hasSupportedNumberOfArguments4)110 TEST_F(LspSuggestionTests, hasSupportedNumberOfArguments4)
111 {
112 const char *source =
113 "function fetchData(name:string):Promise<string>{\n"
114 "\nreturn Promise.resolve(\"Success\");\n}\nfetchData(\"test\").finally();";
115 Initializer initializer = Initializer();
116 es2panda_Context *ctx = initializer.CreateContext("token-pos-literal.ets", ES2PANDA_STATE_CHECKED, source);
117 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
118 std::vector<ark::es2panda::ir::AstNode *> callExprs;
119 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
120 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
121 callExprs.push_back(node);
122 }
123 return false;
124 });
125 EXPECT_TRUE(ark::es2panda::lsp::HasSupportedNumberOfArguments(callExprs.at(0)));
126 initializer.DestroyContext(ctx);
127 }
128
TEST_F(LspSuggestionTests,GetSuggestionDiagnostics)129 TEST_F(LspSuggestionTests, GetSuggestionDiagnostics)
130 {
131 const char *source =
132 "function fetchData(){\nreturn Promise.resolve(\"h\")\n;}\nfunction processData()"
133 "{\nreturn fetchData().finally();\n};\nprocessData();\n";
134 Initializer initializer = Initializer();
135 es2panda_Context *context = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
136 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(context);
137 auto diag = ark::es2panda::lsp::GetSuggestionDiagnosticsImpl(ast);
138 const auto message = "This_may_be_converted_to_an_async_function";
139 auto severity = DiagnosticSeverity::Hint;
140 int const startLine = 0;
141 int const endLine = 2;
142 int const startChar = 9;
143 int const endChar = 52;
144 ASSERT_EQ(diag.at(0).diagnostic.range_.start.line_, startLine);
145 ASSERT_EQ(diag.at(0).diagnostic.range_.end.line_, endLine);
146 ASSERT_EQ(diag.at(0).diagnostic.range_.start.character_, startChar);
147 ASSERT_EQ(diag.at(0).diagnostic.range_.end.character_, endChar);
148 ASSERT_EQ(diag.at(0).diagnostic.message_, message);
149 ASSERT_EQ(diag.at(0).diagnostic.severity_, severity);
150 initializer.DestroyContext(context);
151 }
152
TEST_F(LspSuggestionTests,GetSuggestionDiagnostics2)153 TEST_F(LspSuggestionTests, GetSuggestionDiagnostics2)
154 {
155 Initializer initializer = Initializer();
156 es2panda_Context *ctx =
157 initializer.CreateContext("ds1.ets", ES2PANDA_STATE_CHECKED,
158 "function fetchData(){\nreturn Promise.resolve(\"h\")\n;}\nfunction processData()"
159 "{\nreturn fetchData().finally();\n};\nprocessData();\n");
160 LSPAPI const *lspApi = GetImpl();
161 auto diag = lspApi->getSuggestionDiagnostics(ctx);
162 initializer.DestroyContext(ctx);
163 const auto msg = "This_may_be_converted_to_an_async_function";
164 auto severity = DiagnosticSeverity::Hint;
165 int const startLine = 0;
166 int const endLine = 2;
167 int const startChar = 9;
168 int const endChar = 52;
169 ASSERT_EQ(diag.diagnostic.at(0).range_.start.line_, startLine);
170 ASSERT_EQ(diag.diagnostic.at(0).range_.end.line_, endLine);
171 ASSERT_EQ(diag.diagnostic.at(0).range_.start.character_, startChar);
172 ASSERT_EQ(diag.diagnostic.at(0).range_.end.character_, endChar);
173 ASSERT_EQ(diag.diagnostic.at(0).message_, msg);
174 ASSERT_EQ(diag.diagnostic.at(0).severity_, severity);
175 }
176
TEST_F(LspSuggestionTests,isPromiseHandler)177 TEST_F(LspSuggestionTests, isPromiseHandler)
178 {
179 const char *source =
180 "function fetchData(name:string):Promise<string>{\n"
181 "\nreturn Promise.resolve(\"Success\");\n}\nfetchData(\"test\").finally();";
182 Initializer initializer = Initializer();
183 es2panda_Context *ctx = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
184 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
185 std::vector<ark::es2panda::ir::AstNode *> callExprs;
186 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
187 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
188 callExprs.push_back(node);
189 }
190 return false;
191 });
192 EXPECT_TRUE(ark::es2panda::lsp::IsPromiseHandler(callExprs.at(0)));
193 initializer.DestroyContext(ctx);
194 }
195
TEST_F(LspSuggestionTests,isPromiseHandler2)196 TEST_F(LspSuggestionTests, isPromiseHandler2)
197 {
198 const char *source =
199 "function fetchData():Promise<string>{\n"
200 "\nreturn Promise.reject(\"Error\");\n}\nfetchData().catch();";
201 Initializer initializer = Initializer();
202 es2panda_Context *ctx = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
203 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
204 std::vector<ark::es2panda::ir::AstNode *> callExprs;
205 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
206 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
207 callExprs.push_back(node);
208 }
209 return false;
210 });
211 EXPECT_TRUE(ark::es2panda::lsp::IsPromiseHandler(callExprs.at(0)));
212 initializer.DestroyContext(ctx);
213 }
214
TEST_F(LspSuggestionTests,isPromiseHandler3)215 TEST_F(LspSuggestionTests, isPromiseHandler3)
216 {
217 const char *source =
218 "function fetchData():Promise<string>{\n"
219 "\nreturn Promise.resolve(\"Success\");\n}\nfetchData().then();";
220 Initializer initializer = Initializer();
221 es2panda_Context *ctx = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
222 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
223 std::vector<ark::es2panda::ir::AstNode *> callExprs;
224 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
225 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
226 callExprs.push_back(node);
227 }
228 return false;
229 });
230 EXPECT_TRUE(ark::es2panda::lsp::IsPromiseHandler(callExprs.at(0)));
231 initializer.DestroyContext(ctx);
232 }
233
TEST_F(LspSuggestionTests,isPromiseHandler4)234 TEST_F(LspSuggestionTests, isPromiseHandler4)
235 {
236 const char *source =
237 "function fetchData(name: string,name2:string):Promise<string>{\n"
238 "\nreturn Promise.resolve(\"Success\");\n}\nfetchData(\"test\",\"test2\").finally();";
239 Initializer initializer = Initializer();
240 es2panda_Context *ctx = initializer.CreateContext("sug-diag.ets", ES2PANDA_STATE_CHECKED, source);
241 auto ast = GetAstFromContext<ark::es2panda::ir::AstNode>(ctx);
242 std::vector<ark::es2panda::ir::AstNode *> callExprs;
243 ast->FindChild([&callExprs](ark::es2panda::ir::AstNode *node) {
244 if (node->Type() == ark::es2panda::ir::AstNodeType::CALL_EXPRESSION) {
245 callExprs.push_back(node);
246 }
247 return false;
248 });
249 EXPECT_TRUE(ark::es2panda::lsp::IsPromiseHandler(callExprs.at(0)));
250 initializer.DestroyContext(ctx);
251 }
252 } // namespace