• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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 "ETSAnalyzerHelpers.h"
17 #include "checker/types/ets/etsAsyncFuncReturnType.h"
18 
19 namespace ark::es2panda::checker {
20 
IsValidReceiverParameter(Type * const thisType)21 static bool IsValidReceiverParameter(Type *const thisType)
22 {
23     if (thisType == nullptr) {
24         return false;
25     }
26 
27     if (thisType->IsETSArrayType() || thisType->IsETSTypeParameter()) {
28         return true;
29     }
30 
31     if (!thisType->IsETSObjectType() || thisType->IsETSEnumType()) {
32         return false;
33     }
34 
35     auto *const thisObjectType = thisType->AsETSObjectType();
36     return thisObjectType->HasObjectFlag(ETSObjectFlags::CLASS | ETSObjectFlags::INTERFACE);
37 }
38 
CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker * checker,checker::ETSObjectType * objType,ir::ScriptFunction * extensionFunc,checker::Signature * signature)39 void CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker *checker, checker::ETSObjectType *objType,
40                                                        ir::ScriptFunction *extensionFunc, checker::Signature *signature)
41 {
42     const auto methodName = extensionFunc->Id()->Name();
43 
44     // check if there are class and interfaces' instance fields with the same name as extensions.
45     auto *const fieldVariable = objType->GetOwnProperty<checker::PropertyType::INSTANCE_FIELD>(methodName);
46     if (fieldVariable != nullptr) {
47         checker->LogError(diagnostic::EXTENSION_NAME_CONFLICT_WITH_FIELD, {methodName, objType->Name()},
48                           extensionFunc->Body()->Start());
49         return;
50     }
51 
52     // check if there are class and interfaces' instance methods with the same name as extensions.
53     auto *const methodVariable = objType->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(methodName);
54     if (methodVariable == nullptr || methodVariable->TsType()->IsTypeError()) {
55         return;
56     }
57 
58     const auto *const funcType = methodVariable->TsType()->AsETSFunctionType();
59     for (auto *funcSignature : funcType->CallSignatures()) {
60         ES2PANDA_ASSERT(signature != nullptr);
61         signature->SetReturnType(funcSignature->ReturnType());
62         if (!checker->Relation()->SignatureIsSupertypeOf(signature, funcSignature) &&
63             !checker->HasSameAssemblySignature(signature, funcSignature)) {
64             continue;
65         }
66 
67         if (signature->Function()->IsGetter() || signature->Function()->IsSetter()) {
68             checker->LogError(diagnostic::EXTENSION_NAME_CONFLICT_WITH_METHOD, {funcType->Name(), objType->Name()},
69                               extensionFunc->Body()->Start());
70         } else if (funcSignature->HasSignatureFlag(SignatureFlags::PUBLIC)) {
71             checker->LogError(diagnostic::EXTENSION_FUNC_NAME_CONFLICT_WITH_METH, {funcType->Name(), objType->Name()},
72                               extensionFunc->Body()->Start());
73         } else {
74             checker->LogDiagnostic(diagnostic::EXTENSION_NONPUBLIC_COLLISION, {funcType->Name(), objType->Name()},
75                                    extensionFunc->Body()->Start());
76         }
77         return;
78     }
79 }
80 
CheckExtensionIsShadowedByMethod(checker::ETSChecker * checker,checker::ETSObjectType * objType,ir::ScriptFunction * extensionFunc,checker::Signature * signature)81 void CheckExtensionIsShadowedByMethod(checker::ETSChecker *checker, checker::ETSObjectType *objType,
82                                       ir::ScriptFunction *extensionFunc, checker::Signature *signature)
83 {
84     if (objType == nullptr) {
85         return;
86     }
87 
88     CheckExtensionIsShadowedInCurrentClassOrInterface(checker, objType, extensionFunc, signature);
89 
90     for (auto *interface : objType->Interfaces()) {
91         CheckExtensionIsShadowedByMethod(checker, interface, extensionFunc, signature);
92     }
93 
94     CheckExtensionIsShadowedByMethod(checker, objType->SuperType(), extensionFunc, signature);
95 }
96 
ReplaceThisInExtensionMethod(checker::ETSChecker * checker,ir::ScriptFunction * extensionFunc)97 static void ReplaceThisInExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc)
98 {
99     //  Skip processing of possibly invalid extension method
100     if (extensionFunc->Params().empty() ||
101         !extensionFunc->Params()[0]->AsETSParameterExpression()->Ident()->IsReceiver()) {
102         ES2PANDA_ASSERT(checker->IsAnyError());
103         return;
104     }
105 
106     if (extensionFunc->ReturnTypeAnnotation() != nullptr && extensionFunc->ReturnTypeAnnotation()->IsTSThisType()) {
107         // when return `this` in extensionFunction, the type of `this` actually should be type of the receiver,
108         // so some substitution should be done, temporary solution(xingshunxiang).
109         auto *const thisType = extensionFunc->Signature()->Params()[0]->TsType();
110         auto *const thisTypeAnnotation =
111             extensionFunc->Params()[0]->AsETSParameterExpression()->Ident()->TypeAnnotation();
112         extensionFunc->Signature()->SetReturnType(thisType);
113         extensionFunc->SetReturnTypeAnnotation(thisTypeAnnotation->Clone(checker->ProgramAllocator(), extensionFunc));
114     }
115 
116     auto thisVariable = extensionFunc->Params()[0]->Variable();
117     extensionFunc->Body()->TransformChildrenRecursively(
118         [=](ir::AstNode *ast) {
119             if (ast->IsThisExpression()) {
120                 auto *thisParam = checker->ProgramAllocator()->New<ir::Identifier>(
121                     varbinder::TypedBinder::MANDATORY_PARAM_THIS, checker->ProgramAllocator());
122                 thisParam->SetRange(ast->Range());
123                 thisParam->SetParent(ast->Parent());
124                 thisParam->SetVariable(thisVariable);
125                 return static_cast<ir::AstNode *>(thisParam);
126             }
127             return ast;
128         },
129         "replace-this-in-extension-method");
130 }
131 
CheckExtensionMethod(checker::ETSChecker * checker,ir::ScriptFunction * extensionFunc,ir::AstNode * node)132 void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::AstNode *node)
133 {
134     auto *const thisType =
135         !extensionFunc->Signature()->Params().empty() ? extensionFunc->Signature()->Params()[0]->TsType() : nullptr;
136 
137     // "Extension Functions" are only allowed for classes, interfaces, arrays and type parameters extends from object.
138     if (IsValidReceiverParameter(thisType)) {
139         // Skip for arrays (array does not contain a class definition) and checked class definition.
140         if (!thisType->IsETSArrayType() && !thisType->IsETSTypeParameter() &&
141             thisType->Variable()->Declaration()->Node()->IsClassDefinition() &&
142             !thisType->Variable()->Declaration()->Node()->AsClassDefinition()->IsClassDefinitionChecked()) {
143             thisType->Variable()->Declaration()->Node()->Check(checker);
144         }
145         // NOTE(gogabr): should be done in a lowering
146         ReplaceThisInExtensionMethod(checker, extensionFunc);
147         if (extensionFunc->IsArrow()) {
148             return;
149         }
150 
151         checker::SignatureInfo *originalExtensionSigInfo = checker->ProgramAllocator()->New<checker::SignatureInfo>(
152             extensionFunc->Signature()->GetSignatureInfo(), checker->ProgramAllocator());
153         ES2PANDA_ASSERT(originalExtensionSigInfo != nullptr);
154         originalExtensionSigInfo->minArgCount -= 1U;
155         originalExtensionSigInfo->params.erase(originalExtensionSigInfo->params.begin());
156         checker::Signature *originalExtensionSignature =
157             checker->CreateSignature(originalExtensionSigInfo, extensionFunc->Signature()->ReturnType(), extensionFunc);
158 
159         // The shadowing check is only relevant for classes and interfaces,
160         // since for arrays there are no other ways to declare a method other than "Extension Functions".
161         if (thisType->IsETSObjectType()) {
162             CheckExtensionIsShadowedByMethod(checker, thisType->AsETSObjectType(), extensionFunc,
163                                              originalExtensionSignature);
164         }
165 
166         // etsArrayType may extends method from GlobalETSObjectType
167         if (thisType->IsETSArrayType()) {
168             CheckExtensionIsShadowedByMethod(checker, checker->GlobalETSObjectType(), extensionFunc,
169                                              originalExtensionSignature);
170         }
171     } else {
172         checker->LogError(diagnostic::EXTENSION_FUNC_ON_INEXETENSIBLE, {}, node->Start());
173     }
174 }
175 
CheckMethodBodyForNativeAbstractDeclare(ETSChecker * checker,ir::MethodDefinition * node,ir::ScriptFunction * scriptFunc)176 static void CheckMethodBodyForNativeAbstractDeclare(ETSChecker *checker, ir::MethodDefinition *node,
177                                                     ir::ScriptFunction *scriptFunc)
178 {
179     if ((node->IsNative() && !node->IsConstructor()) || node->IsAbstract() || node->IsDeclare()) {
180         checker->LogError(diagnostic::UNEXPECTED_FUNC_BODY, {}, scriptFunc->Body()->Start());
181     }
182 }
183 
CheckNativeConstructorBody(ETSChecker * checker,ir::MethodDefinition * node,ir::ScriptFunction * scriptFunc)184 static void CheckNativeConstructorBody(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc)
185 {
186     if (node->IsNative() && node->IsConstructor()) {
187         checker->LogError(diagnostic::NATIVE_WITH_BODY, {}, scriptFunc->Body()->Start());
188     }
189 }
190 
DoBodyTypeChecking(ETSChecker * checker,ir::MethodDefinition * node,ir::ScriptFunction * scriptFunc)191 void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc)
192 {
193     if (scriptFunc->HasBody()) {
194         CheckMethodBodyForNativeAbstractDeclare(checker, node, scriptFunc);
195         CheckNativeConstructorBody(checker, node, scriptFunc);
196     }
197 
198     if (!scriptFunc->HasBody() || (scriptFunc->IsExternal() && !scriptFunc->IsExternalOverload())) {
199         return;
200     }
201 
202     checker::ScopeContext scopeCtx(checker, scriptFunc->Scope());
203     checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
204                                               checker->Context().ContainingClass());
205     checker->Context().SetContainingSignature(checker->GetSignatureFromMethodDefinition(node));
206 
207     if (node->IsStatic() && !node->IsConstructor() &&
208         !checker->Context().ContainingClass()->HasObjectFlag(checker::ETSObjectFlags::GLOBAL)) {
209         checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT);
210     }
211 
212     if (node->IsConstructor()) {
213         checker->AddStatus(checker::CheckerStatus::IN_CONSTRUCTOR);
214     }
215 
216     if (node->IsExtensionMethod() && scriptFunc->Signature() != nullptr) {
217         CheckExtensionMethod(checker, scriptFunc, node);
218     }
219 
220     scriptFunc->Body()->Check(checker);
221 
222     if (scriptFunc->ReturnTypeAnnotation() == nullptr) {
223         if (scriptFunc->IsAsyncFunc()) {
224             auto returnType = checker->CreateETSAsyncFuncReturnTypeFromBaseType(scriptFunc->Signature()->ReturnType());
225             ES2PANDA_ASSERT(returnType != nullptr);
226             scriptFunc->Signature()->SetReturnType(returnType->PromiseType());
227             for (auto &returnStatement : scriptFunc->ReturnStatements()) {
228                 returnStatement->SetReturnType(checker, returnType);
229             }
230         } else {
231             if (scriptFunc->IsAsyncImplFunc()) {
232                 ComposeAsyncImplFuncReturnType(checker, scriptFunc);
233             }
234 
235             for (auto &returnStatement : scriptFunc->ReturnStatements()) {
236                 returnStatement->SetReturnType(checker, scriptFunc->Signature()->ReturnType());
237             }
238         }
239     }
240 
241     checker->Context().SetContainingSignature(nullptr);
242 }
243 
ComposeAsyncImplFuncReturnType(ETSChecker * checker,ir::ScriptFunction * scriptFunc)244 void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)
245 {
246     auto const promiseType = checker->CreatePromiseOf(checker->MaybeBoxType(scriptFunc->Signature()->ReturnType()));
247 
248     auto *objectId = checker->ProgramAllocNode<ir::Identifier>(compiler::Signatures::BUILTIN_OBJECT_CLASS,
249                                                                checker->ProgramAllocator());
250     checker->VarBinder()->AsETSBinder()->LookupTypeReference(objectId, false);
251     auto *returnType = checker->ProgramAllocNode<ir::ETSTypeReference>(
252         checker->ProgramAllocNode<ir::ETSTypeReferencePart>(objectId, nullptr, nullptr, checker->ProgramAllocator()),
253         checker->ProgramAllocator());
254     ES2PANDA_ASSERT(returnType != nullptr);
255     objectId->SetParent(returnType->Part());
256     returnType->Part()->SetParent(returnType);
257     returnType->SetTsType(checker->ProgramAllocator()->New<ETSAsyncFuncReturnType>(checker->ProgramAllocator(),
258                                                                                    checker->Relation(), promiseType));
259     returnType->Check(checker);
260     scriptFunc->Signature()->SetReturnType(returnType->TsType());
261 }
262 
CheckPredefinedMethodReturnType(ETSChecker * checker,ir::ScriptFunction * scriptFunc)263 void CheckPredefinedMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)
264 {
265     if (scriptFunc->Signature() == nullptr) {
266         ES2PANDA_ASSERT(checker->IsAnyError());
267         return;
268     }
269 
270     auto const &position = scriptFunc->Start();
271 
272     if (scriptFunc->IsGetter() && (scriptFunc->Signature()->ReturnType() == checker->GlobalVoidType())) {
273         checker->LogError(diagnostic::GETTER_VOID, {}, position);
274     }
275 
276     auto const name = scriptFunc->Id()->Name();
277     auto const methodName = std::string {ir::PREDEFINED_METHOD} + std::string {name.Utf8()};
278 
279     if (name.Is(compiler::Signatures::GET_INDEX_METHOD)) {
280         if (scriptFunc->Signature()->ReturnType() == checker->GlobalVoidType()) {
281             checker->LogError(diagnostic::UNEXPECTED_VOID, {util::StringView(methodName)}, position);
282         }
283     } else if (name.Is(compiler::Signatures::SET_INDEX_METHOD)) {
284         if (scriptFunc->Signature()->ReturnType() != checker->GlobalVoidType()) {
285             checker->LogError(diagnostic::UNEXPECTED_NONVOID, {util::StringView(methodName)}, position);
286         }
287     } else if (name.Is(compiler::Signatures::ITERATOR_METHOD)) {
288         CheckIteratorMethodReturnType(checker, scriptFunc, position, methodName);
289     }
290 }
291 
HasIteratorInterface(ETSObjectType const * const objectType)292 static bool HasIteratorInterface(ETSObjectType const *const objectType)
293 {
294     auto const hasIteratorInterfaceImpl = [](ETSObjectType const *const checkType,
295                                              auto &&iteratorInterfaceImpl) -> bool {
296         if (checkType->Name().Is(ir::ITERATOR_INTERFACE_NAME)) {
297             return true;
298         }
299         for (const auto *const interface : checkType->Interfaces()) {
300             if (iteratorInterfaceImpl(interface, iteratorInterfaceImpl)) {
301                 return true;
302             }
303         }
304         return false;
305     };
306 
307     return hasIteratorInterfaceImpl(objectType, hasIteratorInterfaceImpl);
308 }
309 
CheckIteratorMethodReturnType(ETSChecker * checker,ir::ScriptFunction * scriptFunc,const lexer::SourcePosition & position,const std::string & methodName)310 void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc,
311                                    const lexer::SourcePosition &position, const std::string &methodName)
312 {
313     const auto *returnType = scriptFunc->Signature()->ReturnType();
314 
315     if (returnType == nullptr) {
316         checker->LogError(diagnostic::MISSING_RETURN_TYPE_2, {util::StringView(methodName)}, position);
317         return;
318     }
319 
320     if (returnType->IsETSTypeParameter()) {
321         returnType = checker->GetApparentType(returnType->AsETSTypeParameter()->GetConstraintType());
322     }
323 
324     ES2PANDA_ASSERT(returnType != nullptr);
325     if (returnType->IsETSObjectType() && HasIteratorInterface(returnType->AsETSObjectType())) {
326         return;
327     }
328 
329     while (returnType->IsETSObjectType() && returnType->AsETSObjectType()->SuperType() != nullptr) {
330         returnType = returnType->AsETSObjectType()->SuperType();
331         if (returnType->IsETSObjectType() && HasIteratorInterface(returnType->AsETSObjectType())) {
332             return;
333         }
334     }
335 
336     checker->LogError(diagnostic::RETURN_ISNT_ITERATOR, {scriptFunc->Id()->Name()}, position);
337 }
338 
SwitchMethodCallToFunctionCall(checker::ETSChecker * checker,ir::CallExpression * expr,Signature * signature)339 static void SwitchMethodCallToFunctionCall(checker::ETSChecker *checker, ir::CallExpression *expr, Signature *signature)
340 {
341     ES2PANDA_ASSERT(expr->Callee()->IsMemberExpression());
342     auto *memberExpr = expr->Callee()->AsMemberExpression();
343     if (expr->Arguments().empty() || expr->Arguments()[0] != memberExpr->Object()) {
344         expr->Arguments().insert(expr->Arguments().begin(), memberExpr->Object());
345     }
346     expr->SetSignature(signature);
347     expr->SetCallee(memberExpr->Property());
348     memberExpr->Property()->AsIdentifier()->SetParent(expr);
349     expr->Arguments()[0]->SetParent(expr);
350     checker->HandleUpdatedCallExpressionNode(expr);
351     // Set TsType for new Callee(original member expression's Object)
352     expr->Callee()->Check(checker);
353 }
354 
ResolveCallExtensionFunction(checker::Type * functionType,checker::ETSChecker * checker,ir::CallExpression * expr,const TypeRelationFlag reportFlag)355 checker::Signature *ResolveCallExtensionFunction(checker::Type *functionType, checker::ETSChecker *checker,
356                                                  ir::CallExpression *expr, const TypeRelationFlag reportFlag)
357 {
358     // We have to ways to call ExtensionFunction `function foo(this: A, ...)`:
359     // 1. Make ExtensionFunction as FunctionCall: `foo(a,...);`
360     // 2. Make ExtensionFunction as MethodCall: `a.foo(...)`, here `a` is the receiver;
361     auto &signatures = expr->IsETSConstructorCall()
362                            ? functionType->AsETSObjectType()->ConstructSignatures()
363                            : functionType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow();
364     if (expr->Callee()->IsMemberExpression()) {
365         // when handle extension function as MethodCall, we temporarily transfer the expr node from `a.foo(...)` to
366         // `foo(a, ...)` and resolve the call. If we find the suitable signature, then switch the expr node to
367         // function call.
368         auto *memberExpr = expr->Callee()->AsMemberExpression();
369         expr->Arguments().insert(expr->Arguments().begin(), memberExpr->Object());
370         auto *signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start(), reportFlag);
371         if (signature == nullptr) {
372             expr->Arguments().erase(expr->Arguments().begin());
373             return nullptr;
374         }
375         if (!signature->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION)) {
376             checker->LogError(diagnostic::PROPERTY_NONEXISTENT,
377                               {memberExpr->Property()->AsIdentifier()->Name(), memberExpr->ObjType()->Name()},
378                               memberExpr->Property()->Start());
379             return nullptr;
380         }
381 
382         SwitchMethodCallToFunctionCall(checker, expr, signature);
383         return signature;
384     }
385 
386     return checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start());
387 }
388 
ResolveCallForClassMethod(checker::ETSExtensionFuncHelperType * type,checker::ETSChecker * checker,ir::CallExpression * expr,const TypeRelationFlag reportFlag)389 checker::Signature *ResolveCallForClassMethod(checker::ETSExtensionFuncHelperType *type, checker::ETSChecker *checker,
390                                               ir::CallExpression *expr, const TypeRelationFlag reportFlag)
391 {
392     ES2PANDA_ASSERT(expr->Callee()->IsMemberExpression());
393 
394     auto signature = checker->ResolveCallExpressionAndTrailingLambda(type->ClassMethodType()->CallSignatures(), expr,
395                                                                      expr->Start(), reportFlag);
396     if (signature != nullptr) {
397         auto *memberExpr = expr->Callee()->AsMemberExpression();
398         auto *var = type->ClassMethodType()->Variable();
399         memberExpr->Property()->AsIdentifier()->SetVariable(var);
400     }
401     return signature;
402 }
403 
GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::ETSExtensionFuncHelperType * type,checker::ETSChecker * checker,ir::CallExpression * expr)404 checker::Signature *GetMostSpecificSigFromExtensionFuncAndClassMethod(checker::ETSExtensionFuncHelperType *type,
405                                                                       checker::ETSChecker *checker,
406                                                                       ir::CallExpression *expr)
407 {
408     // We try to find the most suitable signature for a.foo(...) both in ExtensionFunctionType and ClassMethodType.
409     // So we temporarily transfer expr node from `a.foo(...)` to `a.foo(a, ...)`.
410     // For allCallSignatures in ClassMethodType, temporarily insert the dummyReceiver into their signatureInfo,
411     // otherwise we can't get the most suitable classMethod signature if all the extensionFunction signature mismatched.
412     ArenaVector<Signature *> signatures(checker->ProgramAllocator()->Adapter());
413     signatures.insert(signatures.end(), type->ClassMethodType()->CallSignatures().begin(),
414                       type->ClassMethodType()->CallSignatures().end());
415     signatures.insert(signatures.end(), type->ExtensionMethodType()->CallSignatures().begin(),
416                       type->ExtensionMethodType()->CallSignatures().end());
417 
418     auto *memberExpr = expr->Callee()->AsMemberExpression();
419     auto *dummyReceiver = memberExpr->Object();
420     auto *dummyReceiverVar = type->ExtensionMethodType()->CallSignatures()[0]->Params()[0];
421     expr->Arguments().insert(expr->Arguments().begin(), dummyReceiver);
422     const bool typeParamsNeeded = dummyReceiverVar->TsType()->IsETSObjectType();
423 
424     for (auto *methodCallSig : type->ClassMethodType()->CallSignatures()) {
425         methodCallSig->GetSignatureInfo()->minArgCount++;
426         auto &paramsVar = methodCallSig->Params();
427         paramsVar.insert(paramsVar.begin(), dummyReceiverVar);
428         auto &params = methodCallSig->Function()->Params();
429         params.insert(params.begin(), dummyReceiver);
430         if (typeParamsNeeded) {
431             auto &typeParams = methodCallSig->TypeParams();
432             typeParams.insert(typeParams.end(), dummyReceiverVar->TsType()->AsETSObjectType()->TypeArguments().begin(),
433                               dummyReceiverVar->TsType()->AsETSObjectType()->TypeArguments().end());
434         }
435     }
436 
437     auto *signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start(),
438                                                                       checker::TypeRelationFlag::NO_THROW);
439 
440     for (auto *methodCallSig : type->ClassMethodType()->CallSignatures()) {
441         methodCallSig->GetSignatureInfo()->minArgCount--;
442         auto &paramsVar = methodCallSig->Params();
443         paramsVar.erase(paramsVar.begin());
444         auto &params = methodCallSig->Function()->Params();
445         params.erase(params.begin());
446         if (typeParamsNeeded) {
447             auto &typeParams = methodCallSig->TypeParams();
448             typeParams.resize(typeParams.size() -
449                               dummyReceiverVar->TsType()->AsETSObjectType()->TypeArguments().size());
450         }
451     }
452     expr->Arguments().erase(expr->Arguments().begin());
453 
454     if (signature != nullptr) {
455         if (signature->Owner()->GetDeclNode()->IsClassDefinition() &&
456             signature->Owner()->GetDeclNode()->AsClassDefinition()->IsGlobal()) {
457             SwitchMethodCallToFunctionCall(checker, expr, signature);
458         } else {
459             auto *var = type->ClassMethodType()->Variable();
460             memberExpr->Property()->AsIdentifier()->SetVariable(var);
461         }
462     }
463     return signature;
464 }
465 
ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType * type,checker::ETSChecker * checker,ir::CallExpression * expr)466 checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type,
467                                                              checker::ETSChecker *checker, ir::CallExpression *expr)
468 {
469     ES2PANDA_ASSERT(expr->Callee()->IsMemberExpression());
470     auto *calleeObj = expr->Callee()->AsMemberExpression()->Object();
471     bool isCalleeObjETSGlobal = calleeObj->TsType()->AsETSObjectType()->GetDeclNode()->IsClassDefinition() &&
472                                 calleeObj->TsType()->AsETSObjectType()->GetDeclNode()->AsClassDefinition()->IsGlobal();
473     // for callExpr `a.foo`, there are 3 situations:
474     // 1.`a.foo` is private method call of class A;
475     // 2.`a.foo` is extension function of `A`(function with receiver `A`)
476     // 3.`a.foo` is public method call of class A;
477     Signature *signature = nullptr;
478     if (checker->IsTypeIdenticalTo(checker->Context().ContainingClass(), calleeObj->TsType()) || isCalleeObjETSGlobal) {
479         // When called `a.foo` in `a.anotherFunc`, we should find signature through private or protected method firstly.
480         signature = ResolveCallForClassMethod(type, checker, expr, checker::TypeRelationFlag::NO_THROW);
481         if (signature != nullptr) {
482             return signature;
483         }
484     }
485 
486     signature = GetMostSpecificSigFromExtensionFuncAndClassMethod(type, checker, expr);
487     if (signature == nullptr) {
488         checker->ThrowSignatureMismatch(type->ExtensionMethodType()->CallSignatures(), expr->Arguments(), expr->Start(),
489                                         "call");
490     }
491 
492     return signature;
493 }
494 
GetUnionTypeSignatures(ETSChecker * checker,checker::ETSUnionType * etsUnionType)495 ArenaVector<checker::Signature *> GetUnionTypeSignatures(ETSChecker *checker, checker::ETSUnionType *etsUnionType)
496 {
497     ArenaVector<checker::Signature *> callSignatures(checker->ProgramAllocator()->Adapter());
498 
499     for (auto *constituentType : etsUnionType->ConstituentTypes()) {
500         if (constituentType->IsETSFunctionType()) {
501             ArenaVector<checker::Signature *> tmpCallSignatures(checker->ProgramAllocator()->Adapter());
502             tmpCallSignatures = constituentType->AsETSFunctionType()->CallSignatures();
503             callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
504         }
505         if (constituentType->IsETSUnionType()) {
506             ArenaVector<checker::Signature *> tmpCallSignatures(checker->ProgramAllocator()->Adapter());
507             tmpCallSignatures = GetUnionTypeSignatures(checker, constituentType->AsETSUnionType());
508             callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
509         }
510     }
511 
512     return callSignatures;
513 }
514 
ProcessExclamationMark(ETSChecker * checker,ir::UnaryExpression * expr,checker::Type * operandType)515 void ProcessExclamationMark(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)
516 {
517     if (checker->IsNullLikeOrVoidExpression(expr->Argument())) {
518         auto tsType = checker->CreateETSBooleanType(true);
519         CHECK_NOT_NULL(tsType);
520         tsType->AddTypeFlag(checker::TypeFlag::CONSTANT);
521         expr->SetTsType(tsType);
522         return;
523     }
524 
525     if (operandType == nullptr || operandType->IsTypeError()) {
526         expr->SetTsType(checker->GlobalTypeError());
527         return;
528     }
529 
530     auto exprRes = operandType->ResolveConditionExpr();
531     if (std::get<0>(exprRes)) {
532         auto tsType = checker->CreateETSBooleanType(!std::get<1>(exprRes));
533         tsType->AddTypeFlag(checker::TypeFlag::CONSTANT);
534         expr->SetTsType(tsType);
535         return;
536     }
537     expr->SetTsType(checker->GlobalETSBooleanType());
538 }
539 
SetTsTypeForUnaryExpression(ETSChecker * checker,ir::UnaryExpression * expr,checker::Type * operandType)540 void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)
541 {
542     switch (expr->OperatorType()) {
543         case lexer::TokenType::PUNCTUATOR_MINUS:
544         case lexer::TokenType::PUNCTUATOR_PLUS: {
545             if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
546                 checker->LogError(diagnostic::OPERAND_NOT_NUMERIC, {}, expr->Argument()->Start());
547                 expr->SetTsType(checker->GlobalTypeError());
548                 break;
549             }
550 
551             if (operandType->HasTypeFlag(checker::TypeFlag::CONSTANT) &&
552                 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
553                 expr->SetTsType(checker->NegateNumericType(operandType, expr));
554                 break;
555             }
556 
557             expr->SetTsType(operandType);
558             break;
559         }
560         case lexer::TokenType::PUNCTUATOR_TILDE: {
561             if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
562                 checker->LogError(diagnostic::OPERAND_NOT_NUMERIC, {}, expr->Argument()->Start());
563                 expr->SetTsType(checker->GlobalTypeError());
564                 break;
565             }
566 
567             expr->Argument()->SetTsType(expr->SetTsType(checker->SelectGlobalIntegerTypeForNumeric(operandType)));
568             break;
569         }
570         case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
571             ProcessExclamationMark(checker, expr, operandType);
572             break;
573         }
574         default: {
575             ES2PANDA_UNREACHABLE();
576         }
577     }
578 }
579 
GetIteratorType(ETSChecker * checker,checker::Type * elemType,ir::AstNode * left)580 checker::Type *GetIteratorType(ETSChecker *checker, checker::Type *elemType, ir::AstNode *left)
581 {
582     // Just to avoid extra nested level(s)
583     // CC-OFFNXT(G.FMT.14-CPP) project code style
584     auto const getIterType = [checker, elemType](ir::VariableDeclarator *const declarator) -> checker::Type * {
585         if (declarator->TsType() == nullptr) {
586             if (auto *resolved = checker->FindVariableInFunctionScope(declarator->Id()->AsIdentifier()->Name());
587                 resolved != nullptr) {
588                 resolved->SetTsType(elemType);
589                 return elemType;
590             }
591         } else {
592             return declarator->TsType();
593         }
594         return checker->GlobalTypeError();
595     };
596 
597     checker::Type *iterType = nullptr;
598     if (left->IsIdentifier()) {
599         auto *const variable = left->Variable();
600         ES2PANDA_ASSERT(variable != nullptr && variable->Declaration() != nullptr);
601 
602         auto *decl = variable->Declaration();
603         if (decl->IsConstDecl() || decl->IsReadonlyDecl()) {
604             const auto &errorMsg =
605                 decl->IsConstDecl() ? diagnostic::INVALID_CONST_ASSIGNMENT : diagnostic::INVALID_READONLY_ASSIGNMENT;
606             checker->LogError(errorMsg, {variable->Name()}, decl->Node()->Start());
607         }
608         iterType = left->AsIdentifier()->TsType();
609     } else if (left->IsVariableDeclaration()) {
610         if (auto const &declarators = left->AsVariableDeclaration()->Declarators(); !declarators.empty()) {
611             iterType = getIterType(declarators.front());
612         }
613     }
614 
615     if (iterType == nullptr) {
616         checker->LogError(diagnostic::ITERATOR_TYPE_ABSENT, {}, left->Start());
617         return checker->GlobalTypeError();
618     }
619     return checker->GetNonConstantType(iterType);
620 }
621 
CheckArgumentVoidType(checker::Type * & funcReturnType,ETSChecker * checker,const std::string & name,ir::ReturnStatement * st)622 bool CheckArgumentVoidType(checker::Type *&funcReturnType, ETSChecker *checker, const std::string &name,
623                            ir::ReturnStatement *st)
624 {
625     if (name.find(compiler::Signatures::ETS_MAIN_WITH_MANGLE_BEGIN) != std::string::npos) {
626         if (!funcReturnType->IsETSVoidType() && !funcReturnType->IsIntType()) {
627             checker->LogError(diagnostic::MAIN_BAD_RETURN, {}, st->Start());
628         }
629     }
630     return true;
631 }
632 
CheckReturnType(ETSChecker * checker,checker::Type * funcReturnType,checker::Type * argumentType,ir::Expression * stArgument,ir::ScriptFunction * containingFunc)633 bool CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker::Type *argumentType,
634                      ir::Expression *stArgument, ir::ScriptFunction *containingFunc)
635 {
636     if (funcReturnType->IsETSVoidType() || funcReturnType == checker->GlobalVoidType()) {
637         if (argumentType != checker->GlobalVoidType()) {
638             checker->LogError(diagnostic::UNEXPECTED_VALUE_RETURN, {}, stArgument->Start());
639             return false;
640         }
641         if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType,
642                                         stArgument->Start(), std::nullopt,
643                                         checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
644                  // CC-OFFNXT(G.FMT.02) project code style
645                  .IsAssignable()) {
646             checker->LogError(diagnostic::RETURN_TYPE_MISMATCH, {}, stArgument->Start());
647             return false;
648         }
649         return true;
650     }
651 
652     if (containingFunc->IsAsyncFunc() && funcReturnType->IsETSObjectType() &&
653         funcReturnType->AsETSObjectType()->GetOriginalBaseType() == checker->GlobalBuiltinPromiseType()) {
654         auto promiseArg = funcReturnType->AsETSObjectType()->TypeArguments()[0];
655         checker::AssignmentContext(checker->Relation(), stArgument, argumentType, promiseArg, stArgument->Start(),
656                                    std::nullopt,
657                                    checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW);
658         if (checker->Relation()->IsTrue()) {
659             return true;
660         }
661     }
662 
663     if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType, stArgument->Start(),
664                                     std::nullopt,
665                                     checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
666              // CC-OFFNXT(G.FMT.02) project code style
667              .IsAssignable()) {
668         checker->LogError(diagnostic::ARROW_TYPE_MISMATCH, {argumentType, funcReturnType}, stArgument->Start());
669         return false;
670     }
671     return true;
672 }
673 
InferReturnType(ETSChecker * checker,ir::ScriptFunction * containingFunc,checker::Type * & funcReturnType,ir::Expression * stArgument)674 void InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType,
675                      ir::Expression *stArgument)
676 {
677     //  First (or single) return statement in the function:
678     funcReturnType =
679         stArgument == nullptr ? checker->GlobalVoidType() : checker->GetNonConstantType(stArgument->Check(checker));
680     ES2PANDA_ASSERT(funcReturnType != nullptr);
681     if (funcReturnType->IsTypeError()) {
682         containingFunc->Signature()->RemoveSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE);
683         return;
684     }
685 
686     /*
687     when st_argment is ArrowFunctionExpression, need infer type for st_argment
688     example code:
689     ```
690     return () => {}
691     ```
692     */
693     if (stArgument != nullptr && stArgument->IsArrowFunctionExpression()) {
694         auto arrowFunc = stArgument->AsArrowFunctionExpression();
695         auto typeAnnotation = arrowFunc->CreateTypeAnnotation(checker);
696 
697         auto *argumentType = arrowFunc->TsType();
698         ES2PANDA_ASSERT(typeAnnotation != nullptr);
699         funcReturnType = typeAnnotation->GetType(checker);
700         if (!checker::AssignmentContext(checker->Relation(), arrowFunc, argumentType, funcReturnType,
701                                         stArgument->Start(), std::nullopt,
702                                         checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
703                  // CC-OFFNXT(G.FMT.02) project code style
704                  .IsAssignable()) {
705             checker->LogError(diagnostic::ARROW_TYPE_MISMATCH, {argumentType, funcReturnType}, stArgument->Start());
706             funcReturnType = checker->GlobalTypeError();
707             return;
708         }
709     }
710 
711     containingFunc->Signature()->SetReturnType(funcReturnType);
712     containingFunc->Signature()->RemoveSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE);
713     containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
714     checker->VarBinder()->AsETSBinder()->BuildFunctionName(containingFunc);
715 
716     if (stArgument != nullptr && stArgument->IsObjectExpression()) {
717         stArgument->AsObjectExpression()->SetPreferredType(funcReturnType);
718     }
719 }
720 
IsArrayExpressionValidInitializerForType(ETSChecker * checker,const Type * const arrayExprPreferredType)721 bool IsArrayExpressionValidInitializerForType(ETSChecker *checker, const Type *const arrayExprPreferredType)
722 {
723     const auto validForTarget = arrayExprPreferredType == nullptr  // preferred type will be inferred from elements
724                                 || arrayExprPreferredType->IsETSArrayType()           // valid for fixed array type
725                                 || arrayExprPreferredType->IsETSResizableArrayType()  // valid for resizable array type
726                                 || arrayExprPreferredType->IsETSTupleType()           // valid for tuple type
727                                 || checker->Relation()->IsSupertypeOf(arrayExprPreferredType,  // valid for 'Object'
728                                                                       checker->GlobalETSObjectType());
729 
730     return validForTarget;
731 }
732 
CastPossibleTupleOnRHS(ETSChecker * checker,ir::AssignmentExpression * expr)733 void CastPossibleTupleOnRHS(ETSChecker *checker, ir::AssignmentExpression *expr)
734 {
735     if (expr->Left()->IsMemberExpression() &&
736         expr->Left()->AsMemberExpression()->Object()->TsType()->IsETSTupleType() &&
737         expr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
738         auto *storedTupleType = expr->Left()->AsMemberExpression()->Object()->TsType();
739 
740         const checker::CastingContext tupleCast(
741             checker->Relation(), diagnostic::CAST_FAIL_UNREACHABLE, {},
742             checker::CastingContext::ConstructorData {expr->Right(), expr->Right()->TsType(), storedTupleType,
743                                                       expr->Right()->Start(), TypeRelationFlag::NO_THROW});
744     }
745 }
746 
ProcessReturnStatements(ETSChecker * checker,ir::ScriptFunction * containingFunc,checker::Type * & funcReturnType,ir::ReturnStatement * st,ir::Expression * stArgument)747 void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType,
748                              ir::ReturnStatement *st, ir::Expression *stArgument)
749 {
750     funcReturnType = containingFunc->Signature()->ReturnType();
751 
752     if (stArgument == nullptr) {
753         // previous return statement(s) have value
754         if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType()) {
755             checker->LogError(diagnostic::MIXED_VOID_NONVOID, {}, st->Start());
756             return;
757         }
758     } else {
759         if (stArgument->IsObjectExpression()) {
760             stArgument->AsObjectExpression()->SetPreferredType(funcReturnType);
761         }
762 
763         if (stArgument->IsMemberExpression()) {
764             checker->SetArrayPreferredTypeForNestedMemberExpressions(stArgument->AsMemberExpression(), funcReturnType);
765         }
766 
767         checker::Type *argumentType = checker->GetNonConstantType(stArgument->Check(checker));
768 
769         //  previous return statement(s) don't have any value
770         ES2PANDA_ASSERT(argumentType != nullptr);
771         if (funcReturnType->IsETSVoidType() && !argumentType->IsETSVoidType()) {
772             checker->LogError(diagnostic::MIXED_VOID_NONVOID, {}, stArgument->Start());
773             return;
774         }
775 
776         const auto name = containingFunc->Scope()->InternalName().Mutf8();
777         if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) {
778             return;
779         }
780 
781         auto *const relation = checker->Relation();
782         relation->SetNode(stArgument);
783 
784         if (!relation->IsIdenticalTo(funcReturnType, argumentType)) {
785             checker->ResolveReturnStatement(funcReturnType, argumentType, containingFunc, st);
786         }
787 
788         relation->SetNode(nullptr);
789         relation->SetFlags(checker::TypeRelationFlag::NONE);
790     }
791 }
792 
CheckReturnTypeNecessity(ir::MethodDefinition * node)793 bool CheckReturnTypeNecessity(ir::MethodDefinition *node)
794 {
795     bool needReturnType = true;
796     auto *scriptFunc = node->Function();
797     needReturnType &= (node->IsNative() || node->IsDeclare());
798     needReturnType &= !node->IsConstructor();
799     ES2PANDA_ASSERT(scriptFunc != nullptr);
800     needReturnType &= !scriptFunc->IsSetter();
801     return needReturnType;
802 }
803 
CheckAllConstPropertyInitialized(checker::ETSChecker * checker,ir::ETSModule * pkg)804 void CheckAllConstPropertyInitialized(checker::ETSChecker *checker, ir::ETSModule *pkg)
805 {
806     auto globalDecl = std::find_if(pkg->Statements().begin(), pkg->Statements().end(), [](ir::AstNode *node) {
807         return node->IsClassDeclaration() && node->AsClassDeclaration()->Definition()->IsGlobal();
808     });
809     if (globalDecl == pkg->Statements().end()) {
810         return;
811     }
812 
813     auto const &globalClassBody = (*globalDecl)->AsClassDeclaration()->Definition()->AsClassDefinition()->Body();
814     for (auto const *prop : globalClassBody) {
815         if (!prop->IsClassProperty()) {
816             continue;
817         }
818 
819         if (prop->AsClassProperty()->Key()->Variable()->HasFlag(varbinder::VariableFlags::INIT_IN_STATIC_BLOCK) &&
820             !prop->AsClassProperty()->Key()->Variable()->HasFlag(varbinder::VariableFlags::INITIALIZED)) {
821             checker->LogError(diagnostic::MISSING_INIT_FOR_CONST_PACKAGE_PROP, {}, prop->Start());
822         }
823     }
824 }
825 
826 }  // namespace ark::es2panda::checker
827