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