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