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 "ir/typeNode.h" 17#include "ir/base/scriptFunction.h" 18#include "ir/expressions/assignmentExpression.h" 19#include "ir/expressions/binaryExpression.h" 20#include "ir/expressions/memberExpression.h" 21#include "ir/expressions/identifier.h" 22#include "ir/statements/variableDeclarator.h" 23#include "ir/ts/tsQualifiedName.h" 24#include "ir/base/tsPropertySignature.h" 25#include "ir/ts/tsTypeAliasDeclaration.h" 26#include "ir/ts/tsTypeReference.h" 27#include "ir/ts/tsTypeParameterDeclaration.h" 28#include "ir/ts/tsTypeParameter.h" 29#include "varbinder/variable.h" 30#include "varbinder/scope.h" 31#include "util/helpers.h" 32 33#include "checker/ts/typeElaborationContext.h" 34#include "checker/TSchecker.h" 35 36namespace ark::es2panda::checker { 37void TSChecker::CheckTruthinessOfType(Type *type, lexer::SourcePosition lineInfo) 38{ 39 if (type->IsVoidType()) { 40 ThrowTypeError("An expression of type void cannot be tested for truthiness", lineInfo); 41 } 42} 43 44Type *TSChecker::CheckNonNullType(Type *type, lexer::SourcePosition lineInfo) 45{ 46 if (type->IsNullType()) { 47 ThrowTypeError("Object is possibly 'null'.", lineInfo); 48 } 49 50 if (type->IsUndefinedType()) { 51 ThrowTypeError("Object is possibly 'undefined'.", lineInfo); 52 } 53 54 return type; 55} 56 57Type *TSChecker::GetBaseTypeOfLiteralType(Type *type) 58{ 59 if (HasStatus(CheckerStatus::KEEP_LITERAL_TYPE)) { 60 return type; 61 } 62 63 if (type->IsStringLiteralType()) { 64 return GlobalStringType(); 65 } 66 67 if (type->IsNumberLiteralType()) { 68 return GlobalNumberType(); 69 } 70 71 if (type->IsBooleanLiteralType()) { 72 return GlobalBooleanType(); 73 } 74 75 if (type->IsBigintLiteralType()) { 76 return GlobalBigintType(); 77 } 78 79 if (type->IsUnionType()) { 80 auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); 81 ArenaVector<Type *> newConstituentTypes(Allocator()->Adapter()); 82 83 newConstituentTypes.reserve(constituentTypes.size()); 84 for (auto *it : constituentTypes) { 85 newConstituentTypes.push_back(GetBaseTypeOfLiteralType(it)); 86 } 87 88 return CreateUnionType(std::move(newConstituentTypes)); 89 } 90 91 return type; 92} 93 94void TSChecker::CheckReferenceExpression(ir::Expression *expr, const char *invalidReferenceMsg, 95 const char *invalidOptionalChainMsg) 96{ 97 if (expr->IsIdentifier()) { 98 const util::StringView &name = expr->AsIdentifier()->Name(); 99 auto result = Scope()->Find(name); 100 ASSERT(result.variable); 101 102 if (result.variable->HasFlag(varbinder::VariableFlags::ENUM_LITERAL)) { 103 ThrowTypeError({"Cannot assign to '", name, "' because it is not a variable."}, expr->Start()); 104 } 105 } else if (!expr->IsMemberExpression()) { 106 if (expr->IsChainExpression()) { 107 ThrowTypeError(invalidOptionalChainMsg, expr->Start()); 108 } 109 110 ThrowTypeError(invalidReferenceMsg, expr->Start()); 111 } 112} 113 114void TSChecker::CheckTestingKnownTruthyCallableOrAwaitableType([[maybe_unused]] ir::Expression *condExpr, 115 [[maybe_unused]] Type *type, 116 [[maybe_unused]] ir::AstNode *body) 117{ 118 // NOTE: aszilagyi. rework this 119} 120 121Type *TSChecker::ExtractDefinitelyFalsyTypes(Type *type) 122{ 123 if (type->IsStringType()) { 124 return GlobalEmptyStringType(); 125 } 126 127 if (type->IsNumberType()) { 128 return GlobalZeroType(); 129 } 130 131 if (type->IsBigintType()) { 132 return GlobalZeroBigintType(); 133 } 134 135 if (type == GlobalFalseType() || type->DefinitelyETSNullish() || type->HasTypeFlag(TypeFlag::ANY_OR_UNKNOWN) || 136 type->HasTypeFlag(TypeFlag::VOID) || 137 (type->IsStringLiteralType() && IsTypeIdenticalTo(type, GlobalEmptyStringType())) || 138 (type->IsNumberLiteralType() && IsTypeIdenticalTo(type, GlobalZeroType())) || 139 (type->IsBigintLiteralType() && IsTypeIdenticalTo(type, GlobalZeroBigintType()))) { 140 return type; 141 } 142 143 if (type->IsUnionType()) { 144 auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); 145 ArenaVector<Type *> newConstituentTypes(Allocator()->Adapter()); 146 147 newConstituentTypes.reserve(constituentTypes.size()); 148 for (auto &it : constituentTypes) { 149 newConstituentTypes.push_back(ExtractDefinitelyFalsyTypes(it)); 150 } 151 152 return CreateUnionType(std::move(newConstituentTypes)); 153 } 154 155 return GlobalNeverType(); 156} 157 158Type *TSChecker::RemoveDefinitelyFalsyTypes(Type *type) 159{ 160 if ((static_cast<uint64_t>(GetFalsyFlags(type)) & static_cast<uint64_t>(TypeFlag::DEFINITELY_FALSY)) != 0U) { 161 if (!type->IsUnionType()) { 162 return GlobalNeverType(); 163 } 164 165 auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); 166 ArenaVector<Type *> newConstituentTypes(Allocator()->Adapter()); 167 168 for (auto &it : constituentTypes) { 169 if ((static_cast<uint64_t>(GetFalsyFlags(it)) & static_cast<uint64_t>(TypeFlag::DEFINITELY_FALSY)) == 0U) { 170 newConstituentTypes.push_back(it); 171 } 172 } 173 174 if (newConstituentTypes.empty()) { 175 return GlobalNeverType(); 176 } 177 178 if (newConstituentTypes.size() == 1) { 179 return newConstituentTypes[0]; 180 } 181 182 return CreateUnionType(std::move(newConstituentTypes)); 183 } 184 185 return type; 186} 187 188TypeFlag TSChecker::GetFalsyFlags(Type *type) 189{ 190 if (type->IsStringLiteralType()) { 191 return type->AsStringLiteralType()->Value().Empty() ? TypeFlag::STRING_LITERAL : TypeFlag::NONE; 192 } 193 194 if (type->IsNumberLiteralType()) { 195 return type->AsNumberLiteralType()->Value() == 0 ? TypeFlag::NUMBER_LITERAL : TypeFlag::NONE; 196 } 197 198 if (type->IsBigintLiteralType()) { 199 return type->AsBigintLiteralType()->Value() == "0n" ? TypeFlag::BIGINT_LITERAL : TypeFlag::NONE; 200 } 201 202 if (type->IsBooleanLiteralType()) { 203 return type->AsBooleanLiteralType()->Value() ? TypeFlag::NONE : TypeFlag::BOOLEAN_LITERAL; 204 } 205 206 if (type->IsUnionType()) { 207 auto &constituentTypes = type->AsUnionType()->ConstituentTypes(); 208 TypeFlag returnFlag = TypeFlag::NONE; 209 210 for (auto &it : constituentTypes) { 211 returnFlag |= GetFalsyFlags(it); 212 } 213 214 return returnFlag; 215 } 216 217 return static_cast<TypeFlag>(type->TypeFlags() & TypeFlag::POSSIBLY_FALSY); 218} 219 220bool TSChecker::IsVariableUsedInConditionBody(ir::AstNode *parent, varbinder::Variable *searchVar) 221{ 222 bool found = false; 223 224 parent->Iterate([this, searchVar, &found](ir::AstNode *childNode) -> void { 225 varbinder::Variable *resultVar = nullptr; 226 if (childNode->IsIdentifier()) { 227 auto result = Scope()->Find(childNode->AsIdentifier()->Name()); 228 ASSERT(result.variable); 229 resultVar = result.variable; 230 } 231 232 if (searchVar == resultVar) { 233 found = true; 234 return; 235 } 236 237 if (!childNode->IsMemberExpression()) { 238 IsVariableUsedInConditionBody(childNode, searchVar); 239 } 240 }); 241 242 return found; 243} 244 245bool TSChecker::FindVariableInBinaryExpressionChain(ir::AstNode *parent, varbinder::Variable *searchVar) 246{ 247 bool found = false; 248 249 parent->Iterate([this, searchVar, &found](ir::AstNode *childNode) -> void { 250 if (childNode->IsIdentifier()) { 251 auto result = Scope()->Find(childNode->AsIdentifier()->Name()); 252 ASSERT(result.variable); 253 if (result.variable == searchVar) { 254 found = true; 255 return; 256 } 257 } 258 259 FindVariableInBinaryExpressionChain(childNode, searchVar); 260 }); 261 262 return found; 263} 264 265bool TSChecker::IsVariableUsedInBinaryExpressionChain(ir::AstNode *parent, varbinder::Variable *searchVar) 266{ 267 while (parent->IsBinaryExpression() && 268 parent->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { 269 if (FindVariableInBinaryExpressionChain(parent, searchVar)) { 270 return true; 271 } 272 273 parent = parent->Parent(); 274 } 275 276 return false; 277} 278 279void TSChecker::ThrowBinaryLikeError(lexer::TokenType op, Type *leftType, Type *rightType, 280 lexer::SourcePosition lineInfo) 281{ 282 if (!HasStatus(CheckerStatus::IN_CONST_CONTEXT)) { 283 ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", AsSrc(rightType)}, 284 lineInfo); 285 } 286 287 ThrowTypeError({"operator ", op, " cannot be applied to types ", leftType, " and ", rightType}, lineInfo); 288} 289 290void TSChecker::ThrowAssignmentError(Type *source, Type *target, lexer::SourcePosition lineInfo, bool isAsSrcLeftType) 291{ 292 if (isAsSrcLeftType || !target->HasTypeFlag(TypeFlag::LITERAL)) { 293 ThrowTypeError({"Type '", AsSrc(source), "' is not assignable to type '", target, "'."}, lineInfo); 294 } 295 296 ThrowTypeError({"Type '", source, "' is not assignable to type '", target, "'."}, lineInfo); 297} 298 299Type *TSChecker::GetUnaryResultType(Type *operandType) 300{ 301 if (checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) { 302 if (operandType->HasTypeFlag(checker::TypeFlag::UNION_OR_INTERSECTION) && 303 checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::NUMBER_LIKE)) { 304 return GlobalNumberOrBigintType(); 305 } 306 307 return GlobalBigintType(); 308 } 309 310 return GlobalNumberType(); 311} 312 313void TSChecker::ElaborateElementwise(Type *targetType, ir::Expression *sourceNode, const lexer::SourcePosition &pos) 314{ 315 auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::KEEP_LITERAL_TYPE); 316 317 Type *sourceType = CheckTypeCached(sourceNode); 318 319 if (IsTypeAssignableTo(sourceType, targetType)) { 320 return; 321 } 322 323 if (targetType->IsArrayType() && sourceNode->IsArrayExpression()) { 324 ArrayElaborationContext(this, targetType, sourceType, sourceNode, pos).Start(); 325 } else if (targetType->IsObjectType() || targetType->IsUnionType()) { 326 if (sourceNode->IsObjectExpression()) { 327 ObjectElaborationContext(this, targetType, sourceType, sourceNode, pos).Start(); 328 } else if (sourceNode->IsArrayExpression()) { 329 ArrayElaborationContext(this, targetType, sourceType, sourceNode, pos).Start(); 330 } 331 } 332 333 ThrowAssignmentError(sourceType, targetType, pos); 334} 335 336void TSChecker::InferSimpleVariableDeclaratorType(ir::VariableDeclarator *declarator) 337{ 338 ASSERT(declarator->Id()->IsIdentifier()); 339 340 varbinder::Variable *var = declarator->Id()->AsIdentifier()->Variable(); 341 ASSERT(var); 342 343 if (declarator->Id()->AsIdentifier()->TypeAnnotation() != nullptr) { 344 var->SetTsType(declarator->Id()->AsIdentifier()->TypeAnnotation()->GetType(this)); 345 return; 346 } 347 348 if (declarator->Init() != nullptr) { 349 var->SetTsType(CheckTypeCached(declarator->Init())); 350 return; 351 } 352 353 ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."}, 354 declarator->Id()->Start()); 355} 356 357void TSChecker::GetTypeVar(varbinder::Decl *decl) 358{ 359 ir::AstNode *declarator = 360 util::Helpers::FindAncestorGivenByType(decl->Node(), ir::AstNodeType::VARIABLE_DECLARATOR); 361 ASSERT(declarator); 362 363 if (declarator->AsVariableDeclarator()->Id()->IsIdentifier()) { 364 InferSimpleVariableDeclaratorType(declarator->AsVariableDeclarator()); 365 return; 366 } 367 368 declarator->Check(this); 369} 370 371void TSChecker::GetTypeParam(varbinder::Variable *var, varbinder::Decl *decl) 372{ 373 ir::AstNode *declaration = FindAncestorUntilGivenType(decl->Node(), ir::AstNodeType::SCRIPT_FUNCTION); 374 375 if (declaration->IsIdentifier()) { 376 auto *ident = declaration->AsIdentifier(); 377 if (ident->TypeAnnotation() != nullptr) { 378 ASSERT(ident->Variable() == var); 379 var->SetTsType(ident->TypeAnnotation()->GetType(this)); 380 return; 381 } 382 383 ThrowTypeError({"Parameter ", ident->Name(), " implicitly has an 'any' type."}, ident->Start()); 384 } 385 386 if (declaration->IsAssignmentPattern() && declaration->AsAssignmentPattern()->Left()->IsIdentifier()) { 387 ir::Identifier *ident = declaration->AsAssignmentPattern()->Left()->AsIdentifier(); 388 389 if (ident->TypeAnnotation() != nullptr) { 390 ASSERT(ident->Variable() == var); 391 var->SetTsType(ident->TypeAnnotation()->GetType(this)); 392 return; 393 } 394 395 var->SetTsType(declaration->AsAssignmentPattern()->Right()->Check(this)); 396 } 397 398 CheckFunctionParameter(declaration->AsExpression(), nullptr); 399} 400 401void TSChecker::GetTypeEnum(varbinder::Variable *var, varbinder::Decl *decl) 402{ 403 ASSERT(var->IsEnumVariable()); 404 varbinder::EnumVariable *enumVar = var->AsEnumVariable(); 405 406 if (std::holds_alternative<bool>(enumVar->Value())) { 407 ThrowTypeError( 408 "A member initializer in a enum declaration cannot reference members declared after it, " 409 "including " 410 "members defined in other enums.", 411 decl->Node()->Start()); 412 } 413 414 var->SetTsType(std::holds_alternative<double>(enumVar->Value()) ? GlobalNumberType() : GlobalStringType()); 415} 416 417Type *TSChecker::GetDeclTsType(varbinder::Variable *var, varbinder::Decl *decl) 418{ 419 switch (decl->Type()) { 420 case varbinder::DeclType::CONST: 421 case varbinder::DeclType::LET: { 422 if (!decl->Node()->Parent()->IsTSTypeQuery()) { 423 ThrowTypeError({"Block-scoped variable '", var->Name(), "' used before its declaration"}, 424 decl->Node()->Start()); 425 break; 426 } 427 428 [[fallthrough]]; 429 } 430 case varbinder::DeclType::VAR: { 431 GetTypeVar(decl); 432 break; 433 } 434 case varbinder::DeclType::PROPERTY: { 435 var->SetTsType(decl->Node()->AsTSPropertySignature()->TypeAnnotation()->GetType(this)); 436 break; 437 } 438 case varbinder::DeclType::METHOD: { 439 auto *signatureInfo = Allocator()->New<checker::SignatureInfo>(Allocator()); 440 auto *callSignature = Allocator()->New<checker::Signature>(signatureInfo, GlobalAnyType()); 441 var->SetTsType(CreateFunctionTypeWithSignature(callSignature)); 442 break; 443 } 444 case varbinder::DeclType::FUNC: { 445 checker::ScopeContext scopeCtx(this, decl->Node()->AsScriptFunction()->Scope()); 446 InferFunctionDeclarationType(decl->AsFunctionDecl(), var); 447 break; 448 } 449 case varbinder::DeclType::PARAM: { 450 GetTypeParam(var, decl); 451 break; 452 } 453 case varbinder::DeclType::ENUM: { 454 GetTypeEnum(var, decl); 455 break; 456 } 457 case varbinder::DeclType::ENUM_LITERAL: { 458 UNREACHABLE(); // NOTE: aszilagyi. 459 } 460 default: { 461 break; 462 } 463 } 464 465 return var->TsType(); 466} 467 468Type *TSChecker::GetTypeOfVariable(varbinder::Variable *var) 469{ 470 if (var->TsType() != nullptr) { 471 return var->TsType(); 472 } 473 474 varbinder::Decl *decl = var->Declaration(); 475 476 TypeStackElement tse( 477 this, decl->Node(), 478 std::initializer_list<TypeErrorMessageElement> { 479 "'", var->Name(), "' is referenced directly or indirectly in its ", "own initializer ot type annotation."}, 480 decl->Node()->Start()); 481 if (tse.HasTypeError()) { 482 return GlobalErrorType(); 483 } 484 485 return GetDeclTsType(var, decl); 486} 487 488Type *TSChecker::GetTypeFromClassOrInterfaceReference([[maybe_unused]] ir::TSTypeReference *node, 489 varbinder::Variable *var) 490{ 491 Type *resolvedType = var->TsType(); 492 493 if (resolvedType == nullptr) { 494 ObjectDescriptor *desc = Allocator()->New<ObjectDescriptor>(Allocator()); 495 resolvedType = Allocator()->New<InterfaceType>(Allocator(), var->Name(), desc); 496 resolvedType->SetVariable(var); 497 var->SetTsType(resolvedType); 498 } 499 500 return resolvedType; 501} 502 503Type *TSChecker::GetTypeFromTypeAliasReference(ir::TSTypeReference *node, varbinder::Variable *var) 504{ 505 Type *resolvedType = var->TsType(); 506 507 if (resolvedType != nullptr) { 508 return resolvedType; 509 } 510 511 TypeStackElement tse(this, var, {"Type alias ", var->Name(), " circularly refences itself"}, node->Start()); 512 if (tse.HasTypeError()) { 513 return GlobalErrorType(); 514 } 515 516 ASSERT(var->Declaration()->Node() && var->Declaration()->Node()->IsTSTypeAliasDeclaration()); 517 ir::TSTypeAliasDeclaration *declaration = var->Declaration()->Node()->AsTSTypeAliasDeclaration(); 518 resolvedType = declaration->TypeAnnotation()->GetType(this); 519 var->SetTsType(resolvedType); 520 521 return resolvedType; 522} 523 524Type *TSChecker::GetTypeReferenceType(ir::TSTypeReference *node, varbinder::Variable *var) 525{ 526 ASSERT(var->Declaration()); 527 varbinder::Decl *decl = var->Declaration(); 528 529 if (decl->IsInterfaceDecl()) { 530 return GetTypeFromClassOrInterfaceReference(node, var); 531 } 532 533 if (decl->IsTypeAliasDecl()) { 534 return GetTypeFromTypeAliasReference(node, var); 535 } 536 537 ThrowTypeError("This reference refers to a value, but is being used as a type here. Did you mean to use 'typeof'?", 538 node->Start()); 539 return nullptr; 540} 541} // namespace ark::es2panda::checker 542