• 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 "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