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 "checker/ETSchecker.h"
17 #include "checker/types/ets/etsFunctionType.h"
18 #include "isolated_declaration.h"
19 #include "compiler/lowering/util.h"
20 #include "ir/astNode.h"
21 #include "ir/base/classProperty.h"
22 #include "ir/base/methodDefinition.h"
23 #include "ir/base/scriptFunction.h"
24 #include "ir/ets/etsNewClassInstanceExpression.h"
25
26 namespace ark::es2panda::lsp {
27 namespace {
28 constexpr std::array<std::string_view, 2> STRING_TYPE_ARRAY = {"Char", "String"};
29 constexpr std::array<std::string_view, 7> NUMBER_TYPE_ARRAY = {"Long", "Float", "Double", "Byte",
30 "Short", "Int", "Number"};
31
GetStringTypes()32 const std::unordered_set<std::string_view> &GetStringTypes()
33 {
34 static const std::unordered_set<std::string_view> STRING_TYPES(std::begin(STRING_TYPE_ARRAY),
35 std::end(STRING_TYPE_ARRAY));
36 return STRING_TYPES;
37 }
38
GetNumberTypes()39 const std::unordered_set<std::string_view> &GetNumberTypes()
40 {
41 static const std::unordered_set<std::string_view> NUMBER_TYPES(std::begin(NUMBER_TYPE_ARRAY),
42 std::end(NUMBER_TYPE_ARRAY));
43 return NUMBER_TYPES;
44 }
45 } // namespace
46
47 std::optional<std::string> GenReturnTypeStr(const checker::Type *checkerType, checker::ETSChecker *checker);
48 std::optional<std::string> GenUnionType(const checker::ETSUnionType *unionType, checker::ETSChecker *checker,
49 const char splitChar);
50
51 template <class UnionType>
FilterUnionTypes(const ArenaVector<UnionType * > & originTypes,checker::ETSChecker * checker)52 std::vector<UnionType *> FilterUnionTypes(const ArenaVector<UnionType *> &originTypes, checker::ETSChecker *checker)
53 {
54 if (originTypes.empty()) {
55 return {};
56 }
57 bool hasNumber = false;
58 bool hasString = false;
59 std::vector<UnionType *> filteredTypes;
60 for (auto originType : originTypes) {
61 std::string typeStr = originType->ToString();
62 if constexpr (std::is_same_v<UnionType, ir::TypeNode>) {
63 if (originType->IsTSThisType()) {
64 filteredTypes.push_back(originType);
65 continue;
66 }
67 typeStr = originType->GetType(checker)->ToString();
68 typeStr[0] = std::toupper(typeStr[0]);
69 }
70 if (GetStringTypes().count(typeStr) != 0U) {
71 if (hasString) {
72 continue;
73 }
74 hasString = true;
75 } else if (GetNumberTypes().count(typeStr) != 0U) {
76 if (hasNumber) {
77 continue;
78 }
79 hasNumber = true;
80 }
81 filteredTypes.push_back(originType);
82 }
83 return filteredTypes;
84 }
85
HandleSpecificObjectTypes(const checker::ETSObjectType * objectType,checker::ETSChecker * checker)86 std::optional<std::string> HandleSpecificObjectTypes(const checker::ETSObjectType *objectType,
87 [[maybe_unused]] checker::ETSChecker *checker)
88 {
89 if (objectType->IsETSStringType()) {
90 return "string";
91 }
92 if (objectType->IsETSBigIntType()) {
93 return "bigint";
94 }
95 if (objectType->IsETSUnboxableObject()) {
96 return "number";
97 }
98 return std::nullopt;
99 }
100
GenObjectType(const checker::ETSObjectType * objectType,checker::ETSChecker * checker)101 std::optional<std::string> GenObjectType(const checker::ETSObjectType *objectType, checker::ETSChecker *checker)
102 {
103 if (auto specificTypeStr = HandleSpecificObjectTypes(objectType, checker); specificTypeStr.has_value()) {
104 return specificTypeStr.value();
105 }
106 std::string typeStr = objectType->Name().Mutf8();
107 if (typeStr == "Exception" || typeStr == "NullPointerError") {
108 return "Error";
109 }
110 if (typeStr == "Array") {
111 const auto &typeArgs = objectType->TypeArguments();
112 if (typeArgs.empty()) {
113 return std::nullopt;
114 }
115 if (typeArgs[0]->IsETSUnionType()) {
116 auto unionTypeStr = GenUnionType(typeArgs[0]->AsETSUnionType(), checker, ',');
117 auto tupleTypeStr = std::string("[");
118 if (unionTypeStr.has_value()) {
119 tupleTypeStr += unionTypeStr.value();
120 } else {
121 return std::nullopt;
122 }
123 tupleTypeStr += "]";
124 return tupleTypeStr;
125 }
126
127 if (auto elementTypeStr = GenReturnTypeStr(typeArgs[0], checker); elementTypeStr.has_value()) {
128 return elementTypeStr.value() + "[]";
129 }
130
131 return std::nullopt;
132 }
133 if (size_t partialPos = typeStr.find("$partial"); partialPos != std::string::npos) {
134 return "Partial<" + typeStr.substr(0, partialPos) + ">";
135 }
136 return typeStr;
137 }
138
HandleObjectType(const checker::Type * checkerType,checker::ETSChecker * checker)139 std::optional<std::string> HandleObjectType(const checker::Type *checkerType, checker::ETSChecker *checker)
140 {
141 std::string typeStr = checkerType->ToString();
142 if (typeStr == "Boolean") {
143 return "boolean";
144 }
145 if (GetStringTypes().count(typeStr) != 0U) {
146 return "string";
147 }
148 if (GetNumberTypes().count(typeStr) != 0U) {
149 return "number";
150 }
151 if (typeStr == "BigInt") {
152 return "bigint";
153 }
154 return GenObjectType(checkerType->AsETSObjectType(), checker);
155 }
156
GenUnionType(const checker::ETSUnionType * unionType,checker::ETSChecker * checker,const char splitChar='|')157 std::optional<std::string> GenUnionType(const checker::ETSUnionType *unionType, checker::ETSChecker *checker,
158 const char splitChar = '|')
159 {
160 auto originTypes = FilterUnionTypes(unionType->ConstituentTypes(), checker);
161 if (originTypes.empty()) {
162 return std::nullopt;
163 }
164 auto unionTypeStr = std::string();
165 for (size_t i = 0; i < originTypes.size(); ++i) {
166 auto elementTypeStr = GenReturnTypeStr(originTypes[i], checker);
167 if (!elementTypeStr.has_value()) {
168 return std::nullopt;
169 }
170 if (i == 0) {
171 unionTypeStr += elementTypeStr.value();
172 } else {
173 if (splitChar == '|') {
174 unionTypeStr += " | " + elementTypeStr.value();
175 } else if (splitChar == ',') {
176 unionTypeStr += ", " + elementTypeStr.value();
177 }
178 }
179 }
180 return unionTypeStr;
181 }
182
HandleArrayType(const checker::Type * checkerType,checker::ETSChecker * checker)183 std::optional<std::string> HandleArrayType(const checker::Type *checkerType, checker::ETSChecker *checker)
184 {
185 auto arrayTypeStr = std::string();
186 const auto *elementType = checkerType->AsETSArrayType()->ElementType();
187 bool needParentheses = elementType->IsETSUnionType() || elementType->IsETSFunctionType();
188 if (needParentheses) {
189 if (auto elementTypeStr = GenReturnTypeStr(elementType, checker); elementTypeStr.has_value()) {
190 arrayTypeStr += "(" + elementTypeStr.value() + ")";
191 }
192 } else {
193 if (auto elementTypeStr = GenReturnTypeStr(elementType, checker); elementTypeStr.has_value()) {
194 arrayTypeStr += elementTypeStr.value();
195 }
196 }
197 arrayTypeStr += "[]";
198 return arrayTypeStr;
199 }
200
HandleETSSpecificTypes(const checker::Type * checkerType,checker::ETSChecker * checker)201 std::optional<std::string> HandleETSSpecificTypes(const checker::Type *checkerType, checker::ETSChecker *checker)
202 {
203 switch (checker::ETSChecker::ETSType(checkerType)) {
204 case checker::TypeFlag::ETS_VOID:
205 case checker::TypeFlag::ETS_NULL:
206 case checker::TypeFlag::ETS_UNDEFINED:
207 case checker::TypeFlag::ETS_BOOLEAN:
208 case checker::TypeFlag::ETS_TYPE_PARAMETER:
209 case checker::TypeFlag::ETS_NONNULLISH:
210 case checker::TypeFlag::ETS_PARTIAL_TYPE_PARAMETER:
211 case checker::TypeFlag::ETS_NEVER:
212 case checker::TypeFlag::ETS_READONLY:
213 return checkerType->ToString();
214
215 case checker::TypeFlag::ETS_OBJECT:
216 case checker::TypeFlag::ETS_DYNAMIC_TYPE:
217 return HandleObjectType(checkerType, checker);
218
219 case checker::TypeFlag::ETS_ARRAY:
220 return HandleArrayType(checkerType, checker);
221
222 case checker::TypeFlag::ETS_UNION:
223 return GenUnionType(checkerType->AsETSUnionType(), checker);
224 default:
225 return std::nullopt;
226 }
227 return std::nullopt;
228 }
229
HandleBasicTypes(const checker::Type * checkerType)230 std::optional<std::string> HandleBasicTypes(const checker::Type *checkerType)
231 {
232 if (checkerType->IsETSEnumType()) {
233 return checkerType->ToString();
234 }
235 if (checkerType->HasTypeFlag(checker::TypeFlag::CHAR)) {
236 return "string";
237 }
238 if (checkerType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
239 return "number";
240 }
241 return std::nullopt;
242 }
243
GenReturnTypeStr(const checker::Type * checkerType,checker::ETSChecker * checker)244 std::optional<std::string> GenReturnTypeStr(const checker::Type *checkerType, checker::ETSChecker *checker)
245 {
246 if (checkerType == nullptr) {
247 return std::nullopt;
248 }
249
250 if (auto basicTypeStr = HandleBasicTypes(checkerType); basicTypeStr.has_value()) {
251 return basicTypeStr.value();
252 }
253
254 if (checkerType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
255 return "number";
256 }
257 if (checkerType->IsETSStringEnumType()) {
258 return "string";
259 }
260
261 if (auto specificTypeStr = HandleETSSpecificTypes(checkerType, checker); specificTypeStr.has_value()) {
262 return specificTypeStr.value();
263 }
264 return std::nullopt;
265 }
266
GetReturnTypeStr(const checker::Type * checkerType,checker::ETSChecker * checker)267 std::string GetReturnTypeStr(const checker::Type *checkerType, checker::ETSChecker *checker)
268 {
269 if (auto typeStr = GenReturnTypeStr(checkerType, checker); typeStr.has_value()) {
270 return typeStr.value();
271 }
272 return "void";
273 }
274
GetFuncSignature(const checker::ETSFunctionType * etsFunctionType,const ir::MethodDefinition * methodDef)275 const checker::Signature *GetFuncSignature(const checker::ETSFunctionType *etsFunctionType,
276 const ir::MethodDefinition *methodDef)
277 {
278 if (etsFunctionType->IsETSArrowType()) {
279 return etsFunctionType->ArrowSignature();
280 }
281 if (methodDef != nullptr) {
282 return methodDef->Function()->Signature();
283 }
284 return etsFunctionType->CallSignatures()[0];
285 }
286
GetTextChange(ir::ScriptFunction * func,std::string & typeStr,parser::Program * program)287 std::optional<TextChange> GetTextChange(ir::ScriptFunction *func, std::string &typeStr, parser::Program *program)
288 {
289 ES2PANDA_ASSERT(func != nullptr);
290
291 auto returnTypeAnnotation = func->ReturnTypeAnnotation();
292 if (returnTypeAnnotation != nullptr) {
293 auto start = returnTypeAnnotation->Start().index;
294 auto end = returnTypeAnnotation->End().index;
295 return TextChange {TextSpan(start, end - start), typeStr};
296 }
297 // try to find the position of ')' before the function body
298 auto bodyStart = func->Body()->Start().index;
299 auto sourceCode = program->SourceCode().Utf8();
300 auto i = bodyStart;
301 for (; i >= func->Start().index; --i) {
302 if (i >= 1 && sourceCode[i - 1] == ')') {
303 break;
304 }
305 }
306 // set the length to 0, meaning we should insert the text instead of replacing
307 return TextChange {TextSpan(i, 0), ": " + typeStr};
308 }
309
ProcessMethodDefinition(ir::MethodDefinition * method,checker::ETSChecker * checker,parser::Program * program)310 std::optional<TextChange> ProcessMethodDefinition(ir::MethodDefinition *method, checker::ETSChecker *checker,
311 parser::Program *program)
312 {
313 ES2PANDA_ASSERT(method != nullptr);
314
315 auto signature = GetFuncSignature(method->TsType()->AsETSFunctionType(), method);
316 auto typeStr = GetReturnTypeStr(signature->ReturnType(), checker);
317
318 auto textChange = GetTextChange(method->Function(), typeStr, program);
319 return textChange;
320 }
321
ProcessArrowFunction(ir::ArrowFunctionExpression * arrowFunc,checker::ETSChecker * checker,parser::Program * program)322 std::optional<TextChange> ProcessArrowFunction(ir::ArrowFunctionExpression *arrowFunc, checker::ETSChecker *checker,
323 parser::Program *program)
324 {
325 ES2PANDA_ASSERT(arrowFunc != nullptr);
326
327 auto signature = arrowFunc->Function()->Signature();
328 auto typeStr = GetReturnTypeStr(signature->ReturnType(), checker);
329
330 auto textChange = GetTextChange(arrowFunc->Function(), typeStr, program);
331 return textChange;
332 }
333
ProcessIdentifier(ir::Identifier * identifier,checker::ETSChecker * checker,parser::Program * program)334 std::optional<TextChange> ProcessIdentifier(ir::Identifier *identifier, checker::ETSChecker *checker,
335 parser::Program *program)
336 {
337 ES2PANDA_ASSERT(identifier != nullptr);
338
339 auto decl = compiler::DeclarationFromIdentifier(identifier);
340 if (decl != nullptr && decl->IsMethodDefinition()) {
341 return ProcessMethodDefinition(decl->AsMethodDefinition(), checker, program);
342 }
343 if (decl != nullptr && decl->IsArrowFunctionExpression()) {
344 return ProcessArrowFunction(decl->AsArrowFunctionExpression(), checker, program);
345 }
346 if (decl != nullptr && decl->IsClassProperty()) {
347 auto propertyValue = decl->AsClassProperty()->Value();
348 if (propertyValue != nullptr && propertyValue->IsArrowFunctionExpression()) {
349 return ProcessArrowFunction(propertyValue->AsArrowFunctionExpression(), checker, program);
350 }
351 }
352 return std::nullopt;
353 }
354 } // namespace ark::es2panda::lsp