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 ¶msVar = methodCallSig->Params();
427 paramsVar.insert(paramsVar.begin(), dummyReceiverVar);
428 auto ¶ms = 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 ¶msVar = methodCallSig->Params();
443 paramsVar.erase(paramsVar.begin());
444 auto ¶ms = 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