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 "ETSAnalyzer.h"
17 #include "types/signature.h"
18 #include "util/helpers.h"
19 #include "checker/ETSchecker.h"
20 #include "checker/ets/castingContext.h"
21 #include "checker/ets/typeRelationContext.h"
22 #include "checker/types/globalTypesHolder.h"
23 #include "checker/types/ets/etsTupleType.h"
24 #include "checker/types/ets/etsAsyncFuncReturnType.h"
25 #include "evaluate/scopedDebugInfoPlugin.h"
26 #include "ir/statements/namespaceDeclaration.h"
27
28 namespace ark::es2panda::checker {
29
GetETSChecker() const30 ETSChecker *ETSAnalyzer::GetETSChecker() const
31 {
32 return static_cast<ETSChecker *>(GetChecker());
33 }
34
35 // from base folder
Check(ir::CatchClause * st) const36 checker::Type *ETSAnalyzer::Check(ir::CatchClause *st) const
37 {
38 ETSChecker *checker = GetETSChecker();
39 checker::ETSObjectType *exceptionType = checker->GlobalETSObjectType();
40
41 ir::Identifier *paramIdent = st->Param()->AsIdentifier();
42
43 if (paramIdent->TypeAnnotation() != nullptr) {
44 checker::Type *catchParamAnnotationType = paramIdent->TypeAnnotation()->GetType(checker);
45
46 exceptionType = checker->CheckExceptionOrErrorType(catchParamAnnotationType, st->Param()->Start());
47 }
48
49 paramIdent->Variable()->SetTsType(exceptionType);
50
51 st->Body()->Check(checker);
52
53 st->SetTsType(exceptionType);
54 return exceptionType;
55 }
56
Check(ir::ClassDefinition * node) const57 checker::Type *ETSAnalyzer::Check(ir::ClassDefinition *node) const
58 {
59 ETSChecker *checker = GetETSChecker();
60
61 if (node->TsType() == nullptr) {
62 checker->BuildBasicClassProperties(node);
63 }
64
65 if (!node->IsClassDefinitionChecked()) {
66 checker->CheckClassDefinition(node);
67 }
68
69 return node->TsType();
70 }
71
Check(ir::ClassProperty * st) const72 checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const
73 {
74 ASSERT(st->Id() != nullptr);
75 ETSChecker *checker = GetETSChecker();
76
77 if (st->TsType() != nullptr) {
78 return st->TsType();
79 }
80
81 checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
82 checker->Context().ContainingClass(),
83 checker->Context().ContainingSignature());
84
85 if (st->IsStatic()) {
86 checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT);
87 }
88
89 st->SetTsType(checker->CheckVariableDeclaration(st->Id(), st->TypeAnnotation(), st->Value(), st->Modifiers()));
90
91 return st->TsType();
92 }
93
Check(ir::ClassStaticBlock * st) const94 checker::Type *ETSAnalyzer::Check(ir::ClassStaticBlock *st) const
95 {
96 ETSChecker *checker = GetETSChecker();
97
98 if (checker->HasStatus(checker::CheckerStatus::INNER_CLASS)) {
99 checker->LogTypeError("Static initializer is not allowed in inner class.", st->Start());
100 st->SetTsType(checker->GlobalTypeError());
101 return st->TsType();
102 }
103
104 auto *func = st->Function();
105 checker->BuildFunctionSignature(func);
106 if (func->Signature() == nullptr) {
107 return checker->InvalidateType(st->AsTyped());
108 }
109 st->SetTsType(checker->BuildNamedFunctionType(func));
110 checker::ScopeContext scopeCtx(checker, func->Scope());
111 checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
112 checker->Context().ContainingClass());
113 checker->AddStatus(checker::CheckerStatus::IN_STATIC_BLOCK | checker::CheckerStatus::IN_STATIC_CONTEXT);
114 func->Body()->Check(checker);
115 return st->TsType();
116 }
117
118 // Satisfy the Chinese code checker
HandleNativeAndAsyncMethods(ETSChecker * checker,ir::MethodDefinition * node)119 static void HandleNativeAndAsyncMethods(ETSChecker *checker, ir::MethodDefinition *node)
120 {
121 auto *scriptFunc = node->Function();
122 if (node->IsNative()) {
123 if (scriptFunc->ReturnTypeAnnotation() == nullptr) {
124 checker->LogTypeError("'Native' method should have explicit return type", scriptFunc->Start());
125 node->SetTsType(checker->GlobalTypeError());
126 }
127 ASSERT(!scriptFunc->IsGetter() && !scriptFunc->IsSetter());
128 }
129
130 if (IsAsyncMethod(node)) {
131 if (scriptFunc->ReturnTypeAnnotation() != nullptr) {
132 auto *asyncFuncReturnType = scriptFunc->Signature()->ReturnType();
133
134 if (!asyncFuncReturnType->IsETSObjectType() ||
135 asyncFuncReturnType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) {
136 checker->LogTypeError("Return type of async function must be 'Promise'.", scriptFunc->Start());
137 scriptFunc->Signature()->SetReturnType(checker->GlobalTypeError());
138 return;
139 }
140 }
141
142 if (node->Function()->HasBody()) {
143 ComposeAsyncImplMethod(checker, node);
144 }
145 }
146 }
CheckClassProperty(ETSChecker * checker,ir::ScriptFunction * scriptFunc) const147 void ETSAnalyzer::CheckClassProperty(ETSChecker *checker, ir::ScriptFunction *scriptFunc) const
148 {
149 if (checker->CheckDuplicateAnnotations(scriptFunc->Annotations())) {
150 for (auto *it : scriptFunc->Annotations()) {
151 it->Check(checker);
152 }
153 }
154 }
155
Check(ir::MethodDefinition * node) const156 checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const
157 {
158 ETSChecker *checker = GetETSChecker();
159
160 auto *scriptFunc = node->Function();
161
162 CheckClassProperty(checker, scriptFunc);
163
164 if (scriptFunc == nullptr) {
165 checker->LogTypeError("Invalid function expression", node->Start());
166 node->SetTsType(checker->GlobalTypeError());
167 return node->TsType();
168 }
169
170 if (scriptFunc->IsProxy()) {
171 return nullptr;
172 }
173
174 // NOTE: aszilagyi. make it correctly check for open function not have body
175 if (!scriptFunc->HasBody() && !(node->IsAbstract() || node->IsNative() || node->IsDeclare() ||
176 checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) {
177 checker->LogTypeError("Only abstract or native methods can't have body.", scriptFunc->Start());
178 node->SetTsType(checker->GlobalTypeError());
179 return node->TsType();
180 }
181
182 if (scriptFunc->ReturnTypeAnnotation() == nullptr &&
183 (node->IsNative() || (node->IsDeclare() && !node->IsConstructor()))) {
184 checker->LogTypeError("Native and Declare methods should have explicit return type.", scriptFunc->Start());
185 node->SetTsType(checker->GlobalTypeError());
186 return node->TsType();
187 }
188
189 if (node->TsType() == nullptr) {
190 node->SetTsType(checker->BuildMethodSignature(node));
191 }
192
193 this->CheckMethodModifiers(node);
194 HandleNativeAndAsyncMethods(checker, node);
195 DoBodyTypeChecking(checker, node, scriptFunc);
196 CheckPredefinedMethodReturnType(checker, scriptFunc);
197 if (node->TsType()->IsTypeError()) {
198 return node->TsType();
199 }
200 // NOTE(gogabr): temporary, until we have proper bridges, see #16485
201 // Don't check overriding for synthetic functional classes.
202 if ((node->Parent()->Modifiers() & ir::ModifierFlags::FUNCTIONAL) == 0) {
203 checker->CheckOverride(node->TsType()->AsETSFunctionType()->FindSignature(node->Function()));
204 }
205
206 for (auto *overload : node->Overloads()) {
207 overload->Check(checker);
208 }
209
210 if (scriptFunc->IsRethrowing()) {
211 checker->CheckRethrowingFunction(scriptFunc);
212 }
213
214 return node->TsType();
215 }
216
CheckMethodModifiers(ir::MethodDefinition * node) const217 void ETSAnalyzer::CheckMethodModifiers(ir::MethodDefinition *node) const
218 {
219 ETSChecker *checker = GetETSChecker();
220 auto const notValidInAbstract = ir::ModifierFlags::NATIVE | ir::ModifierFlags::PRIVATE |
221 ir::ModifierFlags::OVERRIDE | ir::ModifierFlags::FINAL | ir::ModifierFlags::STATIC;
222
223 if (node->IsAbstract() && (node->flags_ & notValidInAbstract) != 0U) {
224 checker->LogTypeError(
225 "Invalid method modifier(s): an abstract method can't have private, override, static, final or native "
226 "modifier.",
227 node->Start());
228 node->SetTsType(checker->GlobalTypeError());
229 return;
230 }
231
232 if (node->Function() == nullptr) {
233 checker->LogTypeError("Invalid function expression", node->Start());
234 node->SetTsType(checker->GlobalTypeError());
235 return;
236 }
237
238 if ((node->IsAbstract() || (!node->Function()->HasBody() && !node->IsNative() && !node->IsDeclare())) &&
239 !(checker->HasStatus(checker::CheckerStatus::IN_ABSTRACT) ||
240 checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) {
241 checker->LogTypeError("Non abstract class has abstract method.", node->Start());
242 node->SetTsType(checker->GlobalTypeError());
243 }
244
245 auto const notValidInFinal = ir::ModifierFlags::ABSTRACT | ir::ModifierFlags::STATIC;
246
247 if (node->IsFinal() && (node->flags_ & notValidInFinal) != 0U) {
248 checker->LogTypeError("Invalid method modifier(s): a final method can't have abstract or static modifier.",
249 node->Start());
250 node->SetTsType(checker->GlobalTypeError());
251 }
252
253 auto const notValidInStatic = ir::ModifierFlags::ABSTRACT | ir::ModifierFlags::FINAL | ir::ModifierFlags::OVERRIDE;
254
255 if (node->IsStatic() && (node->flags_ & notValidInStatic) != 0U) {
256 checker->LogTypeError(
257 "Invalid method modifier(s): a static method can't have abstract, final or override modifier.",
258 node->Start());
259 node->SetTsType(checker->GlobalTypeError());
260 }
261 }
262
Check(ir::Property * expr) const263 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::Property *expr) const
264 {
265 return nullptr;
266 }
267
Check(ir::SpreadElement * expr) const268 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::SpreadElement *expr) const
269 {
270 ETSChecker *checker = GetETSChecker();
271 Type *exprType = expr->AsSpreadElement()->Argument()->Check(checker);
272 if (!exprType->IsETSArrayType()) {
273 checker->LogTypeError(
274 {"Spread expression can be applied only to array or tuple type, but '", exprType, "' is provided"},
275 expr->Start());
276 expr->SetTsType(checker->GlobalTypeError());
277 return expr->TsType();
278 }
279 checker::Type *elementType = exprType->AsETSArrayType()->ElementType();
280 expr->SetTsType(elementType);
281 return expr->TsType();
282 }
283
Check(ir::TemplateElement * expr) const284 checker::Type *ETSAnalyzer::Check(ir::TemplateElement *expr) const
285 {
286 ETSChecker *checker = GetETSChecker();
287 expr->SetTsType(checker->CreateETSStringLiteralType(expr->Raw()));
288 return expr->TsType();
289 }
290
Check(ir::ETSClassLiteral * expr) const291 checker::Type *ETSAnalyzer::Check(ir::ETSClassLiteral *expr) const
292 {
293 ETSChecker *checker = GetETSChecker();
294 auto *const literal = expr->Expr();
295
296 checker->LogTypeError("Class literal is not yet supported.", literal->Start());
297 expr->SetTsType(checker->GlobalTypeError());
298 return expr->TsType();
299
300 auto *exprType = literal->Check(checker);
301
302 if (exprType->IsETSVoidType()) {
303 checker->LogTypeError("Invalid .class reference", literal->Start());
304 expr->SetTsType(checker->GlobalTypeError());
305 return expr->TsType();
306 }
307
308 ArenaVector<checker::Type *> typeArgTypes(checker->Allocator()->Adapter());
309 typeArgTypes.push_back(exprType); // NOTE: Box it if it's a primitive type
310
311 checker::InstantiationContext ctx(checker, checker->GlobalBuiltinTypeType(), std::move(typeArgTypes),
312 expr->Range().start);
313 expr->SetTsType(ctx.Result());
314
315 return expr->TsType();
316 }
317
Check(ir::ETSFunctionType * node) const318 checker::Type *ETSAnalyzer::Check(ir::ETSFunctionType *node) const
319 {
320 if (node->TsType() != nullptr) {
321 return node->TsType();
322 }
323 ETSChecker *checker = GetETSChecker();
324
325 size_t optionalParameterIndex = node->DefaultParamIndex();
326 auto *genericInterfaceType = checker->GlobalBuiltinFunctionType(node->Params().size(), node->Flags());
327 node->SetFunctionalInterface(genericInterfaceType->GetDeclNode()->AsTSInterfaceDeclaration());
328
329 auto *tsType = checker->GetCachedFunctionalInterface(node);
330 node->SetTsType(tsType);
331 if (tsType != nullptr) {
332 return tsType;
333 }
334
335 ETSObjectType *interfaceType;
336
337 if (optionalParameterIndex == node->Params().size()) {
338 interfaceType = CreateInterfaceTypeForETSFunctionType(checker, node, genericInterfaceType);
339 } else {
340 interfaceType =
341 CreateOptionalSignaturesForFunctionalType(checker, node, genericInterfaceType, optionalParameterIndex);
342 }
343
344 node->SetTsType(interfaceType);
345 return interfaceType;
346 }
347
Check(ir::ETSLaunchExpression * expr) const348 checker::Type *ETSAnalyzer::Check(ir::ETSLaunchExpression *expr) const
349 {
350 ETSChecker *checker = GetETSChecker();
351 expr->expr_->Check(checker);
352
353 // Launch expression returns a Promise<T> type, so we need to insert the expression's type
354 // as type parameter for the Promise class.
355
356 auto exprType = [&checker](auto *tsType) {
357 if (tsType->IsETSPrimitiveType()) {
358 return checker->MaybeBoxInRelation(tsType);
359 }
360
361 return tsType;
362 }(expr->expr_->TsType());
363
364 expr->SetTsType(checker->CreatePromiseOf(exprType));
365 return expr->TsType();
366 }
367
Check(ir::ETSNewArrayInstanceExpression * expr) const368 checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const
369 {
370 ETSChecker *checker = GetETSChecker();
371
372 auto *elementType = expr->TypeReference()->GetType(checker);
373 checker->ValidateArrayIndex(expr->Dimension(), true);
374 if (!elementType->IsETSPrimitiveType()) {
375 if (elementType->IsETSUnionType() && !elementType->AsETSUnionType()->HasNullishType(checker)) {
376 checker->LogTypeError({"Union types in array declaration must include a nullish type."}, expr->Start());
377 expr->SetTsType(checker->GlobalTypeError());
378 return expr->TsType();
379 }
380 if (elementType->IsETSObjectType()) {
381 auto *calleeObj = elementType->AsETSObjectType();
382 const auto flags = checker::ETSObjectFlags::ABSTRACT | checker::ETSObjectFlags::INTERFACE;
383 if (!calleeObj->HasObjectFlag(flags)) {
384 // A workaround check for new Interface[...] in test cases
385 expr->SetSignature(
386 checker->CollectParameterlessConstructor(calleeObj->ConstructSignatures(), expr->Start()));
387 checker->ValidateSignatureAccessibility(calleeObj, nullptr, expr->Signature(), expr->Start());
388 } else {
389 checker->LogTypeError("Cannot use array creation expression with abstract classes and interfaces.",
390 expr->Start());
391 expr->SetTsType(checker->GlobalTypeError());
392 return expr->TsType();
393 }
394 }
395
396 if (elementType->IsETSNeverType()) {
397 checker->LogTypeError("Cannot use array creation expression with never type.", expr->Start());
398 }
399 }
400 expr->SetTsType(checker->CreateETSArrayType(elementType));
401 checker->CreateBuiltinArraySignature(expr->TsType()->AsETSArrayType(), 1);
402 return expr->TsType();
403 }
404
CheckInstantatedClass(ir::ETSNewClassInstanceExpression * expr,ETSObjectType * & calleeObj) const405 void ETSAnalyzer::CheckInstantatedClass(ir::ETSNewClassInstanceExpression *expr, ETSObjectType *&calleeObj) const
406 {
407 ETSChecker *checker = GetETSChecker();
408 if (expr->ClassDefinition() != nullptr) {
409 if (calleeObj->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT) && calleeObj->GetDeclNode()->IsFinal()) {
410 checker->LogTypeError({"Class ", calleeObj->Name(), " cannot be both 'abstract' and 'final'."},
411 calleeObj->GetDeclNode()->Start());
412 expr->SetTsType(checker->GlobalTypeError());
413 return;
414 }
415
416 bool fromInterface = calleeObj->HasObjectFlag(checker::ETSObjectFlags::INTERFACE);
417 auto *classType = checker->BuildAnonymousClassProperties(
418 expr->ClassDefinition(), fromInterface ? checker->GlobalETSObjectType() : calleeObj);
419 if (fromInterface) {
420 classType->AddInterface(calleeObj);
421 calleeObj = checker->GlobalETSObjectType();
422 }
423 expr->ClassDefinition()->SetTsType(classType);
424 checker->CheckClassDefinition(expr->ClassDefinition());
425 checker->CheckInnerClassMembers(classType);
426 expr->SetTsType(classType);
427 } else if (calleeObj->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT)) {
428 checker->LogTypeError({calleeObj->Name(), " is abstract therefore cannot be instantiated."}, expr->Start());
429 expr->SetTsType(checker->GlobalTypeError());
430 }
431
432 if (calleeObj->HasObjectFlag(ETSObjectFlags::REQUIRED) &&
433 !expr->HasAstNodeFlags(ir::AstNodeFlags::ALLOW_REQUIRED_INSTANTIATION)) {
434 checker->LogTypeError("Required type can be instantiated only with object literal",
435 expr->GetTypeRef()->Start());
436 expr->SetTsType(checker->GlobalTypeError());
437 }
438 }
439
Check(ir::ETSNewClassInstanceExpression * expr) const440 checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const
441 {
442 if (expr->TsType() != nullptr) {
443 return expr->TsType();
444 }
445 ETSChecker *checker = GetETSChecker();
446 auto *calleeType = GetCalleeType(checker, expr);
447 if (calleeType == nullptr) {
448 return expr->TsType();
449 }
450
451 if (calleeType->IsTypeError()) {
452 expr->SetTsType(calleeType);
453 return expr->TsType();
454 }
455 auto *calleeObj = calleeType->AsETSObjectType();
456 expr->SetTsType(calleeObj);
457
458 CheckInstantatedClass(expr, calleeObj);
459
460 if (calleeType->IsETSDynamicType() && !calleeType->AsETSDynamicType()->HasDecl()) {
461 auto lang = calleeType->AsETSDynamicType()->Language();
462 expr->SetSignature(checker->ResolveDynamicCallExpression(expr->GetTypeRef(), expr->GetArguments(), lang, true));
463 } else {
464 auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start());
465
466 if (signature == nullptr) {
467 return checker->InvalidateType(expr);
468 }
469
470 checker->CheckObjectLiteralArguments(signature, expr->GetArguments());
471
472 checker->ValidateSignatureAccessibility(calleeObj, nullptr, signature, expr->Start());
473
474 ASSERT(signature->Function() != nullptr);
475
476 if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
477 checker->CheckThrowingStatements(expr);
478 }
479
480 if (calleeType->IsETSDynamicType()) {
481 ASSERT(signature->Function()->IsDynamic());
482 auto lang = calleeType->AsETSDynamicType()->Language();
483 expr->SetSignature(
484 checker->ResolveDynamicCallExpression(expr->GetTypeRef(), signature->Params(), lang, true));
485 } else {
486 ASSERT(!signature->Function()->IsDynamic());
487 expr->SetSignature(signature);
488 }
489 }
490
491 return expr->TsType();
492 }
493
Check(ir::ETSNewMultiDimArrayInstanceExpression * expr) const494 checker::Type *ETSAnalyzer::Check(ir::ETSNewMultiDimArrayInstanceExpression *expr) const
495 {
496 ETSChecker *checker = GetETSChecker();
497 auto *elementType = expr->TypeReference()->GetType(checker);
498
499 for (auto *dim : expr->Dimensions()) {
500 checker->ValidateArrayIndex(dim, true);
501 elementType = checker->CreateETSArrayType(elementType);
502 }
503
504 expr->SetTsType(elementType);
505 expr->SetSignature(checker->CreateBuiltinArraySignature(elementType->AsETSArrayType(), expr->Dimensions().size()));
506 return expr->TsType();
507 }
508
Check(ir::ETSPackageDeclaration * st) const509 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSPackageDeclaration *st) const
510 {
511 return nullptr;
512 }
513
Check(ir::ETSParameterExpression * expr) const514 checker::Type *ETSAnalyzer::Check(ir::ETSParameterExpression *expr) const
515 {
516 ETSChecker *checker = GetETSChecker();
517 if (expr->TsType() == nullptr) {
518 checker::Type *paramType;
519
520 if (expr->Ident()->TsType() != nullptr) {
521 paramType = expr->Ident()->TsType();
522 } else {
523 paramType = !expr->IsRestParameter() ? expr->Ident()->Check(checker) : expr->spread_->Check(checker);
524 if (expr->IsDefault()) {
525 std::cout << __LINE__ << std::endl;
526 [[maybe_unused]] auto *const initType = expr->Initializer()->Check(checker);
527 }
528 }
529
530 expr->SetTsType(paramType);
531 }
532
533 return expr->TsType();
534 }
535
Check(ir::ETSPrimitiveType * node) const536 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSPrimitiveType *node) const
537 {
538 ETSChecker *checker = GetETSChecker();
539 return node->GetType(checker);
540 }
541
Check(ir::ETSStructDeclaration * node) const542 checker::Type *ETSAnalyzer::Check(ir::ETSStructDeclaration *node) const
543 {
544 ETSChecker *checker = GetETSChecker();
545 node->Definition()->Check(checker);
546 return nullptr;
547 }
548
Check(ir::ETSTypeReference * node) const549 checker::Type *ETSAnalyzer::Check(ir::ETSTypeReference *node) const
550 {
551 ETSChecker *checker = GetETSChecker();
552 return node->GetType(checker);
553 }
554
Check(ir::ETSTypeReferencePart * node) const555 checker::Type *ETSAnalyzer::Check(ir::ETSTypeReferencePart *node) const
556 {
557 ETSChecker *checker = GetETSChecker();
558 return node->GetType(checker);
559 }
560
Check(ir::ETSNullType * node) const561 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNullType *node) const
562 {
563 return nullptr;
564 }
565
Check(ir::ETSUndefinedType * node) const566 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSUndefinedType *node) const
567 {
568 return nullptr;
569 }
570
Check(ir::ETSNeverType * node) const571 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNeverType *node) const
572 {
573 return nullptr;
574 }
575
Check(ir::ETSStringLiteralType * node) const576 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSStringLiteralType *node) const
577 {
578 ETSChecker *checker = GetETSChecker();
579 return node->GetType(checker);
580 }
581
582 // compile methods for EXPRESSIONS in alphabetical order
583
GetPreferredType(ir::ArrayExpression * expr) const584 checker::Type *ETSAnalyzer::GetPreferredType(ir::ArrayExpression *expr) const
585 {
586 return expr->preferredType_;
587 }
588
CheckArrayElement(ETSChecker * checker,checker::Type * elementType,std::vector<checker::Type * > targetElementType,ir::Expression * currentElement,bool & isSecondaryChosen)589 static bool CheckArrayElement(ETSChecker *checker, checker::Type *elementType,
590 std::vector<checker::Type *> targetElementType, ir::Expression *currentElement,
591 bool &isSecondaryChosen)
592 {
593 if ((targetElementType[0]->IsETSArrayType() &&
594 targetElementType[0]->AsETSArrayType()->ElementType()->IsETSArrayType() &&
595 !(targetElementType[0]->AsETSArrayType()->ElementType()->IsETSTupleType() &&
596 targetElementType[1] == nullptr)) ||
597 (!checker::AssignmentContext(checker->Relation(), currentElement, elementType, targetElementType[0],
598 currentElement->Start(),
599 {"Array element type '", elementType, "' is not assignable to explicit type '",
600 targetElementType[0], "'"},
601 TypeRelationFlag::NO_THROW)
602 // CC-OFFNXT(G.FMT.02) project code style
603 .IsAssignable() &&
604 !(targetElementType[0]->IsETSArrayType() && currentElement->IsArrayExpression()))) {
605 if (targetElementType[1] == nullptr) {
606 checker->LogTypeError({"Array element type '", elementType, "' is not assignable to explicit type '",
607 targetElementType[0], "'"},
608 currentElement->Start());
609 return false;
610 }
611
612 if (!(targetElementType[0]->IsETSArrayType() && currentElement->IsArrayExpression()) &&
613 !checker::AssignmentContext(checker->Relation(), currentElement, elementType, targetElementType[1],
614 currentElement->Start(),
615 {"Array element type '", elementType, "' is not assignable to explicit type '",
616 targetElementType[1], "'"},
617 TypeRelationFlag::NO_THROW)
618 // CC-OFFNXT(G.FMT.02) project code style
619 .IsAssignable()) {
620 checker->LogTypeError({"Array element type '", elementType, "' is not assignable to explicit type '",
621 targetElementType[1], "'"},
622 currentElement->Start());
623 return false;
624 }
625 isSecondaryChosen = true;
626 }
627 return true;
628 }
629
AddSpreadElementTypes(ETSChecker * checker,ir::Expression * const element,ArenaVector<std::pair<Type *,ir::Expression * >> & elementTypes,bool isPreferredTuple)630 static bool AddSpreadElementTypes(ETSChecker *checker, ir::Expression *const element,
631 ArenaVector<std::pair<Type *, ir::Expression *>> &elementTypes, bool isPreferredTuple)
632 {
633 Type *elementType = element->Check(checker);
634 Type *argumentType = element->AsSpreadElement()->Argument()->Check(checker);
635 if (argumentType->IsETSTupleType()) {
636 for (Type *type : argumentType->AsETSTupleType()->GetTupleTypesList()) {
637 elementTypes.push_back({type, element});
638 }
639 return true;
640 }
641 if (!argumentType->IsETSTupleType() && isPreferredTuple) {
642 checker->LogTypeError({"'", argumentType, "' cannot be spread in tuple."}, element->Start());
643 elementTypes.push_back({elementType, element});
644 return false;
645 }
646 elementTypes.push_back({elementType, element});
647 return true;
648 }
649
GetElementTypes(ir::ArrayExpression * expr,ETSChecker * checker,bool isPreferredTuple,bool & checkResult)650 static ArenaVector<std::pair<Type *, ir::Expression *>> GetElementTypes(ir::ArrayExpression *expr, ETSChecker *checker,
651 bool isPreferredTuple, bool &checkResult)
652 {
653 ArenaVector<std::pair<Type *, ir::Expression *>> elementTypes(checker->Allocator()->Adapter());
654 for (std::size_t idx = 0; idx < expr->Elements().size(); ++idx) {
655 ir::Expression *const element = expr->Elements()[idx];
656
657 if (element->IsArrayExpression() &&
658 !expr->HandleNestedArrayExpression(checker, element->AsArrayExpression(), isPreferredTuple, idx)) {
659 elementTypes.push_back({nullptr, element});
660 continue;
661 }
662
663 if (element->IsObjectExpression()) {
664 element->AsObjectExpression()->SetPreferredType(expr->GetPreferredType()->AsETSArrayType()->ElementType());
665 }
666
667 if (element->IsSpreadElement()) {
668 if (!AddSpreadElementTypes(checker, element, elementTypes, isPreferredTuple)) {
669 checkResult = false;
670 }
671 continue;
672 }
673
674 Type *elementType = element->Check(checker);
675 elementTypes.push_back({elementType, element});
676 }
677
678 return elementTypes;
679 }
680
CheckElement(ir::ArrayExpression * expr,ETSChecker * checker,std::vector<checker::Type * > targetElementType,bool isPreferredTuple)681 static bool CheckElement(ir::ArrayExpression *expr, ETSChecker *checker, std::vector<checker::Type *> targetElementType,
682 bool isPreferredTuple)
683 {
684 bool isSecondaryChosen = false;
685 bool checkResult = true;
686
687 ArenaVector<std::pair<Type *, ir::Expression *>> elementTypes =
688 GetElementTypes(expr, checker, isPreferredTuple, checkResult);
689
690 for (std::size_t idx = 0; idx < elementTypes.size(); ++idx) {
691 auto [elementType, currentElement] = elementTypes[idx];
692 if (elementType == nullptr) {
693 continue;
694 }
695
696 if (!elementType->IsETSArrayType() && isPreferredTuple) {
697 auto const *const tupleType = expr->GetPreferredType()->AsETSTupleType();
698
699 auto *compareType = tupleType->GetTypeAtIndex(idx);
700 if (compareType == nullptr) {
701 checker->LogTypeError({"Too many elements in array initializer for tuple with size of ",
702 static_cast<uint32_t>(tupleType->GetTupleSize())},
703 currentElement->Start());
704 checkResult = false;
705 continue;
706 }
707 // clang-format off
708 if (!AssignmentContext(checker->Relation(), currentElement, elementType, compareType,
709 currentElement->Start(), {}, TypeRelationFlag::NO_THROW).IsAssignable()) {
710 checker->LogTypeError({"Array initializer's type is not assignable to tuple type at index: ", idx},
711 currentElement->Start());
712 checkResult=false;
713 continue;
714 }
715 // clang-format on
716
717 elementType = compareType;
718 }
719
720 if (targetElementType[0] == elementType) {
721 continue;
722 }
723
724 if (!CheckArrayElement(checker, elementType, targetElementType, currentElement, isSecondaryChosen)) {
725 checkResult = false;
726 continue;
727 }
728 }
729
730 return checkResult;
731 }
732
GetUnionPreferredType(ir::ArrayExpression * expr) const733 void ETSAnalyzer::GetUnionPreferredType(ir::ArrayExpression *expr) const
734 {
735 ASSERT(expr->preferredType_->IsETSUnionType());
736 checker::Type *preferredType = nullptr;
737 for (auto &type : expr->preferredType_->AsETSUnionType()->ConstituentTypes()) {
738 if (type->IsETSArrayType()) {
739 if (preferredType != nullptr) {
740 preferredType = nullptr;
741 break;
742 }
743 preferredType = type->AsETSArrayType();
744 }
745 }
746
747 expr->preferredType_ = preferredType;
748 }
749
Check(ir::ArrayExpression * expr) const750 checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const
751 {
752 ETSChecker *checker = GetETSChecker();
753 if (expr->TsType() != nullptr) {
754 return expr->TsType();
755 }
756
757 if (expr->preferredType_ != nullptr) {
758 if (expr->preferredType_->IsETSTypeAliasType()) {
759 expr->preferredType_ = expr->preferredType_->AsETSTypeAliasType()->GetTargetType();
760 }
761
762 if (expr->preferredType_->IsETSUnionType()) {
763 GetUnionPreferredType(expr);
764 }
765 }
766
767 if (expr->preferredType_ != nullptr && !expr->preferredType_->IsETSArrayType() &&
768 !checker->Relation()->IsSupertypeOf(expr->preferredType_, checker->GlobalETSObjectType())) {
769 checker->LogTypeError({"Expected type for array literal should be an array type, got ", expr->preferredType_},
770 expr->Start());
771 return checker->InvalidateType(expr);
772 }
773
774 const bool isArray = (expr->preferredType_ != nullptr) && expr->preferredType_->IsETSArrayType() &&
775 !expr->preferredType_->IsETSTupleType();
776
777 if (!expr->Elements().empty()) {
778 if (expr->preferredType_ == nullptr || expr->preferredType_ == checker->GlobalETSObjectType()) {
779 /*
780 * NOTE(SM): If elements has different types
781 * should calculated as union of types from each element,
782 * otherwise don't properly work with array from union type
783 */
784 expr->preferredType_ =
785 checker->CreateETSArrayType(checker->GetNonConstantType(expr->Elements()[0]->Check(checker)));
786 }
787
788 const bool isPreferredTuple = expr->preferredType_->IsETSTupleType();
789 // NOTE(aakmaev): Need to rework type inference of array literal (#19096 internal issue)
790 auto *targetElementType =
791 checker->GetNonConstantType(expr->GetPreferredType()->AsETSArrayType()->ElementType());
792 Type *targetElementTypeSecondary = nullptr;
793 if (isPreferredTuple && !isArray) {
794 targetElementTypeSecondary = expr->GetPreferredType()->AsETSTupleType()->ElementType();
795 }
796
797 if (!CheckElement(expr, checker, {targetElementType, targetElementTypeSecondary}, isPreferredTuple)) {
798 return checker->InvalidateType(expr);
799 }
800 }
801
802 if (expr->preferredType_ == nullptr) {
803 return checker->TypeError(expr, "Can't resolve array type", expr->Start());
804 }
805
806 expr->SetTsType(expr->preferredType_);
807 auto *const arrayType = expr->TsType()->AsETSArrayType();
808 checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank());
809 return expr->TsType();
810 }
811
Check(ir::ArrowFunctionExpression * expr) const812 checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const
813 {
814 ETSChecker *checker = GetETSChecker();
815
816 if (expr->TsType() != nullptr) {
817 return expr->TsType();
818 }
819 checker::ScopeContext scopeCtx(checker, expr->Function()->Scope());
820
821 if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) {
822 /*
823 example code:
824 ```
825 class A {
826 prop:number
827 }
828 function A.method() {
829 let a = () => {
830 console.println(this.prop)
831 }
832 }
833 ```
834 here the enclosing class of arrow function should be Class A
835 */
836 checker->Context().SetContainingClass(
837 checker->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType());
838 }
839
840 checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
841 checker->Context().ContainingClass());
842
843 checker->AddStatus(checker::CheckerStatus::IN_LAMBDA);
844 checker->Context().SetContainingLambda(expr);
845
846 checker->BuildFunctionSignature(expr->Function(), false);
847 if (expr->Function()->Signature() == nullptr) {
848 return checker->InvalidateType(expr);
849 }
850 auto *signature = expr->Function()->Signature();
851
852 checker->Context().SetContainingSignature(signature);
853 expr->Function()->Body()->Check(checker);
854
855 ArenaVector<Signature *> signatures(checker->Allocator()->Adapter());
856 signatures.push_back(signature);
857 for (auto &sigInfo : checker->ComposeSignatureInfosForArrowFunction(expr)) {
858 auto sig = checker->ComposeSignature(expr->Function(), sigInfo, signature->ReturnType(), nullptr);
859 sig->AddSignatureFlag(signature->GetFlags());
860 signatures.push_back(sig);
861 }
862
863 auto *funcType = checker->CreateETSFunctionType(expr->Function(), std::move(signatures), nullptr);
864 checker->Context().SetContainingSignature(nullptr);
865
866 if (expr->Function()->IsAsyncFunc()) {
867 auto *retType = signature->ReturnType();
868 if (!retType->IsETSObjectType() ||
869 retType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) {
870 checker->LogTypeError("Return type of async lambda must be 'Promise'", expr->Function()->Start());
871 expr->SetTsType(checker->GlobalTypeError());
872 return expr->TsType();
873 }
874 }
875
876 expr->SetTsType(funcType);
877 return expr->TsType();
878 }
879
IsInvalidArrayMemberAssignment(ir::AssignmentExpression * const expr,ETSChecker * checker)880 static bool IsInvalidArrayMemberAssignment(ir::AssignmentExpression *const expr, ETSChecker *checker)
881 {
882 if (expr->Left()->IsMemberExpression() &&
883 expr->Left()->AsMemberExpression()->Object()->TsType()->IsETSArrayType()) {
884 auto *const leftExpr = expr->Left()->AsMemberExpression();
885 if (leftExpr->Property()->IsIdentifier() && leftExpr->Property()->AsIdentifier()->Name().Is("length")) {
886 checker->LogTypeError("Setting the length of an array is not permitted", expr->Left()->Start());
887 return true;
888 }
889 if (leftExpr->Object()->TsType()->HasTypeFlag(TypeFlag::READONLY)) {
890 checker->LogTypeError("Cannot modify an array or tuple content that has the readonly parameter",
891 expr->Left()->Start());
892 return true;
893 }
894 }
895 return false;
896 }
897
GetSmartType(ir::AssignmentExpression * expr,checker::Type * leftType,checker::Type * rightType) const898 checker::Type *ETSAnalyzer::GetSmartType(ir::AssignmentExpression *expr, checker::Type *leftType,
899 checker::Type *rightType) const
900 {
901 ETSChecker *checker = GetETSChecker();
902 checker::Type *smartType = leftType;
903
904 if (expr->Left()->IsIdentifier()) {
905 // Now try to define the actual type of Identifier so that smart cast can be used in further checker processing
906 smartType = checker->ResolveSmartType(rightType, leftType);
907 auto const *const variable = expr->Target();
908
909 // Add/Remove/Modify smart cast for identifier
910 // (excluding the variables defined at top-level scope or captured in lambda-functions!)
911 auto const *const variableScope = variable->GetScope();
912 auto const topLevelVariable =
913 variableScope != nullptr && (variableScope->IsGlobalScope() || (variableScope->Parent() != nullptr &&
914 variableScope->Parent()->IsGlobalScope()));
915 if (!topLevelVariable) {
916 if (checker->Relation()->IsIdenticalTo(leftType, smartType)) {
917 checker->Context().RemoveSmartCast(variable);
918 } else {
919 expr->Left()->SetTsType(smartType);
920 checker->Context().SetSmartCast(variable, smartType);
921 }
922 }
923 }
924 return smartType;
925 }
926
Check(ir::AssignmentExpression * const expr) const927 checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const
928 {
929 if (expr->TsType() != nullptr) {
930 return expr->TsType();
931 }
932
933 ETSChecker *checker = GetETSChecker();
934
935 auto *const leftType = expr->Left()->Check(checker);
936
937 if (IsInvalidArrayMemberAssignment(expr, checker)) {
938 expr->SetTsType(checker->GlobalTypeError());
939 return expr->TsType();
940 }
941
942 if (expr->Left()->IsIdentifier()) {
943 expr->target_ = expr->Left()->AsIdentifier()->Variable();
944 } else if (expr->Left()->IsMemberExpression()) {
945 expr->target_ = expr->Left()->AsMemberExpression()->PropVar();
946 } else {
947 checker->LogTypeError("Invalid left-hand side of assignment expression", expr->Left()->Start());
948 expr->SetTsType(checker->GlobalTypeError());
949 return expr->TsType();
950 }
951
952 if (expr->target_ != nullptr && !expr->IsIgnoreConstAssign()) {
953 checker->ValidateUnaryOperatorOperand(expr->target_);
954 }
955
956 auto [rightType, relationNode] = CheckAssignmentExprOperatorType(expr, leftType);
957 if (rightType == nullptr) {
958 expr->SetTsType(checker->GlobalTypeError());
959 return checker->GlobalTypeError();
960 }
961
962 const checker::Type *targetType = checker->TryGettingFunctionTypeFromInvokeFunction(leftType);
963 const checker::Type *sourceType = checker->TryGettingFunctionTypeFromInvokeFunction(rightType);
964
965 checker::AssignmentContext(checker->Relation(), relationNode, rightType, leftType, expr->Right()->Start(),
966 {"Type '", sourceType, "' cannot be assigned to type '", targetType, "'"});
967
968 checker::Type *smartType = GetSmartType(expr, leftType, rightType);
969
970 expr->SetTsType(smartType);
971 return expr->TsType();
972 }
973
CheckAssignmentExprOperatorType(ir::AssignmentExpression * expr,Type * const leftType) const974 std::tuple<Type *, ir::Expression *> ETSAnalyzer::CheckAssignmentExprOperatorType(ir::AssignmentExpression *expr,
975 Type *const leftType) const
976 {
977 ETSChecker *checker = GetETSChecker();
978 checker::Type *sourceType {};
979 ir::Expression *relationNode = expr->Right();
980 switch (expr->OperatorType()) {
981 case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL:
982 case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL:
983 case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL:
984 case lexer::TokenType::PUNCTUATOR_MOD_EQUAL:
985 case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL:
986 case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL:
987 case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL:
988 case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL:
989 case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL:
990 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL:
991 case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL:
992 case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
993 std::tie(std::ignore, expr->operationType_) = checker->CheckBinaryOperator(
994 expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start(), true);
995
996 auto unboxedLeft = checker->MaybeUnboxInRelation(leftType);
997 sourceType = unboxedLeft == nullptr ? leftType : unboxedLeft;
998
999 relationNode = expr;
1000 break;
1001 }
1002 case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: {
1003 if (leftType->IsETSArrayType() && expr->Right()->IsArrayExpression()) {
1004 checker->ModifyPreferredType(expr->Right()->AsArrayExpression(), leftType);
1005 }
1006
1007 if (expr->Right()->IsObjectExpression()) {
1008 expr->Right()->AsObjectExpression()->SetPreferredType(leftType);
1009 }
1010
1011 sourceType = expr->Right()->Check(checker);
1012 break;
1013 }
1014 default: {
1015 UNREACHABLE();
1016 break;
1017 }
1018 }
1019
1020 return {sourceType, relationNode};
1021 }
1022
Check(ir::AwaitExpression * expr) const1023 checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const
1024 {
1025 ETSChecker *checker = GetETSChecker();
1026 if (expr->TsType() != nullptr) {
1027 return expr->TsType();
1028 }
1029
1030 checker::Type *argType = checker->GetApparentType(expr->argument_->Check(checker));
1031 // Check the argument type of await expression
1032 if (!argType->IsETSObjectType() ||
1033 (argType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType())) {
1034 checker->LogTypeError("'await' expressions require Promise object as argument.", expr->Argument()->Start());
1035 expr->SetTsType(checker->GlobalTypeError());
1036 return expr->TsType();
1037 }
1038
1039 Type *type = argType->AsETSObjectType()->TypeArguments().at(0);
1040 expr->SetTsType(UnwrapPromiseType(type));
1041 return expr->TsType();
1042 }
1043
Check(ir::ImportExpression * expr) const1044 checker::Type *ETSAnalyzer::Check(ir::ImportExpression *expr) const
1045 {
1046 ETSChecker *checker = GetETSChecker();
1047 if (expr->TsType() != nullptr) {
1048 return expr->TsType();
1049 }
1050
1051 Type *const argType = expr->Source()->Check(checker);
1052 if (argType->IsTypeError()) {
1053 expr->SetTsType(checker->GlobalTypeError());
1054 return expr->TsType();
1055 }
1056 if (!checker->Relation()->IsSupertypeOf(checker->GlobalBuiltinETSStringType(), argType)) {
1057 checker->LogTypeError("'import' expressions require string as argument.", expr->Start());
1058 expr->SetTsType(checker->GlobalTypeError());
1059 return expr->TsType();
1060 }
1061
1062 expr->SetTsType(checker->CreatePromiseOf(checker->GlobalBuiltinJSValueType()));
1063 return expr->TsType();
1064 }
1065
UnwrapPromiseType(checker::Type * type) const1066 checker::Type *ETSAnalyzer::UnwrapPromiseType(checker::Type *type) const
1067 {
1068 ETSChecker *checker = GetETSChecker();
1069 checker::Type *promiseType = checker->GlobalBuiltinPromiseType();
1070 while (type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == promiseType) {
1071 type = type->AsETSObjectType()->TypeArguments().at(0);
1072 }
1073 if (!type->IsETSUnionType()) {
1074 return type;
1075 }
1076 const auto &ctypes = type->AsETSUnionType()->ConstituentTypes();
1077 auto it = std::find_if(ctypes.begin(), ctypes.end(), [promiseType](checker::Type *t) {
1078 return t == promiseType || (t->IsETSObjectType() && t->AsETSObjectType()->GetBaseType() == promiseType);
1079 });
1080 if (it == ctypes.end()) {
1081 return type;
1082 }
1083 ArenaVector<Type *> newCTypes(ctypes);
1084 do {
1085 size_t index = it - ctypes.begin();
1086 newCTypes[index] = UnwrapPromiseType(ctypes[index]);
1087 ++it;
1088 it = std::find_if(it, ctypes.end(), [promiseType](checker::Type *t) {
1089 return t == promiseType || t->AsETSObjectType()->GetBaseType() == promiseType;
1090 });
1091 } while (it != ctypes.end());
1092 return checker->CreateETSUnionType(std::move(newCTypes));
1093 }
1094
Check(ir::BinaryExpression * expr) const1095 checker::Type *ETSAnalyzer::Check(ir::BinaryExpression *expr) const
1096 {
1097 if (expr->TsType() != nullptr) {
1098 return expr->TsType();
1099 }
1100
1101 ETSChecker *checker = GetETSChecker();
1102 checker::Type *newTsType {nullptr};
1103 std::tie(newTsType, expr->operationType_) =
1104 checker->CheckBinaryOperator(expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start());
1105 expr->SetTsType(newTsType);
1106
1107 checker->Context().CheckBinarySmartCastCondition(expr);
1108
1109 return expr->TsType();
1110 }
1111
Check(ir::BlockExpression * st) const1112 checker::Type *ETSAnalyzer::Check(ir::BlockExpression *st) const
1113 {
1114 ETSChecker *checker = GetETSChecker();
1115 checker::ScopeContext scopeCtx(checker, st->Scope());
1116
1117 if (st->TsType() == nullptr) {
1118 // NOLINTNEXTLINE(modernize-loop-convert)
1119 for (std::size_t idx = 0; idx < st->Statements().size(); idx++) {
1120 st->Statements()[idx]->Check(checker);
1121 }
1122
1123 auto lastStmt = st->Statements().back();
1124 ASSERT(lastStmt->IsExpressionStatement());
1125 st->SetTsType(lastStmt->AsExpressionStatement()->GetExpression()->TsType());
1126 }
1127
1128 return st->TsType();
1129 }
1130
ResolveSignature(ETSChecker * checker,ir::CallExpression * expr,checker::Type * calleeType,bool isFunctionalInterface,bool isUnionTypeWithFunctionalInterface) const1131 checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallExpression *expr,
1132 checker::Type *calleeType, bool isFunctionalInterface,
1133 bool isUnionTypeWithFunctionalInterface) const
1134 {
1135 bool extensionFunctionType = expr->Callee()->IsMemberExpression() && checker->ExtensionETSFunctionType(calleeType);
1136
1137 if (calleeType->IsETSExtensionFuncHelperType()) {
1138 return ResolveCallForETSExtensionFuncHelperType(calleeType->AsETSExtensionFuncHelperType(), checker, expr);
1139 }
1140 if (extensionFunctionType) {
1141 return ResolveCallExtensionFunction(calleeType->AsETSFunctionType(), checker, expr);
1142 }
1143 auto &signatures = ChooseSignatures(checker, calleeType, expr->IsETSConstructorCall(), isFunctionalInterface,
1144 isUnionTypeWithFunctionalInterface);
1145 // Remove static signatures if the callee is a member expression and the object is initialized
1146 if (expr->Callee()->IsMemberExpression() &&
1147 // NOTE(vpukhov): #20510 member access
1148 !expr->Callee()->AsMemberExpression()->Object()->TsType()->IsETSEnumType() &&
1149 (expr->Callee()->AsMemberExpression()->Object()->IsSuperExpression() ||
1150 (expr->Callee()->AsMemberExpression()->Object()->IsIdentifier() &&
1151 expr->Callee()->AsMemberExpression()->Object()->AsIdentifier()->Variable()->HasFlag(
1152 varbinder::VariableFlags::INITIALIZED)))) {
1153 signatures.erase(
1154 std::remove_if(signatures.begin(), signatures.end(),
1155 [](checker::Signature *signature) { return signature->Function()->IsStatic(); }),
1156 signatures.end());
1157 }
1158
1159 checker::Signature *signature = checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start());
1160 if (signature == nullptr) {
1161 return nullptr;
1162 }
1163
1164 if (signature->Function()->IsExtensionMethod()) {
1165 checker->LogTypeError({"No matching call signature"}, expr->Start());
1166 return nullptr;
1167 }
1168 return signature;
1169 }
1170
GetReturnType(ir::CallExpression * expr,checker::Type * calleeType) const1171 checker::Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, checker::Type *calleeType) const
1172 {
1173 ETSChecker *checker = GetETSChecker();
1174
1175 if (calleeType->IsTypeError()) {
1176 return checker->GlobalTypeError();
1177 }
1178
1179 bool isConstructorCall = expr->IsETSConstructorCall();
1180 bool isUnionTypeWithFunctionalInterface =
1181 calleeType->IsETSUnionType() &&
1182 calleeType->AsETSUnionType()->HasObjectType(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE);
1183 bool isFunctionalInterface = calleeType->IsETSObjectType() && calleeType->AsETSObjectType()->HasObjectFlag(
1184 // CC-OFFNXT(G.FMT.06-CPP) project code style
1185 checker::ETSObjectFlags::FUNCTIONAL_INTERFACE);
1186 bool etsExtensionFuncHelperType = calleeType->IsETSExtensionFuncHelperType();
1187
1188 if (expr->Callee()->IsArrowFunctionExpression()) {
1189 calleeType = InitAnonymousLambdaCallee(checker, expr->Callee(), calleeType);
1190 isFunctionalInterface = true;
1191 }
1192
1193 if (!isFunctionalInterface && !calleeType->IsETSFunctionType() && !isConstructorCall &&
1194 !etsExtensionFuncHelperType && !isUnionTypeWithFunctionalInterface) {
1195 checker->LogTypeError({"Type '", calleeType, "' has no call signatures."}, expr->Start());
1196 return checker->GlobalTypeError();
1197 }
1198
1199 checker::Signature *signature =
1200 ResolveSignature(checker, expr, calleeType, isFunctionalInterface, isUnionTypeWithFunctionalInterface);
1201 if (signature == nullptr) {
1202 return checker->GlobalTypeError();
1203 }
1204
1205 checker->CheckObjectLiteralArguments(signature, expr->Arguments());
1206
1207 if (!isFunctionalInterface) {
1208 checker::ETSObjectType *calleeObj = ChooseCalleeObj(checker, expr, calleeType, isConstructorCall);
1209 checker->ValidateSignatureAccessibility(calleeObj, expr, signature, expr->Start());
1210 }
1211
1212 ASSERT(signature->Function() != nullptr);
1213 if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
1214 checker->CheckThrowingStatements(expr);
1215 }
1216
1217 if (signature->Function()->IsDynamic()) {
1218 ASSERT(signature->Function()->IsDynamic());
1219 auto lang = signature->Function()->Language();
1220 expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), signature->Params(), lang, false));
1221 } else {
1222 ASSERT(!signature->Function()->IsDynamic());
1223 expr->SetSignature(signature);
1224 }
1225
1226 auto *returnType = signature->ReturnType();
1227
1228 if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) {
1229 returnType = ChooseCalleeObj(checker, expr, calleeType, isConstructorCall);
1230 }
1231
1232 return returnType;
1233 }
1234
CheckAbstractCall(ETSChecker * checker,ir::CallExpression * expr)1235 static void CheckAbstractCall(ETSChecker *checker, ir::CallExpression *expr)
1236 {
1237 if (expr->Callee()->IsMemberExpression()) {
1238 auto obj = expr->Callee()->AsMemberExpression()->Object();
1239 if (obj != nullptr && obj->IsSuperExpression()) {
1240 if ((expr->Signature() != nullptr) && (expr->Signature()->HasSignatureFlag(SignatureFlags::ABSTRACT))) {
1241 checker->LogTypeError("Cannot call abstract method!", expr->Start());
1242 expr->SetTsType(checker->GlobalTypeError());
1243 }
1244 }
1245 }
1246 }
1247
CheckCallee(ETSChecker * checker,ir::CallExpression * expr)1248 static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr)
1249 {
1250 checker->CheckNonNullish(expr->Callee());
1251 if (expr->Callee()->IsMemberExpression() && expr->Callee()->AsMemberExpression()->Object() != nullptr &&
1252 expr->Callee()->AsMemberExpression()->Object()->TsType()->IsETSObjectType() &&
1253 expr->Callee()->AsMemberExpression()->Object()->TsType()->AsETSObjectType()->HasObjectFlag(
1254 ETSObjectFlags::READONLY)) {
1255 checker->LogTypeError("Cannot call readonly type methods.", expr->Start());
1256 expr->SetTsType(checker->GlobalTypeError());
1257 }
1258 }
1259
1260 // Restore CheckerContext of the owner class if we want to perform checking
ReconstructOwnerClassContext(ETSChecker * checker,ETSObjectType * owner)1261 static checker::SavedCheckerContext ReconstructOwnerClassContext(ETSChecker *checker, ETSObjectType *owner)
1262 {
1263 if (owner == nullptr) {
1264 return SavedCheckerContext(checker, CheckerStatus::NO_OPTS, nullptr);
1265 }
1266 ASSERT(!owner->HasObjectFlag(ETSObjectFlags::ENUM));
1267 CheckerStatus const status =
1268 (owner->HasObjectFlag(ETSObjectFlags::CLASS) ? CheckerStatus::IN_CLASS : CheckerStatus::IN_INTERFACE) |
1269 (owner->HasObjectFlag(ETSObjectFlags::ABSTRACT) ? CheckerStatus::IN_ABSTRACT : CheckerStatus::NO_OPTS) |
1270 (owner->HasObjectFlag(ETSObjectFlags::INNER) ? CheckerStatus::INNER_CLASS : CheckerStatus::NO_OPTS) |
1271 (owner->GetDeclNode()->IsClassDefinition() && owner->GetDeclNode()->AsClassDefinition()->IsLocal()
1272 ? CheckerStatus::IN_LOCAL_CLASS
1273 : CheckerStatus::NO_OPTS);
1274
1275 return SavedCheckerContext(checker, status, owner);
1276 }
1277
GetCallExpressionReturnType(ir::CallExpression * expr,checker::Type * calleeType) const1278 checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr, checker::Type *calleeType) const
1279 {
1280 ETSChecker *checker = GetETSChecker();
1281 checker::Type *returnType = nullptr;
1282 if (calleeType->IsETSDynamicType() && !calleeType->AsETSDynamicType()->HasDecl()) {
1283 // Trailing lambda for js function call is not supported, check the correctness of `foo() {}`
1284 checker->EnsureValidCurlyBrace(expr);
1285 auto lang = calleeType->AsETSDynamicType()->Language();
1286 expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), expr->Arguments(), lang, false));
1287 returnType = expr->Signature()->ReturnType();
1288 } else {
1289 returnType = GetReturnType(expr, calleeType);
1290 }
1291
1292 if (returnType->IsTypeError()) {
1293 return checker->GlobalTypeError();
1294 }
1295
1296 auto const signature = expr->Signature();
1297 if (signature->RestVar() != nullptr) {
1298 auto *const elementType = signature->RestVar()->TsType()->AsETSArrayType()->ElementType();
1299 auto *const arrayType = checker->CreateETSArrayType(elementType)->AsETSArrayType();
1300 checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank());
1301 }
1302
1303 if (signature->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) {
1304 auto owner = const_cast<ETSObjectType *>(util::Helpers::GetContainingObjectType(signature->Function()));
1305 SavedCheckerContext savedCtx(ReconstructOwnerClassContext(checker, owner));
1306 signature->OwnerVar()->Declaration()->Node()->Check(checker);
1307 if (signature->Function()->HasBody()) {
1308 checker::ScopeContext scopeCtx(checker, signature->Function()->Body()->Scope());
1309 checker->CollectReturnStatements(signature->Function());
1310 }
1311 return signature->ReturnType();
1312 // NOTE(vpukhov): #14902 substituted signature is not updated
1313 }
1314
1315 return returnType;
1316 }
1317
Check(ir::CallExpression * expr) const1318 checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const
1319 {
1320 ETSChecker *checker = GetETSChecker();
1321 if (expr->TsType() != nullptr) {
1322 return expr->TsType();
1323 }
1324 ASSERT(!expr->IsOptional());
1325
1326 auto *oldCallee = expr->Callee();
1327 checker::Type *calleeType = checker->GetApparentType(expr->Callee()->Check(checker));
1328 if (calleeType->IsTypeError()) {
1329 return checker->InvalidateType(expr);
1330 }
1331
1332 if (expr->Callee() != oldCallee) {
1333 // If it is a static invoke, the callee will be transformed from an identifier to a member expression
1334 // Type check the callee again for member expression
1335 calleeType = checker->GetApparentType(expr->Callee()->Check(checker));
1336 }
1337
1338 CheckCallee(checker, expr);
1339
1340 checker::Type *const returnType = GetCallExpressionReturnType(expr, calleeType);
1341 expr->SetTsType(returnType);
1342 if (returnType->IsTypeError()) {
1343 return returnType;
1344 }
1345
1346 expr->SetUncheckedType(checker->GuaranteedTypeForUncheckedCallReturn(expr->Signature()));
1347 if (expr->UncheckedType() != nullptr) {
1348 checker->ComputeApparentType(returnType);
1349 }
1350
1351 if (returnType->IsTypeError()) {
1352 expr->SetTsType(returnType);
1353 return expr->TsType();
1354 }
1355
1356 CheckVoidTypeExpression(checker, expr);
1357 CheckAbstractCall(checker, expr);
1358 return expr->TsType();
1359 }
1360
Check(ir::ConditionalExpression * expr) const1361 checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const
1362 {
1363 if (expr->TsType() != nullptr) {
1364 return expr->TsType();
1365 }
1366
1367 ETSChecker *const checker = GetETSChecker();
1368
1369 SmartCastArray smartCasts = checker->Context().EnterTestExpression();
1370 checker->CheckTruthinessOfType(expr->Test());
1371 SmartCastTypes testedTypes = checker->Context().ExitTestExpression();
1372 if (testedTypes.has_value()) {
1373 for (auto [variable, consequentType, _] : *testedTypes) {
1374 checker->ApplySmartCast(variable, consequentType);
1375 }
1376 }
1377
1378 auto *consequent = expr->Consequent();
1379 auto *consequentType = consequent->Check(checker);
1380
1381 SmartCastArray consequentSmartCasts = checker->Context().CloneSmartCasts();
1382 checker->Context().RestoreSmartCasts(smartCasts);
1383
1384 if (testedTypes.has_value()) {
1385 for (auto [variable, _, alternateType] : *testedTypes) {
1386 checker->ApplySmartCast(variable, alternateType);
1387 }
1388 }
1389
1390 auto *alternate = expr->Alternate();
1391 auto *alternateType = alternate->Check(checker);
1392
1393 // Here we need to combine types from consequent and alternate if blocks.
1394 checker->Context().CombineSmartCasts(consequentSmartCasts);
1395
1396 if (checker->IsTypeIdenticalTo(consequentType, alternateType)) {
1397 expr->SetTsType(checker->GetNonConstantType(consequentType));
1398 } else {
1399 // If possible and required update number literal type to the proper value (identical to left-side type)
1400 if (alternate->IsNumberLiteral() &&
1401 checker->AdjustNumberLiteralType(alternate->AsNumberLiteral(), alternateType, consequentType)) {
1402 expr->SetTsType(consequentType);
1403 } else if (consequent->IsNumberLiteral() &&
1404 checker->AdjustNumberLiteralType(consequent->AsNumberLiteral(), consequentType, alternateType)) {
1405 expr->SetTsType(alternateType);
1406 } else {
1407 expr->SetTsType(checker->CreateETSUnionType({consequentType, alternateType}));
1408 if (expr->TsType()->IsETSReferenceType()) {
1409 checker->MaybeBoxExpression(expr->Consequent());
1410 checker->MaybeBoxExpression(expr->Alternate());
1411 }
1412 }
1413 }
1414
1415 return expr->TsType();
1416 }
1417
Check(ir::Identifier * expr) const1418 checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const
1419 {
1420 if (expr->TsType() == nullptr) {
1421 ETSChecker *checker = GetETSChecker();
1422
1423 auto *identType = checker->ResolveIdentifier(expr);
1424 if (expr->Variable() != nullptr && (expr->Parent() == nullptr || !expr->Parent()->IsAssignmentExpression() ||
1425 expr != expr->Parent()->AsAssignmentExpression()->Left())) {
1426 if (auto *const smartType = checker->Context().GetSmartCast(expr->Variable()); smartType != nullptr) {
1427 identType = smartType;
1428 }
1429 }
1430 expr->SetTsType(identType);
1431 if (!identType->IsTypeError()) {
1432 checker->Context().CheckIdentifierSmartCastCondition(expr);
1433 }
1434 }
1435 return expr->TsType();
1436 }
1437
SearchReExportsType(ETSObjectType * baseType,ir::MemberExpression * expr,util::StringView & aliasName,ETSChecker * checker)1438 std::pair<checker::Type *, util::StringView> SearchReExportsType(ETSObjectType *baseType, ir::MemberExpression *expr,
1439 util::StringView &aliasName, ETSChecker *checker)
1440 {
1441 std::pair<ETSObjectType *, util::StringView> ret {};
1442
1443 for (auto *const item : baseType->ReExports()) {
1444 auto name = item->GetReExportAliasValue(aliasName);
1445 if (name == aliasName && item->IsReExportHaveAliasValue(name)) {
1446 continue;
1447 }
1448
1449 if (item->GetProperty(name, PropertySearchFlags::SEARCH_ALL) != nullptr) {
1450 if (ret.first != nullptr) {
1451 checker->LogTypeError({"Ambiguous reference to '", aliasName, "'"}, expr->Start());
1452 expr->SetTsType(checker->GlobalTypeError());
1453 return ret;
1454 }
1455 ret = {item, name};
1456 }
1457
1458 if (auto reExportType = SearchReExportsType(item, expr, name, checker); reExportType.first != nullptr) {
1459 return reExportType;
1460 }
1461 }
1462
1463 return ret;
1464 }
1465
TypeErrorOnMissingProperty(ir::MemberExpression * expr,checker::Type * baseType,checker::ETSChecker * checker)1466 static void TypeErrorOnMissingProperty(ir::MemberExpression *expr, checker::Type *baseType,
1467 checker::ETSChecker *checker)
1468 {
1469 std::ignore = checker->TypeError(expr,
1470 checker->FormatMsg({"Property '", expr->Property()->AsIdentifier()->Name(),
1471 "' does not exist on type '", baseType, "'"}),
1472 expr->Object()->Start());
1473 }
1474
CheckEnumMemberExpression(ETSEnumType * const baseType,ir::MemberExpression * const expr) const1475 checker::Type *ETSAnalyzer::CheckEnumMemberExpression(ETSEnumType *const baseType,
1476 ir::MemberExpression *const expr) const
1477 {
1478 ETSChecker *checker = GetETSChecker();
1479 auto *const boxedClass = baseType->GetDecl()->BoxedClass();
1480 if (!boxedClass->IsClassDefinitionChecked()) {
1481 // Check enum class first to set types and build signatures
1482 checker->CheckClassDefinition(boxedClass);
1483 }
1484
1485 auto [memberType, memberVar] = expr->ResolveEnumMember(checker, baseType);
1486 expr->SetPropVar(memberVar);
1487 expr->Property()->SetTsType(memberType == nullptr ? checker->GlobalTypeError() : memberType);
1488 return expr->AdjustType(checker, expr->Property()->TsType());
1489 }
1490
Check(ir::MemberExpression * expr) const1491 checker::Type *ETSAnalyzer::Check(ir::MemberExpression *expr) const
1492 {
1493 if (expr->TsType() != nullptr) {
1494 return expr->TsType();
1495 }
1496 ASSERT(!expr->IsOptional());
1497
1498 ETSChecker *checker = GetETSChecker();
1499 auto *baseType = checker->GetNonConstantType(checker->GetApparentType(expr->Object()->Check(checker)));
1500 // Note: don't use possible smart cast to null-like types.
1501 // Such situation should be correctly resolved in the subsequent lowering.
1502 if (baseType->DefinitelyETSNullish() && expr->Object()->IsIdentifier()) {
1503 baseType = expr->Object()->AsIdentifier()->Variable()->TsType();
1504 }
1505
1506 if (baseType->IsETSObjectType() && !baseType->AsETSObjectType()->ReExports().empty() &&
1507 baseType->AsETSObjectType()->GetProperty(expr->Property()->AsIdentifier()->Name(),
1508 PropertySearchFlags::SEARCH_ALL) == nullptr) {
1509 if (auto reExportType = SearchReExportsType(baseType->AsETSObjectType(), expr,
1510 expr->Property()->AsIdentifier()->Name(), checker);
1511 reExportType.first != nullptr) {
1512 baseType = reExportType.first;
1513 expr->object_->AsIdentifier()->SetTsType(baseType);
1514 expr->property_->AsIdentifier()->SetName(reExportType.second);
1515 }
1516 }
1517
1518 if (!checker->CheckNonNullish(expr->Object())) {
1519 return checker->InvalidateType(expr);
1520 }
1521
1522 if (expr->IsComputed()) {
1523 return expr->AdjustType(checker, expr->CheckComputed(checker, baseType));
1524 }
1525
1526 if (baseType->IsETSArrayType()) {
1527 if (expr->Property()->AsIdentifier()->Name().Is("length")) {
1528 return expr->AdjustType(checker, checker->GlobalIntType());
1529 }
1530
1531 return expr->SetAndAdjustType(checker, checker->GlobalETSObjectType());
1532 }
1533
1534 if (baseType->IsETSObjectType()) {
1535 return expr->SetAndAdjustType(checker, baseType->AsETSObjectType());
1536 }
1537
1538 // NOTE(vpukhov): #20510 member access
1539 if (baseType->IsETSEnumType()) {
1540 return CheckEnumMemberExpression(baseType->AsETSEnumType(), expr);
1541 }
1542
1543 if (baseType->IsETSUnionType()) {
1544 return expr->AdjustType(checker, expr->CheckUnionMember(checker, baseType));
1545 }
1546 TypeErrorOnMissingProperty(expr, baseType, checker);
1547 return expr->TsType();
1548 }
1549
PreferredType(ir::ObjectExpression * expr) const1550 checker::Type *ETSAnalyzer::PreferredType(ir::ObjectExpression *expr) const
1551 {
1552 return expr->preferredType_;
1553 }
1554
CheckDynamic(ir::ObjectExpression * expr) const1555 checker::Type *ETSAnalyzer::CheckDynamic(ir::ObjectExpression *expr) const
1556 {
1557 ETSChecker *checker = GetETSChecker();
1558 for (ir::Expression *propExpr : expr->Properties()) {
1559 ASSERT(propExpr->IsProperty());
1560 ir::Property *prop = propExpr->AsProperty();
1561 ir::Expression *value = prop->Value();
1562 value->Check(checker);
1563 ASSERT(value->TsType());
1564 }
1565
1566 expr->SetTsType(expr->PreferredType());
1567 return expr->PreferredType();
1568 }
1569
ValidatePreferredType(ir::ObjectExpression * expr,ETSChecker * checker)1570 static bool ValidatePreferredType(ir::ObjectExpression *expr, ETSChecker *checker)
1571 {
1572 auto preferredType = expr->PreferredType();
1573 if (preferredType == nullptr) {
1574 checker->LogTypeError({"need to specify target type for class composite"}, expr->Start());
1575 return false;
1576 }
1577
1578 if (!preferredType->IsETSObjectType()) {
1579 checker->LogTypeError(
1580 {"Target type for class composite needs to be an object type, found '", preferredType, "'"}, expr->Start());
1581 return false;
1582 }
1583
1584 return true;
1585 }
1586
SetTypeforRecordProperties(const ir::ObjectExpression * expr,checker::ETSObjectType * objType,ETSChecker * checker)1587 static void SetTypeforRecordProperties(const ir::ObjectExpression *expr, checker::ETSObjectType *objType,
1588 ETSChecker *checker)
1589 {
1590 auto recordProperties = expr->Properties();
1591 auto typeArguments = objType->TypeArguments();
1592 auto valueType = typeArguments[1]; // Record<K, V> type arguments
1593
1594 for (auto recordProperty : recordProperties) {
1595 if (!recordProperty->AsProperty()->Value()->IsObjectExpression()) {
1596 continue;
1597 }
1598 auto recordPropertyExpr = recordProperty->AsProperty()->Value()->AsObjectExpression();
1599 recordPropertyExpr->SetPreferredType(valueType);
1600 recordPropertyExpr->Check(checker);
1601 }
1602 }
1603
Check(ir::ObjectExpression * expr) const1604 checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const
1605 {
1606 ETSChecker *checker = GetETSChecker();
1607 if (expr->TsType() != nullptr) {
1608 return expr->TsType();
1609 }
1610
1611 if (!ValidatePreferredType(expr, checker)) {
1612 expr->SetTsType(checker->GlobalTypeError());
1613 return expr->TsType();
1614 }
1615
1616 if (expr->PreferredType()->IsETSDynamicType()) {
1617 return CheckDynamic(expr);
1618 }
1619
1620 checker::ETSObjectType *objType = expr->PreferredType()->AsETSObjectType();
1621 if (objType->HasObjectFlag(checker::ETSObjectFlags::INTERFACE)) {
1622 // Object literal of interface tpye
1623 // Further interfaceObjectLiteralLowering phase will resolve interface type
1624 // and create corresponding anonymous class and class type
1625 // Here we just set the type to pass the checker
1626 CheckObjectExprProps(expr, checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
1627 checker::PropertySearchFlags::SEARCH_IN_INTERFACES);
1628 expr->SetTsType(objType);
1629 return objType;
1630 }
1631
1632 if (objType->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT)) {
1633 checker->LogTypeError({"target type for class composite ", objType->Name(), " is not instantiable"},
1634 expr->Start());
1635 expr->SetTsType(checker->GlobalTypeError());
1636 return expr->TsType();
1637 }
1638
1639 if (expr->PreferredType()->ToAssemblerName().str() == "escompat.Record" ||
1640 expr->PreferredType()->ToAssemblerName().str() == "escompat.Map") {
1641 // 7.6.3 Object Literal of Record Type
1642 // Record is an alias to Map
1643 // Here we just set the type to pass the checker
1644 // See Record Lowering for details
1645 expr->SetTsType(objType);
1646 SetTypeforRecordProperties(expr, objType, checker);
1647 return objType;
1648 }
1649
1650 bool haveEmptyConstructor = false;
1651 for (checker::Signature *sig : objType->ConstructSignatures()) {
1652 if (sig->Params().empty()) {
1653 haveEmptyConstructor = true;
1654 checker->ValidateSignatureAccessibility(objType, nullptr, sig, expr->Start());
1655 break;
1656 }
1657 }
1658 if (!haveEmptyConstructor) {
1659 checker->LogTypeError({"type ", objType->Name(), " has no parameterless constructor"}, expr->Start());
1660 expr->SetTsType(checker->GlobalTypeError());
1661 return expr->TsType();
1662 }
1663
1664 CheckObjectExprProps(expr, checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD |
1665 checker::PropertySearchFlags::SEARCH_IN_BASE |
1666 checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD);
1667
1668 expr->SetTsType(objType);
1669 return objType;
1670 }
1671
CheckObjectExprProps(const ir::ObjectExpression * expr,checker::PropertySearchFlags searchFlags) const1672 void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr, checker::PropertySearchFlags searchFlags) const
1673 {
1674 ETSChecker *checker = GetETSChecker();
1675 checker::ETSObjectType *objType = expr->PreferredType()->AsETSObjectType();
1676
1677 for (ir::Expression *propExpr : expr->Properties()) {
1678 if (!propExpr->IsProperty()) {
1679 checker->LogTypeError({"The object literal properties must be key-value pairs"}, expr->Start());
1680 return;
1681 }
1682 ir::Property *prop = propExpr->AsProperty();
1683 ir::Expression *key = prop->Key();
1684 ir::Expression *value = prop->Value();
1685
1686 util::StringView pname;
1687 if (key->IsStringLiteral()) {
1688 pname = key->AsStringLiteral()->Str();
1689 } else if (key->IsIdentifier()) {
1690 pname = key->AsIdentifier()->Name();
1691 } else {
1692 checker->LogTypeError({"key in class composite should be either identifier or string literal"},
1693 expr->Start());
1694 return;
1695 }
1696 varbinder::LocalVariable *lv = objType->GetProperty(pname, searchFlags);
1697 if (lv == nullptr) {
1698 checker->LogTypeError({"type ", objType->Name(), " has no property named ", pname}, propExpr->Start());
1699 return;
1700 }
1701 checker->ValidatePropertyAccess(lv, objType, propExpr->Start());
1702
1703 if (key->IsIdentifier()) {
1704 key->AsIdentifier()->SetVariable(lv);
1705 }
1706
1707 auto *propType = checker->GetTypeOfVariable(lv);
1708 key->SetTsType(propType);
1709
1710 if (value->IsObjectExpression()) {
1711 value->AsObjectExpression()->SetPreferredType(propType);
1712 }
1713 value->SetTsType(value->Check(checker));
1714
1715 auto *const valueType = value->TsType();
1716 const checker::Type *sourceType = checker->TryGettingFunctionTypeFromInvokeFunction(valueType);
1717 const checker::Type *targetType = checker->TryGettingFunctionTypeFromInvokeFunction(propType);
1718
1719 checker::AssignmentContext(
1720 checker->Relation(), value, valueType, propType, value->Start(),
1721 {"Type '", sourceType, "' is not compatible with type '", targetType, "' at property '", pname, "'"});
1722 }
1723
1724 if (objType->HasObjectFlag(ETSObjectFlags::REQUIRED)) {
1725 checker->ValidateObjectLiteralForRequiredType(objType, expr);
1726 }
1727 }
1728
Check(ir::OpaqueTypeNode * expr) const1729 checker::Type *ETSAnalyzer::Check(ir::OpaqueTypeNode *expr) const
1730 {
1731 return expr->TsType();
1732 }
1733
Check(ir::SequenceExpression * expr) const1734 checker::Type *ETSAnalyzer::Check(ir::SequenceExpression *expr) const
1735 {
1736 ETSChecker *checker = GetETSChecker();
1737 if (expr->TsType() != nullptr) {
1738 return expr->TsType();
1739 }
1740
1741 for (auto *it : expr->Sequence()) {
1742 it->Check(checker);
1743 }
1744 ASSERT(!expr->Sequence().empty());
1745 expr->SetTsType(expr->Sequence().back()->TsType());
1746 return nullptr;
1747 }
1748
Check(ir::SuperExpression * expr) const1749 checker::Type *ETSAnalyzer::Check(ir::SuperExpression *expr) const
1750 {
1751 ETSChecker *checker = GetETSChecker();
1752 if (expr->TsType() != nullptr) {
1753 return expr->TsType();
1754 }
1755
1756 expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass()->SuperType(), "super"));
1757 return expr->TsType();
1758 }
1759
Check(ir::TemplateLiteral * expr) const1760 checker::Type *ETSAnalyzer::Check(ir::TemplateLiteral *expr) const
1761 {
1762 ETSChecker *checker = GetETSChecker();
1763 if (expr->TsType() != nullptr) {
1764 return expr->TsType();
1765 }
1766
1767 if (expr->Quasis().size() != expr->Expressions().size() + 1U) {
1768 checker->LogTypeError("Invalid string template expression", expr->Start());
1769 expr->SetTsType(checker->GlobalTypeError());
1770 return expr->TsType();
1771 }
1772
1773 for (auto *it : expr->Expressions()) {
1774 it->Check(checker);
1775 }
1776
1777 for (auto *it : expr->Quasis()) {
1778 it->Check(checker);
1779 }
1780
1781 expr->SetTsType(checker->GlobalBuiltinETSStringType());
1782 return expr->TsType();
1783 }
1784
Check(ir::ThisExpression * expr) const1785 checker::Type *ETSAnalyzer::Check(ir::ThisExpression *expr) const
1786 {
1787 ETSChecker *checker = GetETSChecker();
1788 if (expr->TsType() != nullptr) {
1789 return expr->TsType();
1790 }
1791
1792 /*
1793 example code:
1794 ```
1795 class A {
1796 prop
1797 }
1798 function A.method() {
1799 let a = () => {
1800 console.println(this.prop)
1801 }
1802 }
1803 is identical to
1804 function method(this: A) {
1805 let a = () => {
1806 console.println(this.prop)
1807 }
1808 }
1809 ```
1810 here when "this" is used inside an extension function, we need to bind "this" to the first
1811 parameter(MANDATORY_PARAM_THIS), and capture the parameter's variable other than containing class's variable
1812 */
1813 auto *variable = checker->AsETSChecker()->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable;
1814 if (checker->HasStatus(checker::CheckerStatus::IN_INSTANCE_EXTENSION_METHOD)) {
1815 ASSERT(variable != nullptr);
1816 expr->SetTsType(variable->TsType());
1817 } else {
1818 expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass(), "this"));
1819 }
1820
1821 return expr->TsType();
1822 }
1823
1824 // Get string literal type as potential typeof result type with respect to spec p.7.17
GetTypeOfStringType(checker::Type * argType,ETSChecker * checker)1825 static checker::Type *GetTypeOfStringType(checker::Type *argType, ETSChecker *checker)
1826 {
1827 if (auto unboxed = checker->MaybeUnboxType(argType); unboxed->IsETSPrimitiveType()) {
1828 switch (checker->TypeKind(unboxed)) {
1829 case TypeFlag::ETS_BOOLEAN:
1830 return checker->CreateETSStringLiteralType("boolean");
1831 case TypeFlag::BYTE:
1832 case TypeFlag::CHAR:
1833 case TypeFlag::SHORT:
1834 case TypeFlag::INT:
1835 case TypeFlag::LONG:
1836 case TypeFlag::FLOAT:
1837 case TypeFlag::DOUBLE:
1838 return checker->CreateETSStringLiteralType("number");
1839 case TypeFlag::ETS_INT_ENUM:
1840 return checker->CreateETSStringLiteralType(argType == unboxed ? "number" : "object");
1841 case TypeFlag::ETS_STRING_ENUM:
1842 return checker->CreateETSStringLiteralType(argType == unboxed ? "string" : "object");
1843 default:
1844 UNREACHABLE();
1845 }
1846 }
1847 if (argType->IsETSUndefinedType()) {
1848 return checker->CreateETSStringLiteralType("undefined");
1849 }
1850 if (argType->IsETSArrayType() || argType->IsETSNullType()) {
1851 return checker->CreateETSStringLiteralType("object");
1852 }
1853 if (argType->IsETSStringType() || argType->IsETSStringEnumType()) {
1854 return checker->CreateETSStringLiteralType("string");
1855 }
1856 if (argType->IsETSBigIntType()) {
1857 return checker->CreateETSStringLiteralType("bigint");
1858 }
1859 if (argType->IsETSFunctionType()) {
1860 return checker->CreateETSStringLiteralType("function");
1861 }
1862
1863 return checker->GlobalBuiltinETSStringType();
1864 }
1865
ComputeTypeOfType(ETSChecker * checker,checker::Type * argType)1866 static checker::Type *ComputeTypeOfType(ETSChecker *checker, checker::Type *argType)
1867 {
1868 checker::Type *ret = nullptr;
1869 ArenaVector<checker::Type *> types(checker->Allocator()->Adapter());
1870 if (argType->IsETSUnionType()) {
1871 for (auto *it : argType->AsETSUnionType()->ConstituentTypes()) {
1872 checker::Type *elType = ComputeTypeOfType(checker, it);
1873 types.push_back(elType);
1874 }
1875 ret = checker->CreateETSUnionType(std::move(types));
1876 } else {
1877 ret = GetTypeOfStringType(argType, checker);
1878 }
1879 return ret;
1880 }
1881
Check(ir::TypeofExpression * expr) const1882 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::TypeofExpression *expr) const
1883 {
1884 ETSChecker *checker = GetETSChecker();
1885 if (expr->TsType() != nullptr) {
1886 return expr->TsType();
1887 }
1888
1889 expr->Argument()->Check(checker);
1890 expr->SetTsType(ComputeTypeOfType(checker, expr->Argument()->TsType()));
1891 return expr->TsType();
1892 }
1893
Check(ir::UnaryExpression * expr) const1894 checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const
1895 {
1896 ETSChecker *checker = GetETSChecker();
1897
1898 if (expr->TsType() != nullptr) {
1899 return expr->TsType();
1900 }
1901
1902 auto argType = expr->argument_->Check(checker);
1903 const auto isCondExpr = expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK;
1904 checker::Type *operandType = checker->ApplyUnaryOperatorPromotion(argType, true, true, isCondExpr);
1905 auto unboxedOperandType =
1906 isCondExpr ? checker->MaybeUnboxConditionalInRelation(argType) : checker->MaybeUnboxInRelation(argType);
1907
1908 if (argType != nullptr && argType->IsETSBigIntType() && argType->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL)) {
1909 switch (expr->OperatorType()) {
1910 case lexer::TokenType::PUNCTUATOR_MINUS: {
1911 checker::Type *type = checker->CreateETSBigIntLiteralType(argType->AsETSBigIntType()->GetValue());
1912
1913 // We do not need this const anymore as we are negating the bigint object in runtime
1914 type->RemoveTypeFlag(checker::TypeFlag::CONSTANT);
1915 expr->argument_->SetTsType(type);
1916 expr->SetTsType(type);
1917 return expr->TsType();
1918 }
1919 default:
1920 // Handled below
1921 // NOTE(kkonsw): handle other unary operators for bigint literals
1922 break;
1923 }
1924 }
1925
1926 if (argType != nullptr && argType->IsETSBigIntType()) {
1927 switch (expr->OperatorType()) {
1928 case lexer::TokenType::PUNCTUATOR_MINUS:
1929 case lexer::TokenType::PUNCTUATOR_PLUS:
1930 case lexer::TokenType::PUNCTUATOR_TILDE: {
1931 expr->SetTsType(argType);
1932 return expr->TsType();
1933 }
1934 default:
1935 break;
1936 }
1937 }
1938
1939 SetTsTypeForUnaryExpression(checker, expr, operandType);
1940
1941 if ((argType != nullptr) && argType->IsETSObjectType() && (unboxedOperandType != nullptr) &&
1942 unboxedOperandType->IsETSPrimitiveType()) {
1943 expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedOperandType));
1944 }
1945
1946 checker->Context().CheckUnarySmartCastCondition(expr);
1947
1948 return expr->TsType();
1949 }
1950
Check(ir::UpdateExpression * expr) const1951 checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const
1952 {
1953 ETSChecker *checker = GetETSChecker();
1954 if (expr->TsType() != nullptr) {
1955 return expr->TsType();
1956 }
1957
1958 checker::Type *operandType = expr->argument_->Check(checker);
1959 if (expr->Argument()->IsIdentifier()) {
1960 checker->ValidateUnaryOperatorOperand(expr->Argument()->AsIdentifier()->Variable());
1961 } else if (expr->Argument()->IsTSAsExpression()) {
1962 if (auto *const asExprVar = expr->Argument()->AsTSAsExpression()->Variable(); asExprVar != nullptr) {
1963 checker->ValidateUnaryOperatorOperand(asExprVar);
1964 }
1965 } else if (expr->Argument()->IsTSNonNullExpression()) {
1966 if (auto *const nonNullExprVar = expr->Argument()->AsTSNonNullExpression()->Variable();
1967 nonNullExprVar != nullptr) {
1968 checker->ValidateUnaryOperatorOperand(nonNullExprVar);
1969 }
1970 } else {
1971 ASSERT(expr->Argument()->IsMemberExpression());
1972 varbinder::LocalVariable *propVar = expr->argument_->AsMemberExpression()->PropVar();
1973 if (propVar != nullptr) {
1974 checker->ValidateUnaryOperatorOperand(propVar);
1975 }
1976 }
1977
1978 if (operandType->IsETSBigIntType()) {
1979 expr->SetTsType(operandType);
1980 return expr->TsType();
1981 }
1982
1983 auto unboxedType = checker->MaybeUnboxInRelation(operandType);
1984 if (unboxedType == nullptr || !unboxedType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
1985 checker->LogTypeError("Bad operand type, the type of the operand must be numeric type.",
1986 expr->Argument()->Start());
1987 expr->SetTsType(checker->GlobalTypeError());
1988 return expr->TsType();
1989 }
1990
1991 if (operandType->IsETSObjectType()) {
1992 expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedType) |
1993 checker->GetBoxingFlag(unboxedType));
1994 }
1995
1996 expr->SetTsType(operandType);
1997 return expr->TsType();
1998 }
1999
2000 // compile methods for LITERAL EXPRESSIONS in alphabetical order
Check(ir::BigIntLiteral * expr) const2001 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::BigIntLiteral *expr) const
2002 {
2003 ETSChecker *checker = GetETSChecker();
2004 expr->SetTsType(checker->CreateETSBigIntLiteralType(expr->Str()));
2005 return expr->TsType();
2006 }
2007
Check(ir::BooleanLiteral * expr) const2008 checker::Type *ETSAnalyzer::Check(ir::BooleanLiteral *expr) const
2009 {
2010 ETSChecker *checker = GetETSChecker();
2011 if (expr->TsType() == nullptr) {
2012 expr->SetTsType(checker->CreateETSBooleanType(expr->Value()));
2013 }
2014 return expr->TsType();
2015 }
2016
Check(ir::CharLiteral * expr) const2017 checker::Type *ETSAnalyzer::Check(ir::CharLiteral *expr) const
2018 {
2019 ETSChecker *checker = GetETSChecker();
2020 if (expr->TsType() == nullptr) {
2021 expr->SetTsType(checker->Allocator()->New<checker::CharType>(expr->Char()));
2022 }
2023 return expr->TsType();
2024 }
2025
Check(ir::NullLiteral * expr) const2026 checker::Type *ETSAnalyzer::Check(ir::NullLiteral *expr) const
2027 {
2028 ETSChecker *checker = GetETSChecker();
2029 if (expr->TsType() == nullptr) {
2030 expr->SetTsType(checker->GlobalETSNullType());
2031 }
2032 return expr->TsType();
2033 }
2034
Check(ir::NamespaceDeclaration * st) const2035 checker::Type *ETSAnalyzer::Check(ir::NamespaceDeclaration *st) const
2036 {
2037 ETSChecker *checker = GetETSChecker();
2038 st->Definition()->Check(checker);
2039 return nullptr;
2040 }
2041
Check(ir::NamespaceDefinition * st) const2042 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::NamespaceDefinition *st) const
2043 {
2044 return nullptr;
2045 }
2046
Check(ir::NumberLiteral * expr) const2047 checker::Type *ETSAnalyzer::Check(ir::NumberLiteral *expr) const
2048 {
2049 ETSChecker *checker = GetETSChecker();
2050 if (expr->Number().IsInt()) {
2051 expr->SetTsType(checker->CreateIntType(expr->Number().GetInt()));
2052 return expr->TsType();
2053 }
2054
2055 if (expr->Number().IsLong()) {
2056 expr->SetTsType(checker->CreateLongType(expr->Number().GetLong()));
2057 return expr->TsType();
2058 }
2059
2060 if (expr->Number().IsFloat()) {
2061 expr->SetTsType(checker->CreateFloatType(expr->Number().GetFloat()));
2062 return expr->TsType();
2063 }
2064
2065 expr->SetTsType(checker->CreateDoubleType(expr->Number().GetDouble()));
2066 return expr->TsType();
2067 }
2068
Check(ir::StringLiteral * expr) const2069 checker::Type *ETSAnalyzer::Check(ir::StringLiteral *expr) const
2070 {
2071 ETSChecker *checker = GetETSChecker();
2072 if (expr->TsType() == nullptr) {
2073 expr->SetTsType(checker->CreateETSStringLiteralType(expr->Str()));
2074 }
2075 return expr->TsType();
2076 }
2077
Check(ir::ImportDeclaration * st) const2078 checker::Type *ETSAnalyzer::Check(ir::ImportDeclaration *st) const
2079 {
2080 ETSChecker *checker = GetETSChecker();
2081 checker::Type *type = nullptr;
2082 for (auto *spec : st->Specifiers()) {
2083 if (spec->IsImportNamespaceSpecifier()) {
2084 type = spec->AsImportNamespaceSpecifier()->Check(checker);
2085 }
2086 }
2087
2088 return type;
2089 }
2090
Check(ir::ImportNamespaceSpecifier * st) const2091 checker::Type *ETSAnalyzer::Check(ir::ImportNamespaceSpecifier *st) const
2092 {
2093 ETSChecker *checker = GetETSChecker();
2094 if (st->Local()->Name().Empty()) {
2095 return nullptr;
2096 }
2097
2098 if (st->Local()->AsIdentifier()->TsType() != nullptr) {
2099 return st->Local()->TsType();
2100 }
2101
2102 auto *importDecl = st->Parent()->AsETSImportDeclaration();
2103
2104 if (importDecl->IsPureDynamic()) {
2105 auto *type = checker->GlobalBuiltinDynamicType(importDecl->Language());
2106 checker->SetrModuleObjectTsType(st->Local(), type);
2107 return type;
2108 }
2109
2110 return checker->GetImportSpecifierObjectType(importDecl, st->Local()->AsIdentifier());
2111 }
2112
2113 // compile methods for STATEMENTS in alphabetical order
Check(ir::AssertStatement * st) const2114 checker::Type *ETSAnalyzer::Check(ir::AssertStatement *st) const
2115 {
2116 ETSChecker *checker = GetETSChecker();
2117 if (!(st->Test()->Check(checker)->HasTypeFlag(TypeFlag::ETS_BOOLEAN | TypeFlag::BOOLEAN_LIKE) ||
2118 st->Test()->Check(checker)->ToString() == "Boolean")) {
2119 checker->LogTypeError("Bad operand type, the type of the operand must be boolean type.", st->Test()->Start());
2120 }
2121
2122 if (st->Second() != nullptr) {
2123 auto *msgType = st->second_->Check(checker);
2124
2125 if (!msgType->IsETSStringType()) {
2126 checker->LogTypeError("Assert message must be string", st->Second()->Start());
2127 }
2128 }
2129
2130 return nullptr;
2131 }
2132
Check(ir::BlockStatement * st) const2133 checker::Type *ETSAnalyzer::Check(ir::BlockStatement *st) const
2134 {
2135 ETSChecker *checker = GetETSChecker();
2136 checker::ScopeContext scopeCtx(checker, st->Scope());
2137
2138 // Iterator type checking of statements is modified to index type, to allow modifying the statement list during
2139 // checking without invalidating the iterator
2140 //---- Don't modify this to iterator, as it may break things during checking
2141 for (std::size_t idx = 0; idx < st->Statements().size(); ++idx) {
2142 auto *stmt = st->Statements()[idx];
2143 stmt->Check(checker);
2144
2145 // NOTE! Processing of trailing blocks was moved here so that smart casts could be applied correctly
2146 if (auto const tb = st->trailingBlocks_.find(stmt); tb != st->trailingBlocks_.end()) {
2147 auto *const trailingBlock = tb->second;
2148 trailingBlock->Check(checker);
2149 st->Statements().emplace(std::next(st->Statements().begin() + idx), trailingBlock);
2150 ++idx;
2151 }
2152 }
2153 if (UNLIKELY(checker->GetDebugInfoPlugin() != nullptr)) {
2154 // Compilation in eval-mode might require to create additional statements.
2155 // In this case, they must be created after iteration through statements ends.
2156 checker->GetDebugInfoPlugin()->AddPrologueEpilogue(st);
2157 }
2158
2159 // Remove possible smart casts for variables declared in inner scope:
2160 if (auto const *const scope = st->Scope();
2161 scope->IsFunctionScope() && st->Parent()->Parent()->Parent()->IsMethodDefinition()) {
2162 // When exiting method definition, just clear all smart casts
2163 checker->Context().ClearSmartCasts();
2164 } else if (!scope->IsGlobalScope()) {
2165 // otherwise only check inner declarations
2166 for (auto const *const decl : scope->Decls()) {
2167 if (decl->IsLetOrConstDecl() && decl->Node()->IsIdentifier()) {
2168 checker->Context().RemoveSmartCast(decl->Node()->AsIdentifier()->Variable());
2169 }
2170 }
2171 }
2172
2173 return nullptr;
2174 }
2175
Check(ir::BreakStatement * st) const2176 checker::Type *ETSAnalyzer::Check(ir::BreakStatement *st) const
2177 {
2178 ETSChecker *checker = GetETSChecker();
2179 auto node = checker->FindJumpTarget(st);
2180 if (!node.has_value()) {
2181 return checker->GlobalTypeError();
2182 }
2183 st->SetTarget(*node);
2184
2185 checker->Context().OnBreakStatement(st);
2186 return nullptr;
2187 }
2188
Check(ir::ClassDeclaration * st) const2189 checker::Type *ETSAnalyzer::Check(ir::ClassDeclaration *st) const
2190 {
2191 ETSChecker *checker = GetETSChecker();
2192 st->Definition()->Check(checker);
2193 return nullptr;
2194 }
2195
Check(ir::AnnotationDeclaration * st) const2196 checker::Type *ETSAnalyzer::Check(ir::AnnotationDeclaration *st) const
2197 {
2198 ETSChecker *checker = GetETSChecker();
2199
2200 if (!st->Expr()->IsIdentifier()) {
2201 st->Expr()->Check(checker);
2202 }
2203
2204 for (auto *it : st->Properties()) {
2205 auto *property = it->AsClassProperty();
2206 property->Check(checker);
2207 checker->CheckAnnotationPropertyType(property);
2208 }
2209
2210 auto *annoDecl = st->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration();
2211 if (annoDecl != st && annoDecl->IsDeclare()) {
2212 checker->CheckAmbientAnnotation(st, annoDecl);
2213 }
2214 return nullptr;
2215 }
2216
Check(ir::AnnotationUsage * st) const2217 checker::Type *ETSAnalyzer::Check(ir::AnnotationUsage *st) const
2218 {
2219 ETSChecker *checker = GetETSChecker();
2220
2221 if (!st->GetBaseName()->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) {
2222 checker->LogTypeError({"'", st->GetBaseName()->Name(), "' is not an annotation."}, st->GetBaseName()->Start());
2223 return nullptr;
2224 }
2225
2226 auto *annoDecl = st->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration();
2227 annoDecl->Check(checker);
2228
2229 if (!st->Expr()->IsIdentifier()) {
2230 st->Expr()->Check(checker);
2231 }
2232
2233 for (auto *it : st->Properties()) {
2234 it->Check(checker);
2235 auto property = it->AsClassProperty();
2236 if (property->Value() != nullptr && property->Value()->IsMemberExpression() &&
2237 !property->TsType()->IsETSEnumType()) {
2238 checker->LogTypeError("Invalid value for annotation field, expected a constant literal.",
2239 property->Value()->Start());
2240 }
2241 }
2242
2243 ArenaUnorderedMap<util::StringView, ir::ClassProperty *> fieldMap {checker->Allocator()->Adapter()};
2244 for (auto *it : annoDecl->Properties()) {
2245 auto *field = it->AsClassProperty();
2246 fieldMap.insert(std::make_pair(field->Id()->Name(), field));
2247 }
2248
2249 if (annoDecl->Properties().size() < st->Properties().size()) {
2250 checker->LogTypeError(
2251 "The number of arguments provided for the annotation exceeds the number of fields defined.", st->Start());
2252 return nullptr;
2253 }
2254
2255 if (st->Properties().size() == 1 &&
2256 st->Properties().at(0)->AsClassProperty()->Id()->Name() == compiler::Signatures::ANNOTATION_KEY_VALUE) {
2257 checker->CheckSinglePropertyAnnotation(st, annoDecl);
2258 fieldMap.clear();
2259 } else {
2260 checker->CheckMultiplePropertiesAnnotation(st, annoDecl, fieldMap);
2261 }
2262
2263 checker->ProcessRequiredFields(fieldMap, st, checker);
2264
2265 return nullptr;
2266 }
2267
Check(ir::ContinueStatement * st) const2268 checker::Type *ETSAnalyzer::Check(ir::ContinueStatement *st) const
2269 {
2270 ETSChecker *checker = GetETSChecker();
2271 auto node = checker->FindJumpTarget(st);
2272 if (!node.has_value()) {
2273 return checker->GlobalTypeError();
2274 }
2275 st->SetTarget(*node);
2276
2277 checker->AddStatus(CheckerStatus::MEET_CONTINUE);
2278 return nullptr;
2279 }
2280
Check(ir::DoWhileStatement * st) const2281 checker::Type *ETSAnalyzer::Check(ir::DoWhileStatement *st) const
2282 {
2283 ETSChecker *checker = GetETSChecker();
2284 checker::ScopeContext scopeCtx(checker, st->Scope());
2285
2286 // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
2287 auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st);
2288
2289 checker->CheckTruthinessOfType(st->Test());
2290 st->Body()->Check(checker);
2291
2292 checker->Context().ExitLoop(smartCasts, clearFlag, st);
2293 return nullptr;
2294 }
2295
Check(ir::EmptyStatement * st) const2296 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::EmptyStatement *st) const
2297 {
2298 return nullptr;
2299 }
2300
Check(ir::ExpressionStatement * st) const2301 checker::Type *ETSAnalyzer::Check(ir::ExpressionStatement *st) const
2302 {
2303 ETSChecker *checker = GetETSChecker();
2304 return st->GetExpression()->Check(checker);
2305 }
2306
ValidateAndProcessIteratorType(ETSChecker * checker,Type * elemType,ir::ForOfStatement * const st)2307 static bool ValidateAndProcessIteratorType(ETSChecker *checker, Type *elemType, ir::ForOfStatement *const st)
2308 {
2309 checker::Type *iterType = GetIteratorType(checker, elemType, st->Left());
2310 if (iterType->IsTypeError()) {
2311 return false;
2312 }
2313 auto *const relation = checker->Relation();
2314 relation->SetFlags(checker::TypeRelationFlag::ASSIGNMENT_CONTEXT);
2315 relation->SetNode(st->Left()->IsVariableDeclaration()
2316 ? st->Left()->AsVariableDeclaration()->Declarators().front()->Id()
2317 : st->Left()->AsIdentifier());
2318
2319 if (!relation->IsAssignableTo(elemType, iterType)) {
2320 std::stringstream ss {};
2321 ss << "Source element type '" << elemType->ToString() << "' is not assignable to the loop iterator type '"
2322 << iterType->ToString() << "'.";
2323 checker->LogTypeError(ss.str(), st->Start());
2324 return false;
2325 }
2326
2327 relation->SetNode(nullptr);
2328 relation->SetFlags(checker::TypeRelationFlag::NONE);
2329
2330 if (iterType->Variable() == nullptr && !iterType->IsETSObjectType() && elemType->IsETSObjectType() &&
2331 st->Left()->IsVariableDeclaration()) {
2332 for (auto &declarator : st->Left()->AsVariableDeclaration()->Declarators()) {
2333 checker->AddBoxingUnboxingFlagsToNode(declarator->Id(), iterType);
2334 }
2335 }
2336 return true;
2337 }
2338 // NOLINTBEGIN(modernize-avoid-c-arrays)
2339 static constexpr char const MISSING_SOURCE_EXPR_TYPE[] =
2340 "Cannot determine source expression type in the 'for-of' statement.";
2341 static constexpr char const INVALID_SOURCE_EXPR_TYPE[] =
2342 "'For-of' statement source expression is not of iterable type.";
2343 // NOLINTEND(modernize-avoid-c-arrays)
2344
Check(ir::ForOfStatement * const st) const2345 checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const
2346 {
2347 ETSChecker *checker = GetETSChecker();
2348 checker::ScopeContext scopeCtx(checker, st->Scope());
2349
2350 // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
2351 auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st);
2352
2353 checker::Type *const exprType = st->Right()->Check(checker);
2354 if (exprType == nullptr) {
2355 checker->LogTypeError(MISSING_SOURCE_EXPR_TYPE, st->Right()->Start());
2356 return checker->GlobalTypeError();
2357 }
2358
2359 checker::Type *elemType = nullptr;
2360
2361 if (exprType->IsETSStringType()) {
2362 elemType = checker->GetGlobalTypesHolder()->GlobalCharType();
2363 } else if (exprType->IsETSArrayType()) {
2364 elemType = exprType->AsETSArrayType()->ElementType();
2365 } else if (exprType->IsETSObjectType() || exprType->IsETSUnionType() || exprType->IsETSTypeParameter()) {
2366 elemType = st->CheckIteratorMethod(checker);
2367 }
2368
2369 if (elemType == nullptr) {
2370 checker->LogTypeError(INVALID_SOURCE_EXPR_TYPE, st->Right()->Start());
2371 return checker->GlobalTypeError();
2372 }
2373
2374 st->Left()->Check(checker);
2375
2376 if (!ValidateAndProcessIteratorType(checker, elemType, st)) {
2377 return checker->GlobalTypeError();
2378 };
2379
2380 st->Body()->Check(checker);
2381
2382 checker->Context().ExitLoop(smartCasts, clearFlag, st);
2383 return nullptr;
2384 }
2385
Check(ir::ForUpdateStatement * st) const2386 checker::Type *ETSAnalyzer::Check(ir::ForUpdateStatement *st) const
2387 {
2388 ETSChecker *checker = GetETSChecker();
2389 checker::ScopeContext scopeCtx(checker, st->Scope());
2390
2391 // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
2392 auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st);
2393
2394 if (st->Init() != nullptr) {
2395 st->Init()->Check(checker);
2396 }
2397
2398 if (st->Test() != nullptr) {
2399 checker->CheckTruthinessOfType(st->Test());
2400 }
2401
2402 if (st->Update() != nullptr) {
2403 st->Update()->Check(checker);
2404 }
2405
2406 st->Body()->Check(checker);
2407
2408 checker->Context().ExitLoop(smartCasts, clearFlag, st);
2409 return nullptr;
2410 }
2411
Check(ir::IfStatement * st) const2412 checker::Type *ETSAnalyzer::Check(ir::IfStatement *st) const
2413 {
2414 ETSChecker *const checker = GetETSChecker();
2415
2416 SmartCastArray smartCasts = checker->Context().EnterTestExpression();
2417 checker->CheckTruthinessOfType(st->Test());
2418 SmartCastTypes testedTypes = checker->Context().ExitTestExpression();
2419 if (testedTypes.has_value()) {
2420 for (auto [variable, consequentType, _] : *testedTypes) {
2421 checker->ApplySmartCast(variable, consequentType);
2422 }
2423 }
2424
2425 checker->Context().EnterPath();
2426 st->Consequent()->Check(checker);
2427 bool const consequentTerminated = checker->Context().ExitPath();
2428 SmartCastArray consequentSmartCasts = checker->Context().CloneSmartCasts();
2429
2430 // Restore smart casts to initial state.
2431 checker->Context().RestoreSmartCasts(smartCasts);
2432 // Apply the alternate smart casts
2433 if (testedTypes.has_value()) {
2434 for (auto [variable, _, alternateType] : *testedTypes) {
2435 checker->ApplySmartCast(variable, alternateType);
2436 }
2437 }
2438
2439 if (st->Alternate() != nullptr) {
2440 checker->Context().EnterPath();
2441 st->Alternate()->Check(checker);
2442 bool const alternateTerminated = checker->Context().ExitPath();
2443 if (alternateTerminated) {
2444 if (!consequentTerminated) {
2445 // Here we need to restore types from consequent if block.
2446 checker->Context().RestoreSmartCasts(consequentSmartCasts);
2447 } else {
2448 // Here we need to restore initial smart types.
2449 checker->Context().RestoreSmartCasts(smartCasts);
2450 }
2451 } else if (!consequentTerminated) {
2452 // Here we need to combine types from consequent and alternate if blocks.
2453 checker->Context().CombineSmartCasts(consequentSmartCasts);
2454 }
2455 } else {
2456 if (!consequentTerminated) {
2457 // Here we need to combine types from consequent if block and initial.
2458 checker->Context().CombineSmartCasts(consequentSmartCasts);
2459 }
2460 }
2461
2462 return nullptr;
2463 }
2464
Check(ir::LabelledStatement * st) const2465 checker::Type *ETSAnalyzer::Check(ir::LabelledStatement *st) const
2466 {
2467 ETSChecker *checker = GetETSChecker();
2468 st->body_->Check(checker);
2469 return nullptr;
2470 }
2471
CheckInferredFunctionReturnType(ir::ReturnStatement * st,ir::ScriptFunction * containingFunc,checker::Type * & funcReturnType,ir::TypeNode * returnTypeAnnotation,ETSChecker * checker) const2472 bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::ScriptFunction *containingFunc,
2473 checker::Type *&funcReturnType, ir::TypeNode *returnTypeAnnotation,
2474 ETSChecker *checker) const
2475 {
2476 funcReturnType = returnTypeAnnotation->GetType(checker);
2477 if (returnTypeAnnotation->IsTSThisType() && (st->Argument() == nullptr || !st->Argument()->IsThisExpression())) {
2478 checker->LogTypeError("The only allowed return value is 'this' if the method's return type is the 'this' type",
2479 st->Start());
2480 return false;
2481 }
2482
2483 // Case when function's return type is defined explicitly:
2484
2485 if (st->argument_ == nullptr) {
2486 if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType() &&
2487 !funcReturnType->IsETSAsyncFuncReturnType()) {
2488 checker->LogTypeError("Missing return value.", st->Start());
2489 return false;
2490 }
2491 funcReturnType = checker->GlobalVoidType();
2492 } else {
2493 const auto name = containingFunc->Scope()->InternalName().Mutf8();
2494 if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) {
2495 return false;
2496 }
2497
2498 if (st->argument_->IsObjectExpression()) {
2499 st->argument_->AsObjectExpression()->SetPreferredType(funcReturnType);
2500 }
2501 if (st->argument_->IsMemberExpression()) {
2502 checker->SetArrayPreferredTypeForNestedMemberExpressions(st->argument_->AsMemberExpression(),
2503 funcReturnType);
2504 }
2505
2506 if (st->argument_->IsArrayExpression()) {
2507 st->argument_->AsArrayExpression()->SetPreferredType(funcReturnType);
2508 }
2509
2510 checker::Type *argumentType = st->argument_->Check(checker);
2511 return CheckReturnType(checker, funcReturnType, argumentType, st->argument_, containingFunc->IsAsyncFunc());
2512 }
2513 return true;
2514 }
2515
GetFunctionReturnType(ir::ReturnStatement * st,ir::ScriptFunction * containingFunc) const2516 checker::Type *ETSAnalyzer::GetFunctionReturnType(ir::ReturnStatement *st, ir::ScriptFunction *containingFunc) const
2517 {
2518 ASSERT(containingFunc->ReturnTypeAnnotation() != nullptr || containingFunc->Signature()->ReturnType() != nullptr);
2519
2520 ETSChecker *checker = GetETSChecker();
2521 checker::Type *funcReturnType = nullptr;
2522
2523 if (auto *const returnTypeAnnotation = containingFunc->ReturnTypeAnnotation(); returnTypeAnnotation != nullptr) {
2524 if (!CheckInferredFunctionReturnType(st, containingFunc, funcReturnType, returnTypeAnnotation, checker)) {
2525 return checker->GlobalTypeError();
2526 }
2527 } else {
2528 // Case when function's return type should be inferred from return statement(s):
2529 if (containingFunc->Signature()->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) {
2530 InferReturnType(checker, containingFunc, funcReturnType,
2531 st->argument_); // This removes the NEED_RETURN_TYPE flag, so only the first return
2532 // statement going to land here...
2533 } else {
2534 // All subsequent return statements:
2535 ProcessReturnStatements(checker, containingFunc, funcReturnType, st,
2536 st->argument_); // and the remaining return statements will get processed here.
2537 }
2538 }
2539
2540 if ((st->argument_ != nullptr) && st->argument_->IsArrayExpression() && funcReturnType->IsArrayType()) {
2541 checker->ModifyPreferredType(st->argument_->AsArrayExpression(), funcReturnType);
2542 st->argument_->Check(checker);
2543 }
2544
2545 return funcReturnType;
2546 }
2547
Check(ir::ReturnStatement * st) const2548 checker::Type *ETSAnalyzer::Check(ir::ReturnStatement *st) const
2549 {
2550 ETSChecker *checker = GetETSChecker();
2551
2552 ir::AstNode *ancestor = util::Helpers::FindAncestorGivenByType(st, ir::AstNodeType::SCRIPT_FUNCTION);
2553 ASSERT(ancestor && ancestor->IsScriptFunction());
2554 auto *containingFunc = ancestor->AsScriptFunction();
2555
2556 checker->AddStatus(CheckerStatus::MEET_RETURN);
2557
2558 if (containingFunc->IsConstructor()) {
2559 if (st->argument_ != nullptr) {
2560 checker->LogTypeError("Return statement with expression isn't allowed in constructor.", st->Start());
2561 return checker->GlobalTypeError();
2562 }
2563 return nullptr;
2564 }
2565
2566 st->returnType_ = GetFunctionReturnType(st, containingFunc);
2567
2568 if (containingFunc->ReturnTypeAnnotation() == nullptr) {
2569 containingFunc->AddReturnStatement(st);
2570 }
2571
2572 return nullptr;
2573 }
2574
Check(ir::SwitchStatement * st) const2575 checker::Type *ETSAnalyzer::Check(ir::SwitchStatement *st) const
2576 {
2577 ETSChecker *checker = GetETSChecker();
2578 checker::ScopeContext scopeCtx(checker, st->Scope());
2579 checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(checker->Relation(),
2580 checker::TypeRelationFlag::NONE);
2581
2582 auto *comparedExprType = checker->CheckSwitchDiscriminant(st->Discriminant());
2583 auto unboxedDiscType = (st->Discriminant()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U
2584 ? checker->MaybeUnboxInRelation(comparedExprType)
2585 : comparedExprType;
2586
2587 SmartCastArray smartCasts = checker->Context().CloneSmartCasts();
2588 bool hasDefaultCase = false;
2589
2590 for (auto &it : st->Cases()) {
2591 checker->Context().EnterPath();
2592 it->CheckAndTestCase(checker, comparedExprType, unboxedDiscType, st->Discriminant(), hasDefaultCase);
2593 bool const caseTerminated = checker->Context().ExitPath();
2594
2595 if (it != st->Cases().back()) {
2596 if (!caseTerminated) {
2597 checker->Context().CombineSmartCasts(smartCasts);
2598 } else {
2599 checker->Context().RestoreSmartCasts(smartCasts);
2600 }
2601 } else {
2602 if (!caseTerminated) {
2603 // if the recent switch case isn't terminated in any way, copy actual smart casts to the array of
2604 // smart casts for the other case blocks so that it can be processed in unified way
2605 checker->Context().AddBreakSmartCasts(st, checker->Context().CloneSmartCasts());
2606 }
2607 checker->Context().ClearSmartCasts();
2608 }
2609 }
2610
2611 // If default case is absent initial smart casts should be also applied here
2612 if (!hasDefaultCase) {
2613 checker->Context().AddBreakSmartCasts(st, std::move(smartCasts));
2614 }
2615
2616 // Combine smart casts from all [non-terminated] case blocks with 'break'
2617 checker->Context().CombineBreakSmartCasts(st);
2618
2619 checker->CheckForSameSwitchCases(st->Cases());
2620 return nullptr;
2621 }
2622
Check(ir::ThrowStatement * st) const2623 checker::Type *ETSAnalyzer::Check(ir::ThrowStatement *st) const
2624 {
2625 ETSChecker *checker = GetETSChecker();
2626 auto *argType = st->argument_->Check(checker);
2627 checker->CheckExceptionOrErrorType(argType, st->Start());
2628
2629 if (checker->Relation()->IsAssignableTo(argType, checker->GlobalBuiltinExceptionType())) {
2630 checker->CheckThrowingStatements(st);
2631 }
2632
2633 checker->AddStatus(CheckerStatus::MEET_THROW);
2634 return nullptr;
2635 }
2636
Check(ir::TryStatement * st) const2637 checker::Type *ETSAnalyzer::Check(ir::TryStatement *st) const
2638 {
2639 ETSChecker *checker = GetETSChecker();
2640 std::vector<checker::ETSObjectType *> exceptions {};
2641
2642 std::vector<SmartCastArray> casts {};
2643 auto smartCasts = checker->Context().CheckTryBlock(*st->Block());
2644 st->Block()->Check(checker);
2645
2646 bool defaultCatchFound = false;
2647 for (auto *catchClause : st->CatchClauses()) {
2648 if (defaultCatchFound) {
2649 checker->LogTypeError("Default catch clause should be the last in the try statement", catchClause->Start());
2650 return checker->GlobalTypeError();
2651 }
2652
2653 checker->Context().RestoreSmartCasts(smartCasts);
2654
2655 if (auto const exceptionType = catchClause->Check(checker);
2656 exceptionType != nullptr && catchClause->Param() != nullptr) {
2657 auto *clauseType = exceptionType->AsETSObjectType();
2658 checker->CheckExceptionClauseType(exceptions, catchClause, clauseType);
2659 exceptions.emplace_back(clauseType);
2660 }
2661
2662 defaultCatchFound = catchClause->IsDefaultCatchClause();
2663
2664 casts.emplace_back(checker->Context().CloneSmartCasts());
2665 }
2666
2667 checker->Context().RestoreSmartCasts(smartCasts);
2668 if (!casts.empty()) {
2669 for (auto const &cast : casts) {
2670 checker->Context().CombineSmartCasts(cast);
2671 }
2672 }
2673
2674 if (st->HasFinalizer()) {
2675 st->FinallyBlock()->Check(checker);
2676 }
2677
2678 return nullptr;
2679 }
2680
Check(ir::VariableDeclarator * st) const2681 checker::Type *ETSAnalyzer::Check(ir::VariableDeclarator *st) const
2682 {
2683 if (st->TsType() != nullptr) {
2684 return st->TsType();
2685 }
2686
2687 ETSChecker *checker = GetETSChecker();
2688 ASSERT(st->Id()->IsIdentifier());
2689 auto *const ident = st->Id()->AsIdentifier();
2690 ir::ModifierFlags flags = ir::ModifierFlags::NONE;
2691
2692 if (ident->Parent()->Parent()->AsVariableDeclaration()->Kind() ==
2693 ir::VariableDeclaration::VariableDeclarationKind::CONST) {
2694 flags |= ir::ModifierFlags::CONST;
2695 }
2696
2697 if (ident->IsOptionalDeclaration()) {
2698 flags |= ir::ModifierFlags::OPTIONAL;
2699 }
2700
2701 auto *const variableType = checker->CheckVariableDeclaration(ident, ident->TypeAnnotation(), st->Init(), flags);
2702 auto *smartType = variableType;
2703
2704 // Now try to define the actual type of Identifier so that smart cast can be used in further checker processing
2705 // NOTE: T_S and K_o_t_l_i_n don't act in such way, but we can try - why not? :)
2706 if (auto *const initType = st->Init() != nullptr ? st->Init()->TsType() : nullptr; initType != nullptr) {
2707 smartType = checker->ResolveSmartType(initType, variableType);
2708 // Set smart type for identifier if it differs from annotated type
2709 // Top-level and captured variables are not processed here!
2710 if (!checker->Relation()->IsIdenticalTo(variableType, smartType)) {
2711 ident->SetTsType(smartType);
2712 checker->Context().SetSmartCast(ident->Variable(), smartType);
2713 }
2714 }
2715
2716 st->SetTsType(smartType);
2717 return smartType;
2718 }
2719
Check(ir::VariableDeclaration * st) const2720 checker::Type *ETSAnalyzer::Check(ir::VariableDeclaration *st) const
2721 {
2722 ETSChecker *checker = GetETSChecker();
2723 for (auto *it : st->Declarators()) {
2724 it->Check(checker);
2725 }
2726
2727 return nullptr;
2728 }
2729
Check(ir::WhileStatement * st) const2730 checker::Type *ETSAnalyzer::Check(ir::WhileStatement *st) const
2731 {
2732 ETSChecker *checker = GetETSChecker();
2733 checker::ScopeContext scopeCtx(checker, st->Scope());
2734
2735 // NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
2736 auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st);
2737
2738 checker->CheckTruthinessOfType(st->Test());
2739 st->Body()->Check(checker);
2740
2741 checker->Context().ExitLoop(smartCasts, clearFlag, st);
2742 return nullptr;
2743 }
2744
Check(ir::TSArrayType * node) const2745 checker::Type *ETSAnalyzer::Check(ir::TSArrayType *node) const
2746 {
2747 ETSChecker *checker = GetETSChecker();
2748 node->elementType_->Check(checker);
2749 node->SetTsType(node->GetType(checker));
2750
2751 const auto arrayType = node->TsType()->AsETSArrayType();
2752 checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank());
2753 return nullptr;
2754 }
2755
Check(ir::TSAsExpression * expr) const2756 checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const
2757 {
2758 ETSChecker *checker = GetETSChecker();
2759
2760 if (expr->TsType() != nullptr) {
2761 return expr->TsType();
2762 }
2763
2764 auto *const targetType = expr->TypeAnnotation()->AsTypeNode()->GetType(checker);
2765 // Object expression requires that its type be set by the context before checking. in this case, the target type
2766 // provides that context.
2767 if (expr->Expr()->IsObjectExpression()) {
2768 expr->Expr()->AsObjectExpression()->SetPreferredType(targetType);
2769 }
2770
2771 if (expr->Expr()->IsArrayExpression()) {
2772 expr->Expr()->AsArrayExpression()->SetPreferredType(targetType);
2773 }
2774
2775 auto *const sourceType = expr->Expr()->Check(checker);
2776 if (sourceType->IsTypeError()) {
2777 expr->SetTsType(checker->GlobalTypeError());
2778 return expr->TsType();
2779 }
2780
2781 // NOTE(vpukhov): #20510 lowering
2782 if (targetType->IsETSPrimitiveType() && sourceType->IsETSReferenceType()) {
2783 auto *const boxedTargetType = checker->MaybeBoxInRelation(targetType);
2784 if (!checker->Relation()->IsIdenticalTo(sourceType, boxedTargetType)) {
2785 expr->Expr()->AddAstNodeFlags(ir::AstNodeFlags::CHECKCAST);
2786 }
2787 }
2788
2789 if (sourceType->DefinitelyETSNullish() && !targetType->PossiblyETSNullish()) {
2790 checker->LogTypeError("Cannot cast 'null' or 'undefined' to non-nullish type.", expr->Expr()->Start());
2791 expr->SetTsType(checker->GlobalTypeError());
2792 return expr->TsType();
2793 }
2794
2795 const checker::CastingContext ctx(
2796 checker->Relation(),
2797 std::initializer_list<TypeErrorMessageElement> {"Cannot cast type '", sourceType, "' to '", targetType, "'"},
2798 checker::CastingContext::ConstructorData {expr->Expr(), sourceType, targetType, expr->Expr()->Start()});
2799
2800 if (sourceType->IsETSDynamicType() && targetType->IsLambdaObject()) {
2801 // NOTE: itrubachev. change targetType to created lambdaobject type.
2802 // Now targetType is not changed, only construct signature is added to it
2803 checker->BuildLambdaObjectClass(targetType->AsETSObjectType(),
2804 expr->TypeAnnotation()->AsETSFunctionType()->ReturnType());
2805 }
2806 expr->isUncheckedCast_ = ctx.UncheckedCast();
2807
2808 // Make sure the array type symbol gets created for the assembler to be able to emit checkcast.
2809 // Because it might not exist, if this particular array type was never created explicitly.
2810 if (!expr->isUncheckedCast_ && targetType->IsETSArrayType()) {
2811 auto *const targetArrayType = targetType->AsETSArrayType();
2812 checker->CreateBuiltinArraySignature(targetArrayType, targetArrayType->Rank());
2813 }
2814
2815 if (targetType == checker->GetGlobalTypesHolder()->GlobalETSNeverType()) {
2816 checker->LogTypeError("Cast to 'never' is prohibited", expr->Start());
2817 expr->SetTsType(checker->GlobalTypeError());
2818 return expr->TsType();
2819 }
2820
2821 checker->ComputeApparentType(targetType);
2822 expr->SetTsType(targetType);
2823 return expr->TsType();
2824 }
2825
Check(ir::TSEnumDeclaration * st) const2826 checker::Type *ETSAnalyzer::Check(ir::TSEnumDeclaration *st) const
2827 {
2828 ETSChecker *checker = GetETSChecker();
2829 varbinder::Variable *enumVar = st->Key()->Variable();
2830 ASSERT(enumVar != nullptr);
2831
2832 if (enumVar->TsType() == nullptr) {
2833 Check(st->BoxedClass());
2834 if (auto *const itemInit = st->Members().front()->AsTSEnumMember()->Init(); itemInit->IsNumberLiteral()) {
2835 checker->CreateEnumIntTypeFromEnumDeclaration(st);
2836 } else if (itemInit->IsStringLiteral()) {
2837 checker->CreateEnumStringTypeFromEnumDeclaration(st);
2838 } else {
2839 checker->LogTypeError("Invalid enumeration value type.", st->Start());
2840 st->SetTsType(checker->GlobalTypeError());
2841 return st->TsType();
2842 }
2843 } else if (st->TsType() == nullptr) {
2844 st->SetTsType(enumVar->TsType());
2845 }
2846
2847 return st->TsType();
2848 }
2849
Check(ir::TSInterfaceDeclaration * st) const2850 checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const
2851 {
2852 if (st->TsType() == nullptr) {
2853 ETSChecker *checker = GetETSChecker();
2854
2855 checker::ETSObjectType *interfaceType = checker->BuildBasicInterfaceProperties(st);
2856 ASSERT(interfaceType != nullptr);
2857
2858 interfaceType->SetSuperType(checker->GlobalETSObjectType());
2859 checker->CheckInvokeMethodsLegitimacy(interfaceType);
2860 st->SetTsType(interfaceType);
2861
2862 checker::ScopeContext scopeCtx(checker, st->Scope());
2863 auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType);
2864
2865 for (auto *it : st->Body()->Body()) {
2866 it->Check(checker);
2867 }
2868 }
2869 return st->TsType();
2870 }
2871
Check(ir::TSNonNullExpression * expr) const2872 checker::Type *ETSAnalyzer::Check(ir::TSNonNullExpression *expr) const
2873 {
2874 if (expr->TsType() == nullptr) {
2875 ETSChecker *checker = GetETSChecker();
2876 auto exprType = expr->expr_->Check(checker);
2877 // If the actual [smart] type is definitely 'null' or 'undefined' then probably CTE should be thrown.
2878 // Anyway we'll definitely obtain NullPointerException at runtime.
2879 if (exprType->DefinitelyETSNullish()) {
2880 checker->LogTypeError(
2881 "Bad operand type, the operand of the non-nullish expression is 'null' or 'undefined'.",
2882 expr->Expr()->Start());
2883 expr->SetTsType(checker->GlobalTypeError());
2884 return expr->TsType();
2885 }
2886 expr->SetTsType(checker->GetNonNullishType(exprType));
2887 }
2888 expr->SetOriginalType(expr->TsType());
2889 return expr->TsType();
2890 }
2891
Check(ir::TSQualifiedName * expr) const2892 checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const
2893 {
2894 ETSChecker *checker = GetETSChecker();
2895 checker::Type *baseType = expr->Left()->Check(checker);
2896
2897 if (baseType->IsETSObjectType()) {
2898 auto importDecl = baseType->AsETSObjectType()->GetDeclNode()->Parent()->Parent();
2899 // clang-format off
2900 auto searchName =
2901 importDecl->IsETSImportDeclaration()
2902 ? checker->VarBinder()->AsETSBinder()->FindNameInAliasMap(
2903 importDecl->AsETSImportDeclaration()->ResolvedSource()->Str(), expr->Right()->Name())
2904 : expr->Right()->Name();
2905 // clang-format on
2906 // NOTE (oeotvos) This should be done differently in the follow-up patch.
2907 if (searchName.Empty()) {
2908 searchName = expr->Right()->Name();
2909 }
2910 varbinder::Variable *prop =
2911 baseType->AsETSObjectType()->GetProperty(searchName, checker::PropertySearchFlags::SEARCH_DECL);
2912 // NOTE(dslynko): in debugger evaluation mode must lazily generate module's properties here.
2913
2914 if (prop == nullptr) {
2915 checker->LogTypeError({"'", expr->Right()->Name(), "' type does not exist."}, expr->Right()->Start());
2916 return checker->GlobalTypeError();
2917 }
2918
2919 if (expr->Right()->Name().Is(searchName.Mutf8()) && prop->Declaration()->Node()->HasExportAlias()) {
2920 checker->LogTypeError({"Cannot find imported element '", searchName, "' exported with alias"},
2921 expr->Right()->Start());
2922 return checker->GlobalTypeError();
2923 }
2924
2925 expr->Right()->SetVariable(prop);
2926 return checker->GetTypeOfVariable(prop);
2927 }
2928
2929 checker->LogTypeError({"'", expr->Right()->Name(), "' type does not exist."}, expr->Right()->Start());
2930 return checker->GlobalTypeError();
2931 }
2932
Check(ir::TSTypeAliasDeclaration * st) const2933 checker::Type *ETSAnalyzer::Check(ir::TSTypeAliasDeclaration *st) const
2934 {
2935 ETSChecker *checker = GetETSChecker();
2936 if (st->TypeParams() == nullptr) {
2937 const checker::SavedTypeRelationFlagsContext savedFlagsCtx(
2938 checker->Relation(), checker::TypeRelationFlag::NO_THROW_GENERIC_TYPEALIAS);
2939
2940 if (st->TypeAnnotation()->TsType() == nullptr) {
2941 st->TypeAnnotation()->Check(checker);
2942 }
2943
2944 return nullptr;
2945 }
2946
2947 if (st->TypeParameterTypes().empty()) {
2948 auto [typeParamTypes, ok] = checker->CreateUnconstrainedTypeParameters(st->TypeParams());
2949 st->SetTypeParameterTypes(std::move(typeParamTypes));
2950 if (ok) {
2951 checker->AssignTypeParameterConstraints(st->TypeParams());
2952 }
2953 }
2954
2955 for (auto *const param : st->TypeParams()->Params()) {
2956 const auto *const res = st->TypeAnnotation()->FindChild([¶m](const ir::AstNode *const node) {
2957 if (!node->IsIdentifier()) {
2958 return false;
2959 }
2960
2961 return param->Name()->AsIdentifier()->Variable() == node->AsIdentifier()->Variable();
2962 });
2963
2964 if (res == nullptr) {
2965 checker->LogTypeError(
2966 {"Type alias generic parameter '", param->Name()->Name(), "' is not used in type annotation"},
2967 param->Start());
2968 return checker->GlobalTypeError();
2969 }
2970 }
2971
2972 const checker::SavedTypeRelationFlagsContext savedFlagsCtx(checker->Relation(),
2973 checker::TypeRelationFlag::NO_THROW_GENERIC_TYPEALIAS);
2974
2975 if (st->TypeAnnotation()->TsType() == nullptr) {
2976 st->TypeAnnotation()->Check(checker);
2977 }
2978
2979 return nullptr;
2980 }
2981 } // namespace ark::es2panda::checker
2982