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 *> ¶ms,
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