1 /*
2  * Copyright (c) 2021-2024 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 "ETSAnalyzerHelpers.h"
17 #include "checker/types/ets/etsAsyncFuncReturnType.h"
18 
19 namespace ark::es2panda::checker {
CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker *checker, checker::ETSObjectType *objType, ir::ScriptFunction *extensionFunc, checker::Signature *signature)20 void CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker *checker, checker::ETSObjectType *objType,
21                                                        ir::ScriptFunction *extensionFunc, checker::Signature *signature)
22 {
23     const auto methodName = extensionFunc->Id()->Name();
24     // Only check if there are class and interfaces' instance methods which would shadow instance extension method
25     auto *const variable = objType->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(methodName);
26     if (variable == nullptr) {
27         return;
28     }
29 
30     const auto *const funcType = variable->TsType()->AsETSFunctionType();
31     for (auto *funcSignature : funcType->CallSignatures()) {
32         signature->SetReturnType(funcSignature->ReturnType());
33         if (!checker->Relation()->IsCompatibleTo(signature, funcSignature)) {
34             continue;
35         }
36 
37         checker->ReportWarning({"extension is shadowed by a instance member function '", funcType->Name(),
38                                 funcSignature, "' in class ", objType->Name()},
39                                extensionFunc->Body()->Start());
40         return;
41     }
42 }
43 
CheckExtensionIsShadowedByMethod(checker::ETSChecker *checker, checker::ETSObjectType *objType, ir::ScriptFunction *extensionFunc, checker::Signature *signature)44 void CheckExtensionIsShadowedByMethod(checker::ETSChecker *checker, checker::ETSObjectType *objType,
45                                       ir::ScriptFunction *extensionFunc, checker::Signature *signature)
46 {
47     if (objType == nullptr) {
48         return;
49     }
50 
51     CheckExtensionIsShadowedInCurrentClassOrInterface(checker, objType, extensionFunc, signature);
52 
53     for (auto *interface : objType->Interfaces()) {
54         CheckExtensionIsShadowedByMethod(checker, interface, extensionFunc, signature);
55     }
56 
57     CheckExtensionIsShadowedByMethod(checker, objType->SuperType(), extensionFunc, signature);
58 }
59 
ReplaceThisInExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc)60 static void ReplaceThisInExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc)
61 {
62     ASSERT(!extensionFunc->Params().empty());
63     ASSERT(extensionFunc->Params()[0]->AsETSParameterExpression()->Ident()->Name() ==
64            varbinder::TypedBinder::MANDATORY_PARAM_THIS);
65     auto thisVariable = extensionFunc->Params()[0]->Variable();
66     extensionFunc->Body()->TransformChildrenRecursively(
67         [=](ir::AstNode *ast) {
68             if (ast->IsThisExpression()) {
69                 auto *thisParam = checker->Allocator()->New<ir::Identifier>(
70                     varbinder::TypedBinder::MANDATORY_PARAM_THIS, checker->Allocator());
71                 thisParam->SetParent(ast->Parent());
72                 thisParam->SetVariable(thisVariable);
73                 return static_cast<ir::AstNode *>(thisParam);
74             }
75             return ast;
76         },
77         "replace-this-in-extension-method");
78 }
79 
CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::MethodDefinition *node)80 void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::MethodDefinition *node)
81 {
82     auto *const classType = checker->GetApparentType(extensionFunc->Signature()->Params()[0]->TsType());
83     if (!classType->IsETSObjectType() ||
84         (!classType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::CLASS) &&
85          !classType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE))) {
86         checker->LogTypeError("Extension function can only defined for class and interface type.", node->Start());
87     }
88     if (classType->Variable()->Declaration()->Node()->IsClassDefinition() &&
89         !classType->Variable()->Declaration()->Node()->AsClassDefinition()->IsClassDefinitionChecked()) {
90         classType->Variable()->Declaration()->Node()->Check(checker);
91     }
92 
93     // NOTE(gogabr): should be done in a lowering
94     ReplaceThisInExtensionMethod(checker, extensionFunc);
95 
96     checker::SignatureInfo *originalExtensionSigInfo = checker->Allocator()->New<checker::SignatureInfo>(
97         extensionFunc->Signature()->GetSignatureInfo(), checker->Allocator());
98     originalExtensionSigInfo->minArgCount -= 1;
99     originalExtensionSigInfo->params.erase(originalExtensionSigInfo->params.begin());
100     checker::Signature *originalExtensionSigature =
101         checker->CreateSignature(originalExtensionSigInfo, extensionFunc->Signature()->ReturnType(), extensionFunc);
102 
103     CheckExtensionIsShadowedByMethod(checker, classType->AsETSObjectType(), extensionFunc, originalExtensionSigature);
104 }
105 
DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc)106 void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc)
107 {
108     if (scriptFunc->HasBody() && (node->IsNative() || node->IsAbstract() || node->IsDeclare())) {
109         checker->LogTypeError("Native, Abstract and Declare methods cannot have body.", scriptFunc->Body()->Start());
110     }
111 
112     if (!scriptFunc->IsAsyncFunc() && scriptFunc->HasBody() &&
113         (!scriptFunc->IsExternal() || scriptFunc->IsExternalOverload())) {
114         checker::ScopeContext scopeCtx(checker, scriptFunc->Scope());
115         checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
116                                                   checker->Context().ContainingClass());
117         checker->Context().SetContainingSignature(checker->GetSignatureFromMethodDefinition(node));
118 
119         if (node->IsStatic() && !node->IsConstructor() &&
120             !checker->Context().ContainingClass()->HasObjectFlag(checker::ETSObjectFlags::GLOBAL)) {
121             checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT);
122         }
123 
124         if (node->IsConstructor()) {
125             checker->AddStatus(checker::CheckerStatus::IN_CONSTRUCTOR);
126         }
127 
128         if (node->IsExtensionMethod()) {
129             CheckExtensionMethod(checker, scriptFunc, node);
130         }
131 
132         scriptFunc->Body()->Check(checker);
133 
134         if (scriptFunc->ReturnTypeAnnotation() == nullptr) {
135             if (scriptFunc->IsAsyncImplFunc()) {
136                 ComposeAsyncImplFuncReturnType(checker, scriptFunc);
137             }
138 
139             for (auto &returnStatement : scriptFunc->ReturnStatements()) {
140                 returnStatement->SetReturnType(checker, scriptFunc->Signature()->ReturnType());
141             }
142         }
143 
144         checker->Context().SetContainingSignature(nullptr);
145     }
146 }
147 
ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)148 void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)
149 {
150     const auto &promiseGlobal = checker->GlobalBuiltinPromiseType()->AsETSObjectType();
151     auto promiseType =
152         promiseGlobal->Instantiate(checker->Allocator(), checker->Relation(), checker->GetGlobalTypesHolder())
153             ->AsETSObjectType();
154     promiseType->AddTypeFlag(checker::TypeFlag::GENERIC);
155     promiseType->TypeArguments().clear();
156     promiseType->TypeArguments().emplace_back(scriptFunc->Signature()->ReturnType());
157 
158     auto *objectId =
159         checker->AllocNode<ir::Identifier>(compiler::Signatures::BUILTIN_OBJECT_CLASS, checker->Allocator());
160     objectId->SetReference();
161     checker->VarBinder()->AsETSBinder()->LookupTypeReference(objectId, false);
162     auto *returnType = checker->AllocNode<ir::ETSTypeReference>(
163         checker->AllocNode<ir::ETSTypeReferencePart>(objectId, nullptr, nullptr));
164     objectId->SetParent(returnType->Part());
165     returnType->Part()->SetParent(returnType);
166     returnType->SetTsType(
167         checker->Allocator()->New<ETSAsyncFuncReturnType>(checker->Allocator(), checker->Relation(), promiseType));
168     returnType->Check(checker);
169     scriptFunc->Signature()->SetReturnType(returnType->TsType());
170 }
171 
ComposeAsyncImplMethod(ETSChecker *checker, ir::MethodDefinition *node)172 void ComposeAsyncImplMethod(ETSChecker *checker, ir::MethodDefinition *node)
173 {
174     auto *classDef = checker->FindAncestorGivenByType(node, ir::AstNodeType::CLASS_DEFINITION)->AsClassDefinition();
175     auto *scriptFunc = node->Function();
176     ir::MethodDefinition *implMethod = checker->CreateAsyncProxy(node, classDef);
177 
178     implMethod->Check(checker);
179     node->SetAsyncPairMethod(implMethod);
180 
181     if (scriptFunc->Signature()->HasSignatureFlag(SignatureFlags::NEED_RETURN_TYPE)) {
182         node->Function()->Signature()->SetReturnType(
183             implMethod->Function()->Signature()->ReturnType()->AsETSAsyncFuncReturnType()->PromiseType());
184         scriptFunc->Signature()->RemoveSignatureFlag(SignatureFlags::NEED_RETURN_TYPE);
185     }
186 
187     if (node->Function()->IsOverload()) {
188         auto *baseOverloadImplMethod = node->BaseOverloadMethod()->AsyncPairMethod();
189         implMethod->Function()->Id()->SetVariable(baseOverloadImplMethod->Function()->Id()->Variable());
190         baseOverloadImplMethod->AddOverload(implMethod);
191     } else {
192         classDef->Body().push_back(implMethod);
193     }
194 }
195 
CheckPredefinedMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)196 void CheckPredefinedMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)
197 {
198     auto const &position = scriptFunc->Start();
199 
200     if (scriptFunc->IsSetter() && (scriptFunc->Signature()->ReturnType() != checker->GlobalVoidType())) {
201         checker->LogTypeError("Setter must have void return type", position);
202     }
203 
204     if (scriptFunc->IsGetter() && (scriptFunc->Signature()->ReturnType() == checker->GlobalVoidType())) {
205         checker->LogTypeError("Getter must return a value", position);
206     }
207 
208     auto const name = scriptFunc->Id()->Name();
209     auto const methodName = std::string {ir::PREDEFINED_METHOD} + std::string {name.Utf8()};
210 
211     if (name.Is(compiler::Signatures::GET_INDEX_METHOD)) {
212         if (scriptFunc->Signature()->ReturnType() == checker->GlobalVoidType()) {
213             checker->LogTypeError(methodName + "' shouldn't have void return type.", position);
214         }
215     } else if (name.Is(compiler::Signatures::SET_INDEX_METHOD)) {
216         if (scriptFunc->Signature()->ReturnType() != checker->GlobalVoidType()) {
217             checker->LogTypeError(methodName + "' should have void return type.", position);
218         }
219     } else if (name.Is(compiler::Signatures::ITERATOR_METHOD)) {
220         CheckIteratorMethodReturnType(checker, scriptFunc, position, methodName);
221     }
222 }
223 
HasIteratorInterface(ETSObjectType const *const objectType)224 static bool HasIteratorInterface(ETSObjectType const *const objectType)
225 {
226     auto const hasIteratorInterfaceImpl = [](ETSObjectType const *const checkType,
227                                              auto &&iteratorInterfaceImpl) -> bool {
228         if (checkType->Name().Is(ir::ITERATOR_INTERFACE_NAME)) {
229             return true;
230         }
231         for (const auto *const interface : checkType->Interfaces()) {
232             if (iteratorInterfaceImpl(interface, iteratorInterfaceImpl)) {
233                 return true;
234             }
235         }
236         return false;
237     };
238 
239     return hasIteratorInterfaceImpl(objectType, hasIteratorInterfaceImpl);
240 }
241 
CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc, const lexer::SourcePosition &position, const std::string &methodName)242 void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc,
243                                    const lexer::SourcePosition &position, const std::string &methodName)
244 {
245     const auto *returnType = scriptFunc->Signature()->ReturnType();
246 
247     if (returnType == nullptr) {
248         checker->LogTypeError(methodName + "' doesn't have return type.", position);
249     }
250 
251     if (returnType->IsETSTypeParameter()) {
252         returnType = checker->GetApparentType(returnType->AsETSTypeParameter()->GetConstraintType());
253     }
254 
255     if (returnType->IsETSObjectType() && HasIteratorInterface(returnType->AsETSObjectType())) {
256         return;
257     }
258 
259     while (returnType->IsETSObjectType() && returnType->AsETSObjectType()->SuperType() != nullptr) {
260         returnType = returnType->AsETSObjectType()->SuperType();
261         if (returnType->IsETSObjectType() && HasIteratorInterface(returnType->AsETSObjectType())) {
262             return;
263         }
264     }
265 
266     checker->LogTypeError(
267         {"The return type of '", scriptFunc->Id()->Name(), "' must be a type that implements Iterator interface."},
268         position);
269 }
270 
InitAnonymousLambdaCallee(checker::ETSChecker *checker, ir::Expression *callee, checker::Type *calleeType)271 checker::Type *InitAnonymousLambdaCallee(checker::ETSChecker *checker, ir::Expression *callee,
272                                          checker::Type *calleeType)
273 {
274     auto *const arrowFunc = callee->AsArrowFunctionExpression()->Function();
275 
276     ArenaVector<ir::Expression *> params {checker->Allocator()->Adapter()};
277     checker->CopyParams(arrowFunc->Params(), params);
278     checker::Type *funcReturnType = nullptr;
279 
280     auto *typeAnnotation = arrowFunc->ReturnTypeAnnotation();
281     if (typeAnnotation != nullptr) {
282         typeAnnotation = typeAnnotation->Clone(checker->Allocator(), nullptr);
283         typeAnnotation->SetTsType(arrowFunc->ReturnTypeAnnotation()->TsType());
284     } else if (arrowFunc->Signature()->ReturnType() != nullptr) {
285         auto newTypeAnnotation = callee->AsArrowFunctionExpression()->CreateTypeAnnotation(checker);
286         typeAnnotation = arrowFunc->ReturnTypeAnnotation();
287         funcReturnType = newTypeAnnotation->GetType(checker);
288     }
289 
290     auto signature = ir::FunctionSignature(nullptr, std::move(params), typeAnnotation);
291     auto *funcType = checker->AllocNode<ir::ETSFunctionType>(std::move(signature), ir::ScriptFunctionFlags::NONE);
292 
293     funcType->SetScope(arrowFunc->Scope()->AsFunctionScope()->ParamScope());
294     auto *const funcIface = typeAnnotation != nullptr ? funcType->Check(checker) : funcReturnType;
295     checker->Relation()->SetNode(callee);
296     checker->Relation()->IsAssignableTo(calleeType, funcIface);
297     return funcIface;
298 }
299 
ResolveCallExtensionFunction(checker::ETSFunctionType *functionType, checker::ETSChecker *checker, ir::CallExpression *expr)300 checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *functionType, checker::ETSChecker *checker,
301                                                  ir::CallExpression *expr)
302 {
303     auto *memberExpr = expr->Callee()->AsMemberExpression();
304     expr->Arguments().insert(expr->Arguments().begin(), memberExpr->Object());
305     auto *signature =
306         checker->ResolveCallExpressionAndTrailingLambda(functionType->CallSignatures(), expr, expr->Start());
307     if (signature == nullptr) {
308         return nullptr;
309     }
310     if (!signature->Function()->IsExtensionMethod()) {
311         checker->LogTypeError({"Property '", memberExpr->Property()->AsIdentifier()->Name(),
312                                "' does not exist on type '", memberExpr->ObjType()->Name(), "'"},
313                               memberExpr->Property()->Start());
314     }
315     expr->SetSignature(signature);
316     expr->SetCallee(memberExpr->Property());
317     memberExpr->Property()->AsIdentifier()->SetParent(expr);
318     expr->Arguments()[0]->SetParent(expr);
319     checker->HandleUpdatedCallExpressionNode(expr);
320     // Set TsType for new Callee(original member expression's Object)
321     expr->Callee()->Check(checker);
322     return signature;
323 }
324 
ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type, checker::ETSChecker *checker, ir::CallExpression *expr)325 checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type,
326                                                              checker::ETSChecker *checker, ir::CallExpression *expr)
327 {
328     checker::Signature *signature = checker->ResolveCallExpressionAndTrailingLambda(
329         type->ClassMethodType()->CallSignatures(), expr, expr->Start(), checker::TypeRelationFlag::NO_THROW);
330 
331     if (signature != nullptr) {
332         if (expr->Callee()->IsMemberExpression()) {
333             auto memberExpr = expr->Callee()->AsMemberExpression();
334             auto var = type->ClassMethodType()->Variable();
335             memberExpr->Property()->AsIdentifier()->SetVariable(var);
336         }
337 
338         return signature;
339     }
340 
341     return ResolveCallExtensionFunction(type->ExtensionMethodType(), checker, expr);
342 }
343 
GetUnionTypeSignatures(ETSChecker *checker, checker::ETSUnionType *etsUnionType)344 ArenaVector<checker::Signature *> GetUnionTypeSignatures(ETSChecker *checker, checker::ETSUnionType *etsUnionType)
345 {
346     ArenaVector<checker::Signature *> callSignatures(checker->Allocator()->Adapter());
347 
348     for (auto *constituentType : etsUnionType->ConstituentTypes()) {
349         if (constituentType->IsETSObjectType()) {
350             ArenaVector<checker::Signature *> tmpCallSignatures(checker->Allocator()->Adapter());
351             tmpCallSignatures = constituentType->AsETSObjectType()
352                                     ->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>("invoke0")
353                                     ->TsType()
354                                     ->AsETSFunctionType()
355                                     ->CallSignatures();
356             callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
357         }
358         if (constituentType->IsETSFunctionType()) {
359             ArenaVector<checker::Signature *> tmpCallSignatures(checker->Allocator()->Adapter());
360             tmpCallSignatures = constituentType->AsETSFunctionType()->CallSignatures();
361             callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
362         }
363         if (constituentType->IsETSUnionType()) {
364             ArenaVector<checker::Signature *> tmpCallSignatures(checker->Allocator()->Adapter());
365             tmpCallSignatures = GetUnionTypeSignatures(checker, constituentType->AsETSUnionType());
366             callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
367         }
368     }
369 
370     return callSignatures;
371 }
372 
ChooseSignatures(ETSChecker *checker, checker::Type *calleeType, bool isConstructorCall, bool isFunctionalInterface, bool isUnionTypeWithFunctionalInterface)373 ArenaVector<checker::Signature *> &ChooseSignatures(ETSChecker *checker, checker::Type *calleeType,
374                                                     bool isConstructorCall, bool isFunctionalInterface,
375                                                     bool isUnionTypeWithFunctionalInterface)
376 {
377     static ArenaVector<checker::Signature *> unionSignatures(checker->Allocator()->Adapter());
378     unionSignatures.clear();
379     if (isConstructorCall) {
380         return calleeType->AsETSObjectType()->ConstructSignatures();
381     }
382     if (isFunctionalInterface) {
383         return calleeType->AsETSObjectType()
384             ->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME)
385             ->TsType()
386             ->AsETSFunctionType()
387             ->CallSignatures();
388     }
389     if (isUnionTypeWithFunctionalInterface) {
390         unionSignatures = GetUnionTypeSignatures(checker, calleeType->AsETSUnionType());
391         return unionSignatures;
392     }
393     return calleeType->AsETSFunctionType()->CallSignatures();
394 }
395 
ChooseCalleeObj(ETSChecker *checker, ir::CallExpression *expr, checker::Type *calleeType, bool isConstructorCall)396 checker::ETSObjectType *ChooseCalleeObj(ETSChecker *checker, ir::CallExpression *expr, checker::Type *calleeType,
397                                         bool isConstructorCall)
398 {
399     if (isConstructorCall) {
400         return calleeType->AsETSObjectType();
401     }
402     if (expr->Callee()->IsIdentifier()) {
403         return checker->Context().ContainingClass();
404     }
405     ASSERT(expr->Callee()->IsMemberExpression());
406     return expr->Callee()->AsMemberExpression()->ObjType();
407 }
408 
ProcessExclamationMark(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)409 void ProcessExclamationMark(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)
410 {
411     if (checker->IsNullLikeOrVoidExpression(expr->Argument())) {
412         auto tsType = checker->CreateETSBooleanType(true);
413         tsType->AddTypeFlag(checker::TypeFlag::CONSTANT);
414         expr->SetTsType(tsType);
415         return;
416     }
417 
418     if (operandType == nullptr || !operandType->IsConditionalExprType()) {
419         checker->LogTypeError("Bad operand type, the type of the operand must be boolean type.",
420                               expr->Argument()->Start());
421         expr->SetTsType(checker->GlobalTypeError());
422         return;
423     }
424 
425     auto exprRes = operandType->ResolveConditionExpr();
426     if (std::get<0>(exprRes)) {
427         auto tsType = checker->CreateETSBooleanType(!std::get<1>(exprRes));
428         tsType->AddTypeFlag(checker::TypeFlag::CONSTANT);
429         expr->SetTsType(tsType);
430         return;
431     }
432 
433     expr->SetTsType(checker->GlobalETSBooleanType());
434 }
435 
SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)436 void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)
437 {
438     switch (expr->OperatorType()) {
439         case lexer::TokenType::PUNCTUATOR_MINUS:
440         case lexer::TokenType::PUNCTUATOR_PLUS: {
441             if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
442                 checker->LogTypeError("Bad operand type, the type of the operand must be numeric type.",
443                                       expr->Argument()->Start());
444                 expr->SetTsType(checker->GlobalTypeError());
445                 break;
446             }
447 
448             if (operandType->HasTypeFlag(checker::TypeFlag::CONSTANT) &&
449                 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
450                 expr->SetTsType(checker->NegateNumericType(operandType, expr));
451                 break;
452             }
453 
454             expr->SetTsType(operandType);
455             break;
456         }
457         case lexer::TokenType::PUNCTUATOR_TILDE: {
458             if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
459                 checker->LogTypeError("Bad operand type, the type of the operand must be numeric type.",
460                                       expr->Argument()->Start());
461                 expr->SetTsType(checker->GlobalTypeError());
462                 break;
463             }
464 
465             if (operandType->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
466                 expr->SetTsType(checker->BitwiseNegateNumericType(operandType, expr));
467                 break;
468             }
469 
470             expr->SetTsType(checker->SelectGlobalIntegerTypeForNumeric(operandType));
471             break;
472         }
473         case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
474             ProcessExclamationMark(checker, expr, operandType);
475             break;
476         }
477         case lexer::TokenType::PUNCTUATOR_DOLLAR_DOLLAR: {
478             expr->SetTsType(expr->Argument()->TsType());
479             break;
480         }
481         default: {
482             UNREACHABLE();
483             break;
484         }
485     }
486 }
487 
CreateSyntheticType(ETSChecker *checker, util::StringView const &syntheticName, checker::ETSObjectType *lastObjectType, ir::Identifier *id)488 checker::ETSObjectType *CreateSyntheticType(ETSChecker *checker, util::StringView const &syntheticName,
489                                             checker::ETSObjectType *lastObjectType, ir::Identifier *id)
490 {
491     auto *syntheticObjType = checker->Allocator()->New<checker::ETSObjectType>(
492         checker->Allocator(), syntheticName, syntheticName,
493         std::make_tuple(id, checker::ETSObjectFlags::NO_OPTS, checker->Relation()));
494 
495     auto *classDecl = checker->Allocator()->New<varbinder::ClassDecl>(syntheticName);
496     varbinder::LocalVariable *var =
497         checker->Allocator()->New<varbinder::LocalVariable>(classDecl, varbinder::VariableFlags::CLASS);
498     var->SetTsType(syntheticObjType);
499     lastObjectType->AddProperty<checker::PropertyType::STATIC_FIELD>(var);
500     syntheticObjType->SetEnclosingType(lastObjectType);
501     return syntheticObjType;
502 }
503 
504 // NOLINTBEGIN(modernize-avoid-c-arrays)
505 static constexpr char const INVALID_CONST_ASSIGNMENT[] = "Cannot assign a value to a constant variable ";
506 static constexpr char const INVALID_READONLY_ASSIGNMENT[] = "Cannot assign a value to a readonly variable ";
507 static constexpr char const ITERATOR_TYPE_ABSENT[] = "Cannot obtain iterator type in 'for-of' statement.";
508 // NOLINTEND(modernize-avoid-c-arrays)
509 
GetIteratorType(ETSChecker *checker, checker::Type *elemType, ir::AstNode *left)510 checker::Type *GetIteratorType(ETSChecker *checker, checker::Type *elemType, ir::AstNode *left)
511 {
512     // Just to avoid extra nested level(s)
513     auto const getIterType = [checker, elemType](ir::VariableDeclarator *const declarator) -> checker::Type * {
514         if (declarator->TsType() == nullptr) {
515             if (auto *resolved = checker->FindVariableInFunctionScope(declarator->Id()->AsIdentifier()->Name(),
516                                                                       varbinder::ResolveBindingOptions::ALL_NON_TYPE);
517                 resolved != nullptr) {
518                 resolved->SetTsType(elemType);
519                 return elemType;
520             }
521         } else {
522             return declarator->TsType();
523         }
524         return checker->GlobalTypeError();
525     };
526 
527     checker::Type *iterType = nullptr;
528     if (left->IsIdentifier()) {
529         if (auto *const variable = left->AsIdentifier()->Variable(); variable != nullptr) {
530             auto *decl = variable->Declaration();
531             if (decl->IsConstDecl() || decl->IsReadonlyDecl()) {
532                 std::string_view errorMsg =
533                     decl->IsConstDecl() ? INVALID_CONST_ASSIGNMENT : INVALID_READONLY_ASSIGNMENT;
534                 checker->LogTypeError({errorMsg, variable->Name()}, decl->Node()->Start());
535             }
536         }
537         iterType = left->AsIdentifier()->TsType();
538     } else if (left->IsVariableDeclaration()) {
539         if (auto const &declarators = left->AsVariableDeclaration()->Declarators(); !declarators.empty()) {
540             iterType = getIterType(declarators.front());
541         }
542     }
543 
544     if (iterType == nullptr) {
545         checker->LogTypeError(ITERATOR_TYPE_ABSENT, left->Start());
546         return checker->GlobalTypeError();
547     }
548     return iterType;
549 }
550 
CheckArgumentVoidType(checker::Type *&funcReturnType, ETSChecker *checker, const std::string &name, ir::ReturnStatement *st)551 bool CheckArgumentVoidType(checker::Type *&funcReturnType, ETSChecker *checker, const std::string &name,
552                            ir::ReturnStatement *st)
553 {
554     if (name.find(compiler::Signatures::ETS_MAIN_WITH_MANGLE_BEGIN) != std::string::npos) {
555         if (!funcReturnType->IsETSVoidType() && !funcReturnType->IsIntType()) {
556             checker->LogTypeError("Bad return type, main enable only void or int type.", st->Start());
557         }
558     }
559     return true;
560 }
561 
CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker::Type *argumentType, ir::Expression *stArgument, bool isAsync)562 bool CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker::Type *argumentType,
563                      ir::Expression *stArgument, bool isAsync)
564 {
565     if (funcReturnType->IsETSVoidType() || funcReturnType == checker->GlobalVoidType()) {
566         if (argumentType != checker->GlobalVoidType()) {
567             checker->LogTypeError("Unexpected return value, enclosing method return type is void.",
568                                   stArgument->Start());
569             return false;
570         }
571         if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType,
572                                         stArgument->Start(), {},
573                                         checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
574                  .IsAssignable()) {
575             checker->LogTypeError({"Return statement type is not compatible with the enclosing method's return type."},
576                                   stArgument->Start());
577             return false;
578         }
579         return true;
580     }
581 
582     if (isAsync && funcReturnType->IsETSObjectType() &&
583         funcReturnType->AsETSObjectType()->GetOriginalBaseType() == checker->GlobalBuiltinPromiseType()) {
584         auto promiseArg = funcReturnType->AsETSObjectType()->TypeArguments()[0];
585         checker::AssignmentContext(checker->Relation(), stArgument, argumentType, promiseArg, stArgument->Start(), {},
586                                    checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW);
587         if (checker->Relation()->IsTrue()) {
588             return true;
589         }
590     }
591 
592     const Type *targetType = checker->TryGettingFunctionTypeFromInvokeFunction(funcReturnType);
593     const Type *sourceType = checker->TryGettingFunctionTypeFromInvokeFunction(argumentType);
594     if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType, stArgument->Start(),
595                                     {}, checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
596              .IsAssignable()) {
597         checker->LogTypeError(
598             {"Type '", sourceType, "' is not compatible with the enclosing method's return type '", targetType, "'"},
599             stArgument->Start());
600         return false;
601     }
602     return true;
603 }
604 
InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, ir::Expression *stArgument)605 void InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType,
606                      ir::Expression *stArgument)
607 {
608     //  First (or single) return statement in the function:
609     funcReturnType =
610         stArgument == nullptr ? checker->GlobalVoidType() : checker->GetNonConstantType(stArgument->Check(checker));
611     /*
612     when st_argment is ArrowFunctionExpression, need infer type for st_argment
613     example code:
614     ```
615     return () => {}
616     ```
617     */
618     if (stArgument != nullptr && stArgument->IsArrowFunctionExpression()) {
619         auto arrowFunc = stArgument->AsArrowFunctionExpression();
620         auto typeAnnotation = arrowFunc->CreateTypeAnnotation(checker);
621 
622         auto *argumentType = arrowFunc->TsType();
623         funcReturnType = typeAnnotation->GetType(checker);
624 
625         const Type *sourceType = checker->TryGettingFunctionTypeFromInvokeFunction(argumentType);
626         const Type *targetType = checker->TryGettingFunctionTypeFromInvokeFunction(funcReturnType);
627 
628         if (!checker::AssignmentContext(checker->Relation(), arrowFunc, argumentType, funcReturnType,
629                                         stArgument->Start(), {},
630                                         checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
631                  .IsAssignable()) {
632             checker->LogTypeError({"Type '", sourceType,
633                                    "' is not compatible with the enclosing method's return type '", targetType, "'"},
634                                   stArgument->Start());
635             funcReturnType = checker->GlobalTypeError();
636             return;
637         }
638     }
639 
640     containingFunc->Signature()->SetReturnType(funcReturnType);
641     containingFunc->Signature()->RemoveSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE);
642     containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
643     checker->VarBinder()->AsETSBinder()->BuildFunctionName(containingFunc);
644 
645     if (stArgument != nullptr && stArgument->IsObjectExpression()) {
646         stArgument->AsObjectExpression()->SetPreferredType(funcReturnType);
647     }
648 }
649 
ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType, ir::ReturnStatement *st, ir::Expression *stArgument)650 void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType,
651                              ir::ReturnStatement *st, ir::Expression *stArgument)
652 {
653     funcReturnType = containingFunc->Signature()->ReturnType();
654 
655     if (stArgument == nullptr) {
656         // previous return statement(s) have value
657         if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType()) {
658             checker->LogTypeError("All return statements in the function should be empty or have a value.",
659                                   st->Start());
660             return;
661         }
662     } else {
663         if (stArgument->IsObjectExpression()) {
664             stArgument->AsObjectExpression()->SetPreferredType(funcReturnType);
665         }
666 
667         if (stArgument->IsMemberExpression()) {
668             checker->SetArrayPreferredTypeForNestedMemberExpressions(stArgument->AsMemberExpression(), funcReturnType);
669         }
670 
671         checker::Type *argumentType = checker->GetNonConstantType(stArgument->Check(checker));
672 
673         //  previous return statement(s) don't have any value
674         if (funcReturnType->IsETSVoidType() && !argumentType->IsETSVoidType()) {
675             checker->LogTypeError("All return statements in the function should be empty or have a value.",
676                                   stArgument->Start());
677             return;
678         }
679 
680         const auto name = containingFunc->Scope()->InternalName().Mutf8();
681         if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) {
682             return;
683         }
684 
685         auto *const relation = checker->Relation();
686         relation->SetNode(stArgument);
687 
688         if (!relation->IsIdenticalTo(funcReturnType, argumentType)) {
689             checker->ResolveReturnStatement(funcReturnType, argumentType, containingFunc, st);
690         }
691 
692         relation->SetNode(nullptr);
693         relation->SetFlags(checker::TypeRelationFlag::NONE);
694     }
695 }
696 
CreateOptionalSignaturesForFunctionalType(ETSChecker *checker, ir::ETSFunctionType *node, ETSObjectType *genericInterfaceType, Substitution *substitution, size_t optionalParameterIndex)697 ETSObjectType *CreateOptionalSignaturesForFunctionalType(ETSChecker *checker, ir::ETSFunctionType *node,
698                                                          ETSObjectType *genericInterfaceType,
699                                                          Substitution *substitution, size_t optionalParameterIndex)
700 {
701     const auto &params = node->Params();
702     auto returnType = node->ReturnType()->GetType(checker);
703 
704     for (size_t i = 0; i < optionalParameterIndex; i++) {
705         checker::ETSChecker::EmplaceSubstituted(
706             substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
707             InstantiateBoxedPrimitiveType(checker, params[i],
708                                           params[i]->AsETSParameterExpression()->TypeAnnotation()->GetType(checker)));
709     }
710 
711     for (size_t i = optionalParameterIndex; i < params.size(); i++) {
712         checker::ETSChecker::EmplaceSubstituted(
713             substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
714             CreateParamTypeWithDefaultParam(checker, params[i]));
715     }
716 
717     checker::ETSChecker::EmplaceSubstituted(
718         substitution,
719         genericInterfaceType->TypeArguments()[genericInterfaceType->TypeArguments().size() - 1]
720             ->AsETSTypeParameter()
721             ->GetOriginal(),
722         InstantiateBoxedPrimitiveType(checker, node->ReturnType(), returnType));
723 
724     return genericInterfaceType->Substitute(checker->Relation(), substitution)->AsETSObjectType();
725 }
726 
CreateInterfaceTypeForETSFunctionType(ETSChecker *checker, ir::ETSFunctionType *node, ETSObjectType *genericInterfaceType, Substitution *substitution)727 ETSObjectType *CreateInterfaceTypeForETSFunctionType(ETSChecker *checker, ir::ETSFunctionType *node,
728                                                      ETSObjectType *genericInterfaceType, Substitution *substitution)
729 {
730     size_t i = 0;
731     if (auto const &params = node->Params(); params.size() < checker->GlobalBuiltinFunctionTypeVariadicThreshold()) {
732         for (; i < params.size(); i++) {
733             checker::ETSChecker::EmplaceSubstituted(
734                 substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
735                 InstantiateBoxedPrimitiveType(
736                     checker, params[i], params[i]->AsETSParameterExpression()->TypeAnnotation()->GetType(checker)));
737         }
738     }
739 
740     checker::ETSChecker::EmplaceSubstituted(
741         substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
742         InstantiateBoxedPrimitiveType(checker, node->ReturnType(), node->ReturnType()->GetType(checker)));
743     return genericInterfaceType->Substitute(checker->Relation(), substitution)->AsETSObjectType();
744 }
745 
CreateParamTypeWithDefaultParam(ETSChecker *checker, ir::Expression *param)746 Type *CreateParamTypeWithDefaultParam(ETSChecker *checker, ir::Expression *param)
747 {
748     if (!param->AsETSParameterExpression()->IsDefault()) {
749         checker->LogTypeError({"Expected initializer for ", param->AsETSParameterExpression()->Ident()->Name()},
750                               param->Start());
751     }
752 
753     ArenaVector<Type *> types(checker->Allocator()->Adapter());
754     types.push_back(InstantiateBoxedPrimitiveType(
755         checker, param, param->AsETSParameterExpression()->TypeAnnotation()->GetType(checker)));
756 
757     if (param->AsETSParameterExpression()->Initializer()->IsUndefinedLiteral()) {
758         types.push_back(checker->GlobalETSUndefinedType());
759     }
760 
761     return checker->CreateETSUnionType(Span<Type *const>(types));
762 }
763 
InstantiateBoxedPrimitiveType(ETSChecker *checker, ir::Expression *param, Type *paramType)764 Type *InstantiateBoxedPrimitiveType(ETSChecker *checker, ir::Expression *param, Type *paramType)
765 {
766     if (paramType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE)) {
767         auto node = checker->Relation()->GetNode();
768         checker->Relation()->SetNode(param);
769         auto *const boxedTypeArg = checker->PrimitiveTypeAsETSBuiltinType(paramType);
770         ASSERT(boxedTypeArg);
771         paramType =
772             boxedTypeArg->Instantiate(checker->Allocator(), checker->Relation(), checker->GetGlobalTypesHolder());
773         checker->Relation()->SetNode(node);
774     }
775 
776     return paramType;
777 }
778 }  // namespace ark::es2panda::checker
779