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_api_test.h"
17 #include "lsp/include/isolated_declaration.h"
18
19 namespace {
20 using ark::es2panda::lsp::Initializer;
21
22 class LSPIsolatedDeclarationTests : public LSPAPITests {};
23
TEST_F(LSPIsolatedDeclarationTests,IsolatedDeclaration1)24 TEST_F(LSPIsolatedDeclarationTests, IsolatedDeclaration1)
25 {
26 std::vector<std::string> files = {"IsolatedDeclaration1_export.ets", "IsolatedDeclaration1.ets"};
27 std::vector<std::string> texts = {R"(export class C {
28 pro1: number = 123;
29 pro2: string = "hello";
30 pro3: boolean = false;
31 }
32 )",
33 R"(import {C} from './IsolatedDeclaration1_export';
34 export function foo(n: number) {
35 if (n < 3) {
36 return n + 2;
37 }
38 const c = new C();
39 if (c.pro1 < 100) {
40 return c;
41 }
42 if (c.pro3) {
43 return c.pro3;
44 }
45 return c.pro2;
46 }
47 )"};
48 auto filePaths = CreateTempFile(files, texts);
49 const size_t expectedFileCount = 2;
50 ASSERT_EQ(filePaths.size(), expectedFileCount);
51
52 Initializer initializer = Initializer();
53 auto context = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
54 auto ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
55 auto checker = reinterpret_cast<ark::es2panda::checker::ETSChecker *>(ctx->checker);
56 auto ast = ctx->parserProgram->Ast();
57 auto id = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
58 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == "foo";
59 });
60 auto textChange = ark::es2panda::lsp::ProcessIdentifier(id->AsIdentifier(), checker, ctx->parserProgram);
61 const size_t expectedStart = 79;
62 ASSERT_EQ(textChange.has_value(), true);
63 ASSERT_EQ(textChange.value().newText, ": string | boolean | number | C");
64 ASSERT_EQ(textChange.value().span.start, expectedStart);
65 ASSERT_EQ(textChange.value().span.length, 0);
66
67 initializer.DestroyContext(context);
68 }
69
TEST_F(LSPIsolatedDeclarationTests,IsolatedDeclaration2)70 TEST_F(LSPIsolatedDeclarationTests, IsolatedDeclaration2)
71 {
72 std::vector<std::string> files = {"IsolatedDeclaration2_export.ets", "IsolatedDeclaration2.ets"};
73 std::vector<std::string> texts = {R"(export const arr = [1, 2, 3];
74 export const tuple = [1, "hello", true];
75 )",
76 R"(import {arr, tuple} from './IsolatedDeclaration2_export';
77 export function foo(n: number) {
78 if (n < 3) {
79 return n + 2;
80 }
81 if (n < 7) {
82 return arr;
83 }
84 return tuple;
85 }
86 )"};
87 auto filePaths = CreateTempFile(files, texts);
88 const size_t expectedFileCount = 2;
89 ASSERT_EQ(filePaths.size(), expectedFileCount);
90
91 Initializer initializer = Initializer();
92 auto context = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
93 auto ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
94 auto checker = reinterpret_cast<ark::es2panda::checker::ETSChecker *>(ctx->checker);
95 auto ast = ctx->parserProgram->Ast();
96 auto id = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
97 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == "foo";
98 });
99 auto textChange = ark::es2panda::lsp::ProcessIdentifier(id->AsIdentifier(), checker, ctx->parserProgram);
100 const size_t expectedStart = 88;
101 ASSERT_EQ(textChange.has_value(), true);
102 ASSERT_EQ(textChange.value().newText, ": [number, string, boolean] | number | number[]");
103 ASSERT_EQ(textChange.value().span.start, expectedStart);
104 ASSERT_EQ(textChange.value().span.length, 0);
105
106 initializer.DestroyContext(context);
107 }
108
TEST_F(LSPIsolatedDeclarationTests,IsolatedDeclaration3)109 TEST_F(LSPIsolatedDeclarationTests, IsolatedDeclaration3)
110 {
111 std::vector<std::string> files = {"IsolatedDeclaration3_export.ets", "IsolatedDeclaration3.ets"};
112 std::vector<std::string> texts = {R"(export class C {
113 pro1: number = 123;
114 pro2: string = "hello";
115 pro3: boolean = false;
116 }
117 )",
118 R"(import {C} from './IsolatedDeclaration3_export';
119 export const foo = () => {
120 const c = new C();
121 if (c.pro1 < 100) {
122 return c.pro1;
123 }
124 return c.pro2;
125 };
126 )"};
127 auto filePaths = CreateTempFile(files, texts);
128 const size_t expectedFileCount = 2;
129 ASSERT_EQ(filePaths.size(), expectedFileCount);
130
131 Initializer initializer = Initializer();
132 auto context = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
133 auto ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
134 auto checker = reinterpret_cast<ark::es2panda::checker::ETSChecker *>(ctx->checker);
135 auto ast = ctx->parserProgram->Ast();
136 auto id = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
137 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == "foo";
138 });
139 auto textChange = ark::es2panda::lsp::ProcessIdentifier(id->AsIdentifier(), checker, ctx->parserProgram);
140 const size_t expectedStart = 70;
141 ASSERT_EQ(textChange.has_value(), true);
142 ASSERT_EQ(textChange.value().newText, ": number | string");
143 ASSERT_EQ(textChange.value().span.start, expectedStart);
144 ASSERT_EQ(textChange.value().span.length, 0);
145
146 initializer.DestroyContext(context);
147 }
148
TEST_F(LSPIsolatedDeclarationTests,IsolatedDeclaration4)149 TEST_F(LSPIsolatedDeclarationTests, IsolatedDeclaration4)
150 {
151 std::vector<std::string> files = {"IsolatedDeclaration4_export.ets", "IsolatedDeclaration4.ets"};
152 std::vector<std::string> texts = {R"(export const arr = [1, 2, 3];
153 export const tuple = [1, "hello", true];
154 )",
155 R"(import {arr, tuple} from './IsolatedDeclaration4_export';
156 export class A {
157 public foo(n: number) {
158 if (n < 3) {
159 return n + 2;
160 }
161 if (n < 7) {
162 return arr;
163 }
164 return tuple;
165 }
166 }
167 )"};
168 auto filePaths = CreateTempFile(files, texts);
169 const size_t expectedFileCount = 2;
170 ASSERT_EQ(filePaths.size(), expectedFileCount);
171
172 Initializer initializer = Initializer();
173 auto context = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
174 auto ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
175 auto checker = reinterpret_cast<ark::es2panda::checker::ETSChecker *>(ctx->checker);
176 auto ast = ctx->parserProgram->Ast();
177 auto id = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
178 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == "foo";
179 });
180 auto textChange = ark::es2panda::lsp::ProcessIdentifier(id->AsIdentifier(), checker, ctx->parserProgram);
181 const size_t expectedStart = 98;
182 ASSERT_EQ(textChange.has_value(), true);
183 ASSERT_EQ(textChange.value().newText, ": [number, string, boolean] | number | number[]");
184 ASSERT_EQ(textChange.value().span.start, expectedStart);
185 ASSERT_EQ(textChange.value().span.length, 0);
186
187 initializer.DestroyContext(context);
188 }
189
TEST_F(LSPIsolatedDeclarationTests,IsolatedDeclaration5)190 TEST_F(LSPIsolatedDeclarationTests, IsolatedDeclaration5)
191 {
192 std::vector<std::string> files = {"IsolatedDeclaration5_export.ets", "IsolatedDeclaration5.ets"};
193 std::vector<std::string> texts = {R"(export const arr = [1, 2, 3];
194 export const tuple = [1, "hello", true];
195 )",
196 R"(import {arr, tuple} from './IsolatedDeclaration5_export';
197 export class A {
198 public foo = (n: number) => {
199 if (n < 3) {
200 return n + 2;
201 }
202 if (n < 7) {
203 return arr;
204 }
205 return tuple;
206 }
207 }
208 )"};
209 auto filePaths = CreateTempFile(files, texts);
210 const size_t expectedFileCount = 2;
211 ASSERT_EQ(filePaths.size(), expectedFileCount);
212
213 Initializer initializer = Initializer();
214 auto context = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
215 auto ctx = reinterpret_cast<ark::es2panda::public_lib::Context *>(context);
216 auto checker = reinterpret_cast<ark::es2panda::checker::ETSChecker *>(ctx->checker);
217 auto ast = ctx->parserProgram->Ast();
218 auto id = ast->FindChild([](ark::es2panda::ir::AstNode *childNode) {
219 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == "foo";
220 });
221 auto textChange = ark::es2panda::lsp::ProcessIdentifier(id->AsIdentifier(), checker, ctx->parserProgram);
222 const size_t expectedStart = 101;
223 ASSERT_EQ(textChange.has_value(), true);
224 ASSERT_EQ(textChange.value().newText, ": [number, string, boolean] | number | number[]");
225 ASSERT_EQ(textChange.value().span.start, expectedStart);
226 ASSERT_EQ(textChange.value().span.length, 0);
227
228 initializer.DestroyContext(context);
229 }
230 } // namespace