• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "util/helpers.h"
17 #include "varbinder/ETSBinder.h"
18 #include "parser/ETSparser.h"
19 
20 #include "checker/types/ets/etsTupleType.h"
21 #include "checker/types/ets/etsReadonlyType.h"
22 #include "checker/ets/typeRelationContext.h"
23 #include "checker/ETSchecker.h"
24 #include "checker/types/globalTypesHolder.h"
25 
26 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
27 #include "generated/signatures.h"
28 
29 namespace ark::es2panda::checker {
FindVariableInFunctionScope(const util::StringView name,const varbinder::ResolveBindingOptions options)30 varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name,
31                                                              const varbinder::ResolveBindingOptions options)
32 {
33     return Scope()->FindInFunctionScope(name, options).variable;
34 }
35 
FindVariableInClassOrEnclosing(const util::StringView name,const ETSObjectType * classType)36 std::pair<const varbinder::Variable *, const ETSObjectType *> ETSChecker::FindVariableInClassOrEnclosing(
37     const util::StringView name, const ETSObjectType *classType)
38 {
39     const auto searchFlags = PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE |
40                              PropertySearchFlags::SEARCH_IN_INTERFACES;
41     auto *resolved = classType->GetProperty(name, searchFlags);
42     while (classType->EnclosingType() != nullptr && resolved == nullptr) {
43         classType = classType->EnclosingType();
44         resolved = classType->GetProperty(name, searchFlags);
45     }
46 
47     return {resolved, classType};
48 }
49 
FindVariableInGlobal(const ir::Identifier * const identifier,const varbinder::ResolveBindingOptions options)50 varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier,
51                                                       const varbinder::ResolveBindingOptions options)
52 {
53     return Scope()->FindInGlobal(identifier->Name(), options).variable;
54 }
55 
IsVariableStatic(const varbinder::Variable * var)56 bool ETSChecker::IsVariableStatic(const varbinder::Variable *var)
57 {
58     if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
59         return var->TsType()->AsETSFunctionType()->CallSignatures()[0]->HasSignatureFlag(SignatureFlags::STATIC);
60     }
61     return var->HasFlag(varbinder::VariableFlags::STATIC);
62 }
63 
IsVariableGetterSetter(const varbinder::Variable * var)64 bool ETSChecker::IsVariableGetterSetter(const varbinder::Variable *var)
65 {
66     return var->TsTypeOrError() != nullptr && var->TsTypeOrError()->HasTypeFlag(TypeFlag::GETTER_SETTER);
67 }
68 
ThrowError(ir::Identifier * const ident)69 void ETSChecker::ThrowError(ir::Identifier *const ident)
70 {
71     ThrowTypeError({"Unresolved reference ", ident->Name()}, ident->Start());
72 }
73 
WrongContextErrorClassifyByType(ir::Identifier * ident,varbinder::Variable * const resolved)74 void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident, varbinder::Variable *const resolved)
75 {
76     std::string identCategoryName;
77     switch (static_cast<varbinder::VariableFlags>(
78         resolved->Flags() &
79         (varbinder::VariableFlags::CLASS_OR_INTERFACE_OR_ENUM | varbinder::VariableFlags::METHOD))) {
80         case varbinder::VariableFlags::CLASS: {
81             identCategoryName = "Class";
82             break;
83         }
84         case varbinder::VariableFlags::METHOD: {
85             identCategoryName = "Function";
86             break;
87         }
88         case varbinder::VariableFlags::INTERFACE: {
89             identCategoryName = "Interface";
90             break;
91         }
92         case varbinder::VariableFlags::ENUM_LITERAL: {
93             identCategoryName = "Enum";
94             break;
95         }
96         default: {
97             ThrowError(ident);
98         }
99     }
100     ThrowTypeError({identCategoryName.c_str(), " name \"", ident->Name(), "\" used in the wrong context"},
101                    ident->Start());
102 }
103 
NotResolvedError(ir::Identifier * const ident,const varbinder::Variable * classVar,const ETSObjectType * classType)104 void ETSChecker::NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar,
105                                   const ETSObjectType *classType)
106 {
107     if (classVar == nullptr) {
108         ThrowError(ident);
109     }
110 
111     if (IsVariableStatic(classVar)) {
112         ThrowTypeError(
113             {"Static property '", ident->Name(), "' must be accessed through it's class '", classType->Name(), "'"},
114             ident->Start());
115     } else {
116         ThrowTypeError({"Property '", ident->Name(), "' must be accessed through 'this'"}, ident->Start());
117     }
118 }
119 
GetTargetIdentifierAndType(ir::Identifier * const ident)120 std::pair<const ir::Identifier *, ir::TypeNode *> ETSChecker::GetTargetIdentifierAndType(ir::Identifier *const ident)
121 {
122     if (ident->Parent()->IsClassProperty()) {
123         const auto *const classProp = ident->Parent()->AsClassProperty();
124         ASSERT(classProp->Value() && classProp->Value() == ident);
125         return std::make_pair(classProp->Key()->AsIdentifier(), classProp->TypeAnnotation());
126     }
127     const auto *const variableDecl = ident->Parent()->AsVariableDeclarator();
128     ASSERT(variableDecl->Init() && variableDecl->Init() == ident);
129     return std::make_pair(variableDecl->Id()->AsIdentifier(), variableDecl->Id()->AsIdentifier()->TypeAnnotation());
130 }
131 
ExtraCheckForResolvedError(ir::Identifier * const ident)132 void ETSChecker::ExtraCheckForResolvedError(ir::Identifier *const ident)
133 {
134     const auto [class_var, class_type] = FindVariableInClassOrEnclosing(ident->Name(), Context().ContainingClass());
135     auto *parentClass = FindAncestorGivenByType(ident, ir::AstNodeType::CLASS_DEFINITION);
136     if (parentClass != nullptr && parentClass->AsClassDefinition()->IsLocal()) {
137         if (parentClass != class_type->GetDeclNode()) {
138             ThrowTypeError({"Property '", ident->Name(), "' of enclosing class '", class_type->Name(),
139                             "' is not allowed to be captured from the local class '",
140                             parentClass->AsClassDefinition()->Ident()->Name(), "'"},
141                            ident->Start());
142         }
143     }
144     NotResolvedError(ident, class_var, class_type);
145 }
146 
SaveCapturedVariableInLocalClass(varbinder::Variable * const var,ir::Identifier * ident)147 bool ETSChecker::SaveCapturedVariableInLocalClass(varbinder::Variable *const var, ir::Identifier *ident)
148 {
149     const auto &pos = ident->Start();
150 
151     if (!HasStatus(CheckerStatus::IN_LOCAL_CLASS)) {
152         return false;
153     }
154 
155     if (!var->HasFlag(varbinder::VariableFlags::LOCAL)) {
156         return false;
157     }
158 
159     LOG(DEBUG, ES2PANDA) << "Checking variable (line:" << pos.line << "): " << var->Name();
160     auto *scopeIter = Scope();
161     bool inStaticMethod = false;
162 
163     auto captureVariable = [this, var, ident, &scopeIter, &inStaticMethod, &pos]() {
164         if (inStaticMethod) {
165             ThrowTypeError({"Not allowed to capture variable '", var->Name(), "' in static method"}, pos);
166         }
167         if (scopeIter->Node()->AsClassDefinition()->CaptureVariable(var)) {
168             LOG(DEBUG, ES2PANDA) << "  Captured in class:" << scopeIter->Node()->AsClassDefinition()->Ident()->Name();
169         }
170 
171         auto *parent = ident->Parent();
172 
173         if (parent->IsVariableDeclarator()) {
174             parent = parent->Parent()->Parent();
175         }
176 
177         if (!(parent->IsUpdateExpression() ||
178               (parent->IsAssignmentExpression() && parent->AsAssignmentExpression()->Left() == ident)) ||
179             var->Declaration() == nullptr) {
180             return;
181         }
182 
183         if (var->Declaration()->IsParameterDecl()) {
184             LOG(DEBUG, ES2PANDA) << "    - Modified parameter ";
185             scopeIter->Node()->AsClassDefinition()->AddToLocalVariableIsNeeded(var);
186         }
187     };
188 
189     while (scopeIter != var->GetScope()) {
190         if (scopeIter->Node() != nullptr) {
191             if (scopeIter->Node()->IsScriptFunction() && scopeIter->Node()->AsScriptFunction()->IsStatic()) {
192                 inStaticMethod = true;
193             }
194 
195             if (scopeIter->Node()->IsClassDefinition()) {
196                 captureVariable();
197                 return true;
198             }
199         }
200         scopeIter = scopeIter->Parent();
201     }
202 
203     return false;
204 }
205 
SaveCapturedVariable(varbinder::Variable * const var,ir::Identifier * ident)206 void ETSChecker::SaveCapturedVariable(varbinder::Variable *const var, ir::Identifier *ident)
207 {
208     const auto &pos = ident->Start();
209 
210     if (!HasStatus(CheckerStatus::IN_LAMBDA)) {
211         return;
212     }
213 
214     if (var->HasFlag(varbinder::VariableFlags::PROPERTY)) {
215         Context().AddCapturedVar(var, pos);
216         return;
217     }
218 
219     if ((!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::METHOD)) ||
220         Context().ContainingLambda()->IsVarFromSubscope(var)) {
221         return;
222     }
223 
224     const auto *scopeIter = Scope();
225     while (scopeIter != var->GetScope()) {
226         if (scopeIter->IsFunctionScope()) {
227             Context().AddCapturedVar(var, pos);
228             return;
229         }
230         scopeIter = scopeIter->Parent();
231     }
232 }
233 
ResolveIdentifier(ir::Identifier * const ident)234 checker::Type *ETSChecker::ResolveIdentifier(ir::Identifier *const ident)
235 {
236     if (ident->Variable() != nullptr) {
237         auto *const resolved = ident->Variable();
238         SaveCapturedVariable(resolved, ident);
239         return GetTypeOfVariable(resolved);
240     }
241 
242     auto options = ident->Parent()->IsTSTypeAliasDeclaration() ? varbinder::ResolveBindingOptions::TYPE_ALIASES
243                                                                : varbinder::ResolveBindingOptions::ALL_NON_TYPE;
244 
245     auto *resolved = FindVariableInFunctionScope(ident->Name(), options);
246     if (resolved == nullptr) {
247         // If the reference is not found already in the current class, then it is not bound to the class, so we have to
248         // find the reference in the global class first, then in the global scope
249         resolved = FindVariableInGlobal(ident, options);
250     }
251 
252     ident->SetVariable(resolved);
253     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
254     ValidateResolvedIdentifier(ident, resolved);
255 
256     ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start());
257     SaveCapturedVariable(resolved, ident);
258 
259     return GetTypeOfVariable(resolved);
260 }
261 
ApplyBinaryOperatorPromotion(Type * left,Type * right,TypeFlag test,bool const doPromotion)262 std::tuple<Type *, bool> ETSChecker::ApplyBinaryOperatorPromotion(Type *left, Type *right, TypeFlag test,
263                                                                   bool const doPromotion)
264 {
265     Type *const unboxedL = ETSBuiltinTypeAsPrimitiveType(left);
266     Type *const unboxedR = ETSBuiltinTypeAsPrimitiveType(right);
267 
268     if (unboxedL == nullptr || unboxedR == nullptr) {
269         return {nullptr, false};
270     }
271 
272     if (!unboxedL->HasTypeFlag(test) || !unboxedR->HasTypeFlag(test)) {
273         return {nullptr, false};
274     }
275 
276     bool const bothConst = unboxedL->HasTypeFlag(TypeFlag::CONSTANT) && unboxedR->HasTypeFlag(TypeFlag::CONSTANT);
277 
278     // extract just to reduce nested levels
279     auto const numericPromotion = [this, unboxedL, unboxedR, bothConst]() -> std::tuple<checker::Type *, bool> {
280         if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) {
281             return {GlobalDoubleType(), bothConst};
282         }
283 
284         if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) {
285             return {GlobalFloatType(), bothConst};
286         }
287 
288         if (unboxedL->IsLongType() || unboxedR->IsLongType()) {
289             return {GlobalLongType(), bothConst};
290         }
291 
292         if (unboxedL->IsCharType() && unboxedR->IsCharType()) {
293             return {GlobalCharType(), bothConst};
294         }
295 
296         return {GlobalIntType(), bothConst};
297     };
298 
299     if (doPromotion) {
300         if (unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) &&
301             unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
302             return numericPromotion();
303         }
304 
305         if (IsTypeIdenticalTo(unboxedL, unboxedR)) {
306             return {unboxedL, bothConst};
307         }
308     }
309 
310     return {unboxedR, bothConst};
311 }
312 
CheckLeftRightType(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)313 std::optional<checker::Type *> CheckLeftRightType(checker::ETSChecker *checker, checker::Type *unboxedL,
314                                                   checker::Type *unboxedR)
315 {
316     if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) {
317         return checker->GlobalDoubleType();
318     }
319     if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) {
320         return checker->GlobalFloatType();
321     }
322     if (unboxedL->IsLongType() || unboxedR->IsLongType()) {
323         return checker->GlobalLongType();
324     }
325     if (unboxedL->IsIntType() || unboxedR->IsIntType() || unboxedL->IsCharType() || unboxedR->IsCharType()) {
326         return checker->GlobalIntType();
327     }
328     if (unboxedL->IsShortType() || unboxedR->IsShortType()) {
329         return checker->GlobalShortType();
330     }
331     if (unboxedL->IsByteType() || unboxedR->IsByteType()) {
332         return checker->GlobalByteType();
333     }
334     return std::nullopt;
335 }
ApplyConditionalOperatorPromotion(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)336 checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxedL,
337                                                              checker::Type *unboxedR)
338 {
339     if ((unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedL->IsIntType()) ||
340         (unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->IsIntType())) {
341         int value = unboxedL->IsIntType() ? unboxedL->AsIntType()->GetValue() : unboxedR->AsIntType()->GetValue();
342         checker::Type *otherType = !unboxedL->IsIntType() ? unboxedL : unboxedR;
343 
344         switch (checker::ETSChecker::ETSType(otherType)) {
345             case checker::TypeFlag::BYTE:
346             case checker::TypeFlag::CHAR: {
347                 if (value <= static_cast<int>(std::numeric_limits<char>::max()) &&
348                     value >= static_cast<int>(std::numeric_limits<char>::min())) {
349                     return checker->GetNonConstantTypeFromPrimitiveType(otherType);
350                 }
351                 break;
352             }
353             case checker::TypeFlag::SHORT: {
354                 if (value <= std::numeric_limits<int16_t>::max() && value >= std::numeric_limits<int16_t>::min()) {
355                     return checker->GlobalShortType();
356                 }
357                 break;
358             }
359             default: {
360                 return otherType;
361             }
362         }
363         return checker->GlobalIntType();
364     }
365 
366     auto checkLeftRight = CheckLeftRightType(checker, unboxedL, unboxedR);
367     if (checkLeftRight.has_value()) {
368         return checkLeftRight.value();
369     }
370     UNREACHABLE();
371 }
372 
ApplyUnaryOperatorPromotion(Type * type,const bool createConst,const bool doPromotion,const bool isCondExpr)373 Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool createConst, const bool doPromotion,
374                                               const bool isCondExpr)
375 {
376     Type *unboxedType = isCondExpr ? ETSBuiltinTypeAsConditionalType(type) : ETSBuiltinTypeAsPrimitiveType(type);
377 
378     if (unboxedType == nullptr) {
379         return nullptr;
380     }
381     if (doPromotion) {
382         switch (ETSType(unboxedType)) {
383             case TypeFlag::BYTE:
384             case TypeFlag::SHORT:
385             case TypeFlag::CHAR: {
386                 if (!createConst) {
387                     return GlobalIntType();
388                 }
389 
390                 return CreateIntTypeFromType(unboxedType);
391             }
392             default: {
393                 break;
394             }
395         }
396     }
397     return unboxedType;
398 }
399 
IsNullLikeOrVoidExpression(const ir::Expression * expr) const400 bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const
401 {
402     return expr->TsType()->DefinitelyETSNullish() || expr->TsType()->IsETSVoidType();
403 }
404 
IsResolvedAndValue(const ir::Expression * expr,Type * type) const405 std::tuple<bool, bool> ETSChecker::IsResolvedAndValue(const ir::Expression *expr, Type *type) const
406 {
407     auto [isResolve, isValue] =
408         IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : type->ResolveConditionExpr();
409 
410     const Type *tsType = expr->TsType();
411     if (tsType->DefinitelyNotETSNullish() && !type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) {
412         isResolve = true;
413         isValue = true;
414     }
415     return std::make_tuple(isResolve, isValue);
416 }
417 
HandleBooleanLogicalOperatorsExtended(Type * leftType,Type * rightType,ir::BinaryExpression * expr)418 Type *ETSChecker::HandleBooleanLogicalOperatorsExtended(Type *leftType, Type *rightType, ir::BinaryExpression *expr)
419 {
420     ASSERT(leftType->IsConditionalExprType() && rightType->IsConditionalExprType());
421 
422     auto [resolveLeft, leftValue] = IsResolvedAndValue(expr->Left(), leftType);
423     auto [resolveRight, rightValue] = IsResolvedAndValue(expr->Right(), rightType);
424 
425     if (!resolveLeft && !resolveRight && IsTypeIdenticalTo(leftType, rightType)) {
426         return leftType;
427     }
428 
429     switch (expr->OperatorType()) {
430         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
431             if (leftValue) {
432                 expr->SetResult(expr->Left());
433                 return leftType->IsETSBooleanType() ? CreateETSBooleanType(true) : leftType;
434             }
435 
436             expr->SetResult(expr->Right());
437             return rightType->IsETSBooleanType() && resolveRight ? CreateETSBooleanType(rightValue) : rightType;
438         }
439         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
440             if (leftValue) {
441                 expr->SetResult(expr->Right());
442                 return rightType->IsETSBooleanType() && resolveRight ? CreateETSBooleanType(rightValue) : rightType;
443             }
444 
445             expr->SetResult(expr->Left());
446             return leftType->IsETSBooleanType() ? CreateETSBooleanType(false) : leftType;
447         }
448         default: {
449             break;
450         }
451     }
452 
453     UNREACHABLE();
454 }
455 
HandleBooleanLogicalOperators(Type * leftType,Type * rightType,lexer::TokenType tokenType)456 Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType)
457 {
458     using UType = typename ETSBooleanType::UType;
459     ASSERT(leftType->IsETSBooleanType() && rightType->IsETSBooleanType());
460 
461     if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
462         return GlobalETSBooleanType();
463     }
464 
465     UType leftValue = leftType->AsETSBooleanType()->GetValue();
466     UType rightValue = rightType->AsETSBooleanType()->GetValue();
467 
468     switch (tokenType) {
469         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
470             return CreateETSBooleanType(leftValue ^ rightValue);
471         }
472         case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
473             return CreateETSBooleanType((static_cast<uint8_t>(leftValue) & static_cast<uint8_t>(rightValue)) != 0);
474         }
475         case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
476             return CreateETSBooleanType((static_cast<uint8_t>(leftValue) | static_cast<uint8_t>(rightValue)) != 0);
477         }
478         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
479             return CreateETSBooleanType(leftValue || rightValue);
480         }
481         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
482             return CreateETSBooleanType(leftValue && rightValue);
483         }
484         default: {
485             break;
486         }
487     }
488 
489     UNREACHABLE();
490     return nullptr;
491 }
492 
ResolveReturnStatement(checker::Type * funcReturnType,checker::Type * argumentType,ir::ScriptFunction * containingFunc,ir::ReturnStatement * st)493 void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType,
494                                         ir::ScriptFunction *containingFunc, ir::ReturnStatement *st)
495 {
496     if (funcReturnType->IsETSReferenceType() || argumentType->IsETSReferenceType()) {
497         // function return type should be of reference (object) type
498         Relation()->SetFlags(checker::TypeRelationFlag::NONE);
499 
500         if (!argumentType->IsETSReferenceType()) {
501             argumentType = PrimitiveTypeAsETSBuiltinType(argumentType);
502             if (argumentType == nullptr) {
503                 ThrowTypeError("Invalid return statement expression", st->Argument()->Start());
504             }
505             st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argumentType));
506         }
507 
508         if (!funcReturnType->IsETSReferenceType()) {
509             funcReturnType = PrimitiveTypeAsETSBuiltinType(funcReturnType);
510             if (funcReturnType == nullptr) {
511                 ThrowTypeError("Invalid return function expression", st->Start());
512             }
513         }
514         funcReturnType = CreateETSUnionType({funcReturnType, argumentType});
515         containingFunc->Signature()->SetReturnType(funcReturnType);
516         containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
517     } else if (funcReturnType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE_RETURN) &&
518                argumentType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE_RETURN)) {
519         // function return type is of primitive type (including enums):
520         Relation()->SetFlags(checker::TypeRelationFlag::DIRECT_RETURN |
521                              checker::TypeRelationFlag::IN_ASSIGNMENT_CONTEXT |
522                              checker::TypeRelationFlag::ASSIGNMENT_CONTEXT);
523         if (Relation()->IsAssignableTo(funcReturnType, argumentType)) {
524             funcReturnType = argumentType;
525             containingFunc->Signature()->SetReturnType(funcReturnType);
526             containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
527         } else if (!Relation()->IsAssignableTo(argumentType, funcReturnType)) {
528             const Type *targetType = TryGettingFunctionTypeFromInvokeFunction(funcReturnType);
529             const Type *sourceType = TryGettingFunctionTypeFromInvokeFunction(argumentType);
530 
531             Relation()->RaiseError(
532                 {"Function cannot have different primitive return types, found '", targetType, "', '", sourceType, "'"},
533                 st->Argument()->Start());
534         }
535     } else {
536         ThrowTypeError("Invalid return statement type(s).", st->Start());
537     }
538 }
539 
CheckArrayElements(ir::ArrayExpression * init)540 checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init)
541 {
542     ArenaVector<checker::Type *> elementTypes(Allocator()->Adapter());
543     for (auto e : init->AsArrayExpression()->Elements()) {
544         elementTypes.push_back(e->Check(this));
545     }
546 
547     if (elementTypes.empty()) {
548         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
549         return Allocator()->New<ETSArrayType>(GlobalETSObjectType());
550     }
551     auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); };
552     auto const elementType = std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric)
553                                  ? GlobalDoubleType()
554                                  : CreateETSUnionType(std::move(elementTypes));
555 
556     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
557     return Allocator()->New<ETSArrayType>(elementType);
558 }
559 
InferAliasLambdaType(ir::TypeNode * localTypeAnnotation,ir::ArrowFunctionExpression * init)560 void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init)
561 {
562     ASSERT(localTypeAnnotation != nullptr);
563 
564     if (localTypeAnnotation->IsETSTypeReference()) {
565         bool isAnnotationTypeAlias = true;
566         while (localTypeAnnotation->IsETSTypeReference() && isAnnotationTypeAlias) {
567             auto *nodeVar = localTypeAnnotation->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Variable();
568             if (nodeVar == nullptr) {
569                 break;
570             }
571 
572             auto *node = nodeVar->Declaration()->Node();
573 
574             isAnnotationTypeAlias = node->IsTSTypeAliasDeclaration();
575             if (isAnnotationTypeAlias) {
576                 localTypeAnnotation = node->AsTSTypeAliasDeclaration()->TypeAnnotation();
577             }
578         }
579     }
580 
581     if (localTypeAnnotation->IsETSFunctionType()) {
582         auto *const arrowFuncExpr = init;
583         ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
584         if (lambda->Params().size() == localTypeAnnotation->AsETSFunctionType()->Params().size() &&
585             NeedTypeInference(lambda)) {
586             InferTypesForLambda(lambda, localTypeAnnotation->AsETSFunctionType());
587         }
588     }
589 }
590 
FixOptionalVariableType(varbinder::Variable * const bindingVar,ir::ModifierFlags flags)591 checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags)
592 {
593     if ((flags & ir::ModifierFlags::OPTIONAL) != 0) {
594         auto type = bindingVar->TsTypeOrError();
595         if (type->IsETSUnionType()) {
596             auto constituentTypes = type->AsETSUnionType()->ConstituentTypes();
597             constituentTypes.push_back(GlobalETSUndefinedType());
598             type = CreateETSUnionType(std::move(constituentTypes));
599         } else {
600             type = CreateETSUnionType({GlobalETSUndefinedType(), type});
601         }
602         bindingVar->SetTsType(type);
603     }
604     return bindingVar->TsTypeOrError();
605 }
606 
PreferredObjectTypeFromAnnotation(checker::Type * annotationType)607 checker::Type *PreferredObjectTypeFromAnnotation(checker::Type *annotationType)
608 {
609     if (!annotationType->IsETSUnionType()) {
610         return annotationType;
611     }
612 
613     checker::Type *resolvedType = nullptr;
614     for (auto constituentType : annotationType->AsETSUnionType()->ConstituentTypes()) {
615         if (constituentType->IsETSObjectType()) {
616             if (resolvedType != nullptr) {
617                 return nullptr;
618             }
619             resolvedType = constituentType;
620         }
621     }
622 
623     return resolvedType;
624 }
625 
CheckInit(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,checker::Type * annotationType,varbinder::Variable * const bindingVar)626 void ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init,
627                            checker::Type *annotationType, varbinder::Variable *const bindingVar)
628 {
629     if (typeAnnotation == nullptr) {
630         if (init->IsArrayExpression()) {
631             annotationType = CheckArrayElements(init->AsArrayExpression());
632             bindingVar->SetTsType(annotationType);
633         }
634 
635         if (init->IsObjectExpression()) {
636             ThrowTypeError(
637                 {"Cannot infer type for ", ident->Name(), " because class composite needs an explicit target type"},
638                 ident->Start());
639         }
640     }
641 
642     if (init->IsMemberExpression() && init->AsMemberExpression()->Object()->IsObjectExpression()) {
643         ThrowTypeError({"Class composite must be constructed separately before referring their members."},
644                        ident->Start());
645     }
646 
647     if ((init->IsMemberExpression()) && (annotationType != nullptr)) {
648         SetArrayPreferredTypeForNestedMemberExpressions(init->AsMemberExpression(), annotationType);
649     }
650 
651     if (init->IsArrayExpression() && (annotationType != nullptr) && annotationType->IsETSArrayType()) {
652         if (annotationType->IsETSTupleType()) {
653             ValidateTupleMinElementSize(init->AsArrayExpression(), annotationType->AsETSTupleType());
654         }
655 
656         init->AsArrayExpression()->SetPreferredType(annotationType);
657     }
658 
659     if (init->IsObjectExpression()) {
660         init->AsObjectExpression()->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType));
661     }
662 
663     if (typeAnnotation != nullptr && init->IsArrowFunctionExpression()) {
664         InferAliasLambdaType(typeAnnotation, init->AsArrowFunctionExpression());
665     }
666 }
CheckEnumType(ir::Expression * init,checker::Type * initType,const util::StringView & varName)667 void ETSChecker::CheckEnumType(ir::Expression *init, checker::Type *initType, const util::StringView &varName)
668 {
669     if (initType->IsETSObjectType() && initType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ENUM) &&
670         !init->IsMemberExpression()) {
671         ThrowTypeError({"Cannot assign type '", initType->AsETSObjectType()->Name(), "' for variable ", varName, "."},
672                        init->Start());
673     }
674 }
675 
CheckVariableDeclaration(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,ir::ModifierFlags const flags)676 checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation,
677                                                     ir::Expression *init, ir::ModifierFlags const flags)
678 {
679     const util::StringView &varName = ident->Name();
680     ASSERT(ident->Variable());
681     varbinder::Variable *const bindingVar = ident->Variable();
682     checker::Type *annotationType = nullptr;
683 
684     const bool isConst = (flags & ir::ModifierFlags::CONST) != 0;
685     const bool isReadonly = (flags & ir::ModifierFlags::READONLY) != 0;
686     const bool isStatic = (flags & ir::ModifierFlags::STATIC) != 0;
687 
688     if (typeAnnotation != nullptr) {
689         annotationType = typeAnnotation->GetType(this);
690         bindingVar->SetTsType(annotationType);
691     }
692 
693     if (init == nullptr) {
694         return FixOptionalVariableType(bindingVar, flags);
695     }
696 
697     CheckInit(ident, typeAnnotation, init, annotationType, bindingVar);
698 
699     checker::Type *initType = init->Check(this);
700 
701     if (initType == nullptr) {
702         ThrowTypeError("Cannot get the expression type", init->Start());
703     }
704 
705     bool isUnionFunction = false;
706 
707     if (typeAnnotation == nullptr && initType->IsETSFunctionType()) {
708         if (initType->AsETSFunctionType()->CallSignatures().size() == 1) {
709             annotationType =
710                 FunctionTypeToFunctionalInterfaceType(initType->AsETSFunctionType()->CallSignatures().front());
711         } else {
712             ArenaVector<Type *> types(Allocator()->Adapter());
713 
714             for (auto &signature : initType->AsETSFunctionType()->CallSignatures()) {
715                 types.push_back(FunctionTypeToFunctionalInterfaceType(signature));
716             }
717 
718             annotationType = CreateETSUnionType(std::move(types));
719             isUnionFunction = true;
720         }
721         bindingVar->SetTsType(annotationType);
722     }
723     if (annotationType != nullptr) {
724         CheckAnnotationTypeForVariableDeclaration(annotationType, isUnionFunction, init, initType);
725 
726         // Note(lujiahui): It should be checked if the readonly function parameter and readonly number[] parameters
727         // are assigned with CONSTANT, which would not be correct. (After feature supported)
728         if ((isConst || (isReadonly && isStatic)) &&
729             ((initType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && annotationType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) ||
730              (initType->IsETSStringType() && annotationType->IsETSStringType()))) {
731             bindingVar->SetTsType(init->TsType());
732         }
733         return FixOptionalVariableType(bindingVar, flags);
734     }
735 
736     CheckEnumType(init, initType, varName);
737 
738     (isConst || (isReadonly && isStatic)) ? bindingVar->SetTsType(initType)
739                                           : bindingVar->SetTsType(GetNonConstantTypeFromPrimitiveType(initType));
740 
741     return FixOptionalVariableType(bindingVar, flags);
742 }
743 
CheckAnnotationTypeForVariableDeclaration(checker::Type * annotationType,bool isUnionFunction,ir::Expression * init,checker::Type * initType)744 void ETSChecker::CheckAnnotationTypeForVariableDeclaration(checker::Type *annotationType, bool isUnionFunction,
745                                                            ir::Expression *init, checker::Type *initType)
746 {
747     Type *sourceType = TryGettingFunctionTypeFromInvokeFunction(initType);
748 
749     if (!isUnionFunction && annotationType->IsETSUnionType()) {
750         for (auto it : annotationType->AsETSUnionType()->ConstituentTypes()) {
751             if (it->IsETSFunctionType() ||
752                 (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) {
753                 isUnionFunction = true;
754                 break;
755             }
756         }
757     }
758 
759     if (isUnionFunction) {
760         // clang-format off
761         if (!AssignmentContext(Relation(), init, initType, annotationType, init->Start(), {},
762                                TypeRelationFlag::NO_THROW).IsAssignable()) {
763             ThrowTypeError({"Type '", sourceType, "' cannot be assigned to type '", annotationType, "'"},
764                            init->Start());
765         }
766         // clang-format on
767     } else {
768         AssignmentContext(Relation(), init, initType, annotationType, init->Start(),
769                           {"Type '", sourceType, "' cannot be assigned to type '",
770                            TryGettingFunctionTypeFromInvokeFunction(annotationType), "'"});
771     }
772 }
773 
774 //==============================================================================//
775 // Smart cast support
776 //==============================================================================//
777 
ResolveSmartType(checker::Type * sourceType,checker::Type * targetType)778 checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType)
779 {
780     //  For left-hand variable of primitive type leave it as is.
781     if (targetType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE_RETURN)) {
782         return targetType;
783     }
784 
785     //  For left-hand variable of tuple type leave it as is.
786     if (targetType->IsETSTupleType()) {
787         return targetType;
788     }
789 
790     //  For left-hand variable of builtin type leave it as is.
791     if (targetType->IsETSObjectType() && targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) {
792         return targetType;
793     }
794 
795     //  For the Function source or target types leave the target type as is
796     //  until we will be able to create the functional interface type from the source.
797     if (targetType->HasTypeFlag(TypeFlag::FUNCTION) || sourceType->HasTypeFlag(TypeFlag::FUNCTION)) {
798         return targetType;
799     }
800 
801     // Nothing to do with identical types:
802     auto *nonConstSourceType = !sourceType->IsConstantType() ? sourceType : sourceType->Clone(this);
803     nonConstSourceType->RemoveTypeFlag(TypeFlag::CONSTANT);
804 
805     auto *nonConstTargetType = !targetType->IsConstantType() ? targetType : targetType->Clone(this);
806     nonConstTargetType->RemoveTypeFlag(TypeFlag::CONSTANT);
807 
808     if (Relation()->IsIdenticalTo(nonConstSourceType, nonConstTargetType) ||
809         Relation()->IsIdenticalTo(GlobalBuiltinJSValueType(), nonConstTargetType)) {
810         return targetType;
811     }
812 
813     //  For type parameter, null or undefined source type return it as is.
814     if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) {
815         return sourceType;
816     }
817 
818     //  In case of Union left-hand type we have to select the proper type from the Union
819     //  NOTE: it always exists at this point!
820     if (targetType->IsETSUnionType()) {
821         sourceType = targetType->AsETSUnionType()->GetAssignableType(this, sourceType);
822         ASSERT(sourceType != nullptr);
823         return sourceType;
824     }
825 
826     //  If source is reference type, set it as the current and use it for identifier smart cast
827     if (sourceType->IsETSReferenceType()) {
828         return sourceType;
829     }
830 
831     //  For right-hand variable of primitive type apply boxing conversion (case: 'let x: Object = 5', then x => Int).
832     if (sourceType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && !sourceType->IsETSVoidType() &&
833         targetType->IsETSObjectType()) {
834         return PrimitiveTypeAsETSBuiltinType(sourceType);
835     }
836 
837     //  NOTE - it seems that all the other possible cases are assignments like:
838     //  'Object = ObjectLiteral' or smth similar ???
839     //  thus for such cases also leave the target type as is.
840     //  Possible errors in tests should clarify this hypothesis sooner or later :)
841     return targetType;
842 }
843 
844 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestNullishCondition(Type * testedType,Type * actualType,bool const strict)845 std::pair<Type *, Type *> ETSChecker::CheckTestNullishCondition(Type *testedType, Type *actualType, bool const strict)
846 {
847     if (!strict) {
848         return RemoveNullishTypes(actualType);
849     }
850 
851     if (testedType->IsETSNullType()) {
852         return {GlobalETSNullType(), RemoveNullType(actualType)};
853     }
854 
855     if (testedType->IsETSUndefinedType()) {
856         return {GlobalETSUndefinedType(), RemoveUndefinedType(actualType)};
857     }
858 
859     return {GlobalETSNullishType(), GetNonNullishType(actualType)};
860 }
861 
862 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSArrayType * testedType,Type * actualType)863 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSArrayType *testedType, Type *actualType)
864 {
865     if (actualType->IsETSUnionType()) {
866         return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
867     }
868 
869     // Both testing and actual (smart) types are arrays. Set types according to their relation.
870     // NOTE: probably the rules of type extraction should be modified later on!
871     if (actualType->IsETSArrayType()) {
872         auto *const arrayType = actualType->AsETSArrayType();
873 
874         if (Relation()->IsIdenticalTo(arrayType, testedType) ||
875             arrayType->AssemblerName() == testedType->AssemblerName()) {
876             return {testedType, GetGlobalTypesHolder()->GlobalNeverType()};
877         }
878 
879         if (Relation()->IsSupertypeOf(arrayType, testedType)) {
880             return {testedType, actualType};
881         }
882 
883         if (Relation()->IsSupertypeOf(testedType, arrayType)) {
884             return {testedType, actualType};
885         }
886     } else if (actualType->IsETSObjectType() && actualType->AsETSObjectType()->IsGlobalETSObjectType()) {
887         return {testedType, actualType};
888     }
889 
890     return {GetGlobalTypesHolder()->GlobalNeverType(), actualType};
891 }
892 
893 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSObjectType * testedType,Type * actualType,bool const strict)894 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType,
895                                                                bool const strict)
896 {
897     if (actualType->IsETSUnionType()) {
898         return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
899     }
900 
901     // Both testing and actual (smart) types are objects. Set types according to their relation.
902     // NOTE: probably the rules of type extraction should be modified later on!
903     if (actualType->IsETSObjectType()) {
904         auto *const objectType = actualType->AsETSObjectType();
905 
906         if (Relation()->IsIdenticalTo(objectType, testedType) ||
907             objectType->AssemblerName() == testedType->AssemblerName()) {
908             return {testedType, strict ? GetGlobalTypesHolder()->GlobalNeverType() : actualType};
909         }
910 
911         if (Relation()->IsSupertypeOf(objectType, testedType)) {
912             return {testedType, actualType};
913         }
914 
915         if (Relation()->IsSupertypeOf(testedType, objectType)) {
916             return {testedType, actualType};
917         }
918 
919         return {GetGlobalTypesHolder()->GlobalNeverType(), actualType};
920     }
921 
922     // NOTE: other cases (for example with functional types) will be implemented later on
923     return {testedType, actualType};
924 }
925 
926 static constexpr std::size_t const VARIABLE_POSITION = 0UL;
927 static constexpr std::size_t const CONSEQUENT_TYPE_POSITION = 1UL;
928 static constexpr std::size_t const ALTERNATE_TYPE_POSITION = 2UL;
929 
CheckTestSmartCastCondition(lexer::TokenType operatorType)930 void CheckerContext::CheckTestSmartCastCondition(lexer::TokenType operatorType)
931 {
932     if (operatorType != lexer::TokenType::EOS && operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_AND &&
933         operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
934         return;
935     }
936 
937     auto types = ResolveSmartCastTypes();
938 
939     if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
940         if (types.has_value()) {
941             auto const &variable = std::get<VARIABLE_POSITION>(*types);
942             //  NOTE: now we support only cases like 'if (x != null && y == null)' but don't support different type
943             //  checks for a single variable (like 'if (x != null && x instanceof string)'), because it seems that
944             //  it doesn't make much sense.
945             //  Can be implemented later on if the need arises.
946             if (auto [_, inserted] =
947                     testSmartCasts_.emplace(variable, std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types),
948                                                                      std::get<ALTERNATE_TYPE_POSITION>(*types)));
949                 !inserted) {
950                 testSmartCasts_[variable] = {nullptr, nullptr};
951             }
952         }
953         //  Clear alternate types, because now they become indefinite
954         for (auto &smartCast : testSmartCasts_) {
955             smartCast.second.second = nullptr;
956         }
957     } else if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
958         if (bool const cleanConsequent = types.has_value() ? CheckTestOrSmartCastCondition(*types) : true;
959             cleanConsequent) {
960             //  Clear consequent types, because now they become indefinite
961             for (auto &smartCast : testSmartCasts_) {
962                 smartCast.second.first = nullptr;
963             }
964         }
965     } else if (types.has_value()) {
966         testSmartCasts_.emplace(
967             std::get<VARIABLE_POSITION>(*types),
968             std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types), std::get<ALTERNATE_TYPE_POSITION>(*types)));
969     }
970 
971     testCondition_ = {};
972     operatorType_ = operatorType;
973 }
974 
ResolveSmartCastTypes()975 std::optional<SmartCastTuple> CheckerContext::ResolveSmartCastTypes()
976 {
977     if (testCondition_.variable == nullptr) {
978         return std::nullopt;
979     }
980 
981     // Exclude processing of global variables and those captured in lambdas and modified there
982     auto const *const variableScope = testCondition_.variable->GetScope();
983     auto const topLevelVariable =
984         variableScope != nullptr ? variableScope->IsGlobalScope() ||
985                                        (variableScope->Parent() != nullptr && variableScope->Parent()->IsGlobalScope())
986                                  : false;
987     if (topLevelVariable) {
988         return std::nullopt;
989     }
990 
991     ASSERT(testCondition_.testedType != nullptr);
992     // NOTE: functional types are not supported now
993     if (!testCondition_.testedType->IsETSReferenceType() ||
994         testCondition_.testedType->HasTypeFlag(TypeFlag::FUNCTION)) {
995         return std::nullopt;
996     }
997 
998     auto *smartType = GetSmartCast(testCondition_.variable);
999     if (smartType == nullptr) {
1000         smartType = testCondition_.variable->TsType();
1001     }
1002 
1003     auto *const checker = parent_->AsETSChecker();
1004     Type *consequentType = nullptr;
1005     Type *alternateType = nullptr;
1006 
1007     if (testCondition_.testedType->DefinitelyETSNullish()) {
1008         // In case of testing for 'null' and/or 'undefined' remove corresponding null-like types.
1009         std::tie(consequentType, alternateType) =
1010             checker->CheckTestNullishCondition(testCondition_.testedType, smartType, testCondition_.strict);
1011     } else {
1012         if (testCondition_.testedType->IsETSObjectType()) {
1013             auto *const testedType = testCondition_.testedType->AsETSObjectType();
1014             std::tie(consequentType, alternateType) =
1015                 checker->CheckTestObjectCondition(testedType, smartType, testCondition_.strict);
1016         } else if (testCondition_.testedType->IsETSArrayType()) {
1017             auto *const testedType = testCondition_.testedType->AsETSArrayType();
1018             std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType);
1019         } else if (testCondition_.testedType->IsETSUnionType()) {
1020             //  NOTE: now we don't support 'instanceof' operation for union types?
1021             UNREACHABLE();
1022         } else {
1023             // NOTE: it seems that no more cases are possible here! :)
1024             UNREACHABLE();
1025         }
1026     }
1027 
1028     return !testCondition_.negate
1029                ? std::make_optional(std::make_tuple(testCondition_.variable, consequentType, alternateType))
1030                : std::make_optional(std::make_tuple(testCondition_.variable, alternateType, consequentType));
1031 }
CheckVoidAnnotation(const ir::ETSPrimitiveType * typeAnnotation)1032 void ETSChecker::CheckVoidAnnotation(const ir::ETSPrimitiveType *typeAnnotation)
1033 {
1034     // Void annotation is valid only when used as 'return type' , 'type parameter instantiation', 'default type'.
1035     if (typeAnnotation->GetPrimitiveType() != ir::PrimitiveType::VOID) {
1036         return;
1037     }
1038 
1039     auto parent = typeAnnotation->Parent();
1040     if (parent->IsScriptFunction() && parent->AsScriptFunction()->ReturnTypeAnnotation() == typeAnnotation) {
1041         return;
1042     }
1043     if (parent->IsETSFunctionType() && parent->AsETSFunctionType()->ReturnType() == typeAnnotation) {
1044         return;
1045     }
1046     if (parent->IsTSTypeParameterInstantiation() || parent->IsTSTypeParameter()) {
1047         return;
1048     }
1049     ThrowTypeError({"'void' used as type annotation."}, typeAnnotation->Start());
1050 }
ApplySmartCast(varbinder::Variable const * const variable,checker::Type * const smartType)1051 void ETSChecker::ApplySmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept
1052 {
1053     ASSERT(variable != nullptr);
1054     if (smartType != nullptr) {
1055         auto *variableType = variable->TsType();
1056 
1057         if (Relation()->IsIdenticalTo(variableType, smartType)) {
1058             Context().RemoveSmartCast(variable);
1059         } else {
1060             Context().SetSmartCast(variable, smartType);
1061         }
1062     }
1063 }
1064 
CheckTestOrSmartCastCondition(SmartCastTuple const & types)1065 bool CheckerContext::CheckTestOrSmartCastCondition(SmartCastTuple const &types)
1066 {
1067     auto *const &variable = std::get<VARIABLE_POSITION>(types);
1068     auto *const &consequentTypeNew = std::get<CONSEQUENT_TYPE_POSITION>(types);
1069     auto *const &alternateTypeNew = std::get<ALTERNATE_TYPE_POSITION>(types);
1070 
1071     if (auto const it = testSmartCasts_.find(variable); it != testSmartCasts_.end()) {
1072         auto *const consequentTypeOld = it->second.first;
1073         if (consequentTypeOld == nullptr) {
1074             return true;
1075         }
1076 
1077         if (consequentTypeNew != nullptr && !parent_->Relation()->IsIdenticalTo(consequentTypeOld, consequentTypeNew)) {
1078             it->second.first = parent_->AsETSChecker()->CreateETSUnionType({consequentTypeOld, consequentTypeNew});
1079         }
1080 
1081         if (auto *const alternateTypeOld = it->second.second; alternateTypeOld != nullptr) {
1082             if (alternateTypeNew != nullptr &&
1083                 !parent_->Relation()->IsIdenticalTo(alternateTypeOld, alternateTypeNew)) {
1084                 it->second.second = parent_->AsETSChecker()->CreateETSUnionType({alternateTypeOld, alternateTypeNew});
1085             }
1086         } else {
1087             it->second.second = alternateTypeNew;
1088         }
1089 
1090         return false;
1091     }
1092 
1093     //  NOTE: now we support only cases like 'if (x != null || y != null)' or 'if (x instanceof A || x instanceof B)'
1094     //  although it seems that the resulting variable type in the second case isn't used in subsequent code directly.
1095     //  More complex conditions can be implemented later on if the need arises.
1096     testSmartCasts_.emplace(variable, std::make_pair(consequentTypeNew, alternateTypeNew));
1097     return true;
1098 }
1099 
1100 //==============================================================================//
1101 
SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression * expr,Type * annotationType)1102 void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType)
1103 {
1104     if ((expr == nullptr) || (annotationType == nullptr)) {
1105         return;
1106     }
1107 
1108     if (expr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1109         return;
1110     }
1111 
1112     // Expand all member expressions
1113     Type *elementType = annotationType;
1114     ir::Expression *object = expr->Object();
1115     while ((object != nullptr) && (object->IsMemberExpression())) {
1116         ir::MemberExpression *memberExpr = object->AsMemberExpression();
1117         if (memberExpr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1118             return;
1119         }
1120 
1121         object = memberExpr->Object();
1122         elementType = CreateETSArrayType(elementType);
1123     }
1124 
1125     // Set explicit target type for array
1126     if ((object != nullptr) && (object->IsArrayExpression())) {
1127         ir::ArrayExpression *array = object->AsArrayExpression();
1128         array->SetPreferredType(CreateETSArrayType(elementType));
1129     }
1130 }
1131 
CheckExpandedType(Type * expandedAliasType,std::set<util::StringView> & parametersNeedToBeBoxed,bool needToBeBoxed)1132 static void CheckExpandedType(Type *expandedAliasType, std::set<util::StringView> &parametersNeedToBeBoxed,
1133                               bool needToBeBoxed)
1134 {
1135     if (expandedAliasType->IsETSTypeParameter()) {
1136         auto paramName = expandedAliasType->AsETSTypeParameter()->GetDeclNode()->Name()->Name();
1137         if (needToBeBoxed) {
1138             parametersNeedToBeBoxed.insert(paramName);
1139         }
1140     } else if (expandedAliasType->IsETSObjectType()) {
1141         auto objectType = expandedAliasType->AsETSObjectType();
1142         needToBeBoxed =
1143             objectType->GetDeclNode()->IsClassDefinition() || objectType->GetDeclNode()->IsTSInterfaceDeclaration();
1144         for (const auto typeArgument : objectType->TypeArguments()) {
1145             CheckExpandedType(typeArgument, parametersNeedToBeBoxed, needToBeBoxed);
1146         }
1147     } else if (expandedAliasType->IsETSTupleType()) {
1148         auto tupleType = expandedAliasType->AsETSTupleType();
1149         needToBeBoxed = false;
1150         for (auto type : tupleType->GetTupleTypesList()) {
1151             CheckExpandedType(type, parametersNeedToBeBoxed, needToBeBoxed);
1152         }
1153     } else if (expandedAliasType->IsETSArrayType()) {
1154         auto arrayType = expandedAliasType->AsETSArrayType();
1155         needToBeBoxed = false;
1156         auto elementType = arrayType->ElementType();
1157         CheckExpandedType(elementType, parametersNeedToBeBoxed, needToBeBoxed);
1158     } else if (expandedAliasType->IsETSUnionType()) {
1159         auto unionType = expandedAliasType->AsETSUnionType();
1160         needToBeBoxed = false;
1161         for (auto type : unionType->ConstituentTypes()) {
1162             CheckExpandedType(type, parametersNeedToBeBoxed, needToBeBoxed);
1163         }
1164     }
1165 }
1166 
HandleTypeAlias(ir::Expression * const name,const ir::TSTypeParameterInstantiation * const typeParams)1167 Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams)
1168 {
1169     ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() &&
1170            name->AsIdentifier()->Variable()->Declaration()->IsTypeAliasDecl());
1171 
1172     auto *const typeAliasNode =
1173         name->AsIdentifier()->Variable()->Declaration()->AsTypeAliasDecl()->Node()->AsTSTypeAliasDeclaration();
1174 
1175     // NOTE (mmartin): modify for default params
1176     if ((typeParams == nullptr) != (typeAliasNode->TypeParams() == nullptr)) {
1177         if (typeParams == nullptr) {
1178             ThrowTypeError("Type alias declaration is generic, but no type parameters were provided", name->Start());
1179         }
1180 
1181         ThrowTypeError("Type alias declaration is not generic, but type parameters were provided", typeParams->Start());
1182     }
1183 
1184     if (typeParams == nullptr) {
1185         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1186         return GetReferencedTypeBase(name);
1187     }
1188 
1189     for (auto *const origTypeParam : typeParams->Params()) {
1190         origTypeParam->Check(this);
1191     }
1192 
1193     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1194     Type *const aliasType = GetReferencedTypeBase(name);
1195     auto *aliasSub = NewSubstitution();
1196     if (typeAliasNode->TypeParams()->Params().size() != typeParams->Params().size()) {
1197         ThrowTypeError("Wrong number of type parameters for generic type alias", typeParams->Start());
1198     }
1199 
1200     std::set<util::StringView> parametersNeedToBeBoxed;
1201     auto expandedAliasType = aliasType->Substitute(Relation(), aliasSub);
1202     CheckExpandedType(expandedAliasType, parametersNeedToBeBoxed, false);
1203 
1204     for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) {
1205         auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name();
1206         auto *typeAliasType = typeAliasTypeName->Variable()->TsType();
1207         if (!typeAliasType->IsETSTypeParameter()) {
1208             continue;
1209         }
1210         auto paramType = typeParams->Params().at(idx)->TsType();
1211         if (parametersNeedToBeBoxed.find(typeAliasTypeName->Name()) != parametersNeedToBeBoxed.end()) {
1212             auto boxedType = PrimitiveTypeAsETSBuiltinType(typeParams->Params().at(idx)->GetType(this));
1213             if (boxedType != nullptr) {
1214                 paramType = boxedType;
1215             }
1216         }
1217         aliasSub->insert({typeAliasType->AsETSTypeParameter(), paramType});
1218     }
1219 
1220     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1221     ValidateGenericTypeAliasForClonedNode(typeAliasNode->AsTSTypeAliasDeclaration(), typeParams);
1222 
1223     return aliasType->Substitute(Relation(), aliasSub);
1224 }
1225 
GetNameForSynteticObjectType(const util::StringView & source)1226 std::vector<util::StringView> ETSChecker::GetNameForSynteticObjectType(const util::StringView &source)
1227 {
1228     const std::string str = source.Mutf8();
1229     std::istringstream ss {str};
1230     const char delimiter = '.';
1231     std::string token;
1232 
1233     std::vector<util::StringView> syntheticName {};
1234 
1235     while (std::getline(ss, token, delimiter)) {
1236         if (!token.empty()) {
1237             util::UString sV(token, Allocator());
1238             syntheticName.emplace_back(sV.View());
1239         }
1240     }
1241 
1242     return syntheticName;
1243 }
1244 
FindSpecifierForModuleObject(ir::ETSImportDeclaration * importDecl,util::StringView const & name)1245 std::pair<bool, util::StringView> FindSpecifierForModuleObject(ir::ETSImportDeclaration *importDecl,
1246                                                                util::StringView const &name)
1247 {
1248     if (importDecl == nullptr) {
1249         return std::make_pair(true, util::StringView());
1250     }
1251 
1252     for (auto item : importDecl->Specifiers()) {
1253         if (item->IsImportSpecifier() && item->AsImportSpecifier()->Imported()->Name().Is(name.Mutf8())) {
1254             if (!item->AsImportSpecifier()->Imported()->Name().Is(item->AsImportSpecifier()->Local()->Name().Mutf8())) {
1255                 return std::make_pair(true, item->AsImportSpecifier()->Local()->Name());
1256             }
1257             return std::make_pair(true, util::StringView());
1258         }
1259     }
1260     return std::make_pair(false, util::StringView());
1261 }
1262 
1263 template <checker::PropertyType TYPE>
BindingsModuleObjectAddProperty(checker::ETSObjectType * moduleObjType,ir::ETSImportDeclaration * importDecl,const varbinder::Scope::VariableMap & bindings)1264 void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType,
1265                                                  ir::ETSImportDeclaration *importDecl,
1266                                                  const varbinder::Scope::VariableMap &bindings)
1267 {
1268     for (auto [_, var] : bindings) {
1269         (void)_;
1270         auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name());
1271         if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() ||
1272              var->AsLocalVariable()->Declaration()->Node()->IsExportedType()) &&
1273             found) {
1274             if (!aliasedName.Empty()) {
1275                 moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName);
1276             }
1277             moduleObjType->AddProperty<TYPE>(var->AsLocalVariable(),
1278                                              FindPropNameForNamespaceImport(var->AsLocalVariable()->Name()));
1279         }
1280     }
1281 }
1282 
FindPropNameForNamespaceImport(const util::StringView & originalName)1283 util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringView &originalName)
1284 {
1285     if (auto relatedMapItem =
1286             VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap().find(Program()->SourceFilePath());
1287         relatedMapItem != VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap().end()) {
1288         if (auto result = std::find_if(relatedMapItem->second.begin(), relatedMapItem->second.end(),
1289                                        [originalName](const auto &item) { return item.second == originalName; });
1290             result != relatedMapItem->second.end()) {
1291             return result->first;
1292         }
1293     }
1294 
1295     return originalName;
1296 }
1297 
1298 // Helps to prevent searching for the imported file among external sources if it is the entry program
SelectEntryOrExternalProgram(varbinder::ETSBinder * etsBinder,const util::StringView & importPath)1299 static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder,
1300                                                      const util::StringView &importPath)
1301 {
1302     if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) {
1303         return etsBinder->GetGlobalRecordTable()->Program();
1304     }
1305 
1306     auto programList = etsBinder->GetProgramList(importPath);
1307     ASSERT(!programList.empty());
1308     return programList.front();
1309 }
1310 
SetPropertiesForModuleObject(checker::ETSObjectType * moduleObjType,const util::StringView & importPath,ir::ETSImportDeclaration * importDecl)1311 void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath,
1312                                               ir::ETSImportDeclaration *importDecl)
1313 {
1314     parser::Program *program =
1315         SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
1316     // Check imported properties before assigning them to module object
1317     if (!program->IsASTChecked()) {
1318         // NOTE: helps to avoid endless loop in case of recursive imports that uses all bindings
1319         program->MarkASTAsChecked();
1320         program->Ast()->Check(this);
1321     }
1322 
1323     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_FIELD>(
1324         moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings());
1325 
1326     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_METHOD>(
1327         moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings());
1328 
1329     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1330         moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings());
1331 
1332     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1333         moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings());
1334 
1335     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1336         moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings());
1337 }
1338 
SetrModuleObjectTsType(ir::Identifier * local,checker::ETSObjectType * moduleObjType)1339 void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType)
1340 {
1341     auto *etsBinder = static_cast<varbinder::ETSBinder *>(VarBinder());
1342 
1343     for (auto [bindingName, var] : etsBinder->TopScope()->Bindings()) {
1344         if (bindingName.Is(local->Name().Mutf8())) {
1345             var->SetTsType(moduleObjType);
1346         }
1347     }
1348 }
1349 
GetReferencedTypeFromBase(Type * baseType,ir::Expression * name)1350 Type *ETSChecker::GetReferencedTypeFromBase([[maybe_unused]] Type *baseType, [[maybe_unused]] ir::Expression *name)
1351 {
1352     return nullptr;
1353 }
1354 
GetReferencedTypeBase(ir::Expression * name)1355 Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name)
1356 {
1357     if (name->IsTSQualifiedName()) {
1358         return name->Check(this);
1359     }
1360 
1361     ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() != nullptr);
1362 
1363     // NOTE: kbaladurin. forbid usage imported entities as types without declarations
1364     auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(name->AsIdentifier()->Variable());
1365     if (importData != nullptr && importData->import->IsPureDynamic()) {
1366         name->SetTsType(GlobalBuiltinDynamicType(importData->import->Language()));
1367         return name->TsType();
1368     }
1369 
1370     auto *refVar = name->AsIdentifier()->Variable()->AsLocalVariable();
1371 
1372     checker::Type *tsType = nullptr;
1373     switch (refVar->Declaration()->Node()->Type()) {
1374         case ir::AstNodeType::TS_INTERFACE_DECLARATION: {
1375             tsType = GetTypeFromInterfaceReference(refVar);
1376             break;
1377         }
1378         case ir::AstNodeType::CLASS_DECLARATION:
1379         case ir::AstNodeType::STRUCT_DECLARATION:
1380         case ir::AstNodeType::CLASS_DEFINITION: {
1381             tsType = GetTypeFromClassReference(refVar);
1382             break;
1383         }
1384         case ir::AstNodeType::TS_ENUM_DECLARATION: {
1385             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1386             tsType = GetTypeFromEnumReference(refVar);
1387             break;
1388         }
1389         case ir::AstNodeType::TS_TYPE_PARAMETER: {
1390             tsType = GetTypeFromTypeParameterReference(refVar, name->Start());
1391             break;
1392         }
1393         case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION: {
1394             tsType = GetTypeFromTypeAliasReference(refVar);
1395             break;
1396         }
1397         default: {
1398             UNREACHABLE();
1399         }
1400     }
1401     name->SetTsType(tsType);
1402     return tsType;
1403 }
1404 
ConcatConstantString(util::UString & target,Type * type)1405 void ETSChecker::ConcatConstantString(util::UString &target, Type *type)
1406 {
1407     switch (ETSType(type)) {
1408         case TypeFlag::ETS_OBJECT: {
1409             ASSERT(type->IsETSStringType());
1410             target.Append(type->AsETSStringType()->GetValue());
1411             break;
1412         }
1413         case TypeFlag::ETS_BOOLEAN: {
1414             target.Append(type->AsETSBooleanType()->GetValue() ? "true" : "false");
1415             break;
1416         }
1417         case TypeFlag::BYTE: {
1418             target.Append(std::to_string(type->AsByteType()->GetValue()));
1419             break;
1420         }
1421         case TypeFlag::CHAR: {
1422             std::string s(1, type->AsCharType()->GetValue());
1423             target.Append(s);
1424             break;
1425         }
1426         case TypeFlag::SHORT: {
1427             target.Append(std::to_string(type->AsShortType()->GetValue()));
1428             break;
1429         }
1430         case TypeFlag::INT: {
1431             target.Append(std::to_string(type->AsIntType()->GetValue()));
1432             break;
1433         }
1434         case TypeFlag::LONG: {
1435             target.Append(std::to_string(type->AsLongType()->GetValue()));
1436             break;
1437         }
1438         case TypeFlag::FLOAT: {
1439             target.Append(std::to_string(type->AsFloatType()->GetValue()));
1440             break;
1441         }
1442         case TypeFlag::DOUBLE: {
1443             target.Append(std::to_string(type->AsDoubleType()->GetValue()));
1444             break;
1445         }
1446         default: {
1447             UNREACHABLE();
1448         }
1449     }
1450 }
1451 
HandleStringConcatenation(Type * leftType,Type * rightType)1452 Type *ETSChecker::HandleStringConcatenation(Type *leftType, Type *rightType)
1453 {
1454     ASSERT(leftType->IsETSStringType() || rightType->IsETSStringType());
1455 
1456     if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT) ||
1457         leftType->IsETSBigIntType() || rightType->IsETSBigIntType()) {
1458         return GlobalETSStringLiteralType();
1459     }
1460 
1461     util::UString concatenated(Allocator());
1462     ConcatConstantString(concatenated, leftType);
1463     ConcatConstantString(concatenated, rightType);
1464 
1465     return CreateETSStringLiteralType(concatenated.View());
1466 }
1467 
FindFunctionInVectorGivenByName(util::StringView name,ArenaVector<checker::ETSFunctionType * > & list)1468 checker::ETSFunctionType *ETSChecker::FindFunctionInVectorGivenByName(util::StringView name,
1469                                                                       ArenaVector<checker::ETSFunctionType *> &list)
1470 {
1471     for (auto *it : list) {
1472         if (it->Name() == name) {
1473             return it;
1474         }
1475     }
1476 
1477     return nullptr;
1478 }
1479 
IsFunctionContainsSignature(checker::ETSFunctionType * funcType,Signature * signature)1480 bool ETSChecker::IsFunctionContainsSignature(checker::ETSFunctionType *funcType, Signature *signature)
1481 {
1482     for (auto *it : funcType->CallSignatures()) {
1483         Relation()->IsCompatibleTo(it, signature);
1484         if (Relation()->IsTrue()) {
1485             return true;
1486         }
1487     }
1488 
1489     return false;
1490 }
1491 
CheckFunctionContainsClashingSignature(const checker::ETSFunctionType * funcType,Signature * signature)1492 void ETSChecker::CheckFunctionContainsClashingSignature(const checker::ETSFunctionType *funcType, Signature *signature)
1493 {
1494     for (auto *it : funcType->CallSignatures()) {
1495         SavedTypeRelationFlagsContext strfCtx(Relation(), TypeRelationFlag::NONE);
1496         Relation()->IsCompatibleTo(it, signature);
1497         if (Relation()->IsTrue() && it->Function()->Id()->Name() == signature->Function()->Id()->Name()) {
1498             std::stringstream ss;
1499             it->ToString(ss, nullptr, true);
1500             auto sigStr1 = ss.str();
1501             ss.str(std::string {});  // Clear buffer
1502             signature->ToString(ss, nullptr, true);
1503             auto sigStr2 = ss.str();
1504             ThrowTypeError({"Function '", it->Function()->Id()->Name(), sigStr1.c_str(),
1505                             "' is redeclared with different signature '", signature->Function()->Id()->Name(),
1506                             sigStr2.c_str(), "'"},
1507                            signature->Function()->ReturnTypeAnnotation()->Start());
1508         }
1509     }
1510 }
1511 
MergeSignatures(checker::ETSFunctionType * target,checker::ETSFunctionType * source)1512 void ETSChecker::MergeSignatures(checker::ETSFunctionType *target, checker::ETSFunctionType *source)
1513 {
1514     for (auto *s : source->CallSignatures()) {
1515         if (IsFunctionContainsSignature(target, s)) {
1516             continue;
1517         }
1518 
1519         CheckFunctionContainsClashingSignature(target, s);
1520         target->AddCallSignature(s);
1521     }
1522 }
1523 
MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType * > & merged,ArenaVector<checker::ETSFunctionType * > & current)1524 void ETSChecker::MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType *> &merged,
1525                                         ArenaVector<checker::ETSFunctionType *> &current)
1526 {
1527     for (auto *curr : current) {
1528         auto name = curr->Name();
1529         auto *found = FindFunctionInVectorGivenByName(name, merged);
1530         if (found != nullptr) {
1531             MergeSignatures(found, curr);
1532             continue;
1533         }
1534 
1535         merged.push_back(curr);
1536     }
1537 }
1538 
FindAncestorGivenByType(ir::AstNode * node,ir::AstNodeType type,const ir::AstNode * endNode)1539 ir::AstNode *ETSChecker::FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type, const ir::AstNode *endNode)
1540 {
1541     auto *iter = node->Parent();
1542 
1543     while (iter != endNode) {
1544         if (iter->Type() == type) {
1545             return iter;
1546         }
1547 
1548         iter = iter->Parent();
1549     }
1550 
1551     return nullptr;
1552 }
1553 
GetContainingObjectNameFromSignature(Signature * signature)1554 util::StringView ETSChecker::GetContainingObjectNameFromSignature(Signature *signature)
1555 {
1556     ASSERT(signature->Function());
1557     auto *iter = signature->Function()->Parent();
1558 
1559     while (iter != nullptr) {
1560         if (iter->IsClassDefinition()) {
1561             return iter->AsClassDefinition()->Ident()->Name();
1562         }
1563 
1564         if (iter->IsTSInterfaceDeclaration()) {
1565             return iter->AsTSInterfaceDeclaration()->Id()->Name();
1566         }
1567 
1568         iter = iter->Parent();
1569     }
1570 
1571     UNREACHABLE();
1572     return {""};
1573 }
1574 
FindJumpTarget(ir::AstNode * node)1575 const ir::AstNode *ETSChecker::FindJumpTarget(ir::AstNode *node)
1576 {
1577     ASSERT(node->IsBreakStatement() || node->IsContinueStatement());
1578 
1579     bool const isContinue = node->IsContinueStatement();
1580 
1581     // Look for label
1582     auto label = isContinue ? node->AsContinueStatement()->Ident() : node->AsBreakStatement()->Ident();
1583     if (label != nullptr) {
1584         auto var = label->Variable();
1585         if (var != nullptr && var->Declaration()->IsLabelDecl()) {
1586             return var->Declaration()->Node();
1587         }
1588 
1589         // Failed to resolve variable for label
1590         ThrowError(label);
1591     }
1592 
1593     // No label, find the nearest loop or switch statement
1594     const auto *iter = node->Parent();
1595     while (iter != nullptr) {
1596         switch (iter->Type()) {
1597             case ir::AstNodeType::DO_WHILE_STATEMENT:
1598             case ir::AstNodeType::WHILE_STATEMENT:
1599             case ir::AstNodeType::FOR_UPDATE_STATEMENT:
1600             case ir::AstNodeType::FOR_OF_STATEMENT:
1601             case ir::AstNodeType::SWITCH_STATEMENT: {
1602                 return iter;
1603             }
1604             default: {
1605                 break;
1606             }
1607         }
1608 
1609         iter = iter->Parent();
1610     }
1611 
1612     UNREACHABLE();
1613 }
1614 
GetAccessFlagFromNode(const ir::AstNode * node)1615 varbinder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *node)
1616 {
1617     if (node->IsPrivate()) {
1618         return varbinder::VariableFlags::PRIVATE;
1619     }
1620 
1621     if (node->IsProtected()) {
1622         return varbinder::VariableFlags::PROTECTED;
1623     }
1624 
1625     return varbinder::VariableFlags::PUBLIC;
1626 }
1627 
CheckSwitchDiscriminant(ir::Expression * discriminant)1628 Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant)
1629 {
1630     discriminant->Check(this);
1631     auto discriminantType = MaybeUnboxExpression(discriminant);
1632     ASSERT(discriminantType != nullptr);
1633 
1634     if (discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) {
1635         return discriminantType;
1636     }
1637 
1638     if (discriminantType->IsETSObjectType() &&
1639         discriminantType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_STRING | ETSObjectFlags::STRING |
1640                                                            ETSObjectFlags::ENUM)) {
1641         return discriminantType;
1642     }
1643 
1644     ThrowTypeError({"Incompatible types. Found: ", discriminantType,
1645                     ", required: char , byte , short , int, long , Char , Byte , Short , Int, Long , String "
1646                     "or an enum type"},
1647                    discriminant->Start());
1648 }
1649 
AddBoxingUnboxingFlagsToNode(ir::AstNode * node,Type * boxingUnboxingType)1650 void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType)
1651 {
1652     if (boxingUnboxingType->IsETSObjectType()) {
1653         node->AddBoxingUnboxingFlags(GetBoxingFlag(boxingUnboxingType));
1654     } else if (!boxingUnboxingType->IsETSUnionType()) {
1655         node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxingUnboxingType));
1656     }
1657 }
1658 
MaybeBoxExpression(ir::Expression * expr)1659 Type *ETSChecker::MaybeBoxExpression(ir::Expression *expr)
1660 {
1661     auto *promoted = MaybePromotedBuiltinType(expr->TsTypeOrError());
1662     if (promoted != expr->TsTypeOrError()) {
1663         expr->AddBoxingUnboxingFlags(GetBoxingFlag(promoted));
1664     }
1665     return promoted;
1666 }
1667 
MaybeUnboxExpression(ir::Expression * expr)1668 Type *ETSChecker::MaybeUnboxExpression(ir::Expression *expr)
1669 {
1670     auto *primitive = MaybePrimitiveBuiltinType(expr->TsType());
1671     if (primitive != expr->TsType()) {
1672         expr->AddBoxingUnboxingFlags(GetUnboxingFlag(primitive));
1673     }
1674     return primitive;
1675 }
1676 
TypeToName(Type * type) const1677 util::StringView ETSChecker::TypeToName(Type *type) const
1678 {
1679     auto typeKind = TypeKind(type);
1680     switch (typeKind) {
1681         case TypeFlag::ETS_BOOLEAN: {
1682             return "boolean";
1683         }
1684         case TypeFlag::BYTE: {
1685             return "byte";
1686         }
1687         case TypeFlag::CHAR: {
1688             return "char";
1689         }
1690         case TypeFlag::SHORT: {
1691             return "short";
1692         }
1693         case TypeFlag::INT: {
1694             return "int";
1695         }
1696         case TypeFlag::LONG: {
1697             return "long";
1698         }
1699         case TypeFlag::FLOAT: {
1700             return "float";
1701         }
1702         case TypeFlag::DOUBLE: {
1703             return "number";
1704         }
1705         default:
1706             UNREACHABLE();
1707     }
1708 }
1709 
CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement * > const & cases)1710 void ETSChecker::CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement *> const &cases)
1711 {
1712     //  Just to avoid extra nesting level
1713     auto const checkEnumType = [this](ir::Expression const *const caseTest, ETSIntEnumType const *const type) -> void {
1714         if (caseTest->TsType()->AsETSIntEnumType()->IsSameEnumLiteralType(type)) {
1715             ThrowTypeError("Case duplicate", caseTest->Start());
1716         }
1717     };
1718 
1719     auto const checkStringEnumType = [this](ir::Expression const *const caseTest,
1720                                             ETSStringEnumType const *const type) -> void {
1721         if (caseTest->TsType()->AsETSStringEnumType()->IsSameEnumLiteralType(type)) {
1722             ThrowTypeError("Case duplicate", caseTest->Start());
1723         }
1724     };
1725 
1726     for (size_t caseNum = 0; caseNum < cases.size(); caseNum++) {
1727         for (size_t compareCase = caseNum + 1; compareCase < cases.size(); compareCase++) {
1728             auto *caseTest = cases.at(caseNum)->Test();
1729             auto *compareCaseTest = cases.at(compareCase)->Test();
1730 
1731             if (caseTest == nullptr || compareCaseTest == nullptr) {
1732                 continue;
1733             }
1734 
1735             if (caseTest->TsType()->IsETSIntEnumType()) {
1736                 checkEnumType(caseTest, compareCaseTest->TsType()->AsETSIntEnumType());
1737                 continue;
1738             }
1739 
1740             if (caseTest->TsType()->IsETSStringEnumType()) {
1741                 checkStringEnumType(caseTest, compareCaseTest->TsType()->AsETSStringEnumType());
1742                 continue;
1743             }
1744 
1745             if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
1746                 CheckIdentifierSwitchCase(caseTest, compareCaseTest, cases.at(caseNum)->Start());
1747                 continue;
1748             }
1749 
1750             if (compareCaseTest->IsIdentifier() || compareCaseTest->IsMemberExpression()) {
1751                 CheckIdentifierSwitchCase(compareCaseTest, caseTest, cases.at(compareCase)->Start());
1752                 continue;
1753             }
1754 
1755             if (caseTest->IsLiteral() && compareCaseTest->IsLiteral() &&
1756                 GetStringFromLiteral(caseTest) != GetStringFromLiteral(compareCaseTest)) {
1757                 continue;
1758             }
1759 
1760             if (!(IsConstantExpression(caseTest, caseTest->TsType()) || caseTest->IsLiteral()) ||
1761                 !(IsConstantExpression(compareCaseTest, compareCaseTest->TsType()) || compareCaseTest->IsLiteral())) {
1762                 continue;
1763             }
1764 
1765             ThrowTypeError("Case duplicate", cases.at(compareCase)->Start());
1766         }
1767     }
1768 }
1769 
GetStringFromIdentifierValue(checker::Type * caseType) const1770 std::string ETSChecker::GetStringFromIdentifierValue(checker::Type *caseType) const
1771 {
1772     const auto identifierTypeKind = ETSChecker::TypeKind(caseType);
1773     switch (identifierTypeKind) {
1774         case TypeFlag::BYTE: {
1775             return std::to_string(caseType->AsByteType()->GetValue());
1776         }
1777         case TypeFlag::SHORT: {
1778             return std::to_string(caseType->AsShortType()->GetValue());
1779         }
1780         case TypeFlag::CHAR: {
1781             return std::to_string(caseType->AsCharType()->GetValue());
1782         }
1783         case TypeFlag::INT: {
1784             return std::to_string(caseType->AsIntType()->GetValue());
1785         }
1786         case TypeFlag::LONG: {
1787             return std::to_string(caseType->AsLongType()->GetValue());
1788         }
1789         case TypeFlag::ETS_OBJECT: {
1790             VarBinder()->ThrowError(caseType->AsETSObjectType()->Variable()->Declaration()->Node()->Start(),
1791                                     "not implemented");
1792         }
1793         default: {
1794             UNREACHABLE();
1795         }
1796     }
1797 }
1798 
IsConstantMemberOrIdentifierExpression(ir::Expression * expression)1799 bool IsConstantMemberOrIdentifierExpression(ir::Expression *expression)
1800 {
1801     if (expression->IsMemberExpression()) {
1802         auto *var = expression->AsMemberExpression()->PropVar();
1803         return var->Declaration()->IsConstDecl() ||
1804                (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC));
1805     }
1806 
1807     if (expression->IsIdentifier()) {
1808         auto *var = expression->AsIdentifier()->Variable();
1809         return var->Declaration()->IsConstDecl() ||
1810                (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC));
1811     }
1812 
1813     return false;
1814 }
1815 
CompareIdentifiersValuesAreDifferent(ir::Expression * compareValue,const std::string & caseValue)1816 bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *compareValue, const std::string &caseValue)
1817 {
1818     if (IsConstantMemberOrIdentifierExpression(compareValue)) {
1819         checker::Type *compareCaseType = compareValue->TsType();
1820 
1821         const auto compareCaseValue = GetStringFromIdentifierValue(compareCaseType);
1822         return caseValue != compareCaseValue;
1823     }
1824 
1825     return caseValue != GetStringFromLiteral(compareValue);
1826 }
1827 
CheckIdentifierSwitchCase(ir::Expression * currentCase,ir::Expression * compareCase,const lexer::SourcePosition & pos)1828 void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase,
1829                                            const lexer::SourcePosition &pos)
1830 {
1831     currentCase->Check(this);
1832 
1833     if (!IsConstantMemberOrIdentifierExpression(currentCase)) {
1834         ThrowTypeError("Constant expression required", pos);
1835     }
1836 
1837     checker::Type *caseType = currentCase->TsType();
1838 
1839     if (!caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE)) {
1840         ThrowTypeError("Unexpected type " + caseType->ToString(), pos);
1841     }
1842 
1843     if (!CompareIdentifiersValuesAreDifferent(compareCase, GetStringFromIdentifierValue(caseType))) {
1844         ThrowTypeError("Variable has same value with another switch case", pos);
1845     }
1846 }
1847 
GetStringFromLiteral(ir::Expression * caseTest) const1848 std::string ETSChecker::GetStringFromLiteral(ir::Expression *caseTest) const
1849 {
1850     switch (caseTest->Type()) {
1851         case ir::AstNodeType::CHAR_LITERAL: {
1852             return std::to_string(caseTest->AsCharLiteral()->Char());
1853         }
1854         case ir::AstNodeType::STRING_LITERAL:
1855         case ir::AstNodeType::NUMBER_LITERAL: {
1856             return util::Helpers::LiteralToPropName(caseTest).Mutf8();
1857         }
1858         default:
1859             UNREACHABLE();
1860     }
1861 }
1862 
IsSameDeclarationType(varbinder::LocalVariable * target,varbinder::LocalVariable * compare)1863 bool ETSChecker::IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare)
1864 {
1865     return target->Declaration()->Type() == compare->Declaration()->Type();
1866 }
1867 
CheckRethrowingParams(const ir::AstNode * ancestorFunction,const ir::AstNode * node)1868 bool ETSChecker::CheckRethrowingParams(const ir::AstNode *ancestorFunction, const ir::AstNode *node)
1869 {
1870     for (const auto param : ancestorFunction->AsScriptFunction()->Signature()->Function()->Params()) {
1871         if (node->AsCallExpression()->Callee()->AsIdentifier()->Name().Is(
1872                 param->AsETSParameterExpression()->Ident()->Name().Mutf8())) {
1873             return true;
1874         }
1875     }
1876     return false;
1877 }
1878 
CheckThrowingStatements(ir::AstNode * node)1879 void ETSChecker::CheckThrowingStatements(ir::AstNode *node)
1880 {
1881     ir::AstNode *ancestorFunction = FindAncestorGivenByType(node, ir::AstNodeType::SCRIPT_FUNCTION);
1882 
1883     if (ancestorFunction == nullptr) {
1884         ThrowTypeError(
1885             "This statement can cause an exception, therefore it must be enclosed in a try statement with a default "
1886             "catch clause",
1887             node->Start());
1888     }
1889 
1890     if (ancestorFunction->AsScriptFunction()->IsThrowing() ||
1891         (ancestorFunction->AsScriptFunction()->IsRethrowing() &&
1892          (!node->IsThrowStatement() && CheckRethrowingParams(ancestorFunction, node)))) {
1893         return;
1894     }
1895 
1896     if (!CheckThrowingPlacement(node, ancestorFunction)) {
1897         if (ancestorFunction->AsScriptFunction()->IsRethrowing() && !node->IsThrowStatement()) {
1898             ThrowTypeError(
1899                 "This statement can cause an exception, re-throwing functions can throw exception only by their "
1900                 "parameters.",
1901                 node->Start());
1902         }
1903 
1904         ThrowTypeError(
1905             "This statement can cause an exception, therefore it must be enclosed in a try statement with a default "
1906             "catch clause",
1907             node->Start());
1908     }
1909 }
1910 
CheckThrowingPlacement(ir::AstNode * node,const ir::AstNode * ancestorFunction)1911 bool ETSChecker::CheckThrowingPlacement(ir::AstNode *node, const ir::AstNode *ancestorFunction)
1912 {
1913     ir::AstNode *startPoint = node;
1914     ir::AstNode *enclosingCatchClause = nullptr;
1915     ir::BlockStatement *enclosingFinallyBlock = nullptr;
1916     ir::AstNode *p = startPoint->Parent();
1917 
1918     bool isHandled = false;
1919     const auto predicateFunc = [&enclosingCatchClause](ir::CatchClause *clause) {
1920         return clause == enclosingCatchClause;
1921     };
1922 
1923     do {
1924         if (p->IsTryStatement() && p->AsTryStatement()->HasDefaultCatchClause()) {
1925             enclosingCatchClause = FindAncestorGivenByType(startPoint, ir::AstNodeType::CATCH_CLAUSE, p);
1926             enclosingFinallyBlock = FindFinalizerOfTryStatement(startPoint, p);
1927             const auto catches = p->AsTryStatement()->CatchClauses();
1928             if (std::any_of(catches.begin(), catches.end(), predicateFunc)) {
1929                 startPoint = enclosingCatchClause;
1930             } else if (enclosingFinallyBlock != nullptr &&
1931                        enclosingFinallyBlock == p->AsTryStatement()->FinallyBlock()) {
1932                 startPoint = enclosingFinallyBlock;
1933             } else {
1934                 isHandled = true;
1935                 break;
1936             }
1937         }
1938 
1939         p = p->Parent();
1940     } while (p != ancestorFunction);
1941 
1942     return isHandled;
1943 }
1944 
FindFinalizerOfTryStatement(ir::AstNode * startFrom,const ir::AstNode * p)1945 ir::BlockStatement *ETSChecker::FindFinalizerOfTryStatement(ir::AstNode *startFrom, const ir::AstNode *p)
1946 {
1947     auto *iter = startFrom->Parent();
1948 
1949     do {
1950         if (iter->IsBlockStatement()) {
1951             ir::BlockStatement *finallyBlock = iter->AsBlockStatement();
1952 
1953             if (finallyBlock == p->AsTryStatement()->FinallyBlock()) {
1954                 return finallyBlock;
1955             }
1956         }
1957 
1958         iter = iter->Parent();
1959     } while (iter != p);
1960 
1961     return nullptr;
1962 }
1963 
CheckRethrowingFunction(ir::ScriptFunction * func)1964 void ETSChecker::CheckRethrowingFunction(ir::ScriptFunction *func)
1965 {
1966     bool foundThrowingParam = false;
1967 
1968     // It doesn't support lambdas yet.
1969     for (auto item : func->Params()) {
1970         auto const *type = item->AsETSParameterExpression()->Ident()->TypeAnnotation();
1971 
1972         if (type->IsETSTypeReference()) {
1973             auto *typeDecl = type->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Variable()->Declaration();
1974             if (typeDecl->IsTypeAliasDecl()) {
1975                 type = typeDecl->Node()->AsTSTypeAliasDeclaration()->TypeAnnotation();
1976             }
1977         }
1978 
1979         if (type->IsETSFunctionType() && type->AsETSFunctionType()->IsThrowing()) {
1980             foundThrowingParam = true;
1981             break;
1982         }
1983     }
1984 
1985     if (!foundThrowingParam) {
1986         ThrowTypeError("A rethrowing function must have a throwing function parameter", func->Start());
1987     }
1988 }
1989 
GetRelevantArgumentedTypeFromChild(ETSObjectType * const child,ETSObjectType * const target)1990 ETSObjectType *ETSChecker::GetRelevantArgumentedTypeFromChild(ETSObjectType *const child, ETSObjectType *const target)
1991 {
1992     if (child->GetDeclNode() == target->GetDeclNode()) {
1993         auto *relevantType = CreateNewETSObjectType(child->Name(), child->GetDeclNode(), child->ObjectFlags());
1994 
1995         ArenaVector<Type *> params = child->TypeArguments();
1996 
1997         relevantType->SetTypeArguments(std::move(params));
1998         relevantType->SetEnclosingType(child->EnclosingType());
1999         relevantType->SetSuperType(child->SuperType());
2000 
2001         return relevantType;
2002     }
2003 
2004     ASSERT(child->SuperType() != nullptr);
2005 
2006     return GetRelevantArgumentedTypeFromChild(child->SuperType(), target);
2007 }
2008 
EmplaceSubstituted(Substitution * substitution,ETSTypeParameter * tparam,Type * typeArg)2009 void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg)
2010 {
2011     substitution->emplace(tparam, typeArg);
2012 }
2013 
GetHashFromTypeArguments(const ArenaVector<Type * > & typeArgTypes)2014 util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes)
2015 {
2016     std::stringstream ss;
2017 
2018     for (auto *it : typeArgTypes) {
2019         it->ToString(ss, true);
2020         ss << compiler::Signatures::MANGLE_SEPARATOR;
2021 
2022         // In case of ETSTypeParameters storing the name might not be sufficient as there can
2023         // be multiple different type parameters with the same name. For those we test identity based
2024         // on their memory address equality, so we store them in the hash to keep it unique.
2025         // To make it consistent we store it for every type.
2026         // NOTE (mmartin): change bare address to something more appropriate unique representation
2027         ss << it << compiler::Signatures::MANGLE_SEPARATOR;
2028     }
2029 
2030     return util::UString(ss.str(), Allocator()).View();
2031 }
2032 
GetHashFromSubstitution(const Substitution * substitution)2033 util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution)
2034 {
2035     std::vector<std::string> fields;
2036     for (auto [k, v] : *substitution) {
2037         std::stringstream ss;
2038         k->ToString(ss, true);
2039         ss << ":";
2040         v->ToString(ss, true);
2041         // NOTE (mmartin): change bare address to something more appropriate unique representation
2042         ss << ":" << k << ":" << v;
2043         fields.push_back(ss.str());
2044     }
2045     std::sort(fields.begin(), fields.end());
2046 
2047     std::stringstream ss;
2048     for (auto &fstr : fields) {
2049         ss << fstr;
2050         ss << ";";
2051     }
2052     return util::UString(ss.str(), Allocator()).View();
2053 }
2054 
GetHashFromFunctionType(ir::ETSFunctionType * type)2055 util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type)
2056 {
2057     std::stringstream ss;
2058     for (auto *p : type->Params()) {
2059         auto *const param = p->AsETSParameterExpression();
2060         param->TypeAnnotation()->GetType(this)->ToString(ss, true);
2061         ss << ";";
2062     }
2063 
2064     type->ReturnType()->GetType(this)->ToString(ss, true);
2065     ss << ";";
2066 
2067     if (type->IsThrowing()) {
2068         ss << "throws;";
2069     }
2070 
2071     if (type->IsRethrowing()) {
2072         ss << "rethrows;";
2073     }
2074 
2075     return util::UString(ss.str(), Allocator()).View();
2076 }
2077 
GetOriginalBaseType(Type * const object)2078 ETSObjectType *ETSChecker::GetOriginalBaseType(Type *const object)
2079 {
2080     if (object == nullptr || !object->IsETSObjectType()) {
2081         return nullptr;
2082     }
2083 
2084     return object->AsETSObjectType()->GetOriginalBaseType();
2085 }
2086 
CheckValidGenericTypeParameter(Type * const argType,const lexer::SourcePosition & pos)2087 void ETSChecker::CheckValidGenericTypeParameter(Type *const argType, const lexer::SourcePosition &pos)
2088 {
2089     if (!argType->IsETSEnumType()) {
2090         return;
2091     }
2092     std::stringstream ss;
2093     argType->ToString(ss);
2094     ThrowTypeError("Type '" + ss.str() + "' is not valid for generic type arguments", pos);
2095 }
2096 
CheckNumberOfTypeArguments(ETSObjectType * const type,ir::TSTypeParameterInstantiation * const typeArgs,const lexer::SourcePosition & pos)2097 void ETSChecker::CheckNumberOfTypeArguments(ETSObjectType *const type, ir::TSTypeParameterInstantiation *const typeArgs,
2098                                             const lexer::SourcePosition &pos)
2099 {
2100     auto const &typeParams = type->TypeArguments();
2101     if (typeParams.empty()) {
2102         if (typeArgs != nullptr) {
2103             ThrowTypeError({"Type '", type, "' is not generic."}, pos);
2104         }
2105         return;
2106     }
2107 
2108     size_t minimumTypeArgs = std::count_if(typeParams.begin(), typeParams.end(), [](Type *param) {
2109         return param->AsETSTypeParameter()->GetDefaultType() == nullptr;
2110     });
2111     if (typeArgs == nullptr && minimumTypeArgs > 0) {
2112         ThrowTypeError({"Type '", type, "' is generic but type argument were not provided."}, pos);
2113     }
2114 
2115     if (typeArgs != nullptr &&
2116         ((minimumTypeArgs > typeArgs->Params().size()) || (typeParams.size() < typeArgs->Params().size()))) {
2117         ThrowTypeError({"Type '", type, "' has ", minimumTypeArgs, " number of type parameters, but ",
2118                         typeArgs->Params().size(), " type arguments were provided."},
2119                        pos);
2120     }
2121 }
2122 
NeedTypeInference(const ir::ScriptFunction * lambda)2123 bool ETSChecker::NeedTypeInference(const ir::ScriptFunction *lambda)
2124 {
2125     if (lambda->ReturnTypeAnnotation() == nullptr) {
2126         return true;
2127     }
2128     for (auto *const param : lambda->Params()) {
2129         const auto *const lambdaParam = param->AsETSParameterExpression()->Ident();
2130         if (lambdaParam->TypeAnnotation() == nullptr) {
2131             return true;
2132         }
2133     }
2134     return false;
2135 }
2136 
FindTypeInferenceArguments(const ArenaVector<ir::Expression * > & arguments)2137 std::vector<bool> ETSChecker::FindTypeInferenceArguments(const ArenaVector<ir::Expression *> &arguments)
2138 {
2139     std::vector<bool> argTypeInferenceRequired(arguments.size());
2140     size_t index = 0;
2141     for (ir::Expression *arg : arguments) {
2142         if (arg->IsArrowFunctionExpression()) {
2143             ir::ScriptFunction *const lambda = arg->AsArrowFunctionExpression()->Function();
2144             if (NeedTypeInference(lambda)) {
2145                 argTypeInferenceRequired[index] = true;
2146             }
2147         }
2148         ++index;
2149     }
2150     return argTypeInferenceRequired;
2151 }
2152 
CheckLambdaAssignableUnion(ir::AstNode * typeAnn,ir::ScriptFunction * lambda)2153 bool ETSChecker::CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunction *lambda)
2154 {
2155     for (auto *type : typeAnn->AsETSUnionType()->Types()) {
2156         if (type->IsETSFunctionType()) {
2157             return lambda->Params().size() == type->AsETSFunctionType()->Params().size();
2158         }
2159     }
2160 
2161     return false;
2162 }
2163 
InferTypesForLambda(ir::ScriptFunction * lambda,ir::ETSFunctionType * calleeType)2164 void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunctionType *calleeType)
2165 {
2166     for (size_t i = 0; i < calleeType->Params().size(); ++i) {
2167         const auto *const calleeParam = calleeType->Params()[i]->AsETSParameterExpression()->Ident();
2168         auto *const lambdaParam = lambda->Params()[i]->AsETSParameterExpression()->Ident();
2169         if (lambdaParam->TypeAnnotation() == nullptr) {
2170             auto *const typeAnnotation = calleeParam->TypeAnnotation()->Clone(Allocator(), lambdaParam);
2171             lambdaParam->SetTsTypeAnnotation(typeAnnotation);
2172             typeAnnotation->SetParent(lambdaParam);
2173         }
2174     }
2175     if (lambda->ReturnTypeAnnotation() == nullptr) {
2176         lambda->SetReturnTypeAnnotation(calleeType->ReturnType()->Clone(Allocator(), lambda));
2177     }
2178 }
2179 
ModifyPreferredType(ir::ArrayExpression * const arrayExpr,Type * const newPreferredType)2180 void ETSChecker::ModifyPreferredType(ir::ArrayExpression *const arrayExpr, Type *const newPreferredType)
2181 {
2182     // After modifying the preferred type of an array expression, it needs to be rechecked at the call site
2183     arrayExpr->SetPreferredType(newPreferredType);
2184     arrayExpr->SetTsType(nullptr);
2185 
2186     for (auto *const element : arrayExpr->Elements()) {
2187         if (element->IsArrayExpression()) {
2188             ModifyPreferredType(element->AsArrayExpression(), nullptr);
2189         }
2190     }
2191 }
2192 
IsInLocalClass(const ir::AstNode * node) const2193 bool ETSChecker::IsInLocalClass(const ir::AstNode *node) const
2194 {
2195     while (node != nullptr) {
2196         if (node->Type() == ir::AstNodeType::CLASS_DEFINITION) {
2197             return node->AsClassDefinition()->IsLocal();
2198         }
2199         node = node->Parent();
2200     }
2201 
2202     return false;
2203 }
2204 
GenerateImplicitInstantiateArg(varbinder::LocalVariable * instantiateMethod,const std::string & className)2205 ir::Expression *ETSChecker::GenerateImplicitInstantiateArg(varbinder::LocalVariable *instantiateMethod,
2206                                                            const std::string &className)
2207 {
2208     auto callSignatures = instantiateMethod->TsType()->AsETSFunctionType()->CallSignatures();
2209     ASSERT(!callSignatures.empty());
2210     auto methodOwner = std::string(callSignatures[0]->Owner()->Name());
2211     std::string implicitInstantiateArgument = "()=>{return new " + className + "()";
2212     if (methodOwner != className) {
2213         implicitInstantiateArgument.append(" as " + methodOwner);
2214     }
2215     implicitInstantiateArgument.append("}");
2216 
2217     parser::Program program(Allocator(), VarBinder());
2218     es2panda::CompilerOptions options;
2219     auto parser = parser::ETSParser(&program, options, parser::ParserStatus::NO_OPTS);
2220     auto *argExpr = parser.CreateExpression(implicitInstantiateArgument);
2221     compiler::InitScopesPhaseETS::RunExternalNode(argExpr, &program);
2222 
2223     return argExpr;
2224 }
2225 
ClassPropToImplementationProp(ir::ClassProperty * classProp,varbinder::ClassScope * scope)2226 ir::ClassProperty *ETSChecker::ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope)
2227 {
2228     classProp->Key()->AsIdentifier()->SetName(
2229         util::UString(std::string(compiler::Signatures::PROPERTY) + classProp->Key()->AsIdentifier()->Name().Mutf8(),
2230                       Allocator())
2231             .View());
2232     classProp->AddModifier(ir::ModifierFlags::PRIVATE);
2233 
2234     auto *fieldDecl = Allocator()->New<varbinder::LetDecl>(classProp->Key()->AsIdentifier()->Name());
2235     fieldDecl->BindNode(classProp);
2236 
2237     auto fieldVar = scope->InstanceFieldScope()->AddDecl(Allocator(), fieldDecl, ScriptExtension::ETS);
2238     fieldVar->AddFlag(varbinder::VariableFlags::PROPERTY);
2239 
2240     classProp->Key()->SetVariable(fieldVar);
2241     classProp->Key()->AsIdentifier()->SetVariable(fieldVar);
2242     fieldVar->SetTsType(classProp->TsType());
2243 
2244     return classProp;
2245 }
2246 
GenerateGetterSetterBody(ArenaVector<ir::Statement * > & stmts,ArenaVector<ir::Expression * > & params,ir::ClassProperty * const field,varbinder::FunctionParamScope * paramScope,bool isSetter)2247 void ETSChecker::GenerateGetterSetterBody(ArenaVector<ir::Statement *> &stmts, ArenaVector<ir::Expression *> &params,
2248                                           ir::ClassProperty *const field, varbinder::FunctionParamScope *paramScope,
2249                                           bool isSetter)
2250 {
2251     auto *classDef = field->Parent()->AsClassDefinition();
2252 
2253     ir::Expression *baseExpression;
2254     if ((field->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U) {
2255         baseExpression = Allocator()->New<ir::SuperExpression>();
2256     } else {
2257         baseExpression = Allocator()->New<ir::ThisExpression>();
2258     }
2259     baseExpression->SetParent(classDef);
2260     baseExpression->SetTsType(classDef->TsType());
2261 
2262     auto *memberExpression =
2263         AllocNode<ir::MemberExpression>(baseExpression, field->Key()->AsIdentifier()->Clone(Allocator(), nullptr),
2264                                         ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
2265     memberExpression->SetTsType(field->TsType());
2266     memberExpression->SetPropVar(field->Key()->Variable()->AsLocalVariable());
2267     memberExpression->SetRange(classDef->Range());
2268     if (memberExpression->ObjType() == nullptr && classDef->TsType() != nullptr) {
2269         memberExpression->SetObjectType(classDef->TsType()->AsETSObjectType());
2270     }
2271 
2272     if (!isSetter) {
2273         stmts.push_back(AllocNode<ir::ReturnStatement>(memberExpression));
2274         return;
2275     }
2276 
2277     auto *paramIdent = field->Key()->AsIdentifier()->Clone(Allocator(), nullptr);
2278     if (field->TypeAnnotation() != nullptr) {
2279         auto *const typeAnnotation = field->TypeAnnotation()->Clone(Allocator(), paramIdent);
2280         paramIdent->SetTsTypeAnnotation(typeAnnotation);
2281     } else {
2282         paramIdent->SetTsType(field->TsType());
2283     }
2284 
2285     auto *paramExpression = AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr);
2286     paramExpression->SetRange(paramIdent->Range());
2287     auto *const paramVar = std::get<2>(paramScope->AddParamDecl(Allocator(), paramExpression));
2288     paramExpression->SetVariable(paramVar);
2289 
2290     params.push_back(paramExpression);
2291 
2292     auto *assignmentExpression = AllocNode<ir::AssignmentExpression>(
2293         memberExpression, paramExpression->Clone(Allocator(), nullptr), lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
2294     assignmentExpression->SetTsType(paramVar->TsType());
2295 
2296     assignmentExpression->SetRange({field->Start(), field->End()});
2297     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2298     stmts.push_back(AllocNode<ir::ExpressionStatement>(assignmentExpression));
2299     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2300     stmts.push_back(Allocator()->New<ir::ReturnStatement>(nullptr));
2301 }
2302 
GenerateDefaultGetterSetter(ir::ClassProperty * const property,ir::ClassProperty * const field,varbinder::ClassScope * classScope,bool isSetter,ETSChecker * checker)2303 ir::MethodDefinition *ETSChecker::GenerateDefaultGetterSetter(ir::ClassProperty *const property,
2304                                                               ir::ClassProperty *const field,
2305                                                               varbinder::ClassScope *classScope, bool isSetter,
2306                                                               ETSChecker *checker)
2307 {
2308     auto *paramScope = checker->Allocator()->New<varbinder::FunctionParamScope>(checker->Allocator(), classScope);
2309     auto *functionScope = checker->Allocator()->New<varbinder::FunctionScope>(checker->Allocator(), paramScope);
2310 
2311     functionScope->BindParamScope(paramScope);
2312     paramScope->BindFunctionScope(functionScope);
2313 
2314     ArenaVector<ir::Expression *> params(checker->Allocator()->Adapter());
2315     ArenaVector<ir::Statement *> stmts(checker->Allocator()->Adapter());
2316     checker->GenerateGetterSetterBody(stmts, params, field, paramScope, isSetter);
2317     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2318     auto *body = checker->AllocNode<ir::BlockStatement>(checker->Allocator(), std::move(stmts));
2319     auto funcFlags = isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER;
2320     auto *const returnTypeAnn = isSetter || field->TypeAnnotation() == nullptr
2321                                     ? nullptr
2322                                     : field->TypeAnnotation()->Clone(checker->Allocator(), nullptr);
2323     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2324     auto *func = checker->AllocNode<ir::ScriptFunction>(
2325         checker->Allocator(),
2326         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), returnTypeAnn),
2327                                                 funcFlags, ir::ModifierFlags::PUBLIC, true});
2328 
2329     if (!isSetter) {
2330         func->AddFlag(ir::ScriptFunctionFlags::HAS_RETURN);
2331     }
2332     func->SetRange(field->Range());
2333     func->SetScope(functionScope);
2334     body->SetScope(functionScope);
2335     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2336     auto *methodIdent = property->Key()->AsIdentifier()->Clone(checker->Allocator(), nullptr);
2337     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2338     auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func);
2339     funcExpr->SetRange(func->Range());
2340     func->AddFlag(ir::ScriptFunctionFlags::METHOD);
2341     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2342     auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr,
2343                                                             ir::ModifierFlags::PUBLIC, checker->Allocator(), false);
2344 
2345     auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(),
2346                                                                     property->Key()->AsIdentifier()->Name(), method);
2347     auto *var = checker->Allocator()->New<varbinder::LocalVariable>(decl, varbinder::VariableFlags::VAR);
2348     var->AddFlag(varbinder::VariableFlags::METHOD);
2349 
2350     methodIdent->SetVariable(var);
2351 
2352     method->Id()->SetMutator();
2353     method->SetRange(field->Range());
2354     method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr));
2355     method->Function()->AddModifier(method->Modifiers());
2356     method->SetVariable(var);
2357     method->SetParent(field->Parent());
2358 
2359     paramScope->BindNode(func);
2360     functionScope->BindNode(func);
2361 
2362     auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(checker->VarBinder(), classScope);
2363     checker->VarBinder()->AsETSBinder()->ResolveMethodDefinition(method);
2364 
2365     functionScope->BindName(classScope->Node()->AsClassDefinition()->InternalName());
2366     method->Check(checker);
2367 
2368     return method;
2369 }
2370 
GetImplementationClassProp(ETSChecker * checker,ir::ClassProperty * interfaceProp,ir::ClassProperty * originalProp,ETSObjectType * classType)2371 ir::ClassProperty *GetImplementationClassProp(ETSChecker *checker, ir::ClassProperty *interfaceProp,
2372                                               ir::ClassProperty *originalProp, ETSObjectType *classType)
2373 {
2374     bool isSuperOwner = ((originalProp->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U);
2375     if (!isSuperOwner) {
2376         auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2377         auto *const scope = checker->Scope()->AsClassScope();
2378         auto *const classProp = checker->ClassPropToImplementationProp(
2379             interfaceProp->Clone(checker->Allocator(), originalProp->Parent()), scope);
2380         classType->AddProperty<PropertyType::INSTANCE_FIELD>(classProp->Key()->Variable()->AsLocalVariable());
2381         classDef->Body().push_back(classProp);
2382         return classProp;
2383     }
2384 
2385     auto *const classProp = classType
2386                                 ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(),
2387                                               PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE)
2388                                 ->Declaration()
2389                                 ->Node()
2390                                 ->AsClassProperty();
2391     classProp->AddModifier(ir::ModifierFlags::SUPER_OWNER);
2392     return classProp;
2393 }
2394 
SetupGetterSetterFlags(ir::ClassProperty * originalProp,ETSObjectType * classType,ir::ClassDefinition * classDef,ir::MethodDefinition * getter,ir::MethodDefinition * setter,const bool inExternal)2395 static void SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObjectType *classType,
2396                                    ir::ClassDefinition *classDef, ir::MethodDefinition *getter,
2397                                    ir::MethodDefinition *setter, const bool inExternal)
2398 {
2399     for (auto &method : {getter, setter}) {
2400         if (method == nullptr) {
2401             continue;
2402         }
2403 
2404         const auto mflag = method == getter ? ir::ModifierFlags::GETTER : ir::ModifierFlags::SETTER;
2405         const auto tflag = method == getter ? TypeFlag::GETTER : TypeFlag::SETTER;
2406 
2407         method->TsType()->AddTypeFlag(tflag);
2408         method->Variable()->SetTsType(method->TsType());
2409         if (((originalProp->Modifiers() & mflag) != 0U)) {
2410             method->Function()->AddModifier(ir::ModifierFlags::OVERRIDE);
2411         }
2412 
2413         if (inExternal) {
2414             method->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
2415         }
2416         method->SetParent(classDef);
2417         classType->AddProperty<checker::PropertyType::INSTANCE_METHOD>(method->Variable()->AsLocalVariable());
2418     }
2419 }
2420 
GenerateGetterSetterPropertyAndMethod(ir::ClassProperty * originalProp,ETSObjectType * classType)2421 void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *originalProp, ETSObjectType *classType)
2422 {
2423     auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2424     auto *interfaceProp = originalProp->Clone(Allocator(), originalProp->Parent());
2425     interfaceProp->ClearModifier(ir::ModifierFlags::GETTER_SETTER);
2426 
2427     auto *const scope = Scope()->AsClassScope();
2428     scope->InstanceFieldScope()->EraseBinding(interfaceProp->Key()->AsIdentifier()->Name());
2429     interfaceProp->SetRange(originalProp->Range());
2430 
2431     auto *const classProp = GetImplementationClassProp(this, interfaceProp, originalProp, classType);
2432 
2433     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2434     ir::MethodDefinition *getter = GenerateDefaultGetterSetter(interfaceProp, classProp, scope, false, this);
2435     classDef->Body().push_back(getter);
2436 
2437     const auto &name = getter->Key()->AsIdentifier()->Name();
2438 
2439     ir::MethodDefinition *setter =
2440         !classProp->IsConst()
2441             ? GenerateDefaultGetterSetter(interfaceProp, classProp, Scope()->AsClassScope(), true, this)
2442             : nullptr;
2443 
2444     auto *const methodScope = scope->InstanceMethodScope();
2445     auto *const decl = Allocator()->New<varbinder::FunctionDecl>(Allocator(), name, getter);
2446     auto *var = methodScope->AddDecl(Allocator(), decl, ScriptExtension::ETS);
2447 
2448     if (var == nullptr) {
2449         auto *const prevDecl = methodScope->FindDecl(name);
2450         for (const auto &method : {getter, setter}) {
2451             if (method != nullptr) {
2452                 prevDecl->Node()->AsMethodDefinition()->AddOverload(method);
2453             }
2454         }
2455         var = methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
2456     }
2457     var->AddFlag(varbinder::VariableFlags::METHOD);
2458 
2459     SetupGetterSetterFlags(originalProp, classType, classDef, getter, setter, HasStatus(CheckerStatus::IN_EXTERNAL));
2460 
2461     if (setter != nullptr) {
2462         getter->Variable()->TsType()->AsETSFunctionType()->AddCallSignature(
2463             setter->TsType()->AsETSFunctionType()->CallSignatures()[0]);
2464     }
2465 
2466     getter->Function()->Id()->SetVariable(var);
2467     if (setter != nullptr) {
2468         getter->AddOverload(setter);
2469     }
2470 }
2471 
TryGettingFunctionTypeFromInvokeFunction(Type * type)2472 Type *ETSChecker::TryGettingFunctionTypeFromInvokeFunction(Type *type)
2473 {
2474     if (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) {
2475         auto const propInvoke = type->AsETSObjectType()->GetProperty(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME,
2476                                                                      PropertySearchFlags::SEARCH_INSTANCE_METHOD);
2477         ASSERT(propInvoke != nullptr);
2478 
2479         return propInvoke->TsType();
2480     }
2481 
2482     return type;
2483 }
2484 
TryTransformingToStaticInvoke(ir::Identifier * const ident,const Type * resolvedType)2485 bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType)
2486 {
2487     ASSERT(ident->Parent()->IsCallExpression());
2488     ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident);
2489 
2490     if (!resolvedType->IsETSObjectType()) {
2491         return false;
2492     }
2493 
2494     auto className = ident->Name();
2495     std::string_view propertyName;
2496 
2497     PropertySearchFlags searchFlag = PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE |
2498                                      PropertySearchFlags::SEARCH_STATIC_METHOD;
2499     // clang-format off
2500     auto *instantiateMethod =
2501         resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlag);
2502     auto *invokeMethod =
2503         resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlag);
2504     if (instantiateMethod != nullptr) {
2505         propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD;
2506     } else if (invokeMethod != nullptr) {
2507         propertyName = compiler::Signatures::STATIC_INVOKE_METHOD;
2508     } else {
2509         ThrowTypeError({"No static ", compiler::Signatures::STATIC_INVOKE_METHOD, " method and static ",
2510                         compiler::Signatures::STATIC_INSTANTIATE_METHOD, " method in ", className, ". ", className,
2511                         "() is not allowed."},
2512                        ident->Start());
2513     }
2514     // clang-format on
2515     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2516     auto *classId = AllocNode<ir::Identifier>(className, Allocator());
2517     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2518     auto *methodId = AllocNode<ir::Identifier>(propertyName, Allocator());
2519     if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) {
2520         methodId->SetVariable(instantiateMethod);
2521     } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) {
2522         methodId->SetVariable(invokeMethod);
2523     }
2524 
2525     auto *transformedCallee =
2526         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2527         AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
2528 
2529     classId->SetRange(ident->Range());
2530     methodId->SetRange(ident->Range());
2531     transformedCallee->SetRange(ident->Range());
2532 
2533     auto *callExpr = ident->Parent()->AsCallExpression();
2534     transformedCallee->SetParent(callExpr);
2535     callExpr->SetCallee(transformedCallee);
2536 
2537     if (instantiateMethod != nullptr) {
2538         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2539         auto *argExpr = GenerateImplicitInstantiateArg(instantiateMethod, std::string(className));
2540 
2541         argExpr->SetParent(callExpr);
2542         argExpr->SetRange(ident->Range());
2543 
2544         VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr);
2545 
2546         auto &arguments = callExpr->Arguments();
2547         arguments.insert(arguments.begin(), argExpr);
2548     }
2549 
2550     return true;
2551 }
2552 
CreateSyntheticType(util::StringView const & syntheticName,checker::ETSObjectType * lastObjectType,ir::Identifier * id)2553 checker::ETSObjectType *ETSChecker::CreateSyntheticType(util::StringView const &syntheticName,
2554                                                         checker::ETSObjectType *lastObjectType, ir::Identifier *id)
2555 {
2556     auto *syntheticObjType = Allocator()->New<checker::ETSObjectType>(Allocator(), syntheticName, syntheticName, id,
2557                                                                       checker::ETSObjectFlags::NO_OPTS);
2558 
2559     auto *classDecl = Allocator()->New<varbinder::ClassDecl>(syntheticName);
2560     varbinder::LocalVariable *var =
2561         Allocator()->New<varbinder::LocalVariable>(classDecl, varbinder::VariableFlags::CLASS);
2562     var->SetTsType(syntheticObjType);
2563     lastObjectType->AddProperty<checker::PropertyType::STATIC_FIELD>(var);
2564     syntheticObjType->SetEnclosingType(lastObjectType);
2565     return syntheticObjType;
2566 }
2567 
ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration * importDecl,checker::ETSObjectType * lastObjectType,ir::Identifier * ident)2568 void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl,
2569                                                           checker::ETSObjectType *lastObjectType, ir::Identifier *ident)
2570 {
2571     for (auto item : VarBinder()->AsETSBinder()->ReExportImports()) {
2572         if (!importDecl->ResolvedSource()->Str().Is(item->GetProgramPath().Mutf8())) {
2573             continue;
2574         }
2575         auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident);
2576         lastObjectType->AddReExports(reExportType);
2577         for (auto node : importDecl->Specifiers()) {
2578             if (node->IsImportSpecifier()) {
2579                 auto specifier = node->AsImportSpecifier();
2580                 lastObjectType->AddReExportAlias(specifier->Imported()->Name(), specifier->Local()->Name());
2581             }
2582         }
2583     }
2584 }
2585 
GetImportSpecifierObjectType(ir::ETSImportDeclaration * importDecl,ir::Identifier * ident)2586 ETSObjectType *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident)
2587 {
2588     auto importPath = importDecl->ResolvedSource()->Str();
2589     parser::Program *program =
2590         SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
2591     std::vector<util::StringView> syntheticNames = GetNameForSynteticObjectType(program->ModuleName());
2592     ASSERT(!syntheticNames.empty());
2593     auto assemblerName = syntheticNames[0];
2594 
2595     if (!program->OmitModuleName()) {
2596         assemblerName = util::UString(assemblerName.Mutf8()
2597                                           .append(compiler::Signatures::METHOD_SEPARATOR)
2598                                           .append(compiler::Signatures::ETS_GLOBAL),
2599                                       Allocator())
2600                             .View();
2601     }
2602 
2603     auto *moduleObjectType =
2604         Allocator()->New<checker::ETSObjectType>(Allocator(), syntheticNames[0], assemblerName,
2605                                                  std::make_tuple(ident, checker::ETSObjectFlags::CLASS, Relation()));
2606 
2607     auto *rootDecl = Allocator()->New<varbinder::ClassDecl>(syntheticNames[0]);
2608     varbinder::LocalVariable *rootVar =
2609         Allocator()->New<varbinder::LocalVariable>(rootDecl, varbinder::VariableFlags::NONE);
2610     rootVar->SetTsType(moduleObjectType);
2611 
2612     syntheticNames.erase(syntheticNames.begin());
2613     checker::ETSObjectType *lastObjectType(moduleObjectType);
2614 
2615     for (const auto &syntheticName : syntheticNames) {
2616         lastObjectType = CreateSyntheticType(syntheticName, lastObjectType, ident);
2617     }
2618 
2619     ImportNamespaceObjectTypeAddReExportType(importDecl, lastObjectType, ident);
2620     SetPropertiesForModuleObject(lastObjectType, importPath,
2621                                  importDecl->Specifiers()[0]->IsImportNamespaceSpecifier() ? nullptr : importDecl);
2622     SetrModuleObjectTsType(ident, lastObjectType);
2623 
2624     return moduleObjectType;
2625 }
2626 }  // namespace ark::es2panda::checker
2627