• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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([&param](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