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