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