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