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 "TSAnalyzer.h"
17
18 #include "checker/TSchecker.h"
19 #include "checker/ts/destructuringContext.h"
20
21 namespace ark::es2panda::checker {
22
GetTSChecker() const23 TSChecker *TSAnalyzer::GetTSChecker() const
24 {
25 return static_cast<TSChecker *>(GetChecker());
26 }
27
28 // from base folder
Check(ir::CatchClause * st) const29 checker::Type *TSAnalyzer::Check(ir::CatchClause *st) const
30 {
31 TSChecker *checker = GetTSChecker();
32 ir::Expression *typeAnnotation = st->Param()->AsAnnotatedExpression()->TypeAnnotation();
33
34 if (typeAnnotation != nullptr) {
35 checker::Type *catchParamType = typeAnnotation->Check(checker);
36
37 if (!catchParamType->HasTypeFlag(checker::TypeFlag::ANY_OR_UNKNOWN)) {
38 checker->ThrowTypeError("Catch clause variable type annotation must be 'any' or 'unknown' if specified",
39 st->Start());
40 }
41 }
42
43 st->Body()->Check(checker);
44
45 return nullptr;
46 }
47
Check(ir::ClassDefinition * node) const48 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ClassDefinition *node) const
49 {
50 TSChecker *checker = GetTSChecker();
51 // NOTE: aszilagyi.
52 return checker->GlobalAnyType();
53 }
54
Check(ir::MetaProperty * expr) const55 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::MetaProperty *expr) const
56 {
57 TSChecker *checker = GetTSChecker();
58 // NOTE: aszilagyi.
59 return checker->GlobalAnyType();
60 }
61
Check(ir::TSIndexSignature * node) const62 checker::Type *TSAnalyzer::Check(ir::TSIndexSignature *node) const
63 {
64 TSChecker *checker = GetTSChecker();
65 if (node->TsType() != nullptr) {
66 return node->TsType();
67 }
68
69 const util::StringView ¶mName = node->Param()->AsIdentifier()->Name();
70 node->typeAnnotation_->Check(checker);
71 checker::Type *indexType = node->typeAnnotation_->GetType(checker);
72 checker::IndexInfo *info =
73 checker->Allocator()->New<checker::IndexInfo>(indexType, paramName, node->Readonly(), node->Start());
74 checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
75 checker::ObjectType *placeholder = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
76
77 ES2PANDA_ASSERT(placeholder != nullptr);
78 if (node->Kind() == ir::TSIndexSignature::TSIndexSignatureKind::NUMBER) {
79 placeholder->Desc()->numberIndexInfo = info;
80 } else {
81 placeholder->Desc()->stringIndexInfo = info;
82 }
83
84 node->SetTsType(placeholder);
85 return placeholder;
86 }
87
Check(ir::TSMethodSignature * node) const88 checker::Type *TSAnalyzer::Check(ir::TSMethodSignature *node) const
89 {
90 TSChecker *checker = GetTSChecker();
91 if (node->Computed()) {
92 checker->CheckComputedPropertyName(node->Key());
93 }
94
95 checker::ScopeContext scopeCtx(checker, node->Scope());
96
97 auto *signatureInfo = checker->Allocator()->New<checker::SignatureInfo>(checker->Allocator());
98 checker->CheckFunctionParameterDeclarations(node->Params(), signatureInfo);
99
100 auto *callSignature = checker->Allocator()->New<checker::Signature>(signatureInfo, checker->GlobalAnyType());
101 node->Variable()->SetTsType(checker->CreateFunctionTypeWithSignature(callSignature));
102
103 auto returnType = node->ReturnTypeAnnotation();
104 if (returnType == nullptr) {
105 checker->ThrowTypeError(
106 "Method signature, which lacks return-type annotation, implicitly has an 'any' return type.",
107 node->Start());
108 }
109
110 returnType->Check(checker);
111 ES2PANDA_ASSERT(callSignature != nullptr);
112 callSignature->SetReturnType(returnType->GetType(checker));
113
114 return nullptr;
115 }
116
Check(ir::TSPropertySignature * node) const117 checker::Type *TSAnalyzer::Check(ir::TSPropertySignature *node) const
118 {
119 TSChecker *checker = GetTSChecker();
120 if (node->TypeAnnotation() != nullptr) {
121 node->TypeAnnotation()->Check(checker);
122 }
123
124 if (node->Computed()) {
125 checker->CheckComputedPropertyName(node->Key());
126 }
127
128 if (node->TypeAnnotation() != nullptr) {
129 node->Variable()->SetTsType(node->TypeAnnotation()->GetType(checker));
130 return nullptr;
131 }
132
133 checker->ThrowTypeError("Property implicitly has an 'any' type.", node->Start());
134 return nullptr;
135 }
136
Check(ir::TSSignatureDeclaration * node) const137 checker::Type *TSAnalyzer::Check(ir::TSSignatureDeclaration *node) const
138 {
139 TSChecker *checker = GetTSChecker();
140 if (node->TsType() != nullptr) {
141 return node->TsType();
142 }
143
144 checker::ScopeContext scopeCtx(checker, node->Scope());
145
146 auto *signatureInfo = checker->Allocator()->New<checker::SignatureInfo>(checker->Allocator());
147 checker->CheckFunctionParameterDeclarations(node->Params(), signatureInfo);
148
149 bool isCallSignature = (node->Kind() == ir::TSSignatureDeclaration::TSSignatureDeclarationKind::CALL_SIGNATURE);
150
151 if (node->ReturnTypeAnnotation() == nullptr) {
152 if (isCallSignature) {
153 checker->ThrowTypeError(
154 "Call signature, which lacks return-type annotation, implicitly has an 'any' return type.",
155 node->Start());
156 }
157
158 checker->ThrowTypeError(
159 "Construct signature, which lacks return-type annotation, implicitly has an 'any' return type.",
160 node->Start());
161 }
162
163 node->ReturnTypeAnnotation()->Check(checker);
164 checker::Type *returnType = node->ReturnTypeAnnotation()->GetType(checker);
165
166 auto *signature = checker->Allocator()->New<checker::Signature>(signatureInfo, returnType);
167
168 checker::Type *placeholderObj = nullptr;
169
170 if (isCallSignature) {
171 placeholderObj = checker->CreateObjectTypeWithCallSignature(signature);
172 } else {
173 placeholderObj = checker->CreateObjectTypeWithConstructSignature(signature);
174 }
175
176 node->SetTsType(placeholderObj);
177 return placeholderObj;
178 }
179
GetSpreadElementType(checker::TSChecker * checker,checker::Type * spreadType,ArenaVector<checker::Type * > & elementTypes,const lexer::SourcePosition & loc)180 static void GetSpreadElementType(checker::TSChecker *checker, checker::Type *spreadType,
181 ArenaVector<checker::Type *> &elementTypes, const lexer::SourcePosition &loc)
182 {
183 bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
184
185 if (spreadType->IsObjectType() && spreadType->AsObjectType()->IsTupleType()) {
186 ArenaVector<checker::Type *> tupleElementTypes(checker->Allocator()->Adapter());
187 checker::TupleType *spreadTuple = spreadType->AsObjectType()->AsTupleType();
188
189 for (auto *it : spreadTuple->Properties()) {
190 if (inConstContext) {
191 elementTypes.push_back(it->TsType());
192 continue;
193 }
194
195 tupleElementTypes.push_back(it->TsType());
196 }
197
198 if (inConstContext) {
199 return;
200 }
201
202 elementTypes.push_back(checker->CreateUnionType(std::move(tupleElementTypes)));
203 return;
204 }
205
206 if (!spreadType->IsUnionType()) {
207 checker->ThrowTypeError(
208 {"Type '", spreadType, "' must have a '[Symbol.iterator]()' method that returns an iterator."}, loc);
209 return;
210 }
211
212 ArenaVector<checker::Type *> spreadTypes(checker->Allocator()->Adapter());
213 bool throwError = false;
214
215 for (auto *type : spreadType->AsUnionType()->ConstituentTypes()) {
216 if (type->IsArrayType()) {
217 spreadTypes.push_back(type->AsArrayType()->ElementType());
218 continue;
219 }
220
221 if (type->IsObjectType() && type->AsObjectType()->IsTupleType()) {
222 checker::TupleType *tuple = type->AsObjectType()->AsTupleType();
223
224 for (auto *it : tuple->Properties()) {
225 spreadTypes.push_back(it->TsType());
226 }
227
228 continue;
229 }
230
231 throwError = true;
232 break;
233 }
234
235 if (!throwError) {
236 elementTypes.push_back(checker->CreateUnionType(std::move(spreadTypes)));
237 return;
238 }
239
240 checker->ThrowTypeError(
241 {"Type '", spreadType, "' must have a '[Symbol.iterator]()' method that returns an iterator."}, loc);
242 }
243
Check(ir::ArrayExpression * expr) const244 checker::Type *TSAnalyzer::Check(ir::ArrayExpression *expr) const
245 {
246 TSChecker *checker = GetTSChecker();
247 ArenaVector<checker::Type *> elementTypes(checker->Allocator()->Adapter());
248 ArenaVector<checker::ElementFlags> elementFlags(checker->Allocator()->Adapter());
249 bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
250 bool createTuple = checker->HasStatus(checker::CheckerStatus::FORCE_TUPLE);
251
252 for (auto *it : expr->Elements()) {
253 if (it->IsSpreadElement()) {
254 checker::Type *spreadType = it->AsSpreadElement()->Argument()->Check(checker);
255
256 if (spreadType->IsArrayType()) {
257 elementTypes.push_back(inConstContext ? spreadType : spreadType->AsArrayType()->ElementType());
258 elementFlags.push_back(checker::ElementFlags::VARIADIC);
259 continue;
260 }
261
262 GetSpreadElementType(checker, spreadType, elementTypes, it->Start());
263 elementFlags.push_back(checker::ElementFlags::REST);
264 continue;
265 }
266
267 checker::Type *elementType = it->Check(checker);
268
269 if (!inConstContext) {
270 elementType = checker->GetBaseTypeOfLiteralType(elementType);
271 }
272
273 elementFlags.push_back(checker::ElementFlags::REQUIRED);
274 elementTypes.push_back(elementType);
275 }
276
277 if (inConstContext || createTuple) {
278 checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
279 uint32_t index = 0;
280
281 for (auto it = elementTypes.begin(); it != elementTypes.end(); it++, index++) {
282 util::StringView memberIndex = util::Helpers::ToStringView(checker->Allocator(), index);
283 varbinder::LocalVariable *tupleMember = varbinder::Scope::CreateVar(
284 checker->Allocator(), memberIndex, varbinder::VariableFlags::PROPERTY, nullptr);
285 ES2PANDA_ASSERT(tupleMember != nullptr);
286 if (inConstContext) {
287 tupleMember->AddFlag(varbinder::VariableFlags::READONLY);
288 }
289
290 tupleMember->SetTsType(*it);
291 ES2PANDA_ASSERT(desc != nullptr);
292 desc->properties.push_back(tupleMember);
293 }
294
295 const checker::TupleTypeInfo tupleTypeInfo = {ElementFlags::REQUIRED, index, index, inConstContext};
296 return checker->CreateTupleType(desc, std::move(elementFlags), tupleTypeInfo);
297 }
298
299 checker::Type *arrayElementType = nullptr;
300 if (elementTypes.empty()) {
301 arrayElementType = checker->GlobalAnyType();
302 } else {
303 arrayElementType = checker->CreateUnionType(std::move(elementTypes));
304 }
305
306 return checker->Allocator()->New<checker::ArrayType>(arrayElementType);
307 }
308
Check(ir::ArrowFunctionExpression * expr) const309 checker::Type *TSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const
310 {
311 TSChecker *checker = GetTSChecker();
312 varbinder::Variable *funcVar = nullptr;
313
314 if (expr->Function()->Parent()->Parent() != nullptr &&
315 expr->Function()->Parent()->Parent()->IsVariableDeclarator() &&
316 expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->IsIdentifier()) {
317 funcVar = expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Variable();
318 }
319
320 checker::ScopeContext scopeCtx(checker, expr->Function()->Scope());
321
322 auto *signatureInfo = checker->Allocator()->New<checker::SignatureInfo>(checker->Allocator());
323 checker->CheckFunctionParameterDeclarations(expr->Function()->Params(), signatureInfo);
324
325 auto *signature = checker->Allocator()->New<checker::Signature>(signatureInfo, checker->GlobalResolvingReturnType(),
326 expr->Function());
327 checker::Type *funcType = checker->CreateFunctionTypeWithSignature(signature);
328
329 if (funcVar != nullptr && funcVar->TsType() == nullptr) {
330 funcVar->SetTsType(funcType);
331 }
332 ES2PANDA_ASSERT(signature != nullptr);
333 signature->SetReturnType(checker->HandleFunctionReturn(expr->Function()));
334
335 if (!expr->Function()->Body()->IsExpression()) {
336 expr->Function()->Body()->Check(checker);
337 }
338
339 return funcType;
340 }
341
CheckAssignmentExprOperatorType(ir::AssignmentExpression * expr,checker::Type * leftType,checker::Type * rightType) const342 checker::Type *TSAnalyzer::CheckAssignmentExprOperatorType(ir::AssignmentExpression *expr, checker::Type *leftType,
343 checker::Type *rightType) const
344 {
345 TSChecker *checker = GetTSChecker();
346 ExpressionTypeInfo leftRightType {};
347 leftRightType.leftType = leftType;
348 leftRightType.rightType = rightType;
349 switch (expr->OperatorType()) {
350 case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL:
351 case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL:
352 case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL:
353 case lexer::TokenType::PUNCTUATOR_MOD_EQUAL:
354 case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL:
355 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL:
356 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL:
357 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL:
358 case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL:
359 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL:
360 case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL: {
361 return checker->CheckBinaryOperator(&leftRightType, expr->Left(), expr->Right(), expr,
362 expr->OperatorType());
363 }
364 case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
365 return checker->CheckPlusOperator(&leftRightType, expr->Left(), expr->Right(), expr, expr->OperatorType());
366 }
367 case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: {
368 checker->CheckAssignmentOperator(expr->OperatorType(), expr->Left(), leftType, rightType);
369 return rightType;
370 }
371 default: {
372 ES2PANDA_UNREACHABLE();
373 break;
374 }
375 }
376
377 return nullptr;
378 }
379
Check(ir::AssignmentExpression * expr) const380 checker::Type *TSAnalyzer::Check(ir::AssignmentExpression *expr) const
381 {
382 TSChecker *checker = GetTSChecker();
383 if (expr->Left()->IsArrayPattern()) {
384 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
385 auto destructuringContext =
386 checker::ArrayDestructuringContext({checker, expr->Left(), true, true, nullptr, expr->Right()});
387 destructuringContext.Start();
388 return destructuringContext.InferredType();
389 }
390
391 if (expr->Left()->IsObjectPattern()) {
392 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
393 auto destructuringContext =
394 checker::ObjectDestructuringContext({checker, expr->Left(), true, true, nullptr, expr->Right()});
395 destructuringContext.Start();
396 return destructuringContext.InferredType();
397 }
398
399 if (expr->Left()->IsIdentifier() && expr->Left()->AsIdentifier()->Variable() != nullptr &&
400 expr->Left()->AsIdentifier()->Variable()->Declaration()->IsConstDecl()) {
401 checker->ThrowTypeError(
402 {"Cannot assign to ", expr->Left()->AsIdentifier()->Name(), " because it is a constant."},
403 expr->Left()->Start());
404 }
405
406 auto *leftType = expr->Left()->Check(checker);
407
408 if (leftType->HasTypeFlag(checker::TypeFlag::READONLY)) {
409 checker->ThrowTypeError("Cannot assign to this property because it is readonly.", expr->Left()->Start());
410 }
411
412 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION) {
413 checker->ElaborateElementwise(leftType, expr->Right(), expr->Left()->Start());
414 return checker->CheckTypeCached(expr->Right());
415 }
416
417 auto *rightType = expr->Right()->Check(checker);
418
419 return CheckAssignmentExprOperatorType(expr, leftType, rightType);
420 }
421
Check(ir::AwaitExpression * expr) const422 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::AwaitExpression *expr) const
423 {
424 TSChecker *checker = GetTSChecker();
425 // NOTE(aszilagyi)
426 return checker->GlobalAnyType();
427 }
428
CheckBinaryExprArithmLogical(ir::BinaryExpression * expr,ExpressionTypeInfo * leftRightType,TSChecker * checker) const429 checker::Type *TSAnalyzer::CheckBinaryExprArithmLogical(ir::BinaryExpression *expr, ExpressionTypeInfo *leftRightType,
430 TSChecker *checker) const
431 {
432 switch (expr->OperatorType()) {
433 case lexer::TokenType::PUNCTUATOR_MULTIPLY:
434 case lexer::TokenType::PUNCTUATOR_EXPONENTIATION:
435 case lexer::TokenType::PUNCTUATOR_DIVIDE:
436 case lexer::TokenType::PUNCTUATOR_MOD:
437 case lexer::TokenType::PUNCTUATOR_MINUS:
438 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT:
439 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT:
440 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT:
441 case lexer::TokenType::PUNCTUATOR_BITWISE_AND:
442 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR:
443 case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
444 return checker->CheckBinaryOperator(leftRightType, expr->Left(), expr->Right(), expr, expr->OperatorType());
445 }
446 case lexer::TokenType::PUNCTUATOR_PLUS: {
447 return checker->CheckPlusOperator(leftRightType, expr->Left(), expr->Right(), expr, expr->OperatorType());
448 }
449 case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
450 return checker->CheckAndOperator(leftRightType->leftType, leftRightType->rightType, expr->Left());
451 }
452 case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
453 return checker->CheckOrOperator(leftRightType->leftType, leftRightType->rightType, expr->Left());
454 }
455 default: {
456 return nullptr;
457 }
458 }
459 }
460
Check(ir::BinaryExpression * expr) const461 checker::Type *TSAnalyzer::Check(ir::BinaryExpression *expr) const
462 {
463 TSChecker *checker = GetTSChecker();
464 ExpressionTypeInfo leftRightType {};
465 leftRightType.leftType = expr->Left()->Check(checker);
466 leftRightType.rightType = expr->Right()->Check(checker);
467
468 auto *checkBinaryExprPunctuator = CheckBinaryExprArithmLogical(expr, &leftRightType, checker);
469 if (checkBinaryExprPunctuator != nullptr) {
470 return checkBinaryExprPunctuator;
471 }
472
473 switch (expr->OperatorType()) {
474 case lexer::TokenType::PUNCTUATOR_LESS_THAN:
475 case lexer::TokenType::PUNCTUATOR_GREATER_THAN: {
476 return checker->CheckCompareOperator(&leftRightType, expr->Left(), expr->Right(), expr,
477 expr->OperatorType());
478 }
479 case lexer::TokenType::PUNCTUATOR_EQUAL:
480 case lexer::TokenType::PUNCTUATOR_NOT_EQUAL:
481 case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
482 case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL: {
483 if (checker->IsTypeEqualityComparableTo(leftRightType.leftType, leftRightType.rightType) ||
484 checker->IsTypeEqualityComparableTo(leftRightType.rightType, leftRightType.leftType)) {
485 return checker->GlobalBooleanType();
486 }
487
488 checker->ThrowBinaryLikeError(expr->OperatorType(), leftRightType.leftType, leftRightType.rightType,
489 expr->Start());
490 }
491 case lexer::TokenType::PUNCTUATOR_NULLISH_COALESCING: {
492 // NOTE: Csaba Repasi. Implement checker for nullish coalescing
493 return checker->GlobalAnyType();
494 }
495 case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: {
496 checker->CheckAssignmentOperator(expr->OperatorType(), expr->Left(), leftRightType.leftType,
497 leftRightType.rightType);
498 return leftRightType.rightType;
499 }
500 case lexer::TokenType::KEYW_INSTANCEOF: {
501 return checker->CheckInstanceofExpression(leftRightType.leftType, leftRightType.rightType, expr->Right(),
502 expr);
503 }
504 case lexer::TokenType::KEYW_IN: {
505 return checker->CheckInExpression(leftRightType.leftType, leftRightType.rightType, expr->Left(),
506 expr->Right(), expr);
507 }
508 default: {
509 ES2PANDA_UNREACHABLE();
510 break;
511 }
512 }
513
514 return nullptr;
515 }
516
Check(ir::CallExpression * expr) const517 checker::Type *TSAnalyzer::Check(ir::CallExpression *expr) const
518 {
519 TSChecker *checker = GetTSChecker();
520 checker::Type *calleeType = expr->callee_->Check(checker);
521
522 // NOTE: aszilagyi. handle optional chain
523 if (calleeType->IsObjectType()) {
524 checker::ObjectType *calleeObj = calleeType->AsObjectType();
525 return checker->ResolveCallOrNewExpression(calleeObj->CallSignatures(), expr->Arguments(), expr->Start());
526 }
527
528 checker->ThrowTypeError("This expression is not callable.", expr->Start());
529 return nullptr;
530 }
531
Check(ir::ChainExpression * expr) const532 checker::Type *TSAnalyzer::Check(ir::ChainExpression *expr) const
533 {
534 TSChecker *checker = GetTSChecker();
535 return expr->expression_->Check(checker);
536 }
537
Check(ir::ConditionalExpression * expr) const538 checker::Type *TSAnalyzer::Check(ir::ConditionalExpression *expr) const
539 {
540 TSChecker *checker = GetTSChecker();
541 checker::Type *testType = expr->Test()->Check(checker);
542
543 checker->CheckTruthinessOfType(testType, expr->Test()->Start());
544 checker->CheckTestingKnownTruthyCallableOrAwaitableType(expr->Test(), testType, expr->Consequent());
545
546 checker::Type *consequentType = expr->Consequent()->Check(checker);
547 checker::Type *alternateType = expr->Alternate()->Check(checker);
548
549 return checker->CreateUnionType({consequentType, alternateType});
550 }
551
Check(ir::FunctionExpression * expr) const552 checker::Type *TSAnalyzer::Check(ir::FunctionExpression *expr) const
553 {
554 TSChecker *checker = GetTSChecker();
555 varbinder::Variable *funcVar = nullptr;
556
557 if (expr->Function()->Parent()->Parent() != nullptr &&
558 expr->Function()->Parent()->Parent()->IsVariableDeclarator() &&
559 expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->IsIdentifier()) {
560 funcVar = expr->Function()->Parent()->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Variable();
561 }
562
563 checker::ScopeContext scopeCtx(checker, expr->Function()->Scope());
564
565 auto *signatureInfo = checker->Allocator()->New<checker::SignatureInfo>(checker->Allocator());
566 checker->CheckFunctionParameterDeclarations(expr->Function()->Params(), signatureInfo);
567
568 auto *signature = checker->Allocator()->New<checker::Signature>(signatureInfo, checker->GlobalResolvingReturnType(),
569 expr->Function());
570 checker::Type *funcType = checker->CreateFunctionTypeWithSignature(signature);
571
572 if (funcVar != nullptr && funcVar->TsType() == nullptr) {
573 funcVar->SetTsType(funcType);
574 }
575 ES2PANDA_ASSERT(signature != nullptr);
576 signature->SetReturnType(checker->HandleFunctionReturn(expr->Function()));
577
578 expr->Function()->Body()->Check(checker);
579
580 return funcType;
581 }
582
Check(ir::Identifier * expr) const583 checker::Type *TSAnalyzer::Check(ir::Identifier *expr) const
584 {
585 TSChecker *checker = GetTSChecker();
586 if (expr->Variable() == nullptr) {
587 if (expr->Name().Is("undefined")) {
588 return checker->GlobalUndefinedType();
589 }
590
591 checker->ThrowTypeError({"Cannot find name ", expr->Name()}, expr->Start());
592 }
593
594 const varbinder::Decl *decl = expr->Variable()->Declaration();
595
596 if (decl->IsTypeAliasDecl() || decl->IsInterfaceDecl()) {
597 checker->ThrowTypeError({expr->Name(), " only refers to a type, but is being used as a value here."},
598 expr->Start());
599 }
600
601 expr->SetTsType(checker->GetTypeOfVariable(expr->Variable()));
602 return expr->TsType();
603 }
604
CheckComputed(ir::MemberExpression * expr,checker::Type * indexType) const605 void TSAnalyzer::CheckComputed(ir::MemberExpression *expr, checker::Type *indexType) const
606 {
607 TSChecker *checker = GetTSChecker();
608 if (!indexType->HasTypeFlag(checker::TypeFlag::STRING_LIKE | checker::TypeFlag::NUMBER_LIKE)) {
609 checker->ThrowTypeError({"Type ", indexType, " cannot be used as index type"}, expr->Property()->Start());
610 }
611
612 if (indexType->IsNumberType()) {
613 checker->ThrowTypeError("No index signature with a parameter of type 'string' was found on type this type",
614 expr->Start());
615 }
616
617 if (indexType->IsStringType()) {
618 checker->ThrowTypeError("No index signature with a parameter of type 'number' was found on type this type",
619 expr->Start());
620 }
621
622 switch (expr->Property()->Type()) {
623 case ir::AstNodeType::IDENTIFIER: {
624 checker->ThrowTypeError(
625 {"Property ", expr->Property()->AsIdentifier()->Name(), " does not exist on this type."},
626 expr->Property()->Start());
627 }
628 case ir::AstNodeType::NUMBER_LITERAL: {
629 checker->ThrowTypeError(
630 {"Property ", expr->Property()->AsNumberLiteral()->Str(), " does not exist on this type."},
631 expr->Property()->Start());
632 }
633 case ir::AstNodeType::STRING_LITERAL: {
634 checker->ThrowTypeError(
635 {"Property ", expr->Property()->AsStringLiteral()->Str(), " does not exist on this type."},
636 expr->Property()->Start());
637 }
638 default: {
639 ES2PANDA_UNREACHABLE();
640 }
641 }
642 }
643
Check(ir::MemberExpression * expr) const644 checker::Type *TSAnalyzer::Check(ir::MemberExpression *expr) const
645 {
646 TSChecker *checker = GetTSChecker();
647 checker::Type *baseType = checker->CheckNonNullType(expr->Object()->Check(checker), expr->Object()->Start());
648
649 if (expr->IsComputed()) {
650 checker::Type *indexType = expr->Property()->Check(checker);
651 checker::Type *indexedAccessType = checker->GetPropertyTypeForIndexType(baseType, indexType);
652
653 if (indexedAccessType != nullptr) {
654 return indexedAccessType;
655 }
656 CheckComputed(expr, indexType);
657 }
658
659 varbinder::Variable *prop = checker->GetPropertyOfType(baseType, expr->Property()->AsIdentifier()->Name());
660
661 if (prop != nullptr) {
662 checker::Type *propType = checker->GetTypeOfVariable(prop);
663 if (prop->HasFlag(varbinder::VariableFlags::READONLY)) {
664 propType->AddTypeFlag(checker::TypeFlag::READONLY);
665 }
666
667 return propType;
668 }
669
670 if (baseType->IsObjectType()) {
671 checker::ObjectType *objType = baseType->AsObjectType();
672
673 if (objType->StringIndexInfo() != nullptr) {
674 checker::Type *indexType = objType->StringIndexInfo()->GetType();
675 if (objType->StringIndexInfo()->Readonly()) {
676 indexType->AddTypeFlag(checker::TypeFlag::READONLY);
677 }
678
679 return indexType;
680 }
681 }
682
683 checker->ThrowTypeError({"Property ", expr->Property()->AsIdentifier()->Name(), " does not exist on this type."},
684 expr->Property()->Start());
685 return nullptr;
686 }
687
Check(ir::NewExpression * expr) const688 checker::Type *TSAnalyzer::Check(ir::NewExpression *expr) const
689 {
690 TSChecker *checker = GetTSChecker();
691 checker::Type *calleeType = expr->callee_->Check(checker);
692
693 if (calleeType->IsObjectType()) {
694 checker::ObjectType *calleeObj = calleeType->AsObjectType();
695 return checker->ResolveCallOrNewExpression(calleeObj->ConstructSignatures(), expr->Arguments(), expr->Start());
696 }
697
698 checker->ThrowTypeError("This expression is not callable.", expr->Start());
699 return nullptr;
700 }
GetPropertyName(const ir::Expression * key)701 static util::StringView GetPropertyName(const ir::Expression *key)
702 {
703 if (key->IsIdentifier()) {
704 return key->AsIdentifier()->Name();
705 }
706
707 if (key->IsStringLiteral()) {
708 return key->AsStringLiteral()->Str();
709 }
710
711 ES2PANDA_ASSERT(key->IsNumberLiteral());
712 return key->AsNumberLiteral()->Str();
713 }
714
GetFlagsForProperty(const ir::Property * prop)715 static varbinder::VariableFlags GetFlagsForProperty(const ir::Property *prop)
716 {
717 if (!prop->IsMethod()) {
718 return varbinder::VariableFlags::PROPERTY;
719 }
720
721 varbinder::VariableFlags propFlags = varbinder::VariableFlags::METHOD;
722
723 if (prop->IsAccessor() && prop->Kind() == ir::PropertyKind::GET) {
724 propFlags |= varbinder::VariableFlags::READONLY;
725 }
726
727 return propFlags;
728 }
729
GetTypeForProperty(ir::Property * prop,checker::TSChecker * checker)730 static checker::Type *GetTypeForProperty(ir::Property *prop, checker::TSChecker *checker)
731 {
732 if (prop->IsAccessor()) {
733 checker::Type *funcType = prop->Value()->Check(checker);
734
735 if (prop->Kind() == ir::PropertyKind::SET) {
736 return checker->GlobalAnyType();
737 }
738
739 ES2PANDA_ASSERT(funcType->IsObjectType() && funcType->AsObjectType()->IsFunctionType());
740 return funcType->AsObjectType()->CallSignatures()[0]->ReturnType();
741 }
742
743 if (prop->IsShorthand()) {
744 return prop->Key()->Check(checker);
745 }
746
747 return prop->Value()->Check(checker);
748 }
749
CheckSpread(std::unordered_map<util::StringView,lexer::SourcePosition> & allPropertiesMap,checker::ObjectDescriptor * desc,ir::Expression * it) const750 void TSAnalyzer::CheckSpread(std::unordered_map<util::StringView, lexer::SourcePosition> &allPropertiesMap,
751 checker::ObjectDescriptor *desc, ir::Expression *it) const
752 {
753 TSChecker *checker = GetTSChecker();
754 ES2PANDA_ASSERT(it->IsSpreadElement());
755
756 checker::Type *const spreadType = it->AsSpreadElement()->Argument()->Check(checker);
757
758 // NOTE: aszilagyi. handle union of object types
759 if (!spreadType->IsObjectType()) {
760 checker->ThrowTypeError("Spread types may only be created from object types.", it->Start());
761 }
762
763 for (auto *spreadProp : spreadType->AsObjectType()->Properties()) {
764 auto found = allPropertiesMap.find(spreadProp->Name());
765 if (found != allPropertiesMap.end()) {
766 checker->ThrowTypeError({found->first, " is specified more than once, so this usage will be overwritten."},
767 found->second);
768 }
769
770 varbinder::LocalVariable *foundMember = desc->FindProperty(spreadProp->Name());
771
772 if (foundMember != nullptr) {
773 foundMember->SetTsType(spreadProp->TsType());
774 continue;
775 }
776
777 desc->properties.push_back(spreadProp);
778 }
779 }
780
CheckNonComputed(checker::ObjectDescriptor * desc,ir::Expression * it,std::unordered_map<util::StringView,lexer::SourcePosition> & allPropertiesMap,bool inConstContext) const781 void TSAnalyzer::CheckNonComputed(checker::ObjectDescriptor *desc, ir::Expression *it,
782 std::unordered_map<util::StringView, lexer::SourcePosition> &allPropertiesMap,
783 bool inConstContext) const
784 {
785 TSChecker *checker = GetTSChecker();
786 auto *prop = it->AsProperty();
787 checker::Type *propType = GetTypeForProperty(prop, checker);
788 varbinder::VariableFlags flags = GetFlagsForProperty(prop);
789 util::StringView propName = GetPropertyName(prop->Key());
790
791 auto *memberVar = varbinder::Scope::CreateVar(checker->Allocator(), propName, flags, it);
792
793 ES2PANDA_ASSERT(memberVar != nullptr);
794 if (inConstContext) {
795 memberVar->AddFlag(varbinder::VariableFlags::READONLY);
796 } else {
797 propType = checker->GetBaseTypeOfLiteralType(propType);
798 }
799
800 memberVar->SetTsType(propType);
801
802 if (prop->Key()->IsNumberLiteral()) {
803 memberVar->AddFlag(varbinder::VariableFlags::NUMERIC_NAME);
804 }
805 ES2PANDA_ASSERT(desc != nullptr);
806 varbinder::LocalVariable *foundMember = desc->FindProperty(propName);
807 allPropertiesMap.insert({propName, it->Start()});
808
809 if (foundMember != nullptr) {
810 foundMember->SetTsType(propType);
811 return;
812 }
813
814 desc->properties.push_back(memberVar);
815 }
816
CreateUnionTypeHelper(ArenaVector<checker::Type * > & computedPropTypes,bool inConstContext) const817 checker::IndexInfo *TSAnalyzer::CreateUnionTypeHelper(ArenaVector<checker::Type *> &computedPropTypes,
818 bool inConstContext) const
819 {
820 TSChecker *checker = GetTSChecker();
821
822 return checker->Allocator()->New<checker::IndexInfo>(checker->CreateUnionType(std::move(computedPropTypes)), "x",
823 inConstContext);
824 }
825
Check(ir::ObjectExpression * expr) const826 checker::Type *TSAnalyzer::Check(ir::ObjectExpression *expr) const
827 {
828 TSChecker *checker = GetTSChecker();
829
830 checker::ObjectDescriptor *desc = checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
831 std::unordered_map<util::StringView, lexer::SourcePosition> allPropertiesMap;
832 bool inConstContext = checker->HasStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
833 ArenaVector<checker::Type *> computedNumberPropTypes(checker->Allocator()->Adapter());
834 ArenaVector<checker::Type *> computedStringPropTypes(checker->Allocator()->Adapter());
835 bool hasComputedNumberProperty = false;
836 bool hasComputedStringProperty = false;
837 bool seenSpread = false;
838
839 for (auto *it : expr->Properties()) {
840 if (it->IsProperty()) {
841 auto *prop = it->AsProperty();
842
843 if (prop->IsComputed() && checker->CheckComputedPropertyName(prop->Key())->IsNumberType()) {
844 hasComputedNumberProperty = true;
845 computedNumberPropTypes.push_back(prop->Value()->Check(checker));
846 continue;
847 }
848
849 if (prop->IsComputed() && checker->CheckComputedPropertyName(prop->Key())->IsStringType()) {
850 hasComputedStringProperty = true;
851 computedStringPropTypes.push_back(prop->Value()->Check(checker));
852 continue;
853 }
854
855 CheckNonComputed(desc, it, allPropertiesMap, inConstContext);
856 }
857
858 if (it->IsSpreadElement()) {
859 CheckSpread(allPropertiesMap, desc, it);
860 seenSpread = true;
861 }
862 }
863
864 if (!seenSpread && (hasComputedNumberProperty || hasComputedStringProperty)) {
865 for (auto *it : desc->properties) {
866 computedStringPropTypes.push_back(it->TsType());
867
868 if (hasComputedNumberProperty && it->HasFlag(varbinder::VariableFlags::NUMERIC_NAME)) {
869 computedNumberPropTypes.push_back(it->TsType());
870 }
871 }
872
873 if (hasComputedNumberProperty) {
874 desc->numberIndexInfo = CreateUnionTypeHelper(computedNumberPropTypes, inConstContext);
875 }
876
877 if (hasComputedStringProperty) {
878 desc->stringIndexInfo = CreateUnionTypeHelper(computedStringPropTypes, inConstContext);
879 }
880 }
881
882 checker::Type *returnType = checker->Allocator()->New<checker::ObjectLiteralType>(desc);
883 ES2PANDA_ASSERT(returnType != nullptr);
884 returnType->AsObjectType()->AddObjectFlag(checker::ObjectFlags::RESOLVED_MEMBERS |
885 checker::ObjectFlags::CHECK_EXCESS_PROPS);
886 return returnType;
887 }
888
Check(ir::OmittedExpression * expr) const889 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::OmittedExpression *expr) const
890 {
891 TSChecker *checker = GetTSChecker();
892 return checker->GlobalUndefinedType();
893 }
894
Check(ir::OpaqueTypeNode * expr) const895 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::OpaqueTypeNode *expr) const
896 {
897 return expr->TsType();
898 }
899
Check(ir::BrokenTypeNode * expr) const900 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::BrokenTypeNode *expr) const
901 {
902 return nullptr;
903 }
904
Check(ir::SequenceExpression * expr) const905 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::SequenceExpression *expr) const
906 {
907 TSChecker *checker = GetTSChecker();
908 // NOTE: aszilagyi.
909 return checker->GlobalAnyType();
910 }
911
Check(ir::SuperExpression * expr) const912 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::SuperExpression *expr) const
913 {
914 TSChecker *checker = GetTSChecker();
915 // NOTE: aszilagyi.
916 return checker->GlobalAnyType();
917 }
918
Check(ir::TaggedTemplateExpression * expr) const919 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TaggedTemplateExpression *expr) const
920 {
921 TSChecker *checker = GetTSChecker();
922 // NOTE: aszilagyi.
923 return checker->GlobalAnyType();
924 }
925
Check(ir::TemplateLiteral * expr) const926 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TemplateLiteral *expr) const
927 {
928 TSChecker *checker = GetTSChecker();
929 // NOTE(aszilagyi)
930 return checker->GlobalAnyType();
931 }
932
Check(ir::ThisExpression * expr) const933 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::ThisExpression *expr) const
934 {
935 TSChecker *checker = GetTSChecker();
936 // NOTE: aszilagyi
937 return checker->GlobalAnyType();
938 }
939
Check(ir::TypeofExpression * expr) const940 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TypeofExpression *expr) const
941 {
942 TSChecker *checker = GetTSChecker();
943 return checker->GlobalStringType();
944 }
945
CheckDeleteKeyword(checker::TSChecker * checker,ir::UnaryExpression * expr) const946 checker::Type *TSAnalyzer::CheckDeleteKeyword([[maybe_unused]] checker::TSChecker *checker,
947 ir::UnaryExpression *expr) const
948 {
949 checker::Type *propType = expr->argument_->Check(checker);
950 if (!expr->Argument()->IsMemberExpression()) {
951 checker->ThrowTypeError("The operand of a delete operator must be a property reference.",
952 expr->Argument()->Start());
953 }
954 if (propType->Variable()->HasFlag(varbinder::VariableFlags::READONLY)) {
955 checker->ThrowTypeError("The operand of a delete operator cannot be a readonly property.",
956 expr->Argument()->Start());
957 }
958 if (!propType->Variable()->HasFlag(varbinder::VariableFlags::OPTIONAL)) {
959 checker->ThrowTypeError("The operand of a delete operator must be a optional.", expr->Argument()->Start());
960 }
961 return checker->GlobalBooleanType();
962 }
963
CheckLiteral(checker::TSChecker * checker,ir::UnaryExpression * expr) const964 checker::Type *TSAnalyzer::CheckLiteral([[maybe_unused]] checker::TSChecker *checker, ir::UnaryExpression *expr) const
965 {
966 if (!expr->Argument()->IsLiteral()) {
967 return nullptr;
968 }
969
970 const ir::Literal *lit = expr->Argument()->AsLiteral();
971 if (lit->IsNumberLiteral()) {
972 auto numberValue = lit->AsNumberLiteral()->Number().GetDouble();
973 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) {
974 return checker->CreateNumberLiteralType(numberValue);
975 }
976 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
977 return checker->CreateNumberLiteralType(-numberValue);
978 }
979 } else if (lit->IsBigIntLiteral() && expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
980 return checker->CreateBigintLiteralType(lit->AsBigIntLiteral()->Str(), true);
981 }
982
983 return nullptr;
984 }
985
Check(ir::UnaryExpression * expr) const986 checker::Type *TSAnalyzer::Check(ir::UnaryExpression *expr) const
987 {
988 TSChecker *checker = GetTSChecker();
989 checker::Type *operandType = expr->argument_->Check(checker);
990
991 if (expr->operator_ == lexer::TokenType::KEYW_TYPEOF) {
992 return operandType;
993 }
994
995 if (expr->operator_ == lexer::TokenType::KEYW_DELETE) {
996 return CheckDeleteKeyword(checker, expr);
997 }
998
999 auto *res = CheckLiteral(checker, expr);
1000 if (res != nullptr) {
1001 return res;
1002 }
1003
1004 switch (expr->operator_) {
1005 case lexer::TokenType::PUNCTUATOR_PLUS:
1006 case lexer::TokenType::PUNCTUATOR_MINUS:
1007 case lexer::TokenType::PUNCTUATOR_TILDE: {
1008 checker->CheckNonNullType(operandType, expr->Start());
1009 // NOTE: aszilagyi. check Symbol like types
1010
1011 if (expr->operator_ == lexer::TokenType::PUNCTUATOR_PLUS) {
1012 if (checker::TSChecker::MaybeTypeOfKind(operandType, checker::TypeFlag::BIGINT_LIKE)) {
1013 checker->ThrowTypeError({"Operator '+' cannot be applied to type '", operandType, "'"},
1014 expr->Start());
1015 }
1016
1017 return checker->GlobalNumberType();
1018 }
1019
1020 return checker->GetUnaryResultType(operandType);
1021 }
1022 case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
1023 checker->CheckTruthinessOfType(operandType, expr->Start());
1024 auto facts = operandType->GetTypeFacts();
1025 if ((facts & checker::TypeFacts::TRUTHY) != 0) {
1026 return checker->GlobalFalseType();
1027 }
1028
1029 if ((facts & checker::TypeFacts::FALSY) != 0) {
1030 return checker->GlobalTrueType();
1031 }
1032
1033 return checker->GlobalBooleanType();
1034 }
1035 default: {
1036 ES2PANDA_UNREACHABLE();
1037 }
1038 }
1039
1040 return nullptr;
1041 }
1042
Check(ir::UpdateExpression * expr) const1043 checker::Type *TSAnalyzer::Check(ir::UpdateExpression *expr) const
1044 {
1045 TSChecker *checker = GetTSChecker();
1046 checker::Type *operandType = expr->argument_->Check(checker);
1047 checker->CheckNonNullType(operandType, expr->Start());
1048
1049 if (!operandType->HasTypeFlag(checker::TypeFlag::VALID_ARITHMETIC_TYPE)) {
1050 checker->ThrowTypeError("An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type.",
1051 expr->Start());
1052 }
1053
1054 checker->CheckReferenceExpression(
1055 expr->argument_, "The operand of an increment or decrement operator must be a variable or a property access",
1056 "The operand of an increment or decrement operator may not be an optional property access");
1057
1058 return checker->GetUnaryResultType(operandType);
1059 }
1060
Check(ir::YieldExpression * expr) const1061 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::YieldExpression *expr) const
1062 {
1063 TSChecker *checker = GetTSChecker();
1064 // NOTE: aszilagyi.
1065 return checker->GlobalAnyType();
1066 }
1067 // compile methods for LITERAL EXPRESSIONS in alphabetical order
Check(ir::BigIntLiteral * expr) const1068 checker::Type *TSAnalyzer::Check(ir::BigIntLiteral *expr) const
1069 {
1070 TSChecker *checker = GetTSChecker();
1071 auto search = checker->BigintLiteralMap().find(expr->Str());
1072 if (search != checker->BigintLiteralMap().end()) {
1073 return search->second;
1074 }
1075
1076 auto *newBigintLiteralType = checker->Allocator()->New<checker::BigintLiteralType>(expr->Str(), false);
1077 checker->BigintLiteralMap().insert({expr->Str(), newBigintLiteralType});
1078 return newBigintLiteralType;
1079 }
1080
Check(ir::BooleanLiteral * expr) const1081 checker::Type *TSAnalyzer::Check(ir::BooleanLiteral *expr) const
1082 {
1083 TSChecker *checker = GetTSChecker();
1084 return expr->Value() ? checker->GlobalTrueType() : checker->GlobalFalseType();
1085 }
1086
Check(ir::NullLiteral * expr) const1087 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::NullLiteral *expr) const
1088 {
1089 TSChecker *checker = GetTSChecker();
1090 return checker->GlobalNullType();
1091 }
1092
Check(ir::NumberLiteral * expr) const1093 checker::Type *TSAnalyzer::Check(ir::NumberLiteral *expr) const
1094 {
1095 TSChecker *checker = GetTSChecker();
1096 auto search = checker->NumberLiteralMap().find(expr->Number().GetDouble());
1097 if (search != checker->NumberLiteralMap().end()) {
1098 return search->second;
1099 }
1100
1101 auto *newNumLiteralType = checker->Allocator()->New<checker::NumberLiteralType>(expr->Number().GetDouble());
1102 checker->NumberLiteralMap().insert({expr->Number().GetDouble(), newNumLiteralType});
1103 return newNumLiteralType;
1104 }
1105
Check(ir::RegExpLiteral * expr) const1106 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::RegExpLiteral *expr) const
1107 {
1108 TSChecker *checker = GetTSChecker();
1109 // NOTE: aszilagyi
1110 return checker->GlobalAnyType();
1111 }
1112
Check(ir::AnnotationDeclaration * expr) const1113 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::AnnotationDeclaration *expr) const
1114 {
1115 return nullptr;
1116 }
1117
Check(ir::AnnotationUsage * expr) const1118 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::AnnotationUsage *expr) const
1119 {
1120 return nullptr;
1121 }
1122
Check(ir::StringLiteral * expr) const1123 checker::Type *TSAnalyzer::Check(ir::StringLiteral *expr) const
1124 {
1125 TSChecker *checker = GetTSChecker();
1126 auto search = checker->StringLiteralMap().find(expr->Str());
1127 if (search != checker->StringLiteralMap().end()) {
1128 return search->second;
1129 }
1130
1131 auto *newStrLiteralType = checker->Allocator()->New<checker::StringLiteralType>(expr->Str());
1132 checker->StringLiteralMap().insert({expr->Str(), newStrLiteralType});
1133
1134 return newStrLiteralType;
1135 }
1136
Check(ir::BlockStatement * st) const1137 checker::Type *TSAnalyzer::Check(ir::BlockStatement *st) const
1138 {
1139 TSChecker *checker = GetTSChecker();
1140 checker::ScopeContext scopeCtx(checker, st->Scope());
1141
1142 for (auto *it : st->Statements()) {
1143 it->Check(checker);
1144 }
1145
1146 return nullptr;
1147 }
1148
Check(ir::BreakStatement * st) const1149 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::BreakStatement *st) const
1150 {
1151 return nullptr;
1152 }
1153
Check(ir::DoWhileStatement * st) const1154 checker::Type *TSAnalyzer::Check(ir::DoWhileStatement *st) const
1155 {
1156 TSChecker *checker = GetTSChecker();
1157 checker::ScopeContext scopeCtx(checker, st->Scope());
1158
1159 checker::Type *testType = st->Test()->Check(checker);
1160 checker->CheckTruthinessOfType(testType, st->Test()->Start());
1161 st->Body()->Check(checker);
1162
1163 return nullptr;
1164 }
1165
Check(ir::EmptyStatement * st) const1166 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::EmptyStatement *st) const
1167 {
1168 return nullptr;
1169 }
1170
Check(ir::ExpressionStatement * st) const1171 checker::Type *TSAnalyzer::Check(ir::ExpressionStatement *st) const
1172 {
1173 TSChecker *checker = GetTSChecker();
1174 return st->GetExpression()->Check(checker);
1175 }
1176
Check(ir::ForUpdateStatement * st) const1177 checker::Type *TSAnalyzer::Check(ir::ForUpdateStatement *st) const
1178 {
1179 TSChecker *checker = GetTSChecker();
1180 checker::ScopeContext scopeCtx(checker, st->Scope());
1181
1182 if (st->Init() != nullptr) {
1183 st->Init()->Check(checker);
1184 }
1185
1186 if (st->Test() != nullptr) {
1187 checker::Type *testType = st->Test()->Check(checker);
1188 checker->CheckTruthinessOfType(testType, st->Start());
1189 }
1190
1191 if (st->Update() != nullptr) {
1192 st->Update()->Check(checker);
1193 }
1194
1195 st->Body()->Check(checker);
1196
1197 return nullptr;
1198 }
1199
Check(ir::FunctionDeclaration * st) const1200 checker::Type *TSAnalyzer::Check(ir::FunctionDeclaration *st) const
1201 {
1202 TSChecker *checker = GetTSChecker();
1203 if (st->Function()->IsOverload()) {
1204 return nullptr;
1205 }
1206
1207 const util::StringView &funcName = st->Function()->Id()->Name();
1208 auto result = checker->Scope()->Find(funcName);
1209 ES2PANDA_ASSERT(result.variable);
1210
1211 checker::ScopeContext scopeCtx(checker, st->Function()->Scope());
1212
1213 if (result.variable->TsType() == nullptr) {
1214 checker->InferFunctionDeclarationType(result.variable->Declaration()->AsFunctionDecl(), result.variable);
1215 }
1216
1217 st->Function()->Body()->Check(checker);
1218
1219 return nullptr;
1220 }
1221
Check(ir::IfStatement * st) const1222 checker::Type *TSAnalyzer::Check(ir::IfStatement *st) const
1223 {
1224 TSChecker *checker = GetTSChecker();
1225 checker::Type *testType = st->Test()->Check(checker);
1226 checker->CheckTruthinessOfType(testType, st->Start());
1227 checker->CheckTestingKnownTruthyCallableOrAwaitableType(st->Test(), testType, st->Consequent());
1228
1229 st->Consequent()->Check(checker);
1230
1231 if (st->Alternate() != nullptr) {
1232 st->Alternate()->Check(checker);
1233 }
1234
1235 return nullptr;
1236 }
1237
Check(ir::ReturnStatement * st) const1238 checker::Type *TSAnalyzer::Check(ir::ReturnStatement *st) const
1239 {
1240 TSChecker *checker = GetTSChecker();
1241 ir::AstNode *ancestor = util::Helpers::FindAncestorGivenByType(st, ir::AstNodeType::SCRIPT_FUNCTION);
1242 ES2PANDA_ASSERT(ancestor && ancestor->IsScriptFunction());
1243 auto *containingFunc = ancestor->AsScriptFunction();
1244
1245 if (containingFunc->Parent()->Parent()->IsMethodDefinition()) {
1246 const ir::MethodDefinition *containingClassMethod = containingFunc->Parent()->Parent()->AsMethodDefinition();
1247 if (containingClassMethod->Kind() == ir::MethodDefinitionKind::SET) {
1248 checker->ThrowTypeError("Setters cannot return a value", st->Start());
1249 }
1250 }
1251
1252 if (containingFunc->ReturnTypeAnnotation() != nullptr) {
1253 checker::Type *returnType = checker->GlobalUndefinedType();
1254 checker::Type *funcReturnType = containingFunc->ReturnTypeAnnotation()->GetType(checker);
1255
1256 if (st->Argument() != nullptr) {
1257 checker->ElaborateElementwise(funcReturnType, st->Argument(), st->Start());
1258 returnType = checker->CheckTypeCached(st->Argument());
1259 }
1260
1261 checker->IsTypeAssignableTo(returnType, funcReturnType, diagnostic::INVALID_ASSIGNMNENT_2,
1262 {returnType, funcReturnType}, st->Start());
1263 }
1264
1265 return nullptr;
1266 }
1267
Check(ir::SwitchStatement * st) const1268 checker::Type *TSAnalyzer::Check(ir::SwitchStatement *st) const
1269 {
1270 TSChecker *checker = GetTSChecker();
1271 checker::ScopeContext scopeCtx(checker, st->Scope());
1272
1273 checker::Type *exprType = st->Discriminant()->Check(checker);
1274 bool exprIsLiteral = checker::TSChecker::IsLiteralType(exprType);
1275
1276 for (auto *it : st->Cases()) {
1277 if (it->Test() != nullptr) {
1278 checker::Type *caseType = it->Test()->Check(checker);
1279 bool caseIsLiteral = checker::TSChecker::IsLiteralType(caseType);
1280 checker::Type *comparedExprType = exprType;
1281
1282 if (!caseIsLiteral || !exprIsLiteral) {
1283 caseType = caseIsLiteral ? checker->GetBaseTypeOfLiteralType(caseType) : caseType;
1284 comparedExprType = checker->GetBaseTypeOfLiteralType(exprType);
1285 }
1286
1287 if (!checker->IsTypeEqualityComparableTo(comparedExprType, caseType) &&
1288 !checker->IsTypeComparableTo(caseType, comparedExprType)) {
1289 checker->ThrowTypeError({"Type ", caseType, " is not comparable to type ", comparedExprType},
1290 it->Test()->Start());
1291 }
1292 }
1293
1294 for (auto *caseStmt : it->Consequent()) {
1295 caseStmt->Check(checker);
1296 }
1297 }
1298
1299 return nullptr;
1300 }
1301
Check(ir::TryStatement * st) const1302 checker::Type *TSAnalyzer::Check(ir::TryStatement *st) const
1303 {
1304 TSChecker *checker = GetTSChecker();
1305 st->Block()->Check(checker);
1306
1307 for (auto *catchClause : st->CatchClauses()) {
1308 if (catchClause != nullptr) {
1309 catchClause->Check(checker);
1310 }
1311 }
1312
1313 if (st->HasFinalizer()) {
1314 st->finalizer_->Check(checker);
1315 }
1316
1317 return nullptr;
1318 }
1319
CheckSimpleVariableDeclaration(checker::TSChecker * checker,ir::VariableDeclarator * declarator)1320 static void CheckSimpleVariableDeclaration(checker::TSChecker *checker, ir::VariableDeclarator *declarator)
1321 {
1322 varbinder::Variable *const bindingVar = declarator->Id()->AsIdentifier()->Variable();
1323 checker::Type *previousType = bindingVar->TsType();
1324 auto *const typeAnnotation = declarator->Id()->AsIdentifier()->TypeAnnotation();
1325 auto *const initializer = declarator->Init();
1326 const bool isConst = declarator->Parent()->AsVariableDeclaration()->Kind() ==
1327 ir::VariableDeclaration::VariableDeclarationKind::CONST;
1328
1329 if (isConst) {
1330 checker->AddStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
1331 }
1332
1333 if (typeAnnotation != nullptr) {
1334 typeAnnotation->Check(checker);
1335 }
1336
1337 if (typeAnnotation != nullptr && initializer != nullptr) {
1338 checker::Type *const annotationType = typeAnnotation->GetType(checker);
1339 checker->ElaborateElementwise(annotationType, initializer, declarator->Id()->Start());
1340 bindingVar->SetTsType(annotationType);
1341 } else if (typeAnnotation != nullptr) {
1342 bindingVar->SetTsType(typeAnnotation->GetType(checker));
1343 } else if (initializer != nullptr) {
1344 checker::Type *initializerType = checker->CheckTypeCached(initializer);
1345
1346 if (!isConst) {
1347 initializerType = checker->GetBaseTypeOfLiteralType(initializerType);
1348 }
1349
1350 ES2PANDA_ASSERT(initializerType != nullptr);
1351 if (initializerType->IsNullType()) {
1352 checker->ThrowTypeError(
1353 {"Cannot infer type for variable '", declarator->Id()->AsIdentifier()->Name(), "'."},
1354 declarator->Id()->Start());
1355 }
1356
1357 bindingVar->SetTsType(initializerType);
1358 } else {
1359 checker->ThrowTypeError({"Variable ", declarator->Id()->AsIdentifier()->Name(), " implicitly has an any type."},
1360 declarator->Id()->Start());
1361 }
1362
1363 if (previousType != nullptr) {
1364 checker->IsTypeIdenticalTo(bindingVar->TsType(), previousType, diagnostic::DIFFERENT_SUBSEQ_DECL,
1365 {bindingVar->Name(), previousType, bindingVar->TsType()}, declarator->Id()->Start());
1366 }
1367
1368 checker->RemoveStatus(checker::CheckerStatus::IN_CONST_CONTEXT);
1369 }
1370
Check(ir::VariableDeclarator * st) const1371 checker::Type *TSAnalyzer::Check(ir::VariableDeclarator *st) const
1372 {
1373 TSChecker *checker = GetTSChecker();
1374
1375 if (st->TsType() == st->CHECKED) {
1376 return nullptr;
1377 }
1378
1379 if (st->Id()->IsIdentifier()) {
1380 CheckSimpleVariableDeclaration(checker, st);
1381 st->SetTsType(st->CHECKED);
1382 return nullptr;
1383 }
1384
1385 if (st->Id()->IsArrayPattern()) {
1386 auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
1387 checker::ArrayDestructuringContext({checker, st->Id(), false,
1388 st->Id()->AsArrayPattern()->TypeAnnotation() == nullptr,
1389 st->Id()->AsArrayPattern()->TypeAnnotation(), st->Init()})
1390 .Start();
1391
1392 st->SetTsType(st->CHECKED);
1393 return nullptr;
1394 }
1395
1396 ES2PANDA_ASSERT(st->Id()->IsObjectPattern());
1397 auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::FORCE_TUPLE);
1398 checker::ObjectDestructuringContext({checker, st->Id(), false,
1399 st->Id()->AsObjectPattern()->TypeAnnotation() == nullptr,
1400 st->Id()->AsObjectPattern()->TypeAnnotation(), st->Init()})
1401 .Start();
1402
1403 st->SetTsType(st->CHECKED);
1404 return nullptr;
1405 }
1406
Check(ir::VariableDeclaration * st) const1407 checker::Type *TSAnalyzer::Check(ir::VariableDeclaration *st) const
1408 {
1409 TSChecker *checker = GetTSChecker();
1410 for (auto *it : st->Declarators()) {
1411 it->Check(checker);
1412 }
1413
1414 return nullptr;
1415 }
1416
Check(ir::WhileStatement * st) const1417 checker::Type *TSAnalyzer::Check(ir::WhileStatement *st) const
1418 {
1419 TSChecker *checker = GetTSChecker();
1420 checker::ScopeContext scopeCtx(checker, st->Scope());
1421
1422 checker::Type *testType = st->Test()->Check(checker);
1423 checker->CheckTruthinessOfType(testType, st->Test()->Start());
1424
1425 st->Body()->Check(checker);
1426 return nullptr;
1427 }
1428 // from ts folder
Check(ir::TSAnyKeyword * node) const1429 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSAnyKeyword *node) const
1430 {
1431 return nullptr;
1432 }
1433
Check(ir::TSArrayType * node) const1434 checker::Type *TSAnalyzer::Check(ir::TSArrayType *node) const
1435 {
1436 TSChecker *checker = GetTSChecker();
1437 node->elementType_->Check(checker);
1438 return nullptr;
1439 }
1440
IsValidConstAssertionArgument(checker::Checker * checker,const ir::AstNode * arg)1441 static bool IsValidConstAssertionArgument(checker::Checker *checker, const ir::AstNode *arg)
1442 {
1443 switch (arg->Type()) {
1444 case ir::AstNodeType::NUMBER_LITERAL:
1445 case ir::AstNodeType::STRING_LITERAL:
1446 case ir::AstNodeType::BIGINT_LITERAL:
1447 case ir::AstNodeType::BOOLEAN_LITERAL:
1448 case ir::AstNodeType::ARRAY_EXPRESSION:
1449 case ir::AstNodeType::OBJECT_EXPRESSION:
1450 case ir::AstNodeType::TEMPLATE_LITERAL: {
1451 return true;
1452 }
1453 case ir::AstNodeType::UNARY_EXPRESSION: {
1454 const ir::UnaryExpression *unaryExpr = arg->AsUnaryExpression();
1455 lexer::TokenType op = unaryExpr->OperatorType();
1456 const ir::Expression *unaryArg = unaryExpr->Argument();
1457 return (op == lexer::TokenType::PUNCTUATOR_MINUS && unaryArg->IsLiteral() &&
1458 (unaryArg->AsLiteral()->IsNumberLiteral() || unaryArg->AsLiteral()->IsBigIntLiteral())) ||
1459 (op == lexer::TokenType::PUNCTUATOR_PLUS && unaryArg->IsLiteral() &&
1460 unaryArg->AsLiteral()->IsNumberLiteral());
1461 }
1462 case ir::AstNodeType::MEMBER_EXPRESSION: {
1463 const ir::MemberExpression *memberExpr = arg->AsMemberExpression();
1464 if (memberExpr->Object()->IsIdentifier()) {
1465 auto result = checker->Scope()->Find(memberExpr->Object()->AsIdentifier()->Name());
1466 constexpr auto ENUM_LITERAL_TYPE = checker::EnumLiteralType::EnumLiteralTypeKind::LITERAL;
1467 if (result.variable != nullptr &&
1468 result.variable->TsType()->HasTypeFlag(checker::TypeFlag::ENUM_LITERAL) &&
1469 result.variable->TsType()->AsEnumLiteralType()->Kind() == ENUM_LITERAL_TYPE) {
1470 return true;
1471 }
1472 }
1473 return false;
1474 }
1475 default:
1476 return false;
1477 }
1478 }
1479
Check(ir::TSAsExpression * expr) const1480 checker::Type *TSAnalyzer::Check(ir::TSAsExpression *expr) const
1481 {
1482 TSChecker *checker = GetTSChecker();
1483 if (expr->IsConst()) {
1484 auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_CONST_CONTEXT);
1485 checker::Type *exprType = expr->Expr()->Check(checker);
1486
1487 if (!IsValidConstAssertionArgument(checker, expr->Expr())) {
1488 checker->ThrowTypeError(
1489 "A 'const' assertions can only be applied to references to enum members, or string, number, "
1490 "boolean, array, or object literals.",
1491 expr->Expr()->Start());
1492 }
1493
1494 return exprType;
1495 }
1496
1497 auto context = checker::SavedCheckerContext(checker, checker::CheckerStatus::NO_OPTS);
1498
1499 expr->TypeAnnotation()->Check(checker);
1500 checker::Type *exprType = checker->GetBaseTypeOfLiteralType(expr->Expr()->Check(checker));
1501 checker::Type *targetType = expr->TypeAnnotation()->GetType(checker);
1502
1503 checker->IsTypeComparableTo(targetType, exprType, diagnostic::DISJOINT_CONVERSION, {exprType, targetType},
1504 expr->Start());
1505
1506 return targetType;
1507 }
1508
Check(ir::TSBigintKeyword * node) const1509 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSBigintKeyword *node) const
1510 {
1511 return nullptr;
1512 }
1513
Check(ir::TSBooleanKeyword * node) const1514 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSBooleanKeyword *node) const
1515 {
1516 return nullptr;
1517 }
1518
Check(ir::TSConstructorType * node) const1519 checker::Type *TSAnalyzer::Check(ir::TSConstructorType *node) const
1520 {
1521 TSChecker *checker = GetTSChecker();
1522 checker::ScopeContext scopeCtx(checker, node->Scope());
1523
1524 auto *signatureInfo = checker->Allocator()->New<checker::SignatureInfo>(checker->Allocator());
1525 checker->CheckFunctionParameterDeclarations(node->Params(), signatureInfo);
1526 node->ReturnType()->Check(checker);
1527 auto *constructSignature =
1528 checker->Allocator()->New<checker::Signature>(signatureInfo, node->ReturnType()->GetType(checker));
1529
1530 return checker->CreateConstructorTypeWithSignature(constructSignature);
1531 }
1532
EvaluateIdentifier(checker::TSChecker * checker,varbinder::EnumVariable * enumVar,const ir::Identifier * expr)1533 static varbinder::EnumMemberResult EvaluateIdentifier(checker::TSChecker *checker, varbinder::EnumVariable *enumVar,
1534 const ir::Identifier *expr)
1535 {
1536 if (expr->Name() == "NaN") {
1537 return std::nan("");
1538 }
1539 if (expr->Name() == "Infinity") {
1540 return std::numeric_limits<double>::infinity();
1541 }
1542
1543 varbinder::Variable *enumMember = expr->AsIdentifier()->Variable();
1544
1545 if (enumMember == nullptr) {
1546 checker->ThrowTypeError({"Cannot find name ", expr->AsIdentifier()->Name()},
1547 enumVar->Declaration()->Node()->Start());
1548 }
1549
1550 if (enumMember->IsEnumVariable()) {
1551 varbinder::EnumVariable *exprEnumVar = enumMember->AsEnumVariable();
1552 if (std::holds_alternative<bool>(exprEnumVar->Value())) {
1553 checker->ThrowTypeError(
1554 "A member initializer in a enum declaration cannot reference members declared after it, "
1555 "including "
1556 "members defined in other enums.",
1557 enumVar->Declaration()->Node()->Start());
1558 }
1559
1560 return exprEnumVar->Value();
1561 }
1562
1563 return false;
1564 }
1565
ToInt(double num)1566 static int32_t ToInt(double num)
1567 {
1568 if (num >= std::numeric_limits<int32_t>::min() && num <= std::numeric_limits<int32_t>::max()) {
1569 return static_cast<int32_t>(num);
1570 }
1571
1572 // NOTE (aszilagyi): Perform ECMA defined toInt conversion
1573
1574 return 0;
1575 }
1576
ToUInt(double num)1577 static uint32_t ToUInt(double num)
1578 {
1579 if (num >= std::numeric_limits<uint32_t>::min() && num <= std::numeric_limits<uint32_t>::max()) {
1580 return static_cast<int32_t>(num);
1581 }
1582
1583 // NOTE (aszilagyi): Perform ECMA defined toInt conversion
1584
1585 return 0;
1586 }
1587
GetOperationResulForDouble(lexer::TokenType type,varbinder::EnumMemberResult left,varbinder::EnumMemberResult right)1588 varbinder::EnumMemberResult GetOperationResulForDouble(lexer::TokenType type, varbinder::EnumMemberResult left,
1589 varbinder::EnumMemberResult right)
1590 {
1591 switch (type) {
1592 case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
1593 return static_cast<double>(ToUInt(std::get<double>(left)) | ToUInt(std::get<double>(right)));
1594 }
1595 case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
1596 return static_cast<double>(ToUInt(std::get<double>(left)) & ToUInt(std::get<double>(right)));
1597 }
1598 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
1599 return static_cast<double>(ToUInt(std::get<double>(left)) ^ ToUInt(std::get<double>(right)));
1600 }
1601 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT: { // NOLINTNEXTLINE(hicpp-signed-bitwise)
1602 return static_cast<double>(ToInt(std::get<double>(left)) << ToUInt(std::get<double>(right)));
1603 }
1604 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT: { // NOLINTNEXTLINE(hicpp-signed-bitwise)
1605 return static_cast<double>(ToInt(std::get<double>(left)) >> ToUInt(std::get<double>(right)));
1606 }
1607 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT: {
1608 return static_cast<double>(ToUInt(std::get<double>(left)) >> ToUInt(std::get<double>(right)));
1609 }
1610 case lexer::TokenType::PUNCTUATOR_PLUS: {
1611 return std::get<double>(left) + std::get<double>(right);
1612 }
1613 case lexer::TokenType::PUNCTUATOR_MINUS: {
1614 return std::get<double>(left) - std::get<double>(right);
1615 }
1616 case lexer::TokenType::PUNCTUATOR_MULTIPLY: {
1617 return std::get<double>(left) * std::get<double>(right);
1618 }
1619 case lexer::TokenType::PUNCTUATOR_DIVIDE: {
1620 return std::get<double>(left) / std::get<double>(right);
1621 }
1622 case lexer::TokenType::PUNCTUATOR_MOD: {
1623 return std::fmod(std::get<double>(left), std::get<double>(right));
1624 }
1625 case lexer::TokenType::PUNCTUATOR_EXPONENTIATION: {
1626 return std::pow(std::get<double>(left), std::get<double>(right));
1627 }
1628 default: {
1629 return false;
1630 }
1631 }
1632 }
1633
EvaluateBinaryExpression(checker::TSChecker * checker,varbinder::EnumVariable * enumVar,const ir::BinaryExpression * expr) const1634 varbinder::EnumMemberResult TSAnalyzer::EvaluateBinaryExpression(checker::TSChecker *checker,
1635 varbinder::EnumVariable *enumVar,
1636 const ir::BinaryExpression *expr) const
1637 {
1638 varbinder::EnumMemberResult left = EvaluateEnumMember(checker, enumVar, expr->AsBinaryExpression()->Left());
1639 varbinder::EnumMemberResult right = EvaluateEnumMember(checker, enumVar, expr->AsBinaryExpression()->Right());
1640 if (std::holds_alternative<double>(left) && std::holds_alternative<double>(right)) {
1641 GetOperationResulForDouble(expr->AsBinaryExpression()->OperatorType(), left, right);
1642 }
1643
1644 if (std::holds_alternative<util::StringView>(left) && std::holds_alternative<util::StringView>(right) &&
1645 expr->AsBinaryExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_PLUS) {
1646 std::stringstream ss;
1647 ss << std::get<util::StringView>(left) << std::get<util::StringView>(right);
1648
1649 util::UString res(ss.str(), checker->Allocator());
1650 return res.View();
1651 }
1652
1653 return false;
1654 }
1655
EvaluateUnaryExpression(checker::TSChecker * checker,varbinder::EnumVariable * enumVar,const ir::UnaryExpression * expr) const1656 varbinder::EnumMemberResult TSAnalyzer::EvaluateUnaryExpression(checker::TSChecker *checker,
1657 varbinder::EnumVariable *enumVar,
1658 const ir::UnaryExpression *expr) const
1659 {
1660 varbinder::EnumMemberResult value = EvaluateEnumMember(checker, enumVar, expr->Argument());
1661 if (!std::holds_alternative<double>(value)) {
1662 return false;
1663 }
1664
1665 switch (expr->OperatorType()) {
1666 case lexer::TokenType::PUNCTUATOR_PLUS: {
1667 return std::get<double>(value);
1668 }
1669 case lexer::TokenType::PUNCTUATOR_MINUS: {
1670 return -std::get<double>(value);
1671 }
1672 case lexer::TokenType::PUNCTUATOR_TILDE: {
1673 return static_cast<double>(~ToInt(std::get<double>(value))); // NOLINT(hicpp-signed-bitwise)
1674 }
1675 default: {
1676 break;
1677 }
1678 }
1679
1680 return false;
1681 }
1682
EvaluateEnumMember(checker::TSChecker * checker,varbinder::EnumVariable * enumVar,const ir::AstNode * expr) const1683 varbinder::EnumMemberResult TSAnalyzer::EvaluateEnumMember(checker::TSChecker *checker,
1684 varbinder::EnumVariable *enumVar,
1685 const ir::AstNode *expr) const
1686 {
1687 switch (expr->Type()) {
1688 case ir::AstNodeType::UNARY_EXPRESSION: {
1689 return EvaluateUnaryExpression(checker, enumVar, expr->AsUnaryExpression());
1690 }
1691 case ir::AstNodeType::BINARY_EXPRESSION: {
1692 return EvaluateBinaryExpression(checker, enumVar, expr->AsBinaryExpression());
1693 }
1694 case ir::AstNodeType::NUMBER_LITERAL: {
1695 return expr->AsNumberLiteral()->Number().GetDouble();
1696 }
1697 case ir::AstNodeType::STRING_LITERAL: {
1698 return expr->AsStringLiteral()->Str();
1699 }
1700 case ir::AstNodeType::IDENTIFIER: {
1701 return EvaluateIdentifier(checker, enumVar, expr->AsIdentifier());
1702 }
1703 case ir::AstNodeType::MEMBER_EXPRESSION: {
1704 return EvaluateEnumMember(checker, enumVar, expr->AsMemberExpression());
1705 }
1706 default:
1707 break;
1708 }
1709
1710 return false;
1711 }
1712
IsComputedEnumMember(const ir::Expression * init)1713 static bool IsComputedEnumMember(const ir::Expression *init)
1714 {
1715 if (init->IsLiteral()) {
1716 return !init->AsLiteral()->IsStringLiteral() && !init->AsLiteral()->IsNumberLiteral();
1717 }
1718
1719 if (init->IsTemplateLiteral()) {
1720 return !init->AsTemplateLiteral()->Quasis().empty();
1721 }
1722
1723 return true;
1724 }
1725
AddEnumValueDeclaration(checker::TSChecker * checker,double number,varbinder::EnumVariable * variable)1726 static void AddEnumValueDeclaration(checker::TSChecker *checker, double number, varbinder::EnumVariable *variable)
1727 {
1728 variable->SetTsType(checker->GlobalNumberType());
1729
1730 util::StringView memberStr = util::Helpers::ToStringView(checker->Allocator(), number);
1731
1732 varbinder::LocalScope *enumScope = checker->Scope()->AsLocalScope();
1733 varbinder::Variable *res = enumScope->FindLocal(memberStr, varbinder::ResolveBindingOptions::BINDINGS);
1734 varbinder::EnumVariable *enumVar = nullptr;
1735
1736 if (res == nullptr) {
1737 auto *decl = checker->Allocator()->New<varbinder::EnumDecl>(memberStr);
1738 ES2PANDA_ASSERT(decl != nullptr);
1739 decl->BindNode(variable->Declaration()->Node());
1740 enumScope->AddDecl(checker->Allocator(), decl, ScriptExtension::TS);
1741 res = enumScope->FindLocal(memberStr, varbinder::ResolveBindingOptions::BINDINGS);
1742 ES2PANDA_ASSERT(res && res->IsEnumVariable());
1743 enumVar = res->AsEnumVariable();
1744 enumVar->AsEnumVariable()->SetBackReference();
1745 enumVar->SetTsType(checker->GlobalStringType());
1746 } else {
1747 ES2PANDA_ASSERT(res->IsEnumVariable());
1748 enumVar = res->AsEnumVariable();
1749 auto *decl = checker->Allocator()->New<varbinder::EnumDecl>(memberStr);
1750 ES2PANDA_ASSERT(decl != nullptr);
1751 decl->BindNode(variable->Declaration()->Node());
1752 enumVar->ResetDecl(decl);
1753 }
1754
1755 enumVar->SetValue(variable->Declaration()->Name());
1756 }
1757
1758 // NOLINTBEGIN(modernize-avoid-c-arrays)
1759 static constexpr char const INVALID_COMPUTED_WITH_STRING[] =
1760 "Computed values are not permitted in an enum with string valued members.";
1761 static constexpr char const INVALID_CONST_MEMBER[] =
1762 "'const' enum member initializers can only contain literal values and other computed enum values.";
1763 static constexpr char const INVALID_CONST_NAN[] =
1764 "'const' enum member initializer was evaluated to disallowed value 'NaN'.";
1765 static constexpr char const INVALID_CONST_INF[] =
1766 "'const' enum member initializer was evaluated to a non-finite value.";
1767 // NOLINTEND(modernize-avoid-c-arrays)
1768
InferEnumVariableType(varbinder::EnumVariable * variable,double * value,bool * initNext,bool * isLiteralEnum,bool isConstEnum) const1769 void TSAnalyzer::InferEnumVariableType(varbinder::EnumVariable *variable, double *value, bool *initNext,
1770 bool *isLiteralEnum, bool isConstEnum) const
1771 {
1772 TSChecker *checker = GetTSChecker();
1773 const ir::Expression *init = variable->Declaration()->Node()->AsTSEnumMember()->Init();
1774
1775 if (init == nullptr && *initNext) {
1776 checker->ThrowTypeError("Enum member must have initializer.", variable->Declaration()->Node()->Start());
1777 }
1778
1779 if (init == nullptr && !*initNext) {
1780 variable->SetValue(++(*value));
1781 AddEnumValueDeclaration(checker, *value, variable);
1782 return;
1783 }
1784
1785 ES2PANDA_ASSERT(init);
1786 if (IsComputedEnumMember(init) && *isLiteralEnum) {
1787 checker->ThrowTypeError(INVALID_COMPUTED_WITH_STRING, init->Start());
1788 }
1789
1790 varbinder::EnumMemberResult res = EvaluateEnumMember(checker, variable, init);
1791 if (std::holds_alternative<util::StringView>(res)) {
1792 *isLiteralEnum = true;
1793 variable->SetTsType(checker->GlobalStringType());
1794 *initNext = true;
1795 return;
1796 }
1797
1798 if (std::holds_alternative<bool>(res)) {
1799 if (isConstEnum) {
1800 checker->ThrowTypeError(INVALID_CONST_MEMBER, init->Start());
1801 }
1802
1803 *initNext = true;
1804 return;
1805 }
1806
1807 ES2PANDA_ASSERT(std::holds_alternative<double>(res));
1808 variable->SetValue(res);
1809
1810 *value = std::get<double>(res);
1811 if (isConstEnum && std::isnan(*value)) {
1812 checker->ThrowTypeError(INVALID_CONST_NAN, init->Start());
1813 }
1814
1815 if (isConstEnum && std::isinf(*value)) {
1816 checker->ThrowTypeError(INVALID_CONST_INF, init->Start());
1817 }
1818
1819 *initNext = false;
1820 AddEnumValueDeclaration(checker, *value, variable);
1821 }
1822
InferType(checker::TSChecker * checker,bool isConst,ir::TSEnumDeclaration * st) const1823 checker::Type *TSAnalyzer::InferType(checker::TSChecker *checker, bool isConst, ir::TSEnumDeclaration *st) const
1824 {
1825 double value = -1.0;
1826
1827 varbinder::LocalScope *enumScope = checker->Scope()->AsLocalScope();
1828
1829 bool initNext = false;
1830 bool isLiteralEnum = false;
1831 size_t localsSize = enumScope->Decls().size();
1832
1833 for (size_t i = 0; i < localsSize; i++) {
1834 const util::StringView ¤tName = enumScope->Decls()[i]->Name();
1835 varbinder::Variable *currentVar = enumScope->FindLocal(currentName, varbinder::ResolveBindingOptions::BINDINGS);
1836 ES2PANDA_ASSERT(currentVar && currentVar->IsEnumVariable());
1837 InferEnumVariableType(currentVar->AsEnumVariable(), &value, &initNext, &isLiteralEnum, isConst);
1838 }
1839
1840 checker::Type *enumType = checker->Allocator()->New<checker::EnumLiteralType>(
1841 st->Key()->Name(), checker->Scope(),
1842 isLiteralEnum ? checker::EnumLiteralType::EnumLiteralTypeKind::LITERAL
1843 : checker::EnumLiteralType::EnumLiteralTypeKind::NUMERIC);
1844
1845 return enumType;
1846 }
1847
Check(ir::TSEnumDeclaration * st) const1848 checker::Type *TSAnalyzer::Check(ir::TSEnumDeclaration *st) const
1849 {
1850 TSChecker *checker = GetTSChecker();
1851 varbinder::Variable *enumVar = st->Key()->Variable();
1852 ES2PANDA_ASSERT(enumVar);
1853
1854 if (enumVar->TsType() == nullptr) {
1855 checker::ScopeContext scopeCtx(checker, st->Scope());
1856 checker::Type *enumType = InferType(checker, st->IsConst(), st);
1857 ES2PANDA_ASSERT(enumType != nullptr);
1858 enumType->SetVariable(enumVar);
1859 enumVar->SetTsType(enumType);
1860 }
1861
1862 return nullptr;
1863 }
1864
Check(ir::TSFunctionType * node) const1865 checker::Type *TSAnalyzer::Check(ir::TSFunctionType *node) const
1866 {
1867 TSChecker *checker = GetTSChecker();
1868 checker::ScopeContext scopeCtx(checker, node->Scope());
1869
1870 auto *signatureInfo = checker->Allocator()->New<checker::SignatureInfo>(checker->Allocator());
1871 checker->CheckFunctionParameterDeclarations(node->Params(), signatureInfo);
1872 node->ReturnType()->Check(checker);
1873 auto *callSignature =
1874 checker->Allocator()->New<checker::Signature>(signatureInfo, node->ReturnType()->GetType(checker));
1875
1876 return checker->CreateFunctionTypeWithSignature(callSignature);
1877 }
1878
Check(ir::TSIndexedAccessType * node) const1879 checker::Type *TSAnalyzer::Check(ir::TSIndexedAccessType *node) const
1880 {
1881 TSChecker *checker = GetTSChecker();
1882 node->objectType_->Check(checker);
1883 node->indexType_->Check(checker);
1884 checker::Type *resolved = node->GetType(checker);
1885
1886 if (resolved != nullptr) {
1887 return nullptr;
1888 }
1889
1890 checker::Type *indexType = checker->CheckTypeCached(node->indexType_);
1891
1892 if (!indexType->HasTypeFlag(checker::TypeFlag::STRING_LIKE | checker::TypeFlag::NUMBER_LIKE)) {
1893 checker->ThrowTypeError({"Type ", indexType, " cannot be used as index type"}, node->IndexType()->Start());
1894 }
1895
1896 if (indexType->IsNumberType()) {
1897 checker->ThrowTypeError("Type has no matching signature for type 'number'", node->Start());
1898 }
1899
1900 checker->ThrowTypeError("Type has no matching signature for type 'string'", node->Start());
1901 return nullptr;
1902 }
1903
Check(ir::TSInterfaceBody * expr) const1904 checker::Type *TSAnalyzer::Check(ir::TSInterfaceBody *expr) const
1905 {
1906 TSChecker *checker = GetTSChecker();
1907 for (auto *it : expr->Body()) {
1908 it->Check(checker);
1909 }
1910
1911 return nullptr;
1912 }
1913
CheckInheritedPropertiesAreIdentical(checker::TSChecker * checker,checker::InterfaceType * type,const lexer::SourcePosition & locInfo)1914 static void CheckInheritedPropertiesAreIdentical(checker::TSChecker *checker, checker::InterfaceType *type,
1915 const lexer::SourcePosition &locInfo)
1916 {
1917 checker->GetBaseTypes(type);
1918
1919 size_t constexpr BASE_SIZE_LIMIT = 2;
1920 if (type->Bases().size() < BASE_SIZE_LIMIT) {
1921 return;
1922 }
1923
1924 checker->ResolveDeclaredMembers(type);
1925
1926 checker::InterfacePropertyMap properties;
1927
1928 for (auto *it : type->Properties()) {
1929 properties.insert({it->Name(), {it, type}});
1930 }
1931
1932 for (auto *base : type->Bases()) {
1933 checker->ResolveStructuredTypeMembers(base);
1934 ArenaVector<varbinder::LocalVariable *> inheritedProperties(checker->Allocator()->Adapter());
1935 base->AsInterfaceType()->CollectProperties(&inheritedProperties);
1936
1937 for (auto *inheritedProp : inheritedProperties) {
1938 auto res = properties.find(inheritedProp->Name());
1939 if (res == properties.end()) {
1940 properties.insert({inheritedProp->Name(), {inheritedProp, base->AsInterfaceType()}});
1941 } else if (res->second.second != type) {
1942 checker::Type *sourceType = checker->GetTypeOfVariable(inheritedProp);
1943 checker::Type *targetType = checker->GetTypeOfVariable(res->second.first);
1944 checker->IsTypeIdenticalTo(sourceType, targetType, diagnostic::IFACE_MULTIPLE_EXTENSION,
1945 {type, res->second.second, base->AsInterfaceType()}, locInfo);
1946 }
1947 }
1948 }
1949 }
1950
Check(ir::TSInterfaceDeclaration * st) const1951 checker::Type *TSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const
1952 {
1953 TSChecker *checker = GetTSChecker();
1954 varbinder::Variable *var = st->Id()->Variable();
1955 ES2PANDA_ASSERT(var->Declaration()->Node() && var->Declaration()->Node()->IsTSInterfaceDeclaration());
1956
1957 if (st == var->Declaration()->Node()) {
1958 checker::Type *resolvedType = var->TsType();
1959
1960 if (resolvedType == nullptr) {
1961 checker::ObjectDescriptor *desc =
1962 checker->Allocator()->New<checker::ObjectDescriptor>(checker->Allocator());
1963 resolvedType =
1964 checker->Allocator()->New<checker::InterfaceType>(checker->Allocator(), st->Id()->Name(), desc);
1965 ES2PANDA_ASSERT(resolvedType != nullptr);
1966 resolvedType->SetVariable(var);
1967 var->SetTsType(resolvedType);
1968 }
1969
1970 checker::InterfaceType *resolvedInterface = resolvedType->AsObjectType()->AsInterfaceType();
1971 CheckInheritedPropertiesAreIdentical(checker, resolvedInterface, st->Id()->Start());
1972
1973 for (auto *base : resolvedInterface->Bases()) {
1974 checker->IsTypeAssignableTo(resolvedInterface, base, diagnostic::IFACE_INVALID_EXTENDS,
1975 {st->Id()->Name(), base}, st->Id()->Start());
1976 }
1977
1978 checker->CheckIndexConstraints(resolvedInterface);
1979 }
1980
1981 st->Body()->Check(checker);
1982
1983 return nullptr;
1984 }
1985
Check(ir::TSLiteralType * node) const1986 checker::Type *TSAnalyzer::Check(ir::TSLiteralType *node) const
1987 {
1988 TSChecker *checker = GetTSChecker();
1989 node->GetType(checker);
1990 return nullptr;
1991 }
1992
Check(ir::TSNamedTupleMember * node) const1993 checker::Type *TSAnalyzer::Check(ir::TSNamedTupleMember *node) const
1994 {
1995 TSChecker *checker = GetTSChecker();
1996 node->ElementType()->Check(checker);
1997 return nullptr;
1998 }
1999
Check(ir::TSNeverKeyword * node) const2000 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSNeverKeyword *node) const
2001 {
2002 return nullptr;
2003 }
2004
Check(ir::TSNullKeyword * node) const2005 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSNullKeyword *node) const
2006 {
2007 return nullptr;
2008 }
2009
Check(ir::TSNumberKeyword * node) const2010 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSNumberKeyword *node) const
2011 {
2012 return nullptr;
2013 }
2014
Check(ir::TSParenthesizedType * node) const2015 checker::Type *TSAnalyzer::Check(ir::TSParenthesizedType *node) const
2016 {
2017 TSChecker *checker = GetTSChecker();
2018 node->type_->Check(checker);
2019 return nullptr;
2020 }
2021
Check(ir::TSQualifiedName * expr) const2022 checker::Type *TSAnalyzer::Check(ir::TSQualifiedName *expr) const
2023 {
2024 TSChecker *checker = GetTSChecker();
2025 checker::Type *baseType = checker->CheckNonNullType(expr->Left()->Check(checker), expr->Left()->Start());
2026 varbinder::Variable *prop = checker->GetPropertyOfType(baseType, expr->Right()->Name());
2027
2028 if (prop != nullptr) {
2029 return checker->GetTypeOfVariable(prop);
2030 }
2031
2032 if (baseType->IsObjectType()) {
2033 checker::ObjectType *objType = baseType->AsObjectType();
2034
2035 if (objType->StringIndexInfo() != nullptr) {
2036 return objType->StringIndexInfo()->GetType();
2037 }
2038 }
2039
2040 checker->ThrowTypeError({"Property ", expr->Right()->Name(), " does not exist on this type."},
2041 expr->Right()->Start());
2042 return nullptr;
2043 }
2044
Check(ir::TSStringKeyword * node) const2045 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSStringKeyword *node) const
2046 {
2047 return nullptr;
2048 }
2049
Check(ir::TSTupleType * node) const2050 checker::Type *TSAnalyzer::Check(ir::TSTupleType *node) const
2051 {
2052 TSChecker *checker = GetTSChecker();
2053 for (auto *it : node->ElementType()) {
2054 it->Check(checker);
2055 }
2056
2057 node->GetType(checker);
2058 return nullptr;
2059 }
2060
Check(ir::TSTypeAliasDeclaration * st) const2061 checker::Type *TSAnalyzer::Check(ir::TSTypeAliasDeclaration *st) const
2062 {
2063 TSChecker *checker = GetTSChecker();
2064 st->TypeAnnotation()->Check(checker);
2065 return nullptr;
2066 }
2067
Check(ir::TSTypeLiteral * node) const2068 checker::Type *TSAnalyzer::Check(ir::TSTypeLiteral *node) const
2069 {
2070 TSChecker *checker = GetTSChecker();
2071
2072 for (auto *it : node->Members()) {
2073 it->Check(checker);
2074 }
2075
2076 checker::Type *type = node->GetType(checker);
2077 checker->CheckIndexConstraints(type);
2078
2079 return nullptr;
2080 }
2081
Check(ir::TSTypeQuery * node) const2082 checker::Type *TSAnalyzer::Check(ir::TSTypeQuery *node) const
2083 {
2084 TSChecker *checker = GetTSChecker();
2085 if (node->TsType() != nullptr) {
2086 return node->TsType();
2087 }
2088
2089 node->SetTsType(node->exprName_->Check(checker));
2090 return node->TsType();
2091 }
2092
Check(ir::TSTypeReference * node) const2093 checker::Type *TSAnalyzer::Check(ir::TSTypeReference *node) const
2094 {
2095 TSChecker *checker = GetTSChecker();
2096 node->GetType(checker);
2097 return nullptr;
2098 }
2099
Check(ir::TSUndefinedKeyword * node) const2100 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSUndefinedKeyword *node) const
2101 {
2102 return nullptr;
2103 }
2104
Check(ir::TSUnionType * node) const2105 checker::Type *TSAnalyzer::Check(ir::TSUnionType *node) const
2106 {
2107 TSChecker *checker = GetTSChecker();
2108 for (auto *it : node->Types()) {
2109 it->Check(checker);
2110 }
2111
2112 node->GetType(checker);
2113 return nullptr;
2114 }
2115
Check(ir::TSUnknownKeyword * node) const2116 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSUnknownKeyword *node) const
2117 {
2118 return nullptr;
2119 }
2120
Check(ir::TSVoidKeyword * node) const2121 checker::Type *TSAnalyzer::Check([[maybe_unused]] ir::TSVoidKeyword *node) const
2122 {
2123 return nullptr;
2124 }
2125 } // namespace ark::es2panda::checker
2126