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 "ir/typeNode.h"
17#include "ir/expressions/literals/stringLiteral.h"
18#include "ir/expressions/literals/bigIntLiteral.h"
19#include "ir/expressions/literals/numberLiteral.h"
20#include "ir/expressions/arrayExpression.h"
21#include "ir/expressions/assignmentExpression.h"
22#include "ir/expressions/callExpression.h"
23#include "ir/expressions/objectExpression.h"
24#include "ir/expressions/identifier.h"
25#include "ir/base/scriptFunction.h"
26#include "ir/base/property.h"
27#include "ir/base/spreadElement.h"
28#include "ir/statements/blockStatement.h"
29#include "ir/statements/returnStatement.h"
30#include "ir/statements/functionDeclaration.h"
31#include "util/helpers.h"
32#include "varbinder/variable.h"
33#include "varbinder/scope.h"
34#include "varbinder/declaration.h"
35
36#include "checker/TSchecker.h"
37#include "checker/ts/destructuringContext.h"
38#include "checker/types/ts/objectDescriptor.h"
39#include "checker/types/ts/objectType.h"
40
41#include <cstddef>
42#include <cstdint>
43#include <memory>
44#include <utility>
45#include <vector>
46
47namespace ark::es2panda::checker {
48Type *TSChecker::HandleFunctionReturn(ir::ScriptFunction *func)
49{
50    if (func->ReturnTypeAnnotation() != nullptr) {
51        func->ReturnTypeAnnotation()->Check(this);
52        Type *returnType = func->ReturnTypeAnnotation()->GetType(this);
53
54        if (func->IsArrow() && func->Body()->IsExpression()) {
55            ElaborateElementwise(returnType, func->Body()->AsExpression(), func->Body()->Start());
56        }
57
58        if (returnType->IsNeverType()) {
59            ThrowTypeError("A function returning 'never' cannot have a reachable end point.",
60                           func->ReturnTypeAnnotation()->Start());
61        }
62
63        if (!MaybeTypeOfKind(returnType, TypeFlag::ANY_OR_VOID)) {
64            CheckAllCodePathsInNonVoidFunctionReturnOrThrow(
65                func, func->ReturnTypeAnnotation()->Start(),
66                "A function whose declared type is neither 'void' nor 'any' must return a value.");
67        }
68
69        return returnType;
70    }
71
72    if (func->Declare()) {
73        return GlobalAnyType();
74    }
75
76    if (func->IsArrow() && func->Body()->IsExpression()) {
77        return func->Body()->Check(this);
78    }
79
80    ArenaVector<Type *> returnTypes(Allocator()->Adapter());
81    CollectTypesFromReturnStatements(func->Body(), &returnTypes);
82
83    if (returnTypes.empty()) {
84        return GlobalVoidType();
85    }
86
87    if (returnTypes.size() == 1 && returnTypes[0] == GlobalResolvingReturnType()) {
88        ThrowReturnTypeCircularityError(func);
89    }
90
91    for (auto *it : returnTypes) {
92        if (it == GlobalResolvingReturnType()) {
93            ThrowReturnTypeCircularityError(func);
94        }
95    }
96
97    return CreateUnionType(std::move(returnTypes));
98}
99
100void TSChecker::ThrowReturnTypeCircularityError(ir::ScriptFunction *func)
101{
102    if (func->ReturnTypeAnnotation() != nullptr) {
103        ThrowTypeError("Return type annotation circularly reference itself", func->ReturnTypeAnnotation()->Start());
104    }
105
106    if (func->Id() != nullptr) {
107        ThrowTypeError({func->Id()->AsIdentifier()->Name(),
108                        " implicitly has return type 'any' because it does not have a return type annotation and is "
109                        "referenced directly or indirectly in one of its return expressions."},
110                       func->Id()->Start());
111    }
112
113    ThrowTypeError(
114        "Function implicitly has return type 'any' because it does not have a return type annotation and is "
115        "referenced directly or indirectly in one of its return expressions.",
116        func->Start());
117}
118
119std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool> TSChecker::CheckFunctionIdentifierParameter(
120    ir::Identifier *param)
121{
122    ASSERT(param->Variable());
123    varbinder::Variable *paramVar = param->Variable();
124    bool isOptional = param->IsOptional();
125
126    if (param->TypeAnnotation() == nullptr) {
127        ThrowTypeError({"Parameter ", param->Name(), " implicitly has any type."}, param->Start());
128    }
129
130    if (isOptional) {
131        paramVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
132    }
133
134    param->TypeAnnotation()->Check(this);
135    paramVar->SetTsType(param->TypeAnnotation()->GetType(this));
136    return {paramVar->AsLocalVariable(), nullptr, isOptional};
137}
138
139Type *TSChecker::CreateParameterTypeForArrayAssignmentPattern(ir::ArrayExpression *arrayPattern, Type *inferredType)
140{
141    if (!inferredType->IsObjectType()) {
142        return inferredType;
143    }
144
145    ASSERT(inferredType->AsObjectType()->IsTupleType());
146    TupleType *inferredTuple = inferredType->AsObjectType()->AsTupleType();
147
148    if (inferredTuple->FixedLength() > arrayPattern->Elements().size()) {
149        return inferredType;
150    }
151
152    TupleType *newTuple =
153        inferredTuple->Instantiate(Allocator(), Relation(), GetGlobalTypesHolder())->AsObjectType()->AsTupleType();
154
155    for (uint32_t index = inferredTuple->FixedLength(); index < arrayPattern->Elements().size(); index++) {
156        util::StringView memberIndex = util::Helpers::ToStringView(Allocator(), index);
157        varbinder::LocalVariable *newMember = varbinder::Scope::CreateVar(
158            Allocator(), memberIndex, varbinder::VariableFlags::PROPERTY | varbinder::VariableFlags::OPTIONAL, nullptr);
159        newMember->SetTsType(GlobalAnyType());
160        newTuple->AddProperty(newMember);
161    }
162
163    return newTuple;
164}
165
166Type *TSChecker::CreateParameterTypeForObjectAssignmentPattern(ir::ObjectExpression *objectPattern, Type *inferredType)
167{
168    if (!inferredType->IsObjectType()) {
169        return inferredType;
170    }
171
172    ObjectType *newObject = inferredType->Instantiate(Allocator(), Relation(), GetGlobalTypesHolder())->AsObjectType();
173
174    for (auto *it : objectPattern->Properties()) {
175        if (it->IsRestElement()) {
176            continue;
177        }
178
179        ir::Property *prop = it->AsProperty();
180        varbinder::LocalVariable *foundVar = newObject->GetProperty(prop->Key()->AsIdentifier()->Name(), true);
181
182        if (foundVar != nullptr) {
183            if (prop->Value()->IsAssignmentPattern()) {
184                foundVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
185            }
186
187            continue;
188        }
189
190        ASSERT(prop->Value()->IsAssignmentPattern());
191        ir::AssignmentExpression *assignmentPattern = prop->Value()->AsAssignmentPattern();
192
193        varbinder::LocalVariable *newProp = varbinder::Scope::CreateVar(
194            Allocator(), prop->Key()->AsIdentifier()->Name(),
195            varbinder::VariableFlags::PROPERTY | varbinder::VariableFlags::OPTIONAL, nullptr);
196        newProp->SetTsType(GetBaseTypeOfLiteralType(CheckTypeCached(assignmentPattern->Right())));
197        newObject->AddProperty(newProp);
198    }
199
200    newObject->AddObjectFlag(ObjectFlags::RESOLVED_MEMBERS);
201    return newObject;
202}
203
204using ReturnedVariable = std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool>;
205ReturnedVariable TSChecker::CheckFunctionAssignmentPatternParameter(ir::AssignmentExpression *param)
206{
207    if (param->Left()->IsIdentifier()) {
208        ir::Identifier *paramIdent = param->Left()->AsIdentifier();
209        varbinder::Variable *paramVar = paramIdent->Variable();
210        ASSERT(paramVar);
211
212        if (paramIdent->TypeAnnotation() != nullptr) {
213            paramIdent->TypeAnnotation()->Check(this);
214            Type *paramType = paramIdent->TypeAnnotation()->GetType(this);
215            paramVar->SetTsType(paramType);
216            ElaborateElementwise(paramType, param->Right(), paramIdent->Start());
217            return {paramVar->AsLocalVariable(), nullptr, true};
218        }
219
220        paramVar->SetTsType(GetBaseTypeOfLiteralType(param->Right()->Check(this)));
221        paramVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
222        return {paramVar->AsLocalVariable(), nullptr, true};
223    }
224
225    Type *paramType = nullptr;
226    std::stringstream ss;
227
228    auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE | CheckerStatus::IN_PARAMETER);
229
230    if (param->Left()->IsArrayPattern()) {
231        ir::ArrayExpression *arrayPattern = param->Left()->AsArrayPattern();
232        auto context = ArrayDestructuringContext(
233            {this, arrayPattern, false, true, arrayPattern->TypeAnnotation(), param->Right()});
234        context.Start();
235        paramType = CreateParameterTypeForArrayAssignmentPattern(arrayPattern, context.InferredType());
236        CreatePatternParameterName(param->Left(), ss);
237    } else {
238        ir::ObjectExpression *objectPattern = param->Left()->AsObjectPattern();
239        auto context = ObjectDestructuringContext(
240            {this, objectPattern, false, true, objectPattern->TypeAnnotation(), param->Right()});
241        context.Start();
242        paramType = CreateParameterTypeForObjectAssignmentPattern(objectPattern, context.InferredType());
243        CreatePatternParameterName(param->Left(), ss);
244    }
245
246    util::UString pn(ss.str(), Allocator());
247    varbinder::LocalVariable *patternVar =
248        varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param);
249    patternVar->SetTsType(paramType);
250    patternVar->AddFlag(varbinder::VariableFlags::OPTIONAL);
251    return {patternVar->AsLocalVariable(), nullptr, true};
252}
253
254std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool> TSChecker::CheckFunctionRestParameter(
255    ir::SpreadElement *param, SignatureInfo *signatureInfo)
256{
257    ir::TypeNode *typeAnnotation = nullptr;
258    if (param->Argument() != nullptr) {
259        typeAnnotation = param->Argument()->AsAnnotatedExpression()->TypeAnnotation();
260    }
261
262    Type *restType = Allocator()->New<ArrayType>(GlobalAnyType());
263
264    if (typeAnnotation != nullptr) {
265        typeAnnotation->Check(this);
266        restType = typeAnnotation->GetType(this);
267        if (!restType->IsArrayType()) {
268            ThrowTypeError("A rest parameter must be of an array type", param->Start());
269        }
270    }
271
272    switch (param->Argument()->Type()) {
273        case ir::AstNodeType::IDENTIFIER: {
274            ir::Identifier *restIdent = param->Argument()->AsIdentifier();
275            ASSERT(restIdent->Variable());
276            restIdent->Variable()->SetTsType(restType->AsArrayType()->ElementType());
277            return {nullptr, restIdent->Variable()->AsLocalVariable(), false};
278        }
279        case ir::AstNodeType::OBJECT_PATTERN: {
280            ASSERT(param->Argument()->IsObjectPattern());
281            auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE);
282            auto destructuringContext =
283                ObjectDestructuringContext({this, param->Argument(), false, false, nullptr, nullptr});
284            destructuringContext.SetInferredType(restType);
285            destructuringContext.SetSignatureInfo(signatureInfo);
286            destructuringContext.Start();
287            return {nullptr, nullptr, false};
288        }
289        case ir::AstNodeType::ARRAY_PATTERN: {
290            auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE);
291            auto destructuringContext =
292                ArrayDestructuringContext({this, param->Argument(), false, false, nullptr, nullptr});
293            destructuringContext.SetInferredType(restType);
294            destructuringContext.SetSignatureInfo(signatureInfo);
295            destructuringContext.Start();
296            return {nullptr, nullptr, false};
297        }
298        default: {
299            UNREACHABLE();
300        }
301    }
302}
303
304std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool> TSChecker::CheckFunctionArrayPatternParameter(
305    ir::ArrayExpression *param)
306{
307    std::stringstream ss;
308    CreatePatternParameterName(param, ss);
309    util::UString pn(ss.str(), Allocator());
310    varbinder::LocalVariable *patternVar =
311        varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param);
312
313    if (param->TypeAnnotation() != nullptr) {
314        auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE);
315        auto destructuringContext =
316            ArrayDestructuringContext({this, param->AsArrayPattern(), false, false, param->TypeAnnotation(), nullptr});
317        destructuringContext.Start();
318        patternVar->SetTsType(destructuringContext.InferredType());
319        return {patternVar->AsLocalVariable(), nullptr, false};
320    }
321
322    patternVar->SetTsType(param->CheckPattern(this));
323    return {patternVar->AsLocalVariable(), nullptr, false};
324}
325
326std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool> TSChecker::CheckFunctionObjectPatternParameter(
327    ir::ObjectExpression *param)
328{
329    std::stringstream ss;
330    CreatePatternParameterName(param, ss);
331    util::UString pn(ss.str(), Allocator());
332    varbinder::LocalVariable *patternVar =
333        varbinder::Scope::CreateVar(Allocator(), pn.View(), varbinder::VariableFlags::NONE, param);
334
335    if (param->TypeAnnotation() != nullptr) {
336        auto savedContext = SavedCheckerContext(this, CheckerStatus::FORCE_TUPLE);
337        auto destructuringContext = ObjectDestructuringContext(
338            {this, param->AsObjectPattern(), false, false, param->TypeAnnotation(), nullptr});
339        destructuringContext.Start();
340        patternVar->SetTsType(destructuringContext.InferredType());
341        return {patternVar->AsLocalVariable(), nullptr, false};
342    }
343
344    patternVar->SetTsType(param->CheckPattern(this));
345    return {patternVar->AsLocalVariable(), nullptr, false};
346}
347
348std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool> TSChecker::CheckFunctionParameter(
349    ir::Expression *param, SignatureInfo *signatureInfo)
350{
351    std::tuple<varbinder::LocalVariable *, varbinder::LocalVariable *, bool> result;
352    if (param->TsType() != nullptr) {
353        ASSERT(param->TsType()->Variable());
354        varbinder::Variable *var = param->TsType()->Variable();
355        result = {var->AsLocalVariable(), nullptr, var->HasFlag(varbinder::VariableFlags::OPTIONAL)};
356        return result;
357    }
358
359    bool cache = true;
360    switch (param->Type()) {
361        case ir::AstNodeType::IDENTIFIER: {
362            result = CheckFunctionIdentifierParameter(param->AsIdentifier());
363            break;
364        }
365        case ir::AstNodeType::ASSIGNMENT_PATTERN: {
366            result = CheckFunctionAssignmentPatternParameter(param->AsAssignmentPattern());
367            break;
368        }
369        case ir::AstNodeType::REST_ELEMENT: {
370            result = CheckFunctionRestParameter(param->AsRestElement(), signatureInfo);
371            cache = false;
372            break;
373        }
374        case ir::AstNodeType::ARRAY_PATTERN: {
375            result = CheckFunctionArrayPatternParameter(param->AsArrayPattern());
376            break;
377        }
378        case ir::AstNodeType::OBJECT_PATTERN: {
379            result = CheckFunctionObjectPatternParameter(param->AsObjectPattern());
380            break;
381        }
382        default: {
383            UNREACHABLE();
384        }
385    }
386
387    if (cache) {
388        Type *placeholder = Allocator()->New<ArrayType>(GlobalAnyType());
389        placeholder->SetVariable(std::get<0>(result));
390        param->SetTsType(placeholder);
391    }
392
393    return result;
394}
395
396void TSChecker::CheckFunctionParameterDeclarations(const ArenaVector<ir::Expression *> &params,
397                                                   SignatureInfo *signatureInfo)
398{
399    signatureInfo->restVar = nullptr;
400    signatureInfo->minArgCount = 0;
401
402    for (auto it = params.rbegin(); it != params.rend(); it++) {
403        auto [paramVar, restVar, isOptional] = CheckFunctionParameter(*it, signatureInfo);
404
405        if (restVar != nullptr) {
406            signatureInfo->restVar = restVar;
407            continue;
408        }
409
410        if (paramVar == nullptr) {
411            continue;
412        }
413
414        signatureInfo->params.insert(signatureInfo->params.begin(), paramVar);
415
416        if (!isOptional) {
417            signatureInfo->minArgCount++;
418        }
419    }
420}
421
422bool ShouldCreatePropertyValueName(ir::Expression *propValue)
423{
424    return propValue->IsArrayPattern() || propValue->IsObjectPattern() ||
425           (propValue->IsAssignmentPattern() && (propValue->AsAssignmentPattern()->Left()->IsArrayPattern() ||
426                                                 propValue->AsAssignmentPattern()->Left()->IsObjectPattern()));
427}
428
429void TSChecker::HandlePropertyPatternParameterName(ir::Property *prop, std::stringstream &ss)
430{
431    util::StringView propName;
432    if (prop->Key()->IsIdentifier()) {
433        propName = prop->Key()->AsIdentifier()->Name();
434    } else {
435        switch (prop->Key()->Type()) {
436            case ir::AstNodeType::NUMBER_LITERAL: {
437                propName =
438                    util::Helpers::ToStringView(Allocator(), prop->Key()->AsNumberLiteral()->Number().GetDouble());
439                break;
440            }
441            case ir::AstNodeType::BIGINT_LITERAL: {
442                propName = prop->Key()->AsBigIntLiteral()->Str();
443                break;
444            }
445            case ir::AstNodeType::STRING_LITERAL: {
446                propName = prop->Key()->AsStringLiteral()->Str();
447                break;
448            }
449            default: {
450                UNREACHABLE();
451                break;
452            }
453        }
454    }
455
456    ss << propName;
457
458    if (ShouldCreatePropertyValueName(prop->Value())) {
459        ss << ": ";
460        TSChecker::CreatePatternParameterName(prop->Value(), ss);
461    }
462}
463
464void TSChecker::CreatePatternParameterName(ir::AstNode *node, std::stringstream &ss)
465{
466    switch (node->Type()) {
467        case ir::AstNodeType::IDENTIFIER: {
468            ss << node->AsIdentifier()->Name();
469            break;
470        }
471        case ir::AstNodeType::ARRAY_PATTERN: {
472            ss << "[";
473
474            const auto &elements = node->AsArrayPattern()->Elements();
475            for (auto it = elements.begin(); it != elements.end(); it++) {
476                CreatePatternParameterName(*it, ss);
477                if (std::next(it) != elements.end()) {
478                    ss << ", ";
479                }
480            }
481
482            ss << "]";
483            break;
484        }
485        case ir::AstNodeType::OBJECT_PATTERN: {
486            ss << "{ ";
487
488            const auto &properties = node->AsObjectPattern()->Properties();
489            for (auto it = properties.begin(); it != properties.end(); it++) {
490                CreatePatternParameterName(*it, ss);
491                if (std::next(it) != properties.end()) {
492                    ss << ", ";
493                }
494            }
495
496            ss << " }";
497            break;
498        }
499        case ir::AstNodeType::ASSIGNMENT_PATTERN: {
500            CreatePatternParameterName(node->AsAssignmentPattern()->Left(), ss);
501            break;
502        }
503        case ir::AstNodeType::PROPERTY: {
504            HandlePropertyPatternParameterName(node->AsProperty(), ss);
505            break;
506        }
507        case ir::AstNodeType::REST_ELEMENT: {
508            ss << "...";
509            TSChecker::CreatePatternParameterName(node->AsRestElement()->Argument(), ss);
510            break;
511        }
512        default:
513            break;
514    }
515}
516
517ir::Statement *FindSubsequentFunctionNode(ir::BlockStatement *block, ir::ScriptFunction *node)
518{
519    for (auto it = block->Statements().begin(); it != block->Statements().end(); it++) {
520        if ((*it)->IsFunctionDeclaration() && (*it)->AsFunctionDeclaration()->Function() == node) {
521            return *(++it);
522        }
523    }
524
525    UNREACHABLE();
526    return nullptr;
527}
528
529void TSChecker::ValidateSubsequentNode(const ir::Statement *const subsequentNode, const ir::ScriptFunction *const func)
530{
531    if (!subsequentNode->IsFunctionDeclaration()) {
532        ThrowTypeError("Function implementation is missing or not immediately following the declaration.",
533                       func->Id()->Start());
534    }
535
536    const ir::ScriptFunction *const subsequentFunc = subsequentNode->AsFunctionDeclaration()->Function();
537    if (subsequentFunc->Id()->Name() != func->Id()->Name()) {
538        ThrowTypeError("Function implementation is missing or not immediately following the declaration.",
539                       func->Id()->Start());
540    }
541
542    if (subsequentFunc->Declare() != func->Declare()) {
543        ThrowTypeError("Overload signatures must all be ambient or non-ambient.", func->Id()->Start());
544    }
545}
546
547void TSChecker::CheckOverloadSignatureCompatibility(Signature *bodyCallSignature, Signature *signature)
548{
549    if (bodyCallSignature->ReturnType()->IsVoidType() ||
550        IsTypeAssignableTo(bodyCallSignature->ReturnType(), signature->ReturnType()) ||
551        IsTypeAssignableTo(signature->ReturnType(), bodyCallSignature->ReturnType())) {
552        bodyCallSignature->AssignmentTarget(Relation(), signature);
553
554        if (Relation()->IsTrue()) {
555            return;
556        }
557    }
558
559    ASSERT(signature->Function());
560    ThrowTypeError("This overload signature is not compatible with its implementation signature",
561                   signature->Function()->Id()->Start());
562}
563
564void TSChecker::InferFunctionDeclarationType(const varbinder::FunctionDecl *decl, varbinder::Variable *funcVar)
565{
566    ir::ScriptFunction *bodyDeclaration = decl->Decls().back();
567    if (bodyDeclaration->IsOverload()) {
568        ThrowTypeError("Function implementation is missing or not immediately following the declaration.",
569                       bodyDeclaration->Id()->Start());
570    }
571
572    ObjectDescriptor *descWithOverload = Allocator()->New<ObjectDescriptor>(Allocator());
573    for (auto it = decl->Decls().begin(); it != decl->Decls().end() - 1; it++) {
574        ir::ScriptFunction *func = *it;
575        ASSERT(func->IsOverload() && (*it)->Parent()->Parent()->IsBlockStatement());
576        ir::Statement *subsequentNode = FindSubsequentFunctionNode((*it)->Parent()->Parent()->AsBlockStatement(), func);
577        ASSERT(subsequentNode);
578        ValidateSubsequentNode(subsequentNode, func);
579
580        ScopeContext scopeCtx(this, func->Scope());
581
582        auto *overloadSignatureInfo = Allocator()->New<checker::SignatureInfo>(Allocator());
583        CheckFunctionParameterDeclarations(func->Params(), overloadSignatureInfo);
584
585        Type *returnType = GlobalAnyType();
586
587        if (func->ReturnTypeAnnotation() != nullptr) {
588            func->ReturnTypeAnnotation()->Check(this);
589            returnType = func->ReturnTypeAnnotation()->GetType(this);
590        }
591
592        Signature *overloadSignature = Allocator()->New<checker::Signature>(overloadSignatureInfo, returnType, func);
593        descWithOverload->callSignatures.push_back(overloadSignature);
594    }
595
596    ScopeContext scopeCtx(this, bodyDeclaration->Scope());
597
598    auto *signatureInfo = Allocator()->New<checker::SignatureInfo>(Allocator());
599    CheckFunctionParameterDeclarations(bodyDeclaration->Params(), signatureInfo);
600    auto *bodyCallSignature = Allocator()->New<checker::Signature>(signatureInfo, GlobalResolvingReturnType());
601
602    if (descWithOverload->callSignatures.empty()) {
603        Type *funcType = CreateFunctionTypeWithSignature(bodyCallSignature);
604        funcType->SetVariable(funcVar);
605        funcVar->SetTsType(funcType);
606    }
607
608    bodyCallSignature->SetReturnType(HandleFunctionReturn(bodyDeclaration));
609
610    if (!descWithOverload->callSignatures.empty()) {
611        Type *funcType = Allocator()->New<FunctionType>(descWithOverload);
612        funcType->SetVariable(funcVar);
613        funcVar->SetTsType(funcType);
614
615        for (auto *iter : descWithOverload->callSignatures) {
616            CheckOverloadSignatureCompatibility(bodyCallSignature, iter);
617        }
618    }
619}
620
621void TSChecker::CollectTypesFromReturnStatements(ir::AstNode *parent, ArenaVector<Type *> *returnTypes)
622{
623    parent->Iterate([this, returnTypes](ir::AstNode *childNode) -> void {
624        if (childNode->IsScriptFunction()) {
625            return;
626        }
627
628        if (childNode->IsReturnStatement()) {
629            ir::ReturnStatement *returnStmt = childNode->AsReturnStatement();
630
631            if (returnStmt->Argument() == nullptr) {
632                return;
633            }
634
635            returnTypes->push_back(
636                GetBaseTypeOfLiteralType(CheckTypeCached(childNode->AsReturnStatement()->Argument())));
637        }
638
639        CollectTypesFromReturnStatements(childNode, returnTypes);
640    });
641}
642
643static bool SearchForReturnOrThrow(ir::AstNode *parent)
644{
645    bool found = false;
646
647    parent->Iterate([&found](ir::AstNode *childNode) -> void {
648        if (childNode->IsThrowStatement() || childNode->IsReturnStatement()) {
649            found = true;
650            return;
651        }
652
653        if (childNode->IsScriptFunction()) {
654            return;
655        }
656
657        SearchForReturnOrThrow(childNode);
658    });
659
660    return found;
661}
662
663void TSChecker::CheckAllCodePathsInNonVoidFunctionReturnOrThrow(ir::ScriptFunction *func,
664                                                                lexer::SourcePosition lineInfo, const char *errMsg)
665{
666    if (!SearchForReturnOrThrow(func->Body())) {
667        ThrowTypeError(errMsg, lineInfo);
668    }
669    // NOTE: aszilagyi. this function is not fully implement the TSC one, in the future if we will have a
670    // noImplicitReturn compiler option for TypeScript we should update this function
671}
672
673ArgRange TSChecker::GetArgRange(const ArenaVector<Signature *> &signatures,
674                                ArenaVector<Signature *> *potentialSignatures, uint32_t callArgsSize,
675                                bool *haveSignatureWithRest)
676{
677    uint32_t minArg = UINT32_MAX;
678    uint32_t maxArg = 0;
679
680    for (auto *it : signatures) {
681        if (it->RestVar() != nullptr) {
682            *haveSignatureWithRest = true;
683        }
684
685        if (it->MinArgCount() < minArg) {
686            minArg = it->MinArgCount();
687        }
688
689        if (it->Params().size() > maxArg) {
690            maxArg = it->Params().size();
691        }
692
693        if (callArgsSize >= it->MinArgCount() && (callArgsSize <= it->Params().size() || it->RestVar() != nullptr)) {
694            potentialSignatures->push_back(it);
695        }
696    }
697
698    return {minArg, maxArg};
699}
700
701bool TSChecker::CallMatchesSignature(const ArenaVector<ir::Expression *> &args, Signature *signature, bool throwError)
702{
703    for (size_t index = 0; index < args.size(); index++) {
704        checker::Type *sigArgType = nullptr;
705        bool validateRestArg = false;
706
707        if (index >= signature->Params().size()) {
708            ASSERT(signature->RestVar());
709            validateRestArg = true;
710            sigArgType = signature->RestVar()->TsType();
711        } else {
712            sigArgType = signature->Params()[index]->TsType();
713        }
714
715        if (validateRestArg || !throwError) {
716            checker::Type *callArgType = GetBaseTypeOfLiteralType(args[index]->Check(this));
717            if (IsTypeAssignableTo(callArgType, sigArgType)) {
718                continue;
719            }
720
721            if (throwError) {
722                ThrowTypeError(
723                    {"Argument of type '", callArgType, "' is not assignable to parameter of type '", sigArgType, "'."},
724                    args[index]->Start());
725            }
726            return false;
727        }
728
729        ElaborateElementwise(sigArgType, args[index], args[index]->Start());
730    }
731
732    return true;
733}
734
735Type *TSChecker::ResolveCallOrNewExpression(const ArenaVector<Signature *> &signatures,
736                                            ArenaVector<ir::Expression *> arguments,
737                                            const lexer::SourcePosition &errPos)
738{
739    if (signatures.empty()) {
740        ThrowTypeError("This expression is not callable.", errPos);
741    }
742
743    ArenaVector<checker::Signature *> potentialSignatures(Allocator()->Adapter());
744    bool haveSignatureWithRest = false;
745
746    auto argRange = GetArgRange(signatures, &potentialSignatures, arguments.size(), &haveSignatureWithRest);
747
748    if (potentialSignatures.empty()) {
749        if (haveSignatureWithRest) {
750            ThrowTypeError({"Expected at least ", argRange.first, " arguments, but got ", arguments.size(), "."},
751                           errPos);
752        }
753
754        if (signatures.size() == 1 && argRange.first == argRange.second) {
755            lexer::SourcePosition loc =
756                (argRange.first > arguments.size()) ? errPos : arguments[argRange.second]->Start();
757            ThrowTypeError({"Expected ", argRange.first, " arguments, but got ", arguments.size(), "."}, loc);
758        }
759
760        ThrowTypeError({"Expected ", argRange.first, "-", argRange.second, " arguments, but got ", arguments.size()},
761                       errPos);
762    }
763
764    checker::Type *returnType = nullptr;
765    for (auto *it : potentialSignatures) {
766        if (CallMatchesSignature(arguments, it, potentialSignatures.size() == 1)) {
767            returnType = it->ReturnType();
768            break;
769        }
770    }
771
772    if (returnType == nullptr) {
773        ThrowTypeError("No overload matches this call.", errPos);
774    }
775
776    return returnType;
777}
778}  // namespace ark::es2panda::checker
779