• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "checker/ETSchecker.h"
17 
18 #include "checker/types/globalTypesHolder.h"
19 #include "checker/types/ets/etsTupleType.h"
20 #include "checker/ets/typeRelationContext.h"
21 #include "checker/ets/typeConverter.h"
22 #include "evaluate/scopedDebugInfoPlugin.h"
23 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
24 #include "compiler/lowering/util.h"
25 #include "util/helpers.h"
26 
27 namespace ark::es2panda::checker {
28 
FindVariableInFunctionScope(const util::StringView name)29 varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name)
30 {
31     return Scope() != nullptr ? Scope()->FindInFunctionScope(name, varbinder::ResolveBindingOptions::ALL).variable
32                               : nullptr;
33 }
34 
FindVariableInClassOrEnclosing(const util::StringView name,const ETSObjectType * classType)35 std::pair<varbinder::Variable *, const ETSObjectType *> ETSChecker::FindVariableInClassOrEnclosing(
36     const util::StringView name, const ETSObjectType *classType)
37 {
38     // For Annotation, it doesnot have containing class, so classType will be nullptr.
39     if (classType == nullptr) {
40         return {nullptr, nullptr};
41     }
42     const auto searchFlags = PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE |
43                              PropertySearchFlags::SEARCH_IN_INTERFACES |
44                              PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
45     auto *resolved = classType->GetProperty(name, searchFlags);
46     while (classType->EnclosingType() != nullptr && resolved == nullptr) {
47         classType = classType->EnclosingType();
48         resolved = classType->GetProperty(name, searchFlags);
49     }
50 
51     return {resolved, classType};
52 }
53 
FindVariableInGlobal(const ir::Identifier * const identifier)54 varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier)
55 {
56     return Scope() != nullptr
57                ? Scope()->FindInGlobal(identifier->Name(), varbinder::ResolveBindingOptions::ALL).variable
58                : nullptr;
59 }
60 
IsVariableStatic(const varbinder::Variable * var)61 bool ETSChecker::IsVariableStatic(const varbinder::Variable *var)
62 {
63     CHECK_NOT_NULL(var);
64     if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
65         return var->TsType()->IsETSFunctionType() &&
66                var->TsType()->AsETSFunctionType()->CallSignatures()[0]->HasSignatureFlag(SignatureFlags::STATIC);
67     }
68     return var->HasFlag(varbinder::VariableFlags::STATIC);
69 }
70 
IsVariableGetterSetter(const varbinder::Variable * var)71 bool ETSChecker::IsVariableGetterSetter(const varbinder::Variable *var)
72 {
73     return var != nullptr && var->TsType() != nullptr && var->TsType()->HasTypeFlag(TypeFlag::GETTER_SETTER);
74 }
75 
IsVariableExtensionAccessor(const varbinder::Variable * var)76 bool ETSChecker::IsVariableExtensionAccessor(const varbinder::Variable *var)
77 {
78     return var != nullptr && var->TsType() != nullptr && var->TsType()->IsETSFunctionType() &&
79            var->TsType()->AsETSFunctionType()->IsExtensionAccessorType();
80 }
81 
LogUnresolvedReferenceError(ir::Identifier * const ident)82 void ETSChecker::LogUnresolvedReferenceError(ir::Identifier *const ident)
83 {
84     if (!ident->IsErrorPlaceHolder()) {
85         LogError(diagnostic::UNRESOLVED_REF, {ident->Name()}, ident->Start());
86     }
87 }
88 
WrongContextErrorClassifyByType(ir::Identifier * ident)89 void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident)
90 {
91     if (ident->IsErrorPlaceHolder()) {
92         return;
93     }
94 
95     std::string identCategoryName {};
96     switch (static_cast<varbinder::VariableFlags>(
97         ident->Variable()->Flags() &
98         (varbinder::VariableFlags::CLASS_OR_INTERFACE_OR_ENUM | varbinder::VariableFlags::METHOD |
99          varbinder::VariableFlags::NAMESPACE | varbinder::VariableFlags::ANNOTATIONDECL |
100          varbinder::VariableFlags::ANNOTATIONUSAGE | varbinder::VariableFlags::TYPE_ALIAS |
101          varbinder::VariableFlags::TYPE))) {
102         case varbinder::VariableFlags::CLASS:
103             identCategoryName = "Class";
104             break;
105 
106         case varbinder::VariableFlags::NAMESPACE:
107             identCategoryName = "Namespace";
108             break;
109 
110         case varbinder::VariableFlags::METHOD:
111             identCategoryName = "Function";
112             break;
113 
114         case varbinder::VariableFlags::INTERFACE:
115             identCategoryName = "Interface";
116             break;
117 
118         case varbinder::VariableFlags::ENUM_LITERAL:
119             identCategoryName = "Enum";
120             break;
121 
122         case varbinder::VariableFlags::ANNOTATIONDECL:
123             [[fallthrough]];
124         case varbinder::VariableFlags::ANNOTATIONUSAGE:
125             identCategoryName = "Annotation";
126             break;
127 
128         case varbinder::VariableFlags::TYPE:
129             [[fallthrough]];
130         case varbinder::VariableFlags::TYPE_ALIAS:
131             identCategoryName = "Type";
132             break;
133 
134         default:
135             LogError(diagnostic::ID_WRONG_CTX, {ident->Name()}, ident->Start());
136             return;
137     }
138     LogError(diagnostic::ID_IN_WRONG_CTX, {identCategoryName.c_str(), ident->Name()}, ident->Start());
139 }
140 
NotResolvedError(ir::Identifier * const ident,const varbinder::Variable * classVar,const ETSObjectType * classType)141 void ETSChecker::NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar,
142                                   const ETSObjectType *classType)
143 {
144     if (classVar == nullptr) {
145         LogUnresolvedReferenceError(ident);
146         return;
147     }
148 
149     if (IsVariableStatic(classVar)) {
150         LogError(diagnostic::STATIC_PROP_INVALID_CTX, {ident->Name(), classType}, ident->Start());
151     } else {
152         LogError(diagnostic::PROP_ACCESS_WITHOUT_THIS, {ident->Name()}, ident->Start());
153     }
154 }
155 
GetTargetIdentifierAndType(ir::Identifier * const ident)156 std::pair<const ir::Identifier *, ir::TypeNode *> ETSChecker::GetTargetIdentifierAndType(ir::Identifier *const ident)
157 {
158     if (ident->Parent()->IsClassProperty()) {
159         const auto *const classProp = ident->Parent()->AsClassProperty();
160         ES2PANDA_ASSERT(classProp->Value() && classProp->Value() == ident);
161         return std::make_pair(classProp->Key()->AsIdentifier(), classProp->TypeAnnotation());
162     }
163     const auto *const variableDecl = ident->Parent()->AsVariableDeclarator();
164     ES2PANDA_ASSERT(variableDecl->Init() && variableDecl->Init() == ident);
165     return std::make_pair(variableDecl->Id()->AsIdentifier(), variableDecl->Id()->AsIdentifier()->TypeAnnotation());
166 }
167 
ExtraCheckForResolvedError(ir::Identifier * const ident)168 varbinder::Variable *ETSChecker::ExtraCheckForResolvedError(ir::Identifier *const ident)
169 {
170     const auto [class_var, class_type] = FindVariableInClassOrEnclosing(ident->Name(), Context().ContainingClass());
171     auto *parentClass = FindAncestorGivenByType(ident, ir::AstNodeType::CLASS_DEFINITION);
172     if (parentClass != nullptr && parentClass->AsClassDefinition()->IsLocal()) {
173         if (parentClass != class_type->GetDeclNode()) {
174             LogError(diagnostic::PROPERTY_CAPTURE,
175                      {ident->Name(), class_type->Name(), parentClass->AsClassDefinition()->Ident()->Name()},
176                      ident->Start());
177         }
178     }
179     NotResolvedError(ident, class_var, class_type);
180     return class_var;
181 }
182 
SaveCapturedVariableInLocalClass(varbinder::Variable * const var,ir::Identifier * ident)183 bool ETSChecker::SaveCapturedVariableInLocalClass(varbinder::Variable *const var, ir::Identifier *ident)
184 {
185     const auto &pos = ident->Start();
186 
187     if (!HasStatus(CheckerStatus::IN_LOCAL_CLASS)) {
188         return false;
189     }
190 
191     if (!var->HasFlag(varbinder::VariableFlags::LOCAL)) {
192         return false;
193     }
194 
195     LOG(DEBUG, ES2PANDA) << "Checking variable (line:" << pos.line << "): " << var->Name();
196     auto *scopeIter = Scope();
197     bool inStaticMethod = false;
198 
199     auto captureVariable = [this, var, ident, &scopeIter, &inStaticMethod, &pos]() {
200         if (inStaticMethod) {
201             LogError(diagnostic::PROPERTY_CAPTURE_IN_STATIC, {var->Name()}, pos);
202             return false;
203         }
204         if (scopeIter->Node()->AsClassDefinition()->CaptureVariable(var)) {
205             LOG(DEBUG, ES2PANDA) << "  Captured in class:" << scopeIter->Node()->AsClassDefinition()->Ident()->Name();
206         }
207 
208         auto *parent = ident->Parent();
209 
210         if (parent->IsVariableDeclarator()) {
211             parent = parent->Parent()->Parent();
212         }
213 
214         if (!(parent->IsUpdateExpression() ||
215               (parent->IsAssignmentExpression() && parent->AsAssignmentExpression()->Left() == ident)) ||
216             var->Declaration() == nullptr) {
217             return false;
218         }
219 
220         if (var->Declaration()->IsParameterDecl()) {
221             LOG(DEBUG, ES2PANDA) << "    - Modified parameter ";
222             scopeIter->Node()->AsClassDefinition()->AddToLocalVariableIsNeeded(var);
223         }
224         return true;
225     };
226 
227     while (scopeIter != var->GetScope()) {
228         if (scopeIter->Node() != nullptr) {
229             if (scopeIter->Node()->IsScriptFunction() && scopeIter->Node()->AsScriptFunction()->IsStatic()) {
230                 inStaticMethod = true;
231             }
232 
233             if (scopeIter->Node()->IsClassDefinition()) {
234                 captureVariable();
235                 return true;
236             }
237         }
238         scopeIter = scopeIter->Parent();
239     }
240 
241     return false;
242 }
243 
SaveCapturedVariable(varbinder::Variable * const var,ir::Identifier * ident)244 void ETSChecker::SaveCapturedVariable(varbinder::Variable *const var, ir::Identifier *ident)
245 {
246     const auto &pos = ident->Start();
247 
248     if (!HasStatus(CheckerStatus::IN_LAMBDA)) {
249         return;
250     }
251 
252     if (var->HasFlag(varbinder::VariableFlags::PROPERTY)) {
253         Context().AddCapturedVar(var, pos);
254         return;
255     }
256 
257     if ((!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::METHOD)) ||
258         Context().ContainingLambda()->IsVarFromSubscope(var)) {
259         return;
260     }
261 
262     const auto *scopeIter = Scope();
263     while (scopeIter != var->GetScope()) {
264         if (scopeIter->IsFunctionScope()) {
265             Context().AddCapturedVar(var, pos);
266             return;
267         }
268         scopeIter = scopeIter->Parent();
269     }
270 }
271 
ResolveIdentifier(ir::Identifier * ident)272 Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident)
273 {
274     if (ident->Variable() != nullptr) {
275         auto *const resolved = ident->Variable();
276         SaveCapturedVariable(resolved, ident);
277         return GetTypeOfVariable(resolved);
278     }
279 
280     auto *resolved = FindVariableInFunctionScope(ident->Name());
281     if (resolved == nullptr) {
282         // If the reference is not found already in the current class, then it is not bound to the class, so we have to
283         // find the reference in the global class first, then in the global scope
284         resolved = FindVariableInGlobal(ident);
285         if (UNLIKELY(resolved == nullptr && debugInfoPlugin_ != nullptr)) {
286             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
287             resolved = debugInfoPlugin_->FindIdentifier(ident);
288         }
289     }
290 
291     if (resolved == nullptr) {
292         resolved = ExtraCheckForResolvedError(ident);
293         if (resolved == nullptr) {
294             if (VarBinder()->GetScope()->IsClassScope() && this->IsAnyError()) {
295                 return ident->SetTsType(GlobalTypeError());
296             }
297             auto [decl, var] = VarBinder()->NewVarDecl<varbinder::LetDecl>(
298                 ident->Start(),
299                 !ident->IsErrorPlaceHolder() ? ident->Name() : compiler::GenName(ProgramAllocator()).View());
300             var->SetScope(VarBinder()->GetScope());
301             ident->SetVariable(var);
302             decl->BindNode(ident);
303             return ident->SetTsType(var->SetTsType(GlobalTypeError()));
304         }
305         ident->SetVariable(resolved);
306         return GetTypeOfVariable(resolved);
307     }
308 
309     ident->SetVariable(resolved);
310     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
311     ValidateResolvedIdentifier(ident);
312 
313     ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start());
314     SaveCapturedVariable(resolved, ident);
315 
316     return GetTypeOfVariable(resolved);
317 }
318 
CheckLeftRightType(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)319 std::optional<checker::Type *> CheckLeftRightType(checker::ETSChecker *checker, checker::Type *unboxedL,
320                                                   checker::Type *unboxedR)
321 {
322     if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) {
323         return checker->GlobalDoubleType();
324     }
325     if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) {
326         return checker->GlobalFloatType();
327     }
328     if (unboxedL->IsLongType() || unboxedR->IsLongType()) {
329         return checker->GlobalLongType();
330     }
331     if (unboxedL->IsIntType() || unboxedR->IsIntType() || unboxedL->IsCharType() || unboxedR->IsCharType()) {
332         return checker->GlobalIntType();
333     }
334     if (unboxedL->IsShortType() || unboxedR->IsShortType()) {
335         return checker->GlobalShortType();
336     }
337     if (unboxedL->IsByteType() || unboxedR->IsByteType()) {
338         return checker->GlobalByteType();
339     }
340     return std::nullopt;
341 }
ApplyConditionalOperatorPromotion(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)342 checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxedL,
343                                                              checker::Type *unboxedR)
344 {
345     if ((unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedL->IsIntType()) ||
346         (unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->IsIntType())) {
347         int value = unboxedL->IsIntType() ? unboxedL->AsIntType()->GetValue() : unboxedR->AsIntType()->GetValue();
348         checker::Type *otherType = !unboxedL->IsIntType() ? unboxedL : unboxedR;
349 
350         switch (checker::ETSChecker::ETSType(otherType)) {
351             case checker::TypeFlag::BYTE:
352             case checker::TypeFlag::CHAR: {
353                 if (value <= static_cast<int>(std::numeric_limits<char>::max()) &&
354                     value >= static_cast<int>(std::numeric_limits<char>::min())) {
355                     return checker->GetNonConstantType(otherType);
356                 }
357                 break;
358             }
359             case checker::TypeFlag::SHORT: {
360                 if (value <= std::numeric_limits<int16_t>::max() && value >= std::numeric_limits<int16_t>::min()) {
361                     return checker->GlobalShortType();
362                 }
363                 break;
364             }
365             default: {
366                 return otherType;
367             }
368         }
369         return checker->GlobalIntType();
370     }
371 
372     auto checkLeftRight = CheckLeftRightType(checker, unboxedL, unboxedR);
373     if (checkLeftRight.has_value()) {
374         return checkLeftRight.value();
375     }
376     ES2PANDA_UNREACHABLE();
377 }
378 
ApplyUnaryOperatorPromotion(Type * type,const bool createConst,const bool doPromotion,const bool isCondExpr)379 Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool createConst, const bool doPromotion,
380                                               const bool isCondExpr)
381 {
382     Type *unboxedType = isCondExpr ? MaybeUnboxConditionalInRelation(type) : MaybeUnboxInRelation(type);
383 
384     if (unboxedType == nullptr) {
385         return nullptr;
386     }
387     if (doPromotion) {
388         switch (ETSType(unboxedType)) {
389             case TypeFlag::BYTE:
390             case TypeFlag::SHORT:
391             case TypeFlag::CHAR: {
392                 if (!createConst) {
393                     return GlobalIntType();
394                 }
395 
396                 return CreateIntTypeFromType(unboxedType);
397             }
398             default: {
399                 break;
400             }
401         }
402     }
403     return unboxedType;
404 }
405 
IsNullLikeOrVoidExpression(const ir::Expression * expr) const406 bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const
407 {
408     // NOTE(vpukhov): #19701 void refactoring
409     return expr->TsType()->DefinitelyETSNullish() || expr->TsType()->IsETSVoidType();
410 }
411 
IsResolvedAndValue(const ir::Expression * expr,Type * type) const412 std::tuple<bool, bool> ETSChecker::IsResolvedAndValue(const ir::Expression *expr, Type *type) const
413 {
414     auto [isResolve, isValue] =
415         IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : type->ResolveConditionExpr();
416 
417     const Type *tsType = expr->TsType();
418     if (tsType->DefinitelyNotETSNullish() && !type->IsETSPrimitiveOrEnumType()) {
419         isResolve = true;
420         isValue = true;
421     }
422     return std::make_tuple(isResolve, isValue);
423 }
424 
HandleBooleanLogicalOperators(Type * leftType,Type * rightType,lexer::TokenType tokenType)425 Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType)
426 {
427     using UType = typename ETSBooleanType::UType;
428     ES2PANDA_ASSERT(leftType->IsETSBooleanType() && rightType->IsETSBooleanType());
429 
430     if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
431         return GlobalETSBooleanType();
432     }
433 
434     UType leftValue = leftType->AsETSBooleanType()->GetValue();
435     UType rightValue = rightType->AsETSBooleanType()->GetValue();
436 
437     switch (tokenType) {
438         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
439             return CreateETSBooleanType(leftValue ^ rightValue);
440         }
441         case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
442             return CreateETSBooleanType((static_cast<uint8_t>(leftValue) & static_cast<uint8_t>(rightValue)) != 0);
443         }
444         case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
445             return CreateETSBooleanType((static_cast<uint8_t>(leftValue) | static_cast<uint8_t>(rightValue)) != 0);
446         }
447         case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
448             return CreateETSBooleanType(leftValue || rightValue);
449         }
450         case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
451             return CreateETSBooleanType(leftValue && rightValue);
452         }
453         default: {
454             break;
455         }
456     }
457 
458     ES2PANDA_UNREACHABLE();
459     return nullptr;
460 }
461 
HandleLogicalPotentialResult(ir::Expression * left,ir::Expression * right,ir::BinaryExpression * expr,checker::Type * leftType)462 bool ETSChecker::HandleLogicalPotentialResult(ir::Expression *left, ir::Expression *right, ir::BinaryExpression *expr,
463                                               checker::Type *leftType)
464 {
465     if (leftType->IsConstantType() && leftType->IsETSBooleanType()) {
466         if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
467             expr->SetResult(leftType->AsETSBooleanType()->GetValue() ? right : left);
468             return true;
469         }
470         expr->SetResult(leftType->AsETSBooleanType()->GetValue() ? left : right);
471         return true;
472     }
473 
474     if (!leftType->IsETSPrimitiveType() && !leftType->PossiblyETSValueTyped()) {
475         expr->SetResult(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ? right : left);
476         return true;
477     }
478     if (leftType->IsETSNullType() || leftType->IsETSUndefinedType()) {
479         expr->SetResult(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ? left : right);
480         return true;
481     }
482 
483     return false;
484 }
485 
ResolveReturnStatement(checker::Type * funcReturnType,checker::Type * argumentType,ir::ScriptFunction * containingFunc,ir::ReturnStatement * st)486 void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType,
487                                         ir::ScriptFunction *containingFunc, ir::ReturnStatement *st)
488 {
489     if (funcReturnType->IsETSPrimitiveOrEnumType() && argumentType->IsETSPrimitiveOrEnumType()) {
490         // function return type is of primitive type (including enums):
491         Relation()->SetFlags(checker::TypeRelationFlag::DIRECT_RETURN |
492                              checker::TypeRelationFlag::IN_ASSIGNMENT_CONTEXT |
493                              checker::TypeRelationFlag::ASSIGNMENT_CONTEXT);
494         if (Relation()->IsAssignableTo(funcReturnType, argumentType)) {
495             funcReturnType = argumentType;
496             containingFunc->Signature()->SetReturnType(funcReturnType);
497             containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
498             st->Argument()->SetTsType(funcReturnType);
499         } else if (!Relation()->IsAssignableTo(argumentType, funcReturnType)) {
500             LogError(diagnostic::RETURN_DIFFERENT_PRIM, {funcReturnType, argumentType}, st->Argument()->Start());
501         }
502     } else if (funcReturnType->IsETSReferenceType() || argumentType->IsETSReferenceType()) {
503         // function return type should be of reference (object) type
504         Relation()->SetFlags(checker::TypeRelationFlag::NONE);
505 
506         if (!argumentType->IsETSReferenceType()) {
507             argumentType = MaybeBoxInRelation(argumentType);
508             if (argumentType == nullptr) {
509                 LogError(diagnostic::INVALID_EXPR_IN_RETURN, {}, st->Argument()->Start());
510             } else {
511                 st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argumentType));
512             }
513         }
514 
515         if (!funcReturnType->IsETSReferenceType()) {
516             funcReturnType = MaybeBoxInRelation(funcReturnType);
517             if (funcReturnType == nullptr) {
518                 LogError(diagnostic::INVALID_RETURN_FUNC_EXPR, {}, st->Start());
519             }
520         }
521         if (argumentType != nullptr && funcReturnType != nullptr) {
522             funcReturnType = CreateETSUnionType({funcReturnType, argumentType});
523             containingFunc->Signature()->SetReturnType(funcReturnType);
524             containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
525         }
526     } else {
527         // Should never in this branch.
528         ES2PANDA_UNREACHABLE();
529         return;
530     }
531 }
532 
CheckArrayElements(ir::ArrayExpression * init)533 checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init)
534 {
535     ArenaVector<checker::Type *> elementTypes(ProgramAllocator()->Adapter());
536     for (auto *elementNode : init->AsArrayExpression()->Elements()) {
537         Type *elementType = elementNode->Check(this);
538         if (elementType->IsTypeError()) {
539             return elementType;
540         }
541 
542         if (elementNode->IsSpreadElement() && elementType->IsETSTupleType()) {
543             for (auto *typeFromTuple : elementType->AsETSTupleType()->GetTupleTypesList()) {
544                 elementTypes.emplace_back(typeFromTuple);
545             }
546 
547             continue;
548         }
549 
550         if (elementNode->IsSpreadElement() && elementType->IsETSArrayType()) {
551             elementType = elementType->AsETSArrayType()->ElementType();
552         }
553 
554         elementTypes.push_back(GetNonConstantType(elementType));
555     }
556 
557     if (elementTypes.empty()) {
558         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
559         return ProgramAllocator()->New<ETSArrayType>(GlobalETSObjectType());
560     }
561     auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); };
562     auto const isChar = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::CHAR); };
563     auto *const arrayElementType =
564         std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric)
565             ? std::all_of(elementTypes.begin(), elementTypes.end(), isChar) ? GlobalCharType() : GlobalDoubleType()
566             : CreateETSUnionType(std::move(elementTypes));
567 
568     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
569     return CreateETSResizableArrayType(arrayElementType);
570 }
571 
InferAliasLambdaType(ir::TypeNode * localTypeAnnotation,ir::ArrowFunctionExpression * init)572 void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init)
573 {
574     ES2PANDA_ASSERT(localTypeAnnotation != nullptr);
575 
576     if (localTypeAnnotation->IsETSTypeReference()) {
577         bool isAnnotationTypeAlias = true;
578         while (localTypeAnnotation->IsETSTypeReference() && isAnnotationTypeAlias) {
579             auto *nodeVar = localTypeAnnotation->AsETSTypeReference()->Part()->GetIdent()->Variable();
580             if (nodeVar == nullptr) {
581                 break;
582             }
583 
584             auto *node = nodeVar->Declaration()->Node();
585 
586             isAnnotationTypeAlias = node->IsTSTypeAliasDeclaration();
587             if (isAnnotationTypeAlias) {
588                 localTypeAnnotation = node->AsTSTypeAliasDeclaration()->TypeAnnotation();
589             }
590         }
591     }
592 
593     if (localTypeAnnotation->IsETSFunctionType()) {
594         auto *const arrowFuncExpr = init;
595         ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
596         if (lambda->Params().size() == localTypeAnnotation->AsETSFunctionType()->Params().size() &&
597             NeedTypeInference(lambda)) {
598             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
599             InferTypesForLambda(lambda, localTypeAnnotation->AsETSFunctionType());
600         }
601     }
602 }
603 
FixOptionalVariableType(varbinder::Variable * const bindingVar,ir::ModifierFlags flags,ir::Expression * init)604 checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags,
605                                                    ir::Expression *init)
606 {
607     ES2PANDA_ASSERT(bindingVar != nullptr);
608     if ((flags & ir::ModifierFlags::OPTIONAL) != 0) {
609         if (init != nullptr) {
610             auto *type = bindingVar->TsType();
611             ES2PANDA_ASSERT(type != nullptr);
612             if (type->IsETSPrimitiveType()) {
613                 init->SetBoxingUnboxingFlags(GetBoxingFlag(bindingVar->TsType()));
614             }
615         }
616         auto *variableType = bindingVar->TsType() != nullptr ? bindingVar->TsType() : GlobalTypeError();
617         bindingVar->SetTsType(CreateETSUnionType({GlobalETSUndefinedType(), variableType}));
618     }
619     return bindingVar->TsType();
620 }
621 
PreferredObjectTypeFromAnnotation(checker::Type * annotationType)622 checker::Type *PreferredObjectTypeFromAnnotation(checker::Type *annotationType)
623 {
624     if (!annotationType->IsETSUnionType()) {
625         return annotationType;
626     }
627 
628     checker::Type *resolvedType = nullptr;
629     int objectTypeCount = 0;
630     for (auto constituentType : annotationType->AsETSUnionType()->ConstituentTypes()) {
631         if (constituentType->IsETSObjectType()) {
632             objectTypeCount++;
633             if (resolvedType == nullptr) {
634                 resolvedType = constituentType;
635             }
636         }
637     }
638 
639     // If there's exactly one object type, return it
640     if (objectTypeCount == 1) {
641         return resolvedType;
642     }
643 
644     // If there are multiple object types, return the union type itself
645     // so that our union resolution logic can handle it
646     if (objectTypeCount > 1) {
647         return annotationType;
648     }
649 
650     // If there are no object types, return nullptr
651     return nullptr;
652 }
653 
SetPreferredTypeForExpression(ETSChecker * checker,ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,checker::Type * annotationType)654 bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, ir::TypeNode *typeAnnotation,
655                                    ir::Expression *init, checker::Type *annotationType)
656 {
657     if (init->IsMemberExpression() && init->AsMemberExpression()->Object()->IsObjectExpression()) {
658         checker->LogError(diagnostic::MEMBER_OF_OBJECT_LIT, {}, ident->Start());
659     }
660 
661     if (annotationType != nullptr && annotationType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
662         return false;
663     }
664 
665     if ((init->IsMemberExpression()) && (annotationType != nullptr)) {
666         checker->SetArrayPreferredTypeForNestedMemberExpressions(init->AsMemberExpression(), annotationType);
667     }
668 
669     if (init->IsArrayExpression() && (annotationType != nullptr) && !annotationType->IsETSDynamicType()) {
670         if (annotationType->IsETSTupleType() &&
671             !checker->IsArrayExprSizeValidForTuple(init->AsArrayExpression(), annotationType->AsETSTupleType())) {
672             return false;
673         }
674 
675         init->AsArrayExpression()->SetPreferredType(annotationType);
676     }
677 
678     if (init->IsObjectExpression() && annotationType != nullptr) {
679         init->AsObjectExpression()->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType));
680     }
681 
682     if (init->IsETSNewArrayInstanceExpression() && annotationType != nullptr) {
683         init->AsETSNewArrayInstanceExpression()->SetPreferredType(annotationType);
684     }
685     if (init->IsETSNewMultiDimArrayInstanceExpression() && annotationType != nullptr) {
686         init->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(annotationType);
687     }
688 
689     if (typeAnnotation != nullptr && init->IsArrowFunctionExpression()) {
690         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
691         checker->InferAliasLambdaType(typeAnnotation, init->AsArrowFunctionExpression());
692     }
693 
694     return true;
695 }
696 
CheckInit(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,checker::Type * annotationType)697 bool ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init,
698                            checker::Type *annotationType)
699 {
700     if (typeAnnotation == nullptr) {
701         if (init->IsArrayExpression()) {
702             annotationType = CheckArrayElements(init->AsArrayExpression());
703         } else if (init->IsETSNewArrayInstanceExpression()) {
704             annotationType = init->AsETSNewArrayInstanceExpression()->TypeReference()->GetType(this);
705             annotationType = CreateETSResizableArrayType(annotationType);
706         } else if (init->IsETSNewMultiDimArrayInstanceExpression()) {
707             auto multiArray = init->AsETSNewMultiDimArrayInstanceExpression();
708             annotationType = multiArray->TypeReference()->GetType(this);
709             annotationType = CreateETSMultiDimResizableArrayType(annotationType, multiArray->Dimensions().size());
710         } else if (init->IsObjectExpression()) {
711             LogError(diagnostic::CANNOT_INFER_OBJ_LIT, {ident->Name()}, ident->Start());
712             return false;
713         }
714     }
715     return SetPreferredTypeForExpression(this, ident, typeAnnotation, init, annotationType);
716 }
717 
CheckEnumType(ir::Expression * init,checker::Type * initType,const util::StringView & varName)718 void ETSChecker::CheckEnumType(ir::Expression *init, checker::Type *initType, const util::StringView &varName)
719 {
720     if (initType->IsETSObjectType() && initType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ENUM) &&
721         !init->IsMemberExpression()) {
722         LogError(diagnostic::TYPE_MISMATCH_ENUM, {initType->AsETSObjectType()->Name(), varName}, init->Start());
723     }
724 }
725 
IsOmitConstInit(ir::ModifierFlags const flags)726 static bool IsOmitConstInit(ir::ModifierFlags const flags)
727 {
728     return ((flags & ir::ModifierFlags::CONST) != 0) ||
729            (((flags & ir::ModifierFlags::READONLY) != 0) && ((flags & ir::ModifierFlags::STATIC) != 0));
730 }
731 
NeedWidening(ir::Expression * e)732 static bool NeedWidening(ir::Expression *e)
733 {
734     // NOTE: need to be done by smart casts. Return true if we need to infer wider type.
735     if (e->IsUnaryExpression()) {
736         return NeedWidening(e->AsUnaryExpression()->Argument());
737     }
738     const bool isConstInit = e->IsIdentifier() && e->Variable()->Declaration()->IsConstDecl();
739 
740     return e->IsConditionalExpression() || e->IsLiteral() || isConstInit || e->IsTemplateLiteral();
741 }
742 
743 // Isolated until 'constant' types are tracked in some cases
ShouldPreserveConstantTypeInVariableDeclaration(Type * annotation,Type * init)744 static bool ShouldPreserveConstantTypeInVariableDeclaration(Type *annotation, Type *init)
745 {
746     auto const isNumericWithConstTracking = [](Type *type) {
747         return type->HasTypeFlag(TypeFlag::ETS_NUMERIC) || type->IsCharType();
748     };
749 
750     return ((isNumericWithConstTracking(init) && isNumericWithConstTracking(annotation)) ||
751             (init->IsETSStringType() && annotation->IsETSStringType()));
752 }
753 
CheckAssignForDeclare(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,ir::ModifierFlags const flags,ETSChecker * check)754 static void CheckAssignForDeclare(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init,
755                                   ir::ModifierFlags const flags, ETSChecker *check)
756 {
757     const bool isDeclare = (flags & ir::ModifierFlags::DECLARE) != 0;
758     const bool isAbstract = (flags & ir::ModifierFlags::ABSTRACT) != 0;
759     if (!isDeclare || isAbstract) {
760         return;
761     }
762     if (typeAnnotation != nullptr && init != nullptr && !init->IsUndefinedLiteral()) {
763         check->LogError(diagnostic::INIT_IN_AMBIENT, {ident->Name()}, init->Start());
764         return;
765     }
766     const bool isConst = (flags & ir::ModifierFlags::CONST) != 0;
767     ES2PANDA_ASSERT(init != nullptr);
768     bool numberLiteralButNotBigInt = init->IsNumberLiteral() && !init->IsBigIntLiteral();
769     bool multilineLiteralWithNoEmbedding =
770         init->IsTemplateLiteral() && init->AsTemplateLiteral()->Expressions().empty();
771     if (isConst && !numberLiteralButNotBigInt && !init->IsStringLiteral() && !multilineLiteralWithNoEmbedding) {
772         check->LogError(diagnostic::AMBIENT_CONST_INVALID_LIT, {ident->Name()}, init->Start());
773     }
774 }
775 
CheckRecordType(ir::Expression * init,checker::Type * annotationType,ETSChecker * checker)776 static void CheckRecordType(ir::Expression *init, checker::Type *annotationType, ETSChecker *checker)
777 {
778     if (!annotationType->IsETSObjectType() || !init->IsObjectExpression()) {
779         return;
780     }
781 
782     std::stringstream ss;
783     init->TsType()->ToAssemblerType(ss);
784     if (ss.str() != "escompat.Record" && ss.str() != "escompat.Map") {
785         return;
786     }
787 
788     auto objectExpr = init->AsObjectExpression();
789     auto typeArguments = annotationType->AsETSObjectType()->TypeArguments();
790     auto properties = objectExpr->Properties();
791 
792     for (const auto &property : properties) {
793         if (!property->IsProperty()) {
794             checker->LogError(diagnostic::IMPROPER_NESTING_INTERFACE, {}, property->Start());
795             continue;
796         }
797         ES2PANDA_ASSERT(property->IsProperty());
798         auto p = property->AsProperty();
799 
800         ETSChecker::SetPreferredTypeIfPossible(p->Key(), typeArguments[0]);
801         ETSChecker::SetPreferredTypeIfPossible(p->Value(), typeArguments[1]);
802 
803         Type *keyType = p->Key()->Check(checker);
804         Type *valueType = p->Value()->Check(checker);
805 
806         checker::AssignmentContext(
807             checker->Relation(), p->Key(), keyType, typeArguments[0], p->Key()->Start(),
808             util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {keyType, typeArguments[0], size_t(1)}});
809         checker::AssignmentContext(
810             checker->Relation(), p->Value(), valueType, typeArguments[1], p->Value()->Start(),
811             util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {valueType, typeArguments[1], size_t(2)}});
812     }
813 }
814 
815 // CC-OFFNXT(huge_method,huge_cca_cyclomatic_complexity,huge_cyclomatic_complexity,G.FUN.01-CPP) solid logic
CheckVariableDeclaration(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,ir::ModifierFlags const flags)816 checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation,
817                                                     ir::Expression *init, ir::ModifierFlags const flags)
818 {
819     ES2PANDA_ASSERT(ident != nullptr);
820     varbinder::Variable *const bindingVar = ident->Variable();
821     checker::Type *annotationType = nullptr;
822 
823     // We have to process possible parser errors when variable was not created and bind:
824     if (bindingVar != nullptr) {
825         if (typeAnnotation != nullptr) {
826             annotationType = typeAnnotation->GetType(this);
827             bindingVar->SetTsType(annotationType);
828         }
829 
830         if (init == nullptr) {
831             return FixOptionalVariableType(bindingVar, flags, init);
832         }
833         CheckAssignForDeclare(ident, typeAnnotation, init, flags, this);
834     } else {
835         ES2PANDA_ASSERT(IsAnyError());
836     }
837 
838     checker::Type *initType = nullptr;
839     if (init != nullptr) {
840         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
841         if (!CheckInit(ident, typeAnnotation, init, annotationType)) {
842             init->SetTsType(GlobalTypeError());
843         } else {
844             initType = init->Check(this);
845         }
846     } else {
847         ES2PANDA_ASSERT(IsAnyError());
848     }
849 
850     if (bindingVar == nullptr) {
851         return annotationType != nullptr ? annotationType : GlobalTypeError();
852     }
853 
854     // initType should not be nullptr. If an error occurs during check, set it to GlobalTypeError().
855     if (initType == nullptr || initType->IsTypeError()) {
856         return bindingVar->SetTsType(annotationType != nullptr ? annotationType : GlobalTypeError());
857     }
858 
859     if (typeAnnotation == nullptr && initType->IsETSFunctionType()) {
860         annotationType = initType->AsETSFunctionType();
861         bindingVar->SetTsType(annotationType);
862     }
863 
864     if (annotationType != nullptr) {
865         if (typeAnnotation != nullptr) {
866             CheckRecordType(init, annotationType, this);
867             AssignmentContext(Relation(), init, initType, annotationType, init->Start(),
868                               {{diagnostic::INVALID_ASSIGNMNENT, {initType, annotationType}}});
869             if (!Relation()->IsTrue()) {
870                 return annotationType;
871             }
872         }
873 
874         if (IsOmitConstInit(flags) && ShouldPreserveConstantTypeInVariableDeclaration(annotationType, initType)) {
875             VariableTypeFromInitializer(bindingVar, MaybeUnboxType(annotationType), initType);
876         }
877     } else {
878         CheckEnumType(init, initType, ident->Name());
879 
880         // NOTE: need to be done by smart casts
881         auto needWidening = !IsOmitConstInit(flags) && typeAnnotation == nullptr && NeedWidening(init);
882         bindingVar->SetTsType(needWidening ? GetNonConstantType(initType) : initType);
883     }
884 
885     return FixOptionalVariableType(bindingVar, flags, init);
886 }
887 
VariableTypeFromInitializer(varbinder::Variable * variable,Type * annotationType,Type * initType)888 void ETSChecker::VariableTypeFromInitializer(varbinder::Variable *variable, Type *annotationType, Type *initType)
889 {
890     if (!initType->IsConstantType()) {
891         return;
892     }
893 
894     if (!initType->IsETSPrimitiveType()) {
895         variable->SetTsType(initType);
896         return;
897     }
898 
899     ES2PANDA_ASSERT(annotationType->IsETSPrimitiveType());
900     //  We suppose here that all required checks were passed and correct conversion is possible without accuracy loss
901     variable->SetTsType(TypeConverter::ConvertConstantTypes(initType, annotationType, ProgramAllocator()));
902 }
903 
ResolveGetter(checker::ETSChecker * checker,ir::MemberExpression * const expr,ETSFunctionType * funcType)904 static checker::Type *ResolveGetter(checker::ETSChecker *checker, ir::MemberExpression *const expr,
905                                     ETSFunctionType *funcType)
906 {
907     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
908     auto signature = checker->FindRelativeExtensionGetter(expr, funcType);
909     if (signature != nullptr) {
910         return signature->ReturnType();
911     }
912 
913     PropertySearchFlags flags = PropertySearchFlags::SEARCH_INSTANCE_METHOD | PropertySearchFlags::SEARCH_IN_BASE |
914                                 PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::IS_GETTER;
915     auto objType = expr->ObjType();
916     auto searchName = objType->GetReExportAliasValue(expr->Property()->AsIdentifier()->Name());
917     auto *prop = objType->GetProperty(searchName, flags);
918     if (prop == nullptr || prop->TsType() == nullptr ||
919         !prop->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) {
920         return nullptr;
921     }
922     auto *propType = prop->TsType()->AsETSFunctionType();
923     auto originalGetter = propType->FindGetter();
924     if (originalGetter == nullptr) {
925         return nullptr;
926     }
927 
928     checker->ValidateSignatureAccessibility(objType, originalGetter, expr->Start());
929     return originalGetter->ReturnType();
930 }
931 
FindRelativeExtensionGetter(ir::MemberExpression * const expr,ETSFunctionType * funcType)932 Signature *ETSChecker::FindRelativeExtensionGetter(ir::MemberExpression *const expr, ETSFunctionType *funcType)
933 {
934     ES2PANDA_ASSERT(expr->ObjType() != nullptr);
935     ES2PANDA_ASSERT(funcType != nullptr && funcType->IsExtensionAccessorType());
936     ES2PANDA_ASSERT(expr->Property()->AsIdentifier()->Name() == funcType->Name());
937     if (auto sig = FindExtensionGetterInMap(funcType->Name(), expr->ObjType()); sig != nullptr) {
938         return sig;
939     }
940 
941     ArenaVector<ir::Expression *> arguments(ProgramAllocator()->Adapter());
942     arguments.insert(arguments.begin(), expr->Object());
943     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
944     Signature *signature = ValidateSignatures(funcType->GetExtensionAccessorSigs(), nullptr, arguments, expr->Start(),
945                                               "call", TypeRelationFlag::NO_THROW);
946     if (signature != nullptr) {
947         InsertExtensionGetterToMap(funcType->Name(), expr->ObjType(), signature);
948     }
949     return signature;
950 }
951 
FindRelativeExtensionSetter(ir::MemberExpression * expr,ETSFunctionType * funcType)952 Signature *ETSChecker::FindRelativeExtensionSetter(ir::MemberExpression *expr, ETSFunctionType *funcType)
953 {
954     ES2PANDA_ASSERT(expr->ObjType() != nullptr);
955     ES2PANDA_ASSERT(funcType != nullptr && funcType->IsExtensionAccessorType());
956     ES2PANDA_ASSERT(expr->Property()->AsIdentifier()->Name() == funcType->Name());
957     ES2PANDA_ASSERT(expr->Parent()->IsAssignmentExpression() || expr->Parent()->IsUpdateExpression());
958     if (auto const sig = FindExtensionSetterInMap(funcType->Name(), expr->ObjType()); sig != nullptr) {
959         return sig;
960     }
961 
962     Signature *signature = nullptr;
963     ArenaVector<ir::Expression *> arguments(ProgramAllocator()->Adapter());
964     arguments.insert(arguments.begin(), expr->Object());
965     if (expr->Parent()->IsAssignmentExpression()) {
966         arguments.emplace_back(expr->Parent()->AsAssignmentExpression()->Right());
967         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
968         signature = ValidateSignatures(funcType->GetExtensionAccessorSigs(), nullptr, arguments, expr->Start(), "call",
969                                        TypeRelationFlag::NO_THROW);
970     } else {
971         // When handle ++a.m, a.m++, is mean to check whether a.m(xx, 1) existed.
972         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
973         Type *getterReturnType = ResolveGetter(this, expr, funcType);
974         expr->SetTsType(getterReturnType);
975         arguments.emplace_back(expr);
976         signature = ValidateSignatures(funcType->GetExtensionAccessorSigs(), nullptr, arguments, expr->Start(), "call",
977                                        TypeRelationFlag::NO_THROW);
978     }
979 
980     if (signature == nullptr) {
981         return nullptr;
982     }
983     InsertExtensionSetterToMap(funcType->Name(), expr->ObjType(), signature);
984     return signature;
985 }
986 
987 // Note: extension accessor looks same as member expression in checker phase, but its return type is FunctionType,
988 // we need to get type of extension accessor in checker. For getter, its type is the return type of the function.
989 // and for setter, it was a member expression set as left child of an assignment expression, we temporarily set its
990 // type the same as the right child type. Further work will be done in lowering.
GetExtensionAccessorReturnType(ir::MemberExpression * expr)991 checker::Type *ETSChecker::GetExtensionAccessorReturnType(ir::MemberExpression *expr)
992 {
993     ES2PANDA_ASSERT(IsExtensionETSFunctionType(expr->TsType()));
994 
995     auto eAccType = expr->TsType()->AsETSFunctionType();
996     auto assignExpr = expr->Parent();
997     bool isExtensionSetterLike =
998         assignExpr->IsAssignmentExpression() && (assignExpr->AsAssignmentExpression()->Left() == expr);
999     if (isExtensionSetterLike) {
1000         if (!assignExpr->Parent()->IsExpressionStatement()) {
1001             LogError(diagnostic::EXTENSION_GETTER_INVALID_CTX, {}, assignExpr->Start());
1002             return GlobalTypeError();
1003         }
1004         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1005         auto signature = FindRelativeExtensionSetter(expr, eAccType);
1006         if (signature != nullptr && !expr->Parent()->IsUpdateExpression()) {
1007             // for a.m += otherExpr, we need to validateSignature again.
1008             ArenaVector<ir::Expression *> arguments(ProgramAllocator()->Adapter());
1009             ArenaVector<Signature *> candidateSig(ProgramAllocator()->Adapter());
1010             candidateSig.emplace_back(signature);
1011             arguments.emplace_back(expr->Object());
1012             arguments.emplace_back(expr->Parent()->AsAssignmentExpression()->Right());
1013             signature =
1014                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1015                 ValidateSignatures(candidateSig, nullptr, arguments, expr->Start(), "call", TypeRelationFlag::NO_THROW);
1016         }
1017 
1018         if (signature == nullptr) {
1019             LogError(diagnostic::MISSING_EXTENSION_ACCESSOR, {}, expr->Start());
1020             return GlobalTypeError();
1021         }
1022     }
1023     expr->SetExtensionAccessorType(eAccType);
1024     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1025     return ResolveGetter(this, expr, eAccType);
1026 }
1027 
1028 //==============================================================================//
1029 // Smart cast support
1030 //==============================================================================//
1031 
ResolveSmartType(checker::Type * sourceType,checker::Type * targetType)1032 checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType)
1033 {
1034     //  For left-hand variable of primitive type leave it as is.
1035     if (targetType->IsETSPrimitiveType()) {
1036         return targetType;
1037     }
1038 
1039     //  For left-hand variable of tuple type leave it as is.
1040     if (targetType->IsETSTupleType()) {
1041         return targetType;
1042     }
1043 
1044     //  For left-hand invalid variable set smart type to right-hand type.
1045     if (targetType->IsTypeError()) {
1046         return sourceType;
1047     }
1048 
1049     //  For left-hand variable of builtin type leave it as is.
1050     if (targetType->IsETSObjectType() && targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) {
1051         return targetType;
1052     }
1053 
1054     // Nothing to do with identical types:
1055     auto *nonConstSourceType = GetNonConstantType(sourceType);
1056     auto *nonConstTargetType = GetNonConstantType(targetType);
1057 
1058     if (Relation()->IsIdenticalTo(nonConstSourceType, nonConstTargetType) ||
1059         Relation()->IsIdenticalTo(GlobalBuiltinJSValueType(), nonConstTargetType)) {
1060         return targetType;
1061     }
1062 
1063     //  For type parameter, null or undefined source type return it as is.
1064     if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) {
1065         return sourceType;
1066     }
1067 
1068     //  In case of Union left-hand type we have to select the proper type from the Union
1069     //  Because now we have logging of errors we have to continue analyze incorrect program, for
1070     //  this case we change typeError to source type.
1071     if (targetType->IsETSUnionType()) {
1072         auto component = targetType->AsETSUnionType()->GetAssignableType(this, sourceType);
1073         return component->IsTypeError() ? MaybeBoxType(sourceType) : component;
1074     }
1075 
1076     //  If source is reference type, set it as the current and use it for identifier smart cast
1077     if (sourceType->IsETSReferenceType()) {
1078         return sourceType;
1079     }
1080 
1081     //  For right-hand variable of primitive type apply boxing conversion (case: 'let x: Object = 5', then x => Int).
1082     if (sourceType->IsETSPrimitiveType() && !sourceType->IsETSVoidType() && targetType->IsETSObjectType()) {
1083         return MaybeBoxInRelation(sourceType);
1084     }
1085 
1086     //  NOTE - it seems that all the other possible cases are assignments like:
1087     //  'Object = ObjectLiteral' or smth similar ???
1088     //  thus for such cases also leave the target type as is.
1089     //  Possible errors in tests should clarify this hypothesis sooner or later :)
1090     return targetType;
1091 }
1092 
1093 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestNullishCondition(Type * testedType,Type * actualType,bool const strict)1094 std::pair<Type *, Type *> ETSChecker::CheckTestNullishCondition(Type *testedType, Type *actualType, bool const strict)
1095 {
1096     if (!strict) {
1097         return RemoveNullishTypes(actualType);
1098     }
1099 
1100     if (testedType->IsETSNullType()) {
1101         return {GlobalETSNullType(), RemoveNullType(actualType)};
1102     }
1103 
1104     if (testedType->IsETSUndefinedType()) {
1105         return {GlobalETSUndefinedType(), RemoveUndefinedType(actualType)};
1106     }
1107 
1108     return {GlobalETSUnionUndefinedNull(), GetNonNullishType(actualType)};
1109 }
1110 
1111 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSArrayType * testedType,Type * actualType)1112 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSArrayType *testedType, Type *actualType)
1113 {
1114     if (actualType->IsETSUnionType()) {
1115         return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
1116     }
1117 
1118     // Both testing and actual (smart) types are arrays. Set types according to their relation.
1119     // NOTE: probably the rules of type extraction should be modified later on!
1120     if (actualType->IsETSArrayType()) {
1121         auto *const arrayType = actualType->AsETSArrayType();
1122 
1123         if (Relation()->IsIdenticalTo(arrayType, testedType)) {
1124             return {testedType, GetGlobalTypesHolder()->GlobalETSNeverType()};
1125         }
1126 
1127         if (Relation()->IsSupertypeOf(arrayType, testedType)) {
1128             return {testedType, actualType};
1129         }
1130 
1131         if (Relation()->IsSupertypeOf(testedType, arrayType)) {
1132             return {testedType, actualType};
1133         }
1134     } else if (actualType->IsETSObjectType() && actualType->AsETSObjectType()->IsGlobalETSObjectType()) {
1135         return {testedType, actualType};
1136     }
1137 
1138     return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType};
1139 }
1140 
1141 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSObjectType * testedType,Type * actualType,bool const strict)1142 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType,
1143                                                                bool const strict)
1144 {
1145     if (actualType->IsETSUnionType()) {
1146         return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
1147     }
1148 
1149     // Both testing and actual (smart) types are objects. Set types according to their relation.
1150     // NOTE: probably the rules of type extraction should be modified later on!
1151     if (actualType->IsETSObjectType()) {
1152         auto *const objectType = actualType->AsETSObjectType();
1153 
1154         if (Relation()->IsIdenticalTo(objectType, testedType) ||
1155             objectType->AssemblerName() == testedType->AssemblerName()) {
1156             return {testedType, strict ? GetGlobalTypesHolder()->GlobalETSNeverType() : actualType};
1157         }
1158 
1159         if (Relation()->IsSupertypeOf(objectType, testedType)) {
1160             return {testedType, actualType};
1161         }
1162 
1163         if (Relation()->IsSupertypeOf(testedType, objectType)) {
1164             return {testedType, actualType};
1165         }
1166 
1167         return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType};
1168     }
1169 
1170     return {testedType, actualType};
1171 }
1172 
1173 // Simple and conservative implementation unless smartcasts are not rewritten completely
ComputeConditionalSubtypes(TypeRelation * relation,Type * condition,Type * actual)1174 static std::pair<Type *, Type *> ComputeConditionalSubtypes(TypeRelation *relation, Type *condition, Type *actual)
1175 {
1176     if (relation->IsSupertypeOf(condition, actual)) {
1177         if (relation->IsIdenticalTo(condition, actual)) {
1178             return {condition, relation->GetChecker()->GetGlobalTypesHolder()->GlobalETSNeverType()};
1179         }
1180         return {actual, actual};
1181     }
1182     return {condition, actual};
1183 }
1184 
GetUnionOfTypes(checker::Type * const type1,checker::Type * const type2) const1185 checker::Type *CheckerContext::GetUnionOfTypes(checker::Type *const type1, checker::Type *const type2) const noexcept
1186 {
1187     if (type1 == nullptr || type2 == nullptr) {
1188         return type1 == nullptr ? type2 : type1;
1189     }
1190 
1191     const auto never = parent_->AsETSChecker()->GlobalETSNeverType();
1192     if (type1 == never || type2 == never) {
1193         return type1 == never ? type2 : type1;
1194     }
1195 
1196     return parent_->AsETSChecker()->CreateETSUnionType({type1, type2});
1197 }
1198 
GetIntersectionOfTypeAndTypeSetIfExist(ETSChecker * checker,checker::Type * type,const ArenaVector<Type * > & typeSet)1199 static std::optional<checker::Type *> GetIntersectionOfTypeAndTypeSetIfExist(ETSChecker *checker, checker::Type *type,
1200                                                                              const ArenaVector<Type *> &typeSet)
1201 {
1202     for (auto *const typeOfTypeSet : typeSet) {
1203         if (checker->Relation()->IsSupertypeOf(type, typeOfTypeSet)) {
1204             return std::make_optional(typeOfTypeSet);
1205         }
1206 
1207         if (checker->Relation()->IsSupertypeOf(typeOfTypeSet, type)) {
1208             return std::make_optional(type);
1209         }
1210     }
1211 
1212     return std::nullopt;
1213 }
1214 
1215 // The method name 'intersection' may be misleading. The function does not create a theoretically correct
1216 // intersection type from 2 arbitrary types, instead does an intersection operation on the type set of 2 (possibly)
1217 // union types. Currently this is good enough, and the correct implementation with type algebra will be in the complete
1218 // smart type rework with CFG.
GetIntersectionOfTypes(checker::Type * const type1,checker::Type * const type2) const1219 checker::Type *CheckerContext::GetIntersectionOfTypes(checker::Type *const type1,
1220                                                       checker::Type *const type2) const noexcept
1221 {
1222     if (type1 == nullptr || type2 == nullptr) {
1223         return type1 == nullptr ? type2 : type1;
1224     }
1225 
1226     auto *const checker = parent_->AsETSChecker();
1227 
1228     if (checker->Relation()->IsIdenticalTo(type1, type2)) {
1229         return type1;
1230     }
1231 
1232     ArenaVector<Type *> typeSet1(checker->ProgramAllocator()->Adapter());
1233     ArenaVector<Type *> typeSet2(checker->ProgramAllocator()->Adapter());
1234     ArenaVector<Type *> intersection(checker->ProgramAllocator()->Adapter());
1235 
1236     if (type1->IsETSUnionType()) {
1237         typeSet1 = type1->AsETSUnionType()->ConstituentTypes();
1238     } else {
1239         typeSet1.push_back(type1);
1240     }
1241 
1242     if (type2->IsETSUnionType()) {
1243         typeSet2 = type2->AsETSUnionType()->ConstituentTypes();
1244     } else {
1245         typeSet2.push_back(type2);
1246     }
1247 
1248     for (auto *const typeOfTypeSet1 : typeSet1) {
1249         auto possibleIntersectionType = GetIntersectionOfTypeAndTypeSetIfExist(checker, typeOfTypeSet1, typeSet2);
1250         if (possibleIntersectionType.has_value()) {
1251             intersection.emplace_back(*possibleIntersectionType);
1252         }
1253     }
1254 
1255     return checker->CreateETSUnionType(std::move(intersection));
1256 }
1257 
1258 static constexpr std::size_t const VARIABLE_POSITION = 0UL;
1259 static constexpr std::size_t const CONSEQUENT_TYPE_POSITION = 1UL;
1260 static constexpr std::size_t const ALTERNATE_TYPE_POSITION = 2UL;
1261 
MergeSmartTypesForLogicalAnd(SmartCastTuple & newSmartCastTypes)1262 void CheckerContext::MergeSmartTypesForLogicalAnd(SmartCastTuple &newSmartCastTypes)
1263 {
1264     auto const &variable = std::get<VARIABLE_POSITION>(newSmartCastTypes);
1265 
1266     if (testSmartCasts_.find(variable) == testSmartCasts_.end()) {
1267         testSmartCasts_.emplace(
1268             variable, std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(newSmartCastTypes), variable->TsType()));
1269         return;
1270     }
1271 
1272     auto *newConsequentType = std::get<CONSEQUENT_TYPE_POSITION>(newSmartCastTypes);
1273     auto *newAlternateType = std::get<ALTERNATE_TYPE_POSITION>(newSmartCastTypes);
1274     auto [currentConsequentType, currentAlternateType] = testSmartCasts_.at(variable);
1275     auto *const mergedConsequentType = GetIntersectionOfTypes(currentConsequentType, newConsequentType);
1276     auto *const mergedAlternateType = GetUnionOfTypes(currentAlternateType, newAlternateType);
1277     testSmartCasts_[variable] = {mergedConsequentType, mergedAlternateType};
1278 }
1279 
InvalidateNecessarySmartCastsInLogicalAnd(std::optional<SmartCastTuple> & newSmartCastTypes)1280 void CheckerContext::InvalidateNecessarySmartCastsInLogicalAnd(std::optional<SmartCastTuple> &newSmartCastTypes)
1281 {
1282     // If there is no new smart casts in the checked expression, then invalidate all smart casts for the alternate
1283     // branch.
1284     // Eg. if(x == null && false) -> we didn't get a new smart cast from 'false', so in the alternate branch 'x'
1285     // can also be 'null'
1286 
1287     if (!newSmartCastTypes.has_value()) {
1288         for (auto &smartCast : testSmartCasts_) {
1289             smartCast.second.second = nullptr;
1290         }
1291 
1292         return;
1293     }
1294 
1295     // If there is already exist a smart cast for a variable, that isn't checked in the current expression for the
1296     // logical and, it means that in the alternate branch it's smart type will not be valid.
1297     //
1298     // Example 1:
1299     // cond: x != null && y != null
1300     // Here the new smart cast will only contain smart type for 'y', but it's unrelated to the type of 'x', so both
1301     // smart type needs to by invalidated in the alternate branch.
1302     //
1303     // Example 2:
1304     // let x: null|undefined|A  <- A is an arbitrary type
1305     // cond: x != null && x != undefined
1306     // Here the new smart type will contain further check for 'x', so it's still checking the same variable. In this
1307     // case the alternate type will contain the already computed union type 'null|undefined' , and not needed to be
1308     // restored to 'null|undefined|A'.
1309 
1310     auto const &variable = std::get<VARIABLE_POSITION>(*newSmartCastTypes);
1311     bool anyOtherSmartCastCleared = false;
1312 
1313     for (auto existingSmartCast : testSmartCasts_) {
1314         if (existingSmartCast.first != variable) {
1315             // If there is a smart cast that doesn't include the current variable, then we need to invalidate the
1316             // consequent types, because from now on these are unrelated restrictions
1317             testSmartCasts_[existingSmartCast.first].second = nullptr;
1318             anyOtherSmartCastCleared = true;
1319         }
1320     }
1321 
1322     if (anyOtherSmartCastCleared) {
1323         testSmartCasts_[variable].second = nullptr;
1324     }
1325 }
1326 
GetReassignedVariablesInNode(const ir::AstNode * const node) const1327 ReassignedVariableMap CheckerContext::GetReassignedVariablesInNode(const ir::AstNode *const node) const
1328 {
1329     ReassignedVariableMap changedVariables {};
1330     node->Iterate([this, &changedVariables](ir::AstNode *childNode) { CheckAssignments(childNode, changedVariables); });
1331     return changedVariables;
1332 }
1333 
CheckTestSmartCastCondition(lexer::TokenType operatorType)1334 void CheckerContext::CheckTestSmartCastCondition(lexer::TokenType operatorType)
1335 {
1336     if (operatorType != lexer::TokenType::EOS && operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_AND &&
1337         operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
1338         return;
1339     }
1340 
1341     auto types = ResolveSmartCastTypes();
1342 
1343     if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
1344         if (types.has_value()) {
1345             MergeSmartTypesForLogicalAnd(*types);
1346         }
1347         InvalidateNecessarySmartCastsInLogicalAnd(types);
1348     } else if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
1349         if (!types.has_value() || CheckTestOrSmartCastCondition(*types)) {
1350             //  Clear consequent types, because now they become indefinite
1351             for (auto &smartCast : testSmartCasts_) {
1352                 smartCast.second.first = nullptr;
1353             }
1354         }
1355     } else if (types.has_value()) {
1356         testSmartCasts_.emplace(
1357             std::get<VARIABLE_POSITION>(*types),
1358             std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types), std::get<ALTERNATE_TYPE_POSITION>(*types)));
1359     }
1360 
1361     testCondition_ = {};
1362     operatorType_ = operatorType;
1363 }
1364 
ResolveSmartCastTypes()1365 std::optional<SmartCastTuple> CheckerContext::ResolveSmartCastTypes()
1366 {
1367     if (testCondition_.variable == nullptr) {
1368         return std::nullopt;
1369     }
1370 
1371     // Exclude processing of global variables and those captured in lambdas and modified there
1372     auto const *const variableScope = testCondition_.variable->GetScope();
1373     auto const topLevelVariable =
1374         variableScope != nullptr ? variableScope->IsGlobalScope() ||
1375                                        (variableScope->Parent() != nullptr && variableScope->Parent()->IsGlobalScope())
1376                                  : false;
1377     if (topLevelVariable) {
1378         return std::nullopt;
1379     }
1380 
1381     ES2PANDA_ASSERT(testCondition_.testedType != nullptr);
1382     if (!testCondition_.testedType->IsETSReferenceType()) {
1383         return std::nullopt;
1384     }
1385 
1386     auto *smartType = GetSmartCast(testCondition_.variable);
1387     if (smartType == nullptr) {
1388         smartType = testCondition_.variable->TsType();
1389         if (smartType == nullptr) {
1390             return std::nullopt;
1391         }
1392     }
1393 
1394     auto *const checker = parent_->AsETSChecker();
1395     Type *consequentType = nullptr;
1396     Type *alternateType = nullptr;
1397 
1398     if (testCondition_.testedType->DefinitelyETSNullish()) {
1399         // In case of testing for 'null' and/or 'undefined' remove corresponding null-like types.
1400         std::tie(consequentType, alternateType) =
1401             checker->CheckTestNullishCondition(testCondition_.testedType, smartType, testCondition_.strict);
1402     } else if (testCondition_.testedType->IsETSObjectType()) {
1403         auto *const testedType = testCondition_.testedType->AsETSObjectType();
1404         std::tie(consequentType, alternateType) =
1405             checker->CheckTestObjectCondition(testedType, smartType, testCondition_.strict);
1406     } else if (testCondition_.testedType->IsETSArrayType()) {
1407         auto *const testedType = testCondition_.testedType->AsETSArrayType();
1408         std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType);
1409     } else if (testCondition_.strict) {
1410         std::tie(consequentType, alternateType) =
1411             ComputeConditionalSubtypes(checker->Relation(), testCondition_.testedType, smartType);
1412     }
1413 
1414     return !testCondition_.negate
1415                ? std::make_optional(std::make_tuple(testCondition_.variable, consequentType, alternateType))
1416                : std::make_optional(std::make_tuple(testCondition_.variable, alternateType, consequentType));
1417 }
CheckVoidAnnotation(const ir::ETSPrimitiveType * typeAnnotation)1418 bool ETSChecker::CheckVoidAnnotation(const ir::ETSPrimitiveType *typeAnnotation)
1419 {
1420     // Void annotation is valid only when used as 'return type' , 'type parameter instantiation', 'default type'.
1421     if (typeAnnotation->GetPrimitiveType() != ir::PrimitiveType::VOID) {
1422         return true;
1423     }
1424 
1425     auto parent = typeAnnotation->Parent();
1426     if (parent->IsScriptFunction() && parent->AsScriptFunction()->ReturnTypeAnnotation() == typeAnnotation) {
1427         return true;
1428     }
1429     if (parent->IsETSFunctionType() && parent->AsETSFunctionType()->ReturnType() == typeAnnotation) {
1430         return true;
1431     }
1432     if (parent->IsTSTypeParameterInstantiation() || parent->IsTSTypeParameter()) {
1433         return true;
1434     }
1435     LogError(diagnostic::ANNOT_IS_VOID, {}, typeAnnotation->Start());
1436     return false;
1437 }
ApplySmartCast(varbinder::Variable const * const variable,checker::Type * const smartType)1438 void ETSChecker::ApplySmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept
1439 {
1440     ES2PANDA_ASSERT(variable != nullptr);
1441     if (smartType != nullptr) {
1442         auto *variableType = variable->TsType();
1443 
1444         if (Relation()->IsIdenticalTo(variableType, smartType)) {
1445             Context().RemoveSmartCast(variable);
1446         } else {
1447             Context().SetSmartCast(variable, smartType);
1448         }
1449     }
1450 }
1451 
CheckTestOrSmartCastCondition(SmartCastTuple const & types)1452 bool CheckerContext::CheckTestOrSmartCastCondition(SmartCastTuple const &types)
1453 {
1454     auto *const &variable = std::get<VARIABLE_POSITION>(types);
1455     auto *const &consequentTypeNew = std::get<CONSEQUENT_TYPE_POSITION>(types);
1456     auto *const &alternateTypeNew = std::get<ALTERNATE_TYPE_POSITION>(types);
1457 
1458     if (auto const it = testSmartCasts_.find(variable); it != testSmartCasts_.end()) {
1459         auto *const consequentTypeOld = it->second.first;
1460         if (consequentTypeOld == nullptr) {
1461             return true;
1462         }
1463 
1464         if (consequentTypeNew != nullptr && !parent_->Relation()->IsIdenticalTo(consequentTypeOld, consequentTypeNew)) {
1465             it->second.first = parent_->AsETSChecker()->CreateETSUnionType({consequentTypeOld, consequentTypeNew});
1466         }
1467 
1468         if (auto *const alternateTypeOld = it->second.second; alternateTypeOld != nullptr) {
1469             if (alternateTypeNew != nullptr &&
1470                 !parent_->Relation()->IsIdenticalTo(alternateTypeOld, alternateTypeNew)) {
1471                 it->second.second = parent_->AsETSChecker()->CreateETSUnionType({alternateTypeOld, alternateTypeNew});
1472             }
1473         } else {
1474             it->second.second = alternateTypeNew;
1475         }
1476 
1477         return false;
1478     }
1479 
1480     //  NOTE: now we support only cases like 'if (x != null || y != null)' or 'if (x instanceof A || x instanceof B)'
1481     //  although it seems that the resulting variable type in the second case isn't used in subsequent code directly.
1482     //  More complex conditions can be implemented later on if the need arises.
1483     testSmartCasts_.emplace(variable, std::make_pair(consequentTypeNew, alternateTypeNew));
1484     return true;
1485 }
1486 
1487 //==============================================================================//
1488 
SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression * expr,Type * annotationType)1489 void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType)
1490 {
1491     if ((expr == nullptr) || (annotationType == nullptr)) {
1492         return;
1493     }
1494 
1495     if (expr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1496         return;
1497     }
1498 
1499     // Expand all member expressions
1500     Type *elementType = annotationType;
1501     ir::Expression *object = expr->Object();
1502     while ((object != nullptr) && (object->IsMemberExpression())) {
1503         ir::MemberExpression *memberExpr = object->AsMemberExpression();
1504         if (memberExpr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1505             return;
1506         }
1507 
1508         object = memberExpr->Object();
1509         elementType = CreateETSArrayType(elementType);
1510     }
1511 
1512     // Set explicit target type for array
1513     if ((object != nullptr) && (object->IsArrayExpression())) {
1514         ir::ArrayExpression *array = object->AsArrayExpression();
1515         array->SetPreferredType(CreateETSArrayType(elementType));
1516     }
1517 }
1518 
1519 // 22955: type alias should be instantiated with Substitute
CollectAliasParametersForBoxing(Type * expandedAliasType,std::set<Type * > & parametersNeedToBeBoxed,bool needToBeBoxed)1520 static void CollectAliasParametersForBoxing(Type *expandedAliasType, std::set<Type *> &parametersNeedToBeBoxed,
1521                                             bool needToBeBoxed)
1522 {
1523     if (expandedAliasType->IsETSTypeParameter() && needToBeBoxed) {
1524         parametersNeedToBeBoxed.insert(expandedAliasType);
1525     } else if (expandedAliasType->IsETSObjectType()) {
1526         auto objectType = expandedAliasType->AsETSObjectType();
1527         needToBeBoxed = objectType->GetDeclNode() != nullptr && (objectType->GetDeclNode()->IsClassDefinition() ||
1528                                                                  objectType->GetDeclNode()->IsTSInterfaceDeclaration());
1529         for (const auto typeArgument : objectType->TypeArguments()) {
1530             CollectAliasParametersForBoxing(typeArgument, parametersNeedToBeBoxed, needToBeBoxed);
1531         }
1532     } else if (expandedAliasType->IsETSTupleType()) {
1533         auto tupleType = expandedAliasType->AsETSTupleType();
1534         needToBeBoxed = false;
1535         for (auto type : tupleType->GetTupleTypesList()) {
1536             CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed);
1537         }
1538     } else if (expandedAliasType->IsETSArrayType()) {
1539         auto arrayType = expandedAliasType->AsETSArrayType();
1540         needToBeBoxed = false;
1541         auto elementType = arrayType->ElementType();
1542         CollectAliasParametersForBoxing(elementType, parametersNeedToBeBoxed, needToBeBoxed);
1543     } else if (expandedAliasType->IsETSUnionType()) {
1544         auto unionType = expandedAliasType->AsETSUnionType();
1545         needToBeBoxed = false;
1546         for (auto type : unionType->ConstituentTypes()) {
1547             CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed);
1548         }
1549     } else if (expandedAliasType->IsETSFunctionType()) {
1550         auto functionType = expandedAliasType->AsETSFunctionType();
1551         needToBeBoxed = true;
1552         for (auto param : functionType->ArrowSignature()->Params()) {
1553             CollectAliasParametersForBoxing(param->TsType(), parametersNeedToBeBoxed, needToBeBoxed);
1554         }
1555         CollectAliasParametersForBoxing(functionType->ArrowSignature()->ReturnType(), parametersNeedToBeBoxed,
1556                                         needToBeBoxed);
1557     }
1558 }
1559 
CheckMinimumTypeArgsPresent(const ir::TSTypeAliasDeclaration * typeAliasNode,const ir::TSTypeParameterInstantiation * typeParams)1560 bool ETSChecker::CheckMinimumTypeArgsPresent(const ir::TSTypeAliasDeclaration *typeAliasNode,
1561                                              const ir::TSTypeParameterInstantiation *typeParams)
1562 {
1563     size_t minNumberOfTypeParams =
1564         std::count_if(typeAliasNode->TypeParams()->Params().begin(), typeAliasNode->TypeParams()->Params().end(),
1565                       [](const ir::TSTypeParameter *param) { return param->DefaultType() == nullptr; });
1566     if (minNumberOfTypeParams > typeParams->Params().size() ||
1567         typeParams->Params().size() > typeAliasNode->TypeParams()->Params().size()) {
1568         LogError(diagnostic::EXPECTED_TYPE_ARGUMENTS, {minNumberOfTypeParams, typeParams->Params().size()},
1569                  typeParams->Start());
1570         return true;
1571     }
1572 
1573     return false;
1574 }
1575 
ResolveTypeNodeForTypeArg(const ir::TSTypeAliasDeclaration * typeAliasNode,const ir::TSTypeParameterInstantiation * typeParams,size_t idx)1576 ir::TypeNode *ETSChecker::ResolveTypeNodeForTypeArg(const ir::TSTypeAliasDeclaration *typeAliasNode,
1577                                                     const ir::TSTypeParameterInstantiation *typeParams, size_t idx)
1578 {
1579     if (typeParams != nullptr && typeParams->Params().size() > idx) {
1580         return typeParams->Params().at(idx);
1581     }
1582 
1583     return typeAliasNode->TypeParams()->Params().at(idx)->DefaultType();
1584 }
1585 
HandleTypeAlias(ir::Expression * const name,const ir::TSTypeParameterInstantiation * const typeParams,ir::TSTypeAliasDeclaration * const typeAliasNode)1586 Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams,
1587                                   ir::TSTypeAliasDeclaration *const typeAliasNode)
1588 {
1589     if (typeParams == nullptr && typeAliasNode->TypeParams() != nullptr) {
1590         auto declTypeParams = typeAliasNode->TypeParams()->Params();
1591         auto isAllTypeParamsHasDefaultType =
1592             std::find_if(declTypeParams.begin(), declTypeParams.end(), [](ir::TSTypeParameter *param) {
1593                 return param->DefaultType() == nullptr;
1594             }) == declTypeParams.end();
1595         if (!isAllTypeParamsHasDefaultType) {
1596             LogError(diagnostic::GENERIC_ALIAS_WITHOUT_PARAMS, {}, name->Start());
1597             return GlobalTypeError();
1598         }
1599     }
1600 
1601     if (typeParams != nullptr && typeAliasNode->TypeParams() == nullptr) {
1602         LogError(diagnostic::NON_GENERIC_ALIAS_WITH_PARAMS, {}, typeParams->Start());
1603         return GlobalTypeError();
1604     }
1605 
1606     if (typeParams == nullptr && typeAliasNode->TypeParams() == nullptr) {
1607         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1608         return GetReferencedTypeBase(name);
1609     }
1610 
1611     if (typeParams != nullptr) {
1612         for (auto *const origTypeParam : typeParams->Params()) {
1613             origTypeParam->Check(this);
1614         }
1615         if (CheckMinimumTypeArgsPresent(typeAliasNode, typeParams)) {
1616             return GlobalTypeError();
1617         }
1618     }
1619 
1620     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1621     Type *const aliasType = GetReferencedTypeBase(name);
1622     auto *substitution = NewSubstitution();
1623 
1624     std::set<Type *> parametersNeedToBeBoxed;
1625     auto expandedAliasType = aliasType->Substitute(Relation(), substitution);
1626     CollectAliasParametersForBoxing(expandedAliasType, parametersNeedToBeBoxed, false);
1627 
1628     ES2PANDA_ASSERT(substitution != nullptr);
1629     for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) {
1630         auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name();
1631         auto *typeAliasType = typeAliasTypeName->Variable()->TsType();
1632         if (!typeAliasType->IsETSTypeParameter()) {
1633             continue;
1634         }
1635 
1636         ir::TypeNode *typeNode = ResolveTypeNodeForTypeArg(typeAliasNode, typeParams, idx);
1637         auto paramType = typeNode->GetType(this);
1638 
1639         if (parametersNeedToBeBoxed.find(typeAliasType) != parametersNeedToBeBoxed.end()) {
1640             if (const auto boxedType = MaybeBoxInRelation(typeNode->GetType(this)); boxedType != nullptr) {
1641                 paramType = boxedType;
1642             }
1643         }
1644         substitution->insert({typeAliasType->AsETSTypeParameter(), paramType});  // #21835: type argument is not boxed
1645     }
1646 
1647     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1648     ValidateGenericTypeAliasForClonedNode(typeAliasNode->AsTSTypeAliasDeclaration(), typeParams);
1649 
1650     return aliasType->Substitute(Relation(), substitution);
1651 }
1652 
GetNameForSynteticObjectType(const util::StringView & source)1653 std::vector<util::StringView> ETSChecker::GetNameForSynteticObjectType(const util::StringView &source)
1654 {
1655     const std::string str = source.Mutf8();
1656     std::istringstream ss {str};
1657     const char delimiter = '.';
1658     std::string token;
1659 
1660     std::vector<util::StringView> syntheticName {};
1661 
1662     while (std::getline(ss, token, delimiter)) {
1663         if (!token.empty()) {
1664             util::UString sV(token, ProgramAllocator());
1665             syntheticName.emplace_back(sV.View());
1666         }
1667     }
1668 
1669     return syntheticName;
1670 }
1671 
FindSpecifierForModuleObject(ir::ETSImportDeclaration * importDecl,util::StringView const & name)1672 std::pair<bool, util::StringView> FindSpecifierForModuleObject(ir::ETSImportDeclaration *importDecl,
1673                                                                util::StringView const &name)
1674 {
1675     if (importDecl == nullptr) {
1676         return std::make_pair(true, util::StringView());
1677     }
1678 
1679     for (auto item : importDecl->Specifiers()) {
1680         if (item->IsImportSpecifier() && item->AsImportSpecifier()->Imported()->Name().Is(name.Mutf8())) {
1681             if (!item->AsImportSpecifier()->Imported()->Name().Is(item->AsImportSpecifier()->Local()->Name().Mutf8())) {
1682                 return std::make_pair(true, item->AsImportSpecifier()->Local()->Name());
1683             }
1684             return std::make_pair(true, util::StringView());
1685         }
1686     }
1687     return std::make_pair(false, util::StringView());
1688 }
1689 
1690 template <checker::PropertyType TYPE>
BindingsModuleObjectAddProperty(checker::ETSObjectType * moduleObjType,ir::ETSImportDeclaration * importDecl,const varbinder::Scope::VariableMap & bindings,const util::StringView & importPath)1691 void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType,
1692                                                  ir::ETSImportDeclaration *importDecl,
1693                                                  const varbinder::Scope::VariableMap &bindings,
1694                                                  const util::StringView &importPath)
1695 {
1696     for (auto [_, var] : bindings) {
1697         (void)_;
1698         auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name());
1699         if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() ||
1700              var->AsLocalVariable()->Declaration()->Node()->HasExportAlias()) &&
1701             found) {
1702             if (!aliasedName.Empty()) {
1703                 moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName);
1704             }
1705             moduleObjType->AddProperty<TYPE>(
1706                 var->AsLocalVariable(), FindPropNameForNamespaceImport(var->AsLocalVariable()->Name(), importPath));
1707         }
1708     }
1709 }
1710 
FindPropNameForNamespaceImport(const util::StringView & originalName,const util::StringView & importPath)1711 util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringView &originalName,
1712                                                             const util::StringView &importPath)
1713 {
1714     auto exportAliases = VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap();
1715     auto relatedMapItem = exportAliases.find(importPath);
1716     if (relatedMapItem != exportAliases.end()) {
1717         if (auto result = std::find_if(relatedMapItem->second.begin(), relatedMapItem->second.end(),
1718                                        [originalName](const auto &item) { return item.second.first == originalName; });
1719             result != relatedMapItem->second.end()) {
1720             return result->first;
1721         }
1722     }
1723 
1724     return originalName;
1725 }
1726 
1727 // 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)1728 static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder,
1729                                                      const util::StringView &importPath)
1730 {
1731     if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) {
1732         return etsBinder->GetGlobalRecordTable()->Program();
1733     }
1734 
1735     auto programList = etsBinder->GetProgramList(importPath);
1736     return !programList.empty() ? programList.front() : nullptr;
1737 }
1738 
SetPropertiesForModuleObject(checker::ETSObjectType * moduleObjType,const util::StringView & importPath,ir::ETSImportDeclaration * importDecl)1739 void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath,
1740                                               ir::ETSImportDeclaration *importDecl)
1741 {
1742     parser::Program *program =
1743         SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
1744     // Check imported properties before assigning them to module object
1745     ES2PANDA_ASSERT(program != nullptr);
1746     if (!program->IsASTChecked()) {
1747         // NOTE: helps to avoid endless loop in case of recursive imports that uses all bindings
1748         program->SetASTChecked();
1749         program->Ast()->Check(this);
1750     }
1751 
1752     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_FIELD>(
1753         moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath);
1754 
1755     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_METHOD>(
1756         moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath);
1757 
1758     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1759         moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath);
1760 
1761     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1762         moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath);
1763 
1764     BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1765         moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath);
1766 }
1767 
SetrModuleObjectTsType(ir::Identifier * local,checker::ETSObjectType * moduleObjType)1768 void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType)
1769 {
1770     auto *etsBinder = static_cast<varbinder::ETSBinder *>(VarBinder());
1771 
1772     for (auto [bindingName, var] : etsBinder->TopScope()->Bindings()) {
1773         if (bindingName.Is(local->Name().Mutf8())) {
1774             var->SetTsType(moduleObjType);
1775         }
1776     }
1777 }
1778 
GetReferencedTypeFromBase(Type * baseType,ir::Expression * name)1779 Type *ETSChecker::GetReferencedTypeFromBase([[maybe_unused]] Type *baseType, [[maybe_unused]] ir::Expression *name)
1780 {
1781     return TypeError(name, diagnostic::INVALID_TYPE_REF, name->Start());
1782 }
1783 
GetReferencedTypeBase(ir::Expression * name)1784 Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name)
1785 {
1786     if (name->IsTSQualifiedName()) {
1787         return name->Check(this);
1788     }
1789 
1790     ES2PANDA_ASSERT(name->IsIdentifier());
1791     if (name->AsIdentifier()->Variable() == nullptr) {
1792         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1793         VarBinder()->AsETSBinder()->LookupTypeReference(name->AsIdentifier(), false);
1794     }
1795 
1796     auto *const var = name->AsIdentifier()->Variable();
1797     ES2PANDA_ASSERT(var != nullptr);
1798 
1799     if (var->TsType() != nullptr && var->TsType()->IsTypeError()) {
1800         return name->SetTsType(GlobalTypeError());
1801     }
1802 
1803     auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(var);
1804     if (importData != nullptr && importData->import->IsPureDynamic()) {
1805         return name->SetTsType(GlobalBuiltinDynamicType(importData->import->Language()));
1806     }
1807 
1808     return name->SetTsType(ResolveReferencedType(var->AsLocalVariable(), name));
1809 }
1810 
ResolveReferencedType(varbinder::LocalVariable * refVar,const ir::Expression * name)1811 Type *ETSChecker::ResolveReferencedType(varbinder::LocalVariable *refVar, const ir::Expression *name)
1812 {
1813     switch (refVar->Declaration()->Node()->Type()) {
1814         case ir::AstNodeType::TS_INTERFACE_DECLARATION:
1815             return GetTypeFromInterfaceReference(refVar);
1816         case ir::AstNodeType::TS_ENUM_DECLARATION:
1817             return GlobalTypeError();
1818         case ir::AstNodeType::CLASS_DECLARATION:
1819         case ir::AstNodeType::STRUCT_DECLARATION:
1820         case ir::AstNodeType::CLASS_DEFINITION:
1821             if (refVar->Declaration()->Node()->AsClassDefinition()->IsNamespaceTransformed()) {
1822                 LogError(diagnostic::NAMESPACE_AS_TYPE, {refVar->Name()}, name->Start());
1823                 return GlobalTypeError();
1824             }
1825             return GetTypeFromClassReference(refVar);
1826         case ir::AstNodeType::TS_TYPE_PARAMETER:
1827             return GetTypeFromTypeParameterReference(refVar, name->Start());
1828         case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
1829             return GetTypeFromTypeAliasReference(refVar);
1830         case ir::AstNodeType::ANNOTATION_DECLARATION:
1831             LogError(diagnostic::ANNOTATION_AS_TYPE, {}, name->Start());
1832             return GlobalTypeError();
1833 
1834         default:
1835             ES2PANDA_UNREACHABLE();
1836     }
1837 }
1838 
GetElementTypeOfArray(checker::Type * type)1839 checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type)
1840 {
1841     if (type->IsTypeError()) {
1842         return GlobalTypeError();
1843     }
1844     if (type->IsETSArrayType()) {
1845         return type->AsETSArrayType()->ElementType();
1846     }
1847 
1848     ES2PANDA_ASSERT(type->IsETSResizableArrayType());
1849     return type->AsETSResizableArrayType()->ElementType();
1850 }
1851 
GetElementTypeOfArray(const checker::Type * type) const1852 const checker::Type *ETSChecker::GetElementTypeOfArray(const checker::Type *type) const
1853 {
1854     if (type->IsETSArrayType()) {
1855         return type->AsETSArrayType()->ElementType();
1856     }
1857 
1858     ES2PANDA_ASSERT(type->IsETSResizableArrayType());
1859     return type->AsETSResizableArrayType()->ElementType();
1860 }
1861 
ConcatConstantString(util::UString & target,Type * type)1862 void ETSChecker::ConcatConstantString(util::UString &target, Type *type)
1863 {
1864     switch (ETSType(type)) {
1865         case TypeFlag::ETS_OBJECT: {
1866             ES2PANDA_ASSERT(type->IsETSStringType());
1867             target.Append(type->AsETSStringType()->GetValue());
1868             break;
1869         }
1870         case TypeFlag::ETS_BOOLEAN: {
1871             target.Append(type->AsETSBooleanType()->GetValue() ? "true" : "false");
1872             break;
1873         }
1874         case TypeFlag::BYTE: {
1875             target.Append(std::to_string(type->AsByteType()->GetValue()));
1876             break;
1877         }
1878         case TypeFlag::CHAR: {
1879             std::string s(1, type->AsCharType()->GetValue());
1880             target.Append(s);
1881             break;
1882         }
1883         case TypeFlag::SHORT: {
1884             target.Append(std::to_string(type->AsShortType()->GetValue()));
1885             break;
1886         }
1887         case TypeFlag::INT: {
1888             target.Append(std::to_string(type->AsIntType()->GetValue()));
1889             break;
1890         }
1891         case TypeFlag::LONG: {
1892             target.Append(std::to_string(type->AsLongType()->GetValue()));
1893             break;
1894         }
1895         case TypeFlag::FLOAT: {
1896             target.Append(std::to_string(type->AsFloatType()->GetValue()));
1897             break;
1898         }
1899         case TypeFlag::DOUBLE: {
1900             target.Append(std::to_string(type->AsDoubleType()->GetValue()));
1901             break;
1902         }
1903         default: {
1904             ES2PANDA_UNREACHABLE();
1905         }
1906     }
1907 }
1908 
HandleStringConcatenation(Type * leftType,Type * rightType)1909 Type *ETSChecker::HandleStringConcatenation(Type *leftType, Type *rightType)
1910 {
1911     ES2PANDA_ASSERT(leftType->IsETSStringType() || rightType->IsETSStringType());
1912 
1913     if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT) ||
1914         leftType->IsETSBigIntType() || rightType->IsETSBigIntType()) {
1915         return GlobalETSStringLiteralType();
1916     }
1917 
1918     util::UString concatenated(ProgramAllocator());
1919     ConcatConstantString(concatenated, leftType);
1920     ConcatConstantString(concatenated, rightType);
1921 
1922     return CreateETSStringLiteralType(concatenated.View());
1923 }
1924 
FindFunctionInVectorGivenByName(util::StringView name,ArenaVector<checker::ETSFunctionType * > & list)1925 checker::ETSFunctionType *ETSChecker::FindFunctionInVectorGivenByName(util::StringView name,
1926                                                                       ArenaVector<checker::ETSFunctionType *> &list)
1927 {
1928     for (auto *it : list) {
1929         if (it->Name() == name) {
1930             return it;
1931         }
1932     }
1933 
1934     return nullptr;
1935 }
1936 
IsFunctionContainsSignature(checker::ETSFunctionType * funcType,Signature * signature)1937 bool ETSChecker::IsFunctionContainsSignature(checker::ETSFunctionType *funcType, Signature *signature)
1938 {
1939     for (auto *it : funcType->CallSignatures()) {
1940         Relation()->SignatureIsSupertypeOf(it, signature);
1941         if (Relation()->IsTrue()) {
1942             return true;
1943         }
1944     }
1945 
1946     return false;
1947 }
1948 
CheckFunctionContainsClashingSignature(const checker::ETSFunctionType * funcType,Signature * signature)1949 bool ETSChecker::CheckFunctionContainsClashingSignature(const checker::ETSFunctionType *funcType, Signature *signature)
1950 {
1951     for (auto *it : funcType->CallSignatures()) {
1952         SavedTypeRelationFlagsContext strfCtx(Relation(), TypeRelationFlag::NONE);
1953         Relation()->SignatureIsSupertypeOf(it, signature);
1954         if (Relation()->IsTrue() && it->Function()->Id()->Name() == signature->Function()->Id()->Name()) {
1955             std::stringstream ss;
1956             it->ToString(ss, nullptr, true);
1957             auto sigStr1 = ss.str();
1958             ss.str(std::string {});  // Clear buffer
1959             signature->ToString(ss, nullptr, true);
1960             auto sigStr2 = ss.str();
1961             LogError(
1962                 diagnostic::FUNCTION_REDECLERATION,
1963                 {it->Function()->Id()->Name(), sigStr1.c_str(), signature->Function()->Id()->Name(), sigStr2.c_str()},
1964                 signature->Function()->ReturnTypeAnnotation()->Start());
1965             return false;
1966         }
1967     }
1968     return true;
1969 }
1970 
MergeSignatures(checker::ETSFunctionType * target,checker::ETSFunctionType * source)1971 void ETSChecker::MergeSignatures(checker::ETSFunctionType *target, checker::ETSFunctionType *source)
1972 {
1973     for (auto *s : source->CallSignatures()) {
1974         if (IsFunctionContainsSignature(target, s)) {
1975             continue;
1976         }
1977 
1978         if (!CheckFunctionContainsClashingSignature(target, s)) {
1979             continue;
1980         }
1981         target->AddCallSignature(s);
1982     }
1983 }
1984 
MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType * > & merged,ArenaVector<checker::ETSFunctionType * > & current)1985 void ETSChecker::MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType *> &merged,
1986                                         ArenaVector<checker::ETSFunctionType *> &current)
1987 {
1988     for (auto *curr : current) {
1989         auto name = curr->Name();
1990         auto *found = FindFunctionInVectorGivenByName(name, merged);
1991         if (found != nullptr) {
1992             MergeSignatures(found, curr);
1993             continue;
1994         }
1995 
1996         merged.push_back(curr);
1997     }
1998 }
1999 
FindAncestorGivenByType(ir::AstNode * node,ir::AstNodeType type,const ir::AstNode * endNode)2000 ir::AstNode *ETSChecker::FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type, const ir::AstNode *endNode)
2001 {
2002     auto *iter = node->Parent();
2003 
2004     while (iter != endNode) {
2005         if (iter->Type() == type) {
2006             return iter;
2007         }
2008 
2009         iter = iter->Parent();
2010     }
2011 
2012     return nullptr;
2013 }
2014 
GetContainingObjectNameFromSignature(Signature * signature)2015 util::StringView ETSChecker::GetContainingObjectNameFromSignature(Signature *signature)
2016 {
2017     ES2PANDA_ASSERT(signature->Function());
2018     auto *iter = signature->Function()->Parent();
2019 
2020     while (iter != nullptr) {
2021         if (iter->IsClassDefinition()) {
2022             return iter->AsClassDefinition()->Ident()->Name();
2023         }
2024 
2025         if (iter->IsTSInterfaceDeclaration()) {
2026             return iter->AsTSInterfaceDeclaration()->Id()->Name();
2027         }
2028 
2029         iter = iter->Parent();
2030     }
2031 
2032     ES2PANDA_UNREACHABLE();
2033     return {""};
2034 }
2035 
GetAccessFlagFromNode(const ir::AstNode * node)2036 varbinder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *node)
2037 {
2038     if (node->IsPrivate()) {
2039         return varbinder::VariableFlags::PRIVATE;
2040     }
2041 
2042     if (node->IsProtected()) {
2043         return varbinder::VariableFlags::PROTECTED;
2044     }
2045 
2046     return varbinder::VariableFlags::PUBLIC;
2047 }
2048 
CheckSwitchDiscriminant(ir::Expression * discriminant)2049 Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant)
2050 {
2051     discriminant->Check(this);
2052     auto *discriminantType = GetNonConstantType(MaybeUnboxExpression(discriminant));
2053     ES2PANDA_ASSERT(discriminantType != nullptr);
2054     if (!discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) {
2055         if (!(discriminantType->IsETSObjectType() &&
2056               discriminantType->AsETSObjectType()->HasObjectFlag(
2057                   ETSObjectFlags::BUILTIN_STRING | ETSObjectFlags::STRING | ETSObjectFlags::ENUM_OBJECT))) {
2058             LogError(diagnostic::ENUM_INVALID_DISCRIMINANT, {discriminantType}, discriminant->Start());
2059         }
2060     }
2061 
2062     return discriminantType;
2063 }
2064 
AddBoxingUnboxingFlagsToNode(ir::AstNode * node,Type * boxingUnboxingType)2065 void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType)
2066 {
2067     if (boxingUnboxingType->IsETSObjectType()) {
2068         node->AddBoxingUnboxingFlags(GetBoxingFlag(boxingUnboxingType));
2069     } else if (!boxingUnboxingType->IsETSUnionType()) {
2070         node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxingUnboxingType));
2071     }
2072 }
2073 
MaybeBoxExpression(ir::Expression * expr)2074 Type *ETSChecker::MaybeBoxExpression(ir::Expression *expr)
2075 {
2076     auto *promoted = MaybeBoxType(expr->TsType());
2077     if (promoted != expr->TsType()) {
2078         expr->AddBoxingUnboxingFlags(GetBoxingFlag(promoted));
2079     }
2080     return promoted;
2081 }
2082 
MaybeUnboxExpression(ir::Expression * expr)2083 Type *ETSChecker::MaybeUnboxExpression(ir::Expression *expr)
2084 {
2085     auto *primitive = MaybeUnboxType(expr->TsType());
2086     if (primitive != expr->TsType()) {
2087         expr->AddBoxingUnboxingFlags(GetUnboxingFlag(primitive));
2088     }
2089     return primitive;
2090 }
2091 
CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement * > const & cases)2092 void ETSChecker::CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement *> const &cases)
2093 {
2094     CheckItemCasesConstant(cases);
2095     CheckItemCasesDuplicate(cases);
2096 }
2097 
GetStringFromIdentifierValue(checker::Type * caseType) const2098 std::string ETSChecker::GetStringFromIdentifierValue(checker::Type *caseType) const
2099 {
2100     if (caseType->IsETSStringType()) {
2101         return std::string(caseType->AsETSStringType()->GetValue());
2102     }
2103     const auto identifierTypeKind = ETSChecker::TypeKind(caseType);
2104     switch (identifierTypeKind) {
2105         case TypeFlag::BYTE: {
2106             return std::to_string(caseType->AsByteType()->GetValue());
2107         }
2108         case TypeFlag::SHORT: {
2109             return std::to_string(caseType->AsShortType()->GetValue());
2110         }
2111         case TypeFlag::CHAR: {
2112             return std::to_string(caseType->AsCharType()->GetValue());
2113         }
2114         case TypeFlag::INT: {
2115             return std::to_string(caseType->AsIntType()->GetValue());
2116         }
2117         case TypeFlag::LONG: {
2118             return std::to_string(caseType->AsLongType()->GetValue());
2119         }
2120         case TypeFlag::ETS_OBJECT: {
2121             VarBinder()->ThrowError(caseType->AsETSObjectType()->Variable()->Declaration()->Node()->Start(),
2122                                     diagnostic::NOT_IMPLEMENTED);
2123             return "error";
2124         }
2125         default: {
2126             ES2PANDA_UNREACHABLE();
2127         }
2128     }
2129 }
2130 
IsConstantMemberOrIdentifierExpression(ir::Expression * expression,bool checkForConst=false)2131 bool IsConstantMemberOrIdentifierExpression(ir::Expression *expression, bool checkForConst = false)
2132 {
2133     varbinder::Variable *var = nullptr;
2134     if (expression->IsIdentifier()) {
2135         var = expression->AsIdentifier()->Variable();
2136     } else if (expression->IsMemberExpression()) {
2137         var = expression->AsMemberExpression()->PropVar();
2138     }
2139 
2140     if (var == nullptr) {
2141         return false;
2142     }
2143     bool isConst = checkForConst ? (var->TsType()->HasTypeFlag(checker::TypeFlag::CONSTANT)) : true;
2144     return ((var->Declaration()->IsConstDecl() && isConst) ||
2145             (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC)));
2146 }
2147 
IsValidSwitchType(checker::Type * caseType)2148 static bool IsValidSwitchType(checker::Type *caseType)
2149 {
2150     return caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE) || caseType->IsETSStringType() ||
2151            caseType->IsETSEnumType();
2152 }
2153 
CheckEnumCaseUnqualified(ETSChecker * checker,ir::Expression const * const caseTest)2154 void CheckEnumCaseUnqualified(ETSChecker *checker, ir::Expression const *const caseTest)
2155 {
2156     if (!caseTest->IsMemberExpression()) {
2157         checker->LogError(diagnostic::SWITCH_ENUM_NOT_UNQUALIFIED_ENUM_CONST, {}, caseTest->Start());
2158         return;
2159     }
2160 
2161     auto caseMember = caseTest->AsMemberExpression();
2162     auto baseObject = caseMember->Object();
2163 
2164     util::StringView enumName;
2165     if (baseObject->IsIdentifier()) {
2166         enumName = baseObject->AsIdentifier()->Name();
2167     } else if (baseObject->IsMemberExpression()) {
2168         enumName = baseObject->AsMemberExpression()->Property()->AsIdentifier()->Name();
2169     } else {
2170         checker->LogError(diagnostic::SWITCH_ENUM_NOT_UNQUALIFIED_ENUM_CONST, {}, caseTest->Start());
2171     }
2172 
2173     auto enumType = caseTest->TsType()->AsETSObjectType();
2174     if (enumName != enumType->Name()) {
2175         checker->LogError(diagnostic::SWITCH_ENUM_NOT_UNQUALIFIED_ENUM_CONST, {}, caseTest->Start());
2176     }
2177 }
2178 
CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement * > const & cases)2179 void ETSChecker::CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement *> const &cases)
2180 {
2181     for (auto &it : cases) {
2182         auto *caseTest = it->Test();
2183         if (caseTest == nullptr) {
2184             continue;
2185         }
2186         auto *caseType = caseTest->TsType();
2187         if (caseType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
2188             continue;
2189         }
2190 
2191         if (caseTest->TsType()->IsETSEnumType()) {
2192             CheckEnumCaseUnqualified(this, caseTest);
2193             continue;
2194         }
2195 
2196         if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
2197             if (!IsConstantMemberOrIdentifierExpression(caseTest)) {
2198                 LogError(diagnostic::NOT_CONSTANT, {}, it->Start());
2199                 continue;
2200             }
2201 
2202             if (!IsValidSwitchType(caseType)) {
2203                 LogError(diagnostic::SWITCH_CASE_INVALID_TYPE, {caseType}, it->Start());
2204             }
2205         }
2206     }
2207 }
2208 
CheckItemEnumType(ir::Expression const * const caseTest,ir::Expression const * const compareCaseTest,ETSChecker * checker,bool & isDup)2209 void CheckItemEnumType(ir::Expression const *const caseTest, ir::Expression const *const compareCaseTest,
2210                        ETSChecker *checker, bool &isDup)
2211 {
2212     // These case has logged error before, no need log error.
2213     if (!caseTest->IsMemberExpression() || !compareCaseTest->IsMemberExpression()) {
2214         return;
2215     }
2216     if (!caseTest->AsMemberExpression()->Object()->IsIdentifier() ||
2217         !compareCaseTest->AsMemberExpression()->Object()->IsIdentifier()) {
2218         return;
2219     }
2220     if (caseTest->AsMemberExpression()->Object()->AsIdentifier()->Name() !=
2221         compareCaseTest->AsMemberExpression()->Object()->AsIdentifier()->Name()) {
2222         return;
2223     }
2224 
2225     if (!caseTest->AsMemberExpression()->Property()->IsIdentifier() ||
2226         !compareCaseTest->AsMemberExpression()->Property()->IsIdentifier()) {
2227         return;
2228     }
2229     if (caseTest->AsMemberExpression()->Property()->AsIdentifier()->Name() ==
2230         compareCaseTest->AsMemberExpression()->Property()->AsIdentifier()->Name()) {
2231         isDup = true;
2232         checker->LogError(diagnostic::SWITCH_CASE_DUPLICATE, {}, caseTest->Start());
2233     }
2234 }
2235 
CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement * > const & cases)2236 void ETSChecker::CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement *> const &cases)
2237 {
2238     for (size_t caseNum = 0; caseNum < cases.size(); caseNum++) {
2239         bool isItemDuplicate = false;
2240         for (size_t compareCase = caseNum + 1; compareCase < cases.size(); compareCase++) {
2241             auto *caseTest = cases.at(caseNum)->Test();
2242             auto *compareCaseTest = cases.at(compareCase)->Test();
2243 
2244             if (caseTest == nullptr || compareCaseTest == nullptr) {
2245                 continue;
2246             }
2247 
2248             if (caseTest->TsType()->IsETSEnumType() && compareCaseTest->TsType()->IsETSEnumType()) {
2249                 CheckItemEnumType(caseTest, compareCaseTest, this, isItemDuplicate);
2250                 continue;
2251             }
2252 
2253             if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
2254                 CheckIdentifierSwitchCase(caseTest, compareCaseTest, cases.at(caseNum)->Start());
2255                 continue;
2256             }
2257 
2258             if (compareCaseTest->IsIdentifier() || compareCaseTest->IsMemberExpression()) {
2259                 CheckIdentifierSwitchCase(compareCaseTest, caseTest, cases.at(compareCase)->Start());
2260                 continue;
2261             }
2262 
2263             if (caseTest->IsLiteral() && compareCaseTest->IsLiteral() &&
2264                 GetStringFromLiteral(caseTest) != GetStringFromLiteral(compareCaseTest)) {
2265                 continue;
2266             }
2267 
2268             if (!(IsConstantExpression(caseTest, caseTest->TsType()) || caseTest->IsLiteral()) ||
2269                 !(IsConstantExpression(compareCaseTest, compareCaseTest->TsType()) || compareCaseTest->IsLiteral())) {
2270                 continue;
2271             }
2272 
2273             if (!isItemDuplicate) {
2274                 isItemDuplicate = true;
2275                 LogError(diagnostic::SWITCH_CASE_DUPLICATE, {}, cases.at(compareCase)->Start());
2276             }
2277         }
2278     }
2279 }
2280 
CompareIdentifiersValuesAreDifferent(ir::Expression * compareValue,const std::string & caseValue)2281 bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *compareValue, const std::string &caseValue)
2282 {
2283     checker::Type *compareCaseType = compareValue->TsType();
2284 
2285     if (compareCaseType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
2286         return true;
2287     }
2288 
2289     if (IsConstantMemberOrIdentifierExpression(compareValue)) {
2290         const auto compareCaseValue = GetStringFromIdentifierValue(compareCaseType);
2291         return caseValue != compareCaseValue;
2292     }
2293 
2294     return caseValue != GetStringFromLiteral(compareValue);
2295 }
2296 
CheckIdentifierSwitchCase(ir::Expression * currentCase,ir::Expression * compareCase,const lexer::SourcePosition & pos)2297 void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase,
2298                                            const lexer::SourcePosition &pos)
2299 {
2300     currentCase->Check(this);
2301 
2302     if (!IsConstantMemberOrIdentifierExpression(currentCase, true)) {
2303         return;
2304     }
2305 
2306     checker::Type *caseType = currentCase->TsType();
2307 
2308     if (!IsValidSwitchType(caseType)) {
2309         return;
2310     }
2311 
2312     if (!CompareIdentifiersValuesAreDifferent(compareCase, GetStringFromIdentifierValue(caseType))) {
2313         LogError(diagnostic::SWITCH_CASE_VAR_DUPLICATE_VAL, {}, pos);
2314         return;
2315     }
2316 }
2317 
GetStringFromLiteral(ir::Expression * caseTest) const2318 std::string ETSChecker::GetStringFromLiteral(ir::Expression *caseTest) const
2319 {
2320     switch (caseTest->Type()) {
2321         case ir::AstNodeType::CHAR_LITERAL: {
2322             return std::to_string(caseTest->AsCharLiteral()->Char());
2323         }
2324         case ir::AstNodeType::STRING_LITERAL:
2325         case ir::AstNodeType::NULL_LITERAL:
2326         case ir::AstNodeType::UNDEFINED_LITERAL:
2327         case ir::AstNodeType::NUMBER_LITERAL: {
2328             return util::Helpers::LiteralToPropName(caseTest).Mutf8();
2329         }
2330         default:
2331             ES2PANDA_UNREACHABLE();
2332     }
2333 }
2334 
IsSameDeclarationType(varbinder::LocalVariable * target,varbinder::LocalVariable * compare)2335 bool ETSChecker::IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare)
2336 {
2337     return target->Declaration()->Type() == compare->Declaration()->Type();
2338 }
2339 
FindFinalizerOfTryStatement(ir::AstNode * startFrom,const ir::AstNode * p)2340 ir::BlockStatement *ETSChecker::FindFinalizerOfTryStatement(ir::AstNode *startFrom, const ir::AstNode *p)
2341 {
2342     auto *iter = startFrom->Parent();
2343 
2344     do {
2345         if (iter->IsBlockStatement()) {
2346             ir::BlockStatement *finallyBlock = iter->AsBlockStatement();
2347 
2348             if (finallyBlock == p->AsTryStatement()->FinallyBlock()) {
2349                 return finallyBlock;
2350             }
2351         }
2352 
2353         iter = iter->Parent();
2354     } while (iter != p);
2355 
2356     return nullptr;
2357 }
2358 
GetRelevantArgumentedTypeFromChild(ETSObjectType * const child,ETSObjectType * const target)2359 ETSObjectType *ETSChecker::GetRelevantArgumentedTypeFromChild(ETSObjectType *const child, ETSObjectType *const target)
2360 {
2361     if (child->GetDeclNode() == target->GetDeclNode()) {
2362         auto *relevantType = CreateETSObjectType(child->GetDeclNode(), child->ObjectFlags());
2363 
2364         ArenaVector<Type *> params = child->TypeArguments();
2365         ES2PANDA_ASSERT(relevantType != nullptr);
2366         relevantType->SetTypeArguments(std::move(params));
2367         relevantType->SetEnclosingType(child->EnclosingType());
2368         relevantType->SetSuperType(child->SuperType());
2369 
2370         return relevantType;
2371     }
2372 
2373     ES2PANDA_ASSERT(child->SuperType() != nullptr);
2374 
2375     return GetRelevantArgumentedTypeFromChild(child->SuperType(), target);
2376 }
2377 
EmplaceSubstituted(Substitution * substitution,ETSTypeParameter * tparam,Type * typeArg)2378 void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg)
2379 {
2380     // *only* reference type may be substituted, no exceptions
2381     ES2PANDA_ASSERT(typeArg->IsETSReferenceType());
2382     ES2PANDA_ASSERT(substitution != nullptr);
2383     substitution->emplace(tparam, typeArg);
2384 }
2385 
GetHashFromTypeArguments(const ArenaVector<Type * > & typeArgTypes)2386 util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes)
2387 {
2388     std::stringstream ss;
2389 
2390     for (auto *it : typeArgTypes) {
2391         it->ToString(ss, true);
2392         ss << compiler::Signatures::MANGLE_SEPARATOR;
2393 
2394         // In case of ETSTypeParameters storing the name might not be sufficient as there can
2395         // be multiple different type parameters with the same name. For those we test identity based
2396         // on their memory address equality, so we store them in the hash to keep it unique.
2397         // To make it consistent we store it for every type.
2398         // NOTE (mmartin): change bare address to something more appropriate unique representation
2399         ss << it << compiler::Signatures::MANGLE_SEPARATOR;
2400     }
2401 
2402     return util::UString(ss.str(), ProgramAllocator()).View();
2403 }
2404 
GetHashFromSubstitution(const Substitution * substitution,const bool extensionFuncFlag)2405 util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution, const bool extensionFuncFlag)
2406 {
2407     std::vector<std::string> fields;
2408     for (auto [k, v] : *substitution) {
2409         std::stringstream ss;
2410         k->ToString(ss, true);
2411         ss << ":";
2412         v->ToString(ss, true);
2413         // NOTE (mmartin): change bare address to something more appropriate unique representation
2414         ss << ":" << k << ":" << v;
2415         fields.push_back(ss.str());
2416     }
2417     std::sort(fields.begin(), fields.end());
2418 
2419     std::stringstream ss;
2420     for (auto &fstr : fields) {
2421         ss << fstr;
2422         ss << ";";
2423     }
2424 
2425     if (extensionFuncFlag) {
2426         ss << "extensionFunctionType;";
2427     }
2428     return util::UString(ss.str(), ProgramAllocator()).View();
2429 }
2430 
GetHashFromFunctionType(ir::ETSFunctionType * type)2431 util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type)
2432 {
2433     std::stringstream ss;
2434     for (auto *p : type->Params()) {
2435         auto *const param = p->AsETSParameterExpression();
2436         auto *paramType = param->TypeAnnotation()->GetType(this);
2437         ES2PANDA_ASSERT(paramType != nullptr);
2438         paramType->ToString(ss, true);
2439         ss << ";";
2440     }
2441 
2442     if (type->IsExtensionFunction()) {
2443         if (type->ReturnType()->IsTSThisType()) {
2444             type->Params()[0]->AsETSParameterExpression()->TypeAnnotation()->TsType()->ToString(ss, true);
2445         } else {
2446             auto *returnType = type->ReturnType()->GetType(this);
2447             ES2PANDA_ASSERT(returnType != nullptr);
2448             returnType->ToString(ss, true);
2449         }
2450         ss << "extensionFunction;";
2451     } else {
2452         auto *returnType = type->ReturnType()->GetType(this);
2453         ES2PANDA_ASSERT(returnType != nullptr);
2454         returnType->ToString(ss, true);
2455     }
2456 
2457     ss << ";";
2458 
2459     if (type->IsThrowing()) {
2460         ss << "throws;";
2461     }
2462 
2463     if (type->IsRethrowing()) {
2464         ss << "rethrows;";
2465     }
2466 
2467     return util::UString(ss.str(), ProgramAllocator()).View();
2468 }
2469 
GetOriginalBaseType(Type * const object)2470 ETSObjectType *ETSChecker::GetOriginalBaseType(Type *const object)
2471 {
2472     if (object == nullptr || !object->IsETSObjectType()) {
2473         return nullptr;
2474     }
2475 
2476     return object->AsETSObjectType()->GetOriginalBaseType();
2477 }
2478 
CheckValidGenericTypeParameter(Type * const argType,const lexer::SourcePosition & pos)2479 void ETSChecker::CheckValidGenericTypeParameter(Type *const argType, const lexer::SourcePosition &pos)
2480 {
2481     std::stringstream ss;
2482     argType->ToString(ss);
2483     LogError(diagnostic::INVALID_TYPE_PARAM, {ss.str()}, pos);
2484 }
2485 
CheckNumberOfTypeArguments(ETSObjectType * const type,ir::TSTypeParameterInstantiation * const typeArgs,const lexer::SourcePosition & pos)2486 bool ETSChecker::CheckNumberOfTypeArguments(ETSObjectType *const type, ir::TSTypeParameterInstantiation *const typeArgs,
2487                                             const lexer::SourcePosition &pos)
2488 {
2489     auto const &typeParams = type->TypeArguments();
2490     if (typeParams.empty()) {
2491         if (typeArgs != nullptr) {
2492             LogError(diagnostic::NOT_GENERIC, {type}, pos);
2493             return false;
2494         }
2495         return true;
2496     }
2497 
2498     size_t minimumTypeArgs = std::count_if(typeParams.begin(), typeParams.end(), [](Type *param) {
2499         return param->IsETSTypeParameter() && param->AsETSTypeParameter()->GetDefaultType() == nullptr;
2500     });
2501     if (typeArgs == nullptr && minimumTypeArgs > 0) {
2502         LogError(diagnostic::GENERIC_WITHOUT_TYPE_PARAMS, {type}, pos);
2503         return false;
2504     }
2505 
2506     if (typeArgs != nullptr &&
2507         ((minimumTypeArgs > typeArgs->Params().size()) || (typeParams.size() < typeArgs->Params().size()))) {
2508         LogError(diagnostic::GENERIC_TYPE_PARAM_COUNT_MISMATCH, {type, minimumTypeArgs, typeArgs->Params().size()},
2509                  pos);
2510         return false;
2511     }
2512     return true;
2513 }
2514 
NeedTypeInference(const ir::ScriptFunction * lambda)2515 bool ETSChecker::NeedTypeInference(const ir::ScriptFunction *lambda)
2516 {
2517     if (lambda->ReturnTypeAnnotation() == nullptr) {
2518         return true;
2519     }
2520     for (auto *const param : lambda->Params()) {
2521         if (param->AsETSParameterExpression()->TypeAnnotation() == nullptr) {
2522             return true;
2523         }
2524     }
2525     return false;
2526 }
2527 
FindTypeInferenceArguments(const ArenaVector<ir::Expression * > & arguments)2528 std::vector<bool> ETSChecker::FindTypeInferenceArguments(const ArenaVector<ir::Expression *> &arguments)
2529 {
2530     std::vector<bool> argTypeInferenceRequired(arguments.size());
2531     size_t index = 0;
2532     for (ir::Expression *arg : arguments) {
2533         if (arg->IsArrowFunctionExpression()) {
2534             ir::ScriptFunction *const lambda = arg->AsArrowFunctionExpression()->Function();
2535             if (NeedTypeInference(lambda)) {
2536                 argTypeInferenceRequired[index] = true;
2537             }
2538         }
2539         ++index;
2540     }
2541     return argTypeInferenceRequired;
2542 }
2543 
2544 // #22952: optional arrow leftovers
CheckLambdaAssignableUnion(ir::AstNode * typeAnn,ir::ScriptFunction * lambda)2545 bool ETSChecker::CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunction *lambda)
2546 {
2547     bool assignable = false;
2548     for (auto *type : typeAnn->AsETSUnionType()->Types()) {
2549         if (type->IsETSFunctionType()) {
2550             assignable |= lambda->Params().size() == type->AsETSFunctionType()->Params().size();
2551             continue;
2552         }
2553 
2554         if (type->IsETSTypeReference()) {
2555             auto aliasType = util::Helpers::DerefETSTypeReference(type);
2556             assignable |= aliasType->IsETSFunctionType() &&
2557                           lambda->Params().size() == aliasType->AsETSFunctionType()->Params().size();
2558         }
2559     }
2560 
2561     return assignable;
2562 }
2563 
InferTypesForLambda(ir::ScriptFunction * lambda,ir::ETSFunctionType * calleeType,Signature * maybeSubstitutedFunctionSig)2564 void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunctionType *calleeType,
2565                                      Signature *maybeSubstitutedFunctionSig)
2566 {
2567     for (size_t i = 0; i < lambda->Params().size(); ++i) {
2568         if (!lambda->Params().at(i)->IsETSParameterExpression()) {
2569             LogError(diagnostic::INVALID_LAMBDA_PARAMETER, lambda->Params().at(i)->Start());
2570             continue;
2571         }
2572         auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident();
2573         if (lambdaParam->TypeAnnotation() == nullptr) {
2574             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2575             Type *inferredType = calleeType->Params()[i]->AsETSParameterExpression()->TypeAnnotation()->Check(this);
2576             bool isPrimitive = inferredType != nullptr && inferredType->IsETSPrimitiveType();
2577             if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) {
2578                 ES2PANDA_ASSERT(maybeSubstitutedFunctionSig->Params().size() == calleeType->Params().size());
2579                 inferredType = maybeSubstitutedFunctionSig->Params()[i]->TsType();
2580             }
2581             lambdaParam->Variable()->SetTsType(inferredType);
2582             lambdaParam->SetTsType(inferredType);
2583         }
2584     }
2585 
2586     if (lambda->ReturnTypeAnnotation() == nullptr) {
2587         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2588         Type *inferredReturnType = calleeType->ReturnType()->GetType(this);
2589         bool isPrimitive = inferredReturnType != nullptr && inferredReturnType->IsETSPrimitiveType();
2590         if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) {
2591             inferredReturnType = maybeSubstitutedFunctionSig->ReturnType();
2592         }
2593         lambda->SetPreferredReturnType(inferredReturnType);
2594     }
2595 }
2596 
InferTypesForLambda(ir::ScriptFunction * lambda,Signature * signature)2597 void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, Signature *signature)
2598 {
2599     ES2PANDA_ASSERT(signature->Params().size() >= lambda->Params().size());
2600     for (size_t i = 0; i < lambda->Params().size(); ++i) {
2601         if (!lambda->Params().at(i)->IsETSParameterExpression()) {
2602             LogError(diagnostic::INVALID_LAMBDA_PARAMETER, lambda->Params().at(i)->Start());
2603             continue;
2604         }
2605         auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident();
2606         if (lambdaParam->TypeAnnotation() == nullptr) {
2607             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2608             lambdaParam->Variable()->SetTsType(signature->Params().at(i)->TsType());
2609             lambdaParam->SetTsType(signature->Params().at(i)->TsType());
2610         }
2611     }
2612 
2613     if (lambda->ReturnTypeAnnotation() == nullptr) {
2614         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2615         lambda->SetPreferredReturnType(signature->ReturnType());
2616     }
2617 }
2618 
ModifyPreferredType(ir::ArrayExpression * const arrayExpr,Type * const newPreferredType)2619 void ETSChecker::ModifyPreferredType(ir::ArrayExpression *const arrayExpr, Type *const newPreferredType)
2620 {
2621     // After modifying the preferred type of an array expression, it needs to be rechecked at the call site
2622     arrayExpr->SetPreferredType(newPreferredType);
2623     arrayExpr->SetTsType(nullptr);
2624 
2625     for (auto *const element : arrayExpr->Elements()) {
2626         if (element->IsArrayExpression()) {
2627             ModifyPreferredType(element->AsArrayExpression(), nullptr);
2628         }
2629     }
2630 }
2631 
TryInferTypeForLambdaTypeAlias(ir::ArrowFunctionExpression * expr,ETSFunctionType * calleeType)2632 void ETSChecker::TryInferTypeForLambdaTypeAlias(ir::ArrowFunctionExpression *expr, ETSFunctionType *calleeType)
2633 {
2634     ES2PANDA_ASSERT(expr->IsArrowFunctionExpression());
2635     ES2PANDA_ASSERT(calleeType->IsETSArrowType());
2636 
2637     ir::ScriptFunction *const lambda = expr->AsArrowFunctionExpression()->Function();
2638 
2639     auto *signature = calleeType->CallSignaturesOfMethodOrArrow()[0];
2640     if (signature->Params().size() >= lambda->Params().size() && NeedTypeInference(lambda)) {
2641         InferTypesForLambda(lambda, signature);
2642     }
2643 }
2644 
IsInLocalClass(const ir::AstNode * node) const2645 bool ETSChecker::IsInLocalClass(const ir::AstNode *node) const
2646 {
2647     while (node != nullptr) {
2648         if (node->Type() == ir::AstNodeType::CLASS_DEFINITION) {
2649             return node->AsClassDefinition()->IsLocal();
2650         }
2651         node = node->Parent();
2652     }
2653 
2654     return false;
2655 }
2656 
GenerateImplicitInstantiateArg(const std::string & className)2657 ir::Expression *ETSChecker::GenerateImplicitInstantiateArg(const std::string &className)
2658 {
2659     std::string implicitInstantiateArgument = "()=>{return new " + className + "()}";
2660     parser::Program program(ProgramAllocator(), VarBinder());
2661     auto parser = parser::ETSParser(&program, nullptr, DiagnosticEngine());
2662     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2663     auto *argExpr = parser.CreateExpression(implicitInstantiateArgument);
2664     // NOTE(kaskov): #23399 We temporary delete SourceRange of all artificially created nodes (not from original
2665     // Lexer()), because all errors, which created by this code, will got a segfault. That caused because Program exist
2666     // till the end this function, and not avaible in diagnosticEngine. Now errors printed, but whitout position
2667     // because it doesn't actually exist). PS.Previously written competely wrong positons and file, so situation
2668     // isn't changed.
2669     compiler::SetSourceRangesRecursively(argExpr, lexer::SourceRange());
2670     argExpr->IterateRecursively([](ir::AstNode *node) -> void { node->SetRange(lexer::SourceRange()); });
2671     compiler::InitScopesPhaseETS::RunExternalNode(argExpr, &program);
2672 
2673     return argExpr;
2674 }
2675 
ReInitScopesForTypeAnnotation(ETSChecker * checker,ir::TypeNode * typeAnno,varbinder::Scope * paramScope)2676 static void ReInitScopesForTypeAnnotation(ETSChecker *checker, ir::TypeNode *typeAnno, varbinder::Scope *paramScope)
2677 {
2678     if (typeAnno != nullptr) {
2679         compiler::ClearTypesVariablesAndScopes(typeAnno);
2680         auto classCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), paramScope);
2681         compiler::InitScopesPhaseETS::RunExternalNode(typeAnno, checker->VarBinder());
2682     }
2683 }
2684 
ClassPropToImplementationProp(ir::ClassProperty * classProp,varbinder::ClassScope * scope)2685 ir::ClassProperty *ETSChecker::ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope)
2686 {
2687     classProp->Key()->AsIdentifier()->SetName(
2688         util::UString(std::string(compiler::Signatures::PROPERTY) + classProp->Key()->AsIdentifier()->Name().Mutf8(),
2689                       ProgramAllocator())
2690             .View());
2691     classProp->AddModifier(ir::ModifierFlags::PRIVATE);
2692 
2693     auto *fieldDecl = ProgramAllocator()->New<varbinder::LetDecl>(classProp->Key()->AsIdentifier()->Name());
2694     ES2PANDA_ASSERT(fieldDecl != nullptr);
2695     fieldDecl->BindNode(classProp);
2696 
2697     auto fieldVar = scope->InstanceFieldScope()->AddDecl(ProgramAllocator(), fieldDecl, ScriptExtension::ETS);
2698     fieldVar->AddFlag(varbinder::VariableFlags::PROPERTY);
2699     fieldVar->SetScope(scope->InstanceFieldScope());
2700 
2701     classProp->Key()->SetVariable(fieldVar);
2702     fieldVar->SetTsType(classProp->TsType());
2703 
2704     auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(VarBinder(), scope);
2705     ReInitScopesForTypeAnnotation(this, classProp->TypeAnnotation(), scope);
2706     compiler::InitScopesPhaseETS::RunExternalNode(classProp->Value(), VarBinder());
2707 
2708     return classProp;
2709 }
2710 
2711 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
GenerateGetterSetterBody(ArenaVector<ir::Statement * > & stmts,ArenaVector<ir::Expression * > & params,ir::ClassProperty * const field,varbinder::FunctionParamScope * paramScope,bool isSetter)2712 void ETSChecker::GenerateGetterSetterBody(ArenaVector<ir::Statement *> &stmts, ArenaVector<ir::Expression *> &params,
2713                                           ir::ClassProperty *const field, varbinder::FunctionParamScope *paramScope,
2714                                           bool isSetter)
2715 {
2716     auto *classDef = field->Parent()->AsClassDefinition();
2717 
2718     ir::Expression *baseExpression;
2719     if ((field->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U) {
2720         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2721         baseExpression = ProgramAllocator()->New<ir::SuperExpression>();
2722     } else {
2723         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2724         baseExpression = ProgramAllocator()->New<ir::ThisExpression>();
2725     }
2726     baseExpression->SetParent(classDef);
2727     baseExpression->SetTsType(classDef->TsType());
2728 
2729     auto *memberExpression =
2730         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2731         ProgramAllocNode<ir::MemberExpression>(baseExpression,
2732                                                // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2733                                                field->Key()->AsIdentifier()->Clone(ProgramAllocator(), nullptr),
2734                                                ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
2735     memberExpression->SetTsType(field->TsType());
2736     memberExpression->SetPropVar(field->Key()->Variable()->AsLocalVariable());
2737     memberExpression->SetRange(classDef->Range());
2738     if (memberExpression->ObjType() == nullptr && classDef->TsType() != nullptr) {
2739         memberExpression->SetObjectType(classDef->TsType()->AsETSObjectType());
2740     }
2741 
2742     if (!isSetter) {
2743         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2744         stmts.push_back(ProgramAllocNode<ir::ReturnStatement>(memberExpression));
2745         return;
2746     }
2747 
2748     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2749     auto *paramIdent = field->Key()->AsIdentifier()->Clone(ProgramAllocator(), nullptr);
2750     if (field->TypeAnnotation() != nullptr) {
2751         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2752         auto *const typeAnnotation = field->TypeAnnotation()->Clone(ProgramAllocator(), paramIdent);
2753         paramIdent->SetTsTypeAnnotation(typeAnnotation);
2754         ReInitScopesForTypeAnnotation(this, typeAnnotation, paramScope);
2755     } else {
2756         paramIdent->SetTsType(field->TsType());
2757     }
2758 
2759     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2760     auto *paramExpression = ProgramAllocNode<ir::ETSParameterExpression>(paramIdent, false, ProgramAllocator());
2761     paramExpression->SetRange(paramIdent->Range());
2762 
2763     auto [paramVar, node] = paramScope->AddParamDecl(ProgramAllocator(), VarBinder(), paramExpression);
2764     if (node != nullptr) {
2765         VarBinder()->ThrowRedeclaration(node->Start(), paramVar->Name(), paramVar->Declaration()->Type());
2766     }
2767 
2768     paramIdent->SetVariable(paramVar);
2769     paramExpression->SetVariable(paramVar);
2770     params.push_back(paramExpression);
2771 
2772     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2773     auto ident = ProgramAllocNode<ir::Identifier>(paramExpression->Ident()->Name(), ProgramAllocator());
2774     ident->SetVariable(paramExpression->Variable());
2775     auto *assignmentExpression =
2776         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2777         ProgramAllocNode<ir::AssignmentExpression>(memberExpression, ident, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
2778     ident->SetParent(assignmentExpression);
2779     assignmentExpression->SetTsType(paramVar->TsType());
2780 
2781     assignmentExpression->SetRange({field->Start(), field->End()});
2782     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2783     stmts.push_back(ProgramAllocNode<ir::ExpressionStatement>(assignmentExpression));
2784     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2785     stmts.push_back(ProgramAllocator()->New<ir::ReturnStatement>(nullptr));
2786 }
2787 
GenGetterSetterBodyHelper(ETSChecker * checker,ArenaVector<ir::Statement * > & stmts,ir::ClassProperty * const property,varbinder::FunctionScope * functionScope)2788 static ir::BlockStatement *GenGetterSetterBodyHelper(ETSChecker *checker, ArenaVector<ir::Statement *> &stmts,
2789                                                      ir::ClassProperty *const property,
2790                                                      varbinder::FunctionScope *functionScope)
2791 {
2792     if (property->IsDeclare()) {
2793         return nullptr;
2794     }
2795     auto *body = checker->ProgramAllocNode<ir::BlockStatement>(checker->ProgramAllocator(), std::move(stmts));
2796     ES2PANDA_ASSERT(body != nullptr);
2797     body->SetScope(functionScope);
2798     return body;
2799 }
2800 
2801 // Need to avoid codecheck
GenGetterSetterScriptFunc(ir::ClassProperty * const property,ir::ClassProperty * const field,varbinder::ClassScope * classScope,bool isSetter,ETSChecker * checker)2802 static std::tuple<ir::ScriptFunction *, varbinder::FunctionScope *, ir::ModifierFlags> GenGetterSetterScriptFunc(
2803     ir::ClassProperty *const property, ir::ClassProperty *const field, varbinder::ClassScope *classScope, bool isSetter,
2804     ETSChecker *checker)
2805 {
2806     auto *paramScope =
2807         checker->ProgramAllocator()->New<varbinder::FunctionParamScope>(checker->ProgramAllocator(), classScope);
2808     auto *functionScope =
2809         checker->ProgramAllocator()->New<varbinder::FunctionScope>(checker->ProgramAllocator(), paramScope);
2810 
2811     functionScope->BindParamScope(paramScope);
2812     paramScope->BindFunctionScope(functionScope);
2813 
2814     ArenaVector<ir::Expression *> params(checker->ProgramAllocator()->Adapter());
2815     ArenaVector<ir::Statement *> stmts(checker->ProgramAllocator()->Adapter());
2816     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2817     checker->GenerateGetterSetterBody(stmts, params, field, paramScope, isSetter);
2818     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2819     auto funcFlags = isSetter ? ir::ScriptFunctionFlags::SETTER
2820                               : ir::ScriptFunctionFlags::GETTER | ir::ScriptFunctionFlags::HAS_RETURN;
2821     auto *returnTypeAnn = isSetter || field->TypeAnnotation() == nullptr
2822                               ? nullptr
2823                               // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2824                               : field->TypeAnnotation()->Clone(checker->ProgramAllocator(), nullptr);
2825     ReInitScopesForTypeAnnotation(checker, returnTypeAnn, paramScope);
2826     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2827     ir::ModifierFlags modifierFlag =
2828         (ir::ModifierFlags::PUBLIC |
2829          static_cast<ir::ModifierFlags>(property->Modifiers() & ir::ModifierFlags::DECLARE) |
2830          (isSetter ? ir::ModifierFlags::SETTER : ir::ModifierFlags::GETTER));
2831     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2832     auto *func = checker->ProgramAllocNode<ir::ScriptFunction>(
2833         checker->ProgramAllocator(),
2834         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2835         ir::ScriptFunction::ScriptFunctionData {GenGetterSetterBodyHelper(checker, stmts, property, functionScope),
2836                                                 ir::FunctionSignature(nullptr, std::move(params), returnTypeAnn),
2837                                                 funcFlags, modifierFlag});
2838     paramScope->BindNode(func);
2839     return {func, functionScope, modifierFlag};
2840 }
2841 
GenerateDefaultGetterSetter(ir::ClassProperty * const property,ir::ClassProperty * const field,varbinder::ClassScope * classScope,bool isSetter,ETSChecker * checker)2842 ir::MethodDefinition *ETSChecker::GenerateDefaultGetterSetter(ir::ClassProperty *const property,
2843                                                               ir::ClassProperty *const field,
2844                                                               varbinder::ClassScope *classScope, bool isSetter,
2845                                                               ETSChecker *checker)
2846 {
2847     auto [func, functionScope, modifierFlag] =
2848         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2849         GenGetterSetterScriptFunc(property, field, classScope, isSetter, checker);
2850     func->SetRange(field->Range());
2851     func->SetScope(functionScope);
2852     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2853     auto *methodIdent = property->Key()->AsIdentifier()->Clone(checker->ProgramAllocator(), nullptr);
2854     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2855     auto *funcExpr = checker->ProgramAllocNode<ir::FunctionExpression>(func);
2856     CHECK_NOT_NULL(funcExpr);
2857     funcExpr->SetRange(func->Range());
2858     func->AddFlag(ir::ScriptFunctionFlags::METHOD);
2859     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2860     auto *method = checker->ProgramAllocNode<ir::MethodDefinition>(
2861         isSetter ? ir::MethodDefinitionKind::SET : ir::MethodDefinitionKind::GET, methodIdent, funcExpr, modifierFlag,
2862         checker->ProgramAllocator(), false);
2863     auto *decl = checker->ProgramAllocator()->New<varbinder::FunctionDecl>(
2864         checker->ProgramAllocator(), property->Key()->AsIdentifier()->Name(), method);
2865     auto *var = checker->ProgramAllocator()->New<varbinder::LocalVariable>(decl, varbinder::VariableFlags::VAR);
2866     CHECK_NOT_NULL(var);
2867     var->AddFlag(varbinder::VariableFlags::METHOD);
2868 
2869     methodIdent->SetVariable(var);
2870 
2871     auto *methodId = method->Id();
2872     CHECK_NOT_NULL(methodId);
2873     methodId->SetMutator();
2874     method->SetRange(field->Range());
2875     auto *methodFunc = method->Function();
2876     CHECK_NOT_NULL(methodFunc);
2877     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2878     methodFunc->SetIdent(methodId->Clone(checker->ProgramAllocator(), nullptr));
2879     methodFunc->AddModifier(method->Modifiers());
2880     method->SetVariable(var);
2881     method->SetParent(field->Parent());
2882 
2883     functionScope->BindNode(func);
2884 
2885     auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(checker->VarBinder(), classScope);
2886     checker->VarBinder()->AsETSBinder()->ResolveMethodDefinition(method);
2887 
2888     functionScope->BindName(classScope->Node()->AsClassDefinition()->InternalName());
2889     method->Check(checker);
2890 
2891     return method;
2892 }
2893 
GetImplementationClassProp(ETSChecker * checker,ir::ClassProperty * interfaceProp,ir::ClassProperty * originalProp,ETSObjectType * classType)2894 ir::ClassProperty *GetImplementationClassProp(ETSChecker *checker, ir::ClassProperty *interfaceProp,
2895                                               ir::ClassProperty *originalProp, ETSObjectType *classType)
2896 {
2897     bool isSuperOwner = ((originalProp->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U);
2898     if (!isSuperOwner) {
2899         auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2900         auto *const scope = checker->Scope()->AsClassScope();
2901         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2902         auto *const classProp = checker->ClassPropToImplementationProp(
2903             interfaceProp->Clone(checker->ProgramAllocator(), originalProp->Parent()), scope);
2904         classType->AddProperty<PropertyType::INSTANCE_FIELD>(classProp->Key()->Variable()->AsLocalVariable());
2905         classDef->Body().push_back(classProp);
2906         return classProp;
2907     }
2908 
2909     auto *const classProp = classType
2910                                 ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(),
2911                                               PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE)
2912                                 ->Declaration()
2913                                 ->Node()
2914                                 ->AsClassProperty();
2915     classProp->AddModifier(ir::ModifierFlags::SUPER_OWNER);
2916     return classProp;
2917 }
2918 
SetupGetterSetterFlags(ir::ClassProperty * originalProp,ETSObjectType * classType,ir::MethodDefinition * getter,ir::MethodDefinition * setter,const bool inExternal)2919 void ETSChecker::SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObjectType *classType,
2920                                         ir::MethodDefinition *getter, ir::MethodDefinition *setter,
2921                                         const bool inExternal)
2922 {
2923     auto getProgram = [](ir::AstNode *node) { return node->Range().start.Program(); };
2924 
2925     auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2926     for (auto &method : {getter, setter}) {
2927         if (method == nullptr) {
2928             continue;
2929         }
2930 
2931         const auto mflag = method == getter ? ir::ModifierFlags::GETTER : ir::ModifierFlags::SETTER;
2932         const auto tflag = method == getter ? TypeFlag::GETTER : TypeFlag::SETTER;
2933 
2934         method->TsType()->AddTypeFlag(tflag);
2935         method->Variable()->SetTsType(method->TsType());
2936         auto *func = method->Function();
2937         ES2PANDA_ASSERT(func != nullptr);
2938         if (((originalProp->Modifiers() & mflag) != 0U)) {
2939             func->AddModifier(ir::ModifierFlags::OVERRIDE);
2940         }
2941 
2942         if (inExternal && !getProgram(originalProp)->IsGenAbcForExternal()) {
2943             func->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
2944         }
2945 
2946         if (originalProp->IsDeclare()) {
2947             method->AddModifier(ir::ModifierFlags::DECLARE);
2948             func->AddModifier(ir::ModifierFlags::DECLARE);
2949         }
2950         this->CheckOverride(func->Signature());
2951         method->SetParent(classDef);
2952         classType->AddProperty<checker::PropertyType::INSTANCE_METHOD>(method->Variable()->AsLocalVariable());
2953     }
2954 }
2955 
GenerateGetterSetterPropertyAndMethod(ir::ClassProperty * originalProp,ETSObjectType * classType)2956 void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *originalProp, ETSObjectType *classType)
2957 {
2958     auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2959     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2960     auto *interfaceProp = originalProp->Clone(ProgramAllocator(), originalProp->Parent());
2961     interfaceProp->ClearModifier(ir::ModifierFlags::GETTER_SETTER);
2962 
2963     auto *const scope = Scope()->AsClassScope();
2964     scope->InstanceFieldScope()->EraseBinding(interfaceProp->Key()->AsIdentifier()->Name());
2965     interfaceProp->SetRange(originalProp->Range());
2966 
2967     auto classCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(VarBinder(), scope);
2968     ReInitScopesForTypeAnnotation(this, interfaceProp->TypeAnnotation(), scope);
2969     compiler::InitScopesPhaseETS::RunExternalNode(interfaceProp->Value(), VarBinder());
2970 
2971     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2972     auto *const classProp = GetImplementationClassProp(this, interfaceProp, originalProp, classType);
2973 
2974     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2975     ir::MethodDefinition *getter = GenerateDefaultGetterSetter(interfaceProp, classProp, scope, false, this);
2976     classDef->Body().push_back(getter);
2977 
2978     const auto &name = getter->Key()->AsIdentifier()->Name();
2979 
2980     ir::MethodDefinition *setter =
2981         !classProp->IsConst()
2982             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2983             ? GenerateDefaultGetterSetter(interfaceProp, classProp, Scope()->AsClassScope(), true, this)
2984             : nullptr;
2985 
2986     auto *const methodScope = scope->InstanceMethodScope();
2987     auto *const decl = ProgramAllocator()->New<varbinder::FunctionDecl>(ProgramAllocator(), name, getter);
2988 
2989     auto *var = methodScope->AddDecl(ProgramAllocator(), decl, ScriptExtension::ETS);
2990     if (var == nullptr) {
2991         auto *const prevDecl = methodScope->FindDecl(name);
2992         for (const auto &method : {getter, setter}) {
2993             if (method != nullptr) {
2994                 prevDecl->Node()->AsMethodDefinition()->AddOverload(method);
2995             }
2996         }
2997         var = methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
2998         var->AddFlag(varbinder::VariableFlags::METHOD);
2999     }
3000 
3001     getter->Function()->Id()->SetVariable(var);
3002 
3003     SetupGetterSetterFlags(originalProp, classType, getter, setter, HasStatus(CheckerStatus::IN_EXTERNAL));
3004 
3005     if (setter != nullptr && !setter->TsType()->IsTypeError()) {
3006         getter->Variable()->TsType()->AsETSFunctionType()->AddCallSignature(
3007             setter->TsType()->AsETSFunctionType()->CallSignatures()[0]);
3008         getter->AddOverload(setter);
3009     }
3010 }
3011 
3012 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TryTransformingToStaticInvoke(ir::Identifier * const ident,const Type * resolvedType)3013 bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType)
3014 {
3015     ES2PANDA_ASSERT(ident->Parent()->IsCallExpression());
3016     ES2PANDA_ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident);
3017 
3018     if (!resolvedType->IsETSObjectType()) {
3019         return false;
3020     }
3021 
3022     auto className = ident->Name();
3023     std::string_view propertyName;
3024 
3025     PropertySearchFlags searchFlag = PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE |
3026                                      PropertySearchFlags::SEARCH_STATIC_METHOD;
3027     auto *instantiateMethod =
3028         resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlag);
3029     auto *invokeMethod =
3030         resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlag);
3031     if (instantiateMethod != nullptr) {
3032         propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD;
3033     } else if (invokeMethod != nullptr) {
3034         propertyName = compiler::Signatures::STATIC_INVOKE_METHOD;
3035     } else {
3036         LogError(diagnostic::NO_STATIC_INVOKE,
3037                  {compiler::Signatures::STATIC_INVOKE_METHOD, compiler::Signatures::STATIC_INSTANTIATE_METHOD,
3038                   className, className},
3039                  ident->Start());
3040         return true;
3041     }
3042     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3043     auto *classId = ProgramAllocNode<ir::Identifier>(className, ProgramAllocator());
3044     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3045     auto *methodId = ProgramAllocNode<ir::Identifier>(propertyName, ProgramAllocator());
3046     if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) {
3047         methodId->SetVariable(instantiateMethod);
3048     } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) {
3049         methodId->SetVariable(invokeMethod);
3050     }
3051 
3052     auto *transformedCallee =
3053         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3054         ProgramAllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false,
3055                                                false);
3056 
3057     classId->SetRange(ident->Range());
3058     methodId->SetRange(ident->Range());
3059     transformedCallee->SetRange(ident->Range());
3060 
3061     auto *callExpr = ident->Parent()->AsCallExpression();
3062     transformedCallee->SetParent(callExpr);
3063     callExpr->SetCallee(transformedCallee);
3064 
3065     if (instantiateMethod != nullptr) {
3066         auto lexScope {varbinder::LexicalScope<varbinder::Scope>::Enter(VarBinder(), compiler::NearestScope(callExpr))};
3067         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3068         auto *argExpr = GenerateImplicitInstantiateArg(std::string(className));
3069 
3070         argExpr->SetParent(callExpr);
3071         argExpr->SetRange(ident->Range());
3072 
3073         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3074         VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr);
3075 
3076         auto &arguments = callExpr->Arguments();
3077         arguments.insert(arguments.begin(), argExpr);
3078     }
3079 
3080     return true;
3081 }
3082 
ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration * importDecl,checker::ETSObjectType * lastObjectType,ir::Identifier * ident)3083 void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl,
3084                                                           checker::ETSObjectType *lastObjectType, ir::Identifier *ident)
3085 {
3086     for (auto item : VarBinder()->AsETSBinder()->ReExportImports()) {
3087         if (importDecl->ResolvedSource() != item->GetProgramPath().Mutf8()) {
3088             continue;
3089         }
3090         auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident);
3091         if (reExportType->IsTypeError()) {
3092             continue;
3093         }
3094         ES2PANDA_ASSERT(lastObjectType != nullptr);
3095         lastObjectType->AddReExports(reExportType->AsETSObjectType());
3096         for (auto node : importDecl->Specifiers()) {
3097             if (node->IsImportSpecifier()) {
3098                 auto specifier = node->AsImportSpecifier();
3099                 lastObjectType->AddReExportAlias(specifier->Imported()->Name(), specifier->Local()->Name());
3100             }
3101         }
3102     }
3103 }
3104 
GetImportSpecifierObjectType(ir::ETSImportDeclaration * importDecl,ir::Identifier * ident)3105 Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident)
3106 {
3107     auto importPath = importDecl->ResolvedSource();
3108     parser::Program *program =
3109         SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
3110     if (program == nullptr) {
3111         return GlobalTypeError();
3112     }
3113 
3114     auto const moduleName = program->ModuleName();
3115     auto const internalName =
3116         util::UString(
3117             moduleName.Mutf8().append(compiler::Signatures::METHOD_SEPARATOR).append(compiler::Signatures::ETS_GLOBAL),
3118             ProgramAllocator())
3119             .View();
3120 
3121     auto *moduleObjectType =
3122         ProgramAllocator()->New<ETSObjectType>(ProgramAllocator(), moduleName, internalName,
3123                                                std::make_tuple(ident, checker::ETSObjectFlags::CLASS, Relation()));
3124 
3125     auto *rootDecl = ProgramAllocator()->New<varbinder::ClassDecl>(moduleName);
3126     varbinder::LocalVariable *rootVar =
3127         ProgramAllocator()->New<varbinder::LocalVariable>(rootDecl, varbinder::VariableFlags::NONE);
3128     ES2PANDA_ASSERT(rootVar != nullptr);
3129     rootVar->SetTsType(moduleObjectType);
3130 
3131     ImportNamespaceObjectTypeAddReExportType(importDecl, moduleObjectType, ident);
3132     SetPropertiesForModuleObject(moduleObjectType, importPath,
3133                                  importDecl->Specifiers()[0]->IsImportNamespaceSpecifier() ? nullptr : importDecl);
3134     SetrModuleObjectTsType(ident, moduleObjectType);
3135 
3136     return moduleObjectType;
3137 }
3138 
FormNamedAccessMetadata(varbinder::Variable const * prop)3139 ETSChecker::NamedAccessMeta ETSChecker::FormNamedAccessMetadata(varbinder::Variable const *prop)
3140 {
3141     CHECK_NOT_NULL(prop);
3142     const auto *field = prop->Declaration()->Node()->AsClassProperty();
3143     const auto *owner = field->Parent()->AsClassDefinition();
3144     auto *fieldId = field->Id();
3145     CHECK_NOT_NULL(fieldId);
3146     return {owner->TsType()->AsETSObjectType(), field->TsType(), fieldId->Name()};
3147 }
3148 
ETSObjectTypeDeclNode(ETSChecker * checker,ETSObjectType * const objectType)3149 void ETSChecker::ETSObjectTypeDeclNode(ETSChecker *checker, ETSObjectType *const objectType)
3150 {
3151     auto *declNode = objectType->AsETSObjectType()->GetDeclNode();
3152     if (declNode == nullptr) {
3153         return;
3154     }
3155 
3156     if (declNode->IsClassDefinition() && !declNode->AsClassDefinition()->IsClassDefinitionChecked()) {
3157         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3158         checker->CheckClassDefinition(declNode->AsClassDefinition());
3159     }
3160 }
3161 
CreateExtensionAccessorCall(ETSChecker * checker,ir::MemberExpression * expr,ArenaVector<ir::Expression * > && args)3162 ir::CallExpression *ETSChecker::CreateExtensionAccessorCall(ETSChecker *checker, ir::MemberExpression *expr,
3163                                                             ArenaVector<ir::Expression *> &&args)
3164 {
3165     ir::Expression *callExpr = nullptr;
3166     if (expr->Object()->IsETSNewClassInstanceExpression()) {
3167         args.insert(args.begin(), expr->Object());
3168         callExpr =
3169             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3170             checker->ProgramAllocNode<ir::CallExpression>(expr->Property(), std::move(args), nullptr, false, false);
3171     } else {
3172         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3173         callExpr = checker->ProgramAllocNode<ir::CallExpression>(expr, std::move(args), nullptr, false, false);
3174     }
3175     ES2PANDA_ASSERT(callExpr != nullptr);
3176     callExpr->SetRange(expr->Range());
3177     return callExpr->AsCallExpression();
3178 }
3179 
CheckTypeParameterVariance(ir::ClassDefinition * classDef)3180 void ETSChecker::CheckTypeParameterVariance(ir::ClassDefinition *classDef)
3181 {
3182     if (classDef->TypeParams() == nullptr) {
3183         return;
3184     }
3185 
3186     Context().SetContainingClass(classDef->TsType()->AsETSObjectType());
3187     auto checkVariance = [this](VarianceFlag varianceFlag, ir::Expression *expression, Type *type) {
3188         Relation()->Result(RelationResult::TRUE);
3189         Relation()->SetNode(expression);
3190         Relation()->CheckVarianceRecursively(type, varianceFlag);
3191         Relation()->SetNode(nullptr);
3192     };
3193 
3194     for (auto *it : classDef->Body()) {
3195         if (!it->IsClassProperty() || it->AsClassProperty()->TypeAnnotation() == nullptr) {
3196             continue;
3197         }
3198         // Readonly Fields may have out type parameters, otherwise fields should be invariant type parameters
3199         checkVariance(it->AsClassProperty()->IsReadonly() ? VarianceFlag::COVARIANT : VarianceFlag::INVARIANT,
3200                       it->AsClassProperty()->TypeAnnotation(), it->AsClassProperty()->TsType());
3201     }
3202 
3203     for (auto *it : classDef->Body()) {
3204         if (!it->IsMethodDefinition() || it->AsMethodDefinition()->IsConstructor()) {
3205             continue;
3206         }
3207         // Methods may have out type parameters as return types, and in type parameters as parameter types,(in)=>out
3208         checkVariance(VarianceFlag::COVARIANT, it->AsMethodDefinition()->Id(), it->Check(this));
3209     }
3210 
3211     if (classDef->Super() != nullptr) {
3212         checkVariance(VarianceFlag::COVARIANT, classDef->Super(), classDef->Super()->Check(this));
3213     }
3214 
3215     for (auto *implement : classDef->Implements()) {
3216         checkVariance(VarianceFlag::COVARIANT, implement, implement->Expr()->AsTypeNode()->Check(this));
3217     }
3218 }
3219 
SetPreferredTypeIfPossible(ir::Expression * const expr,Type * const targetType)3220 void ETSChecker::SetPreferredTypeIfPossible(ir::Expression *const expr, Type *const targetType)
3221 {
3222     // Object expression requires that its type be set by the context before checking. in this case, the target type
3223     // provides that context.
3224     if (expr->IsObjectExpression()) {
3225         expr->AsObjectExpression()->SetPreferredType(targetType);
3226     }
3227 
3228     if (expr->IsArrayExpression()) {
3229         expr->AsArrayExpression()->SetPreferredType(targetType);
3230     }
3231 }
3232 
IntersectSignatureSets(const checker::ETSFunctionType * left,const checker::ETSFunctionType * right)3233 checker::ETSFunctionType *ETSChecker::IntersectSignatureSets(const checker::ETSFunctionType *left,
3234                                                              const checker::ETSFunctionType *right)
3235 {
3236     auto sameSig = [this](checker::Signature *leftSig, checker::Signature *rightSig) {
3237         auto relation = Relation();
3238         if ((leftSig->Flags() & ~checker::SignatureFlags::FINAL) !=
3239             (rightSig->Flags() & ~checker::SignatureFlags::FINAL)) {
3240             return false;
3241         }
3242         return relation->SignatureIsIdenticalTo(rightSig, leftSig);
3243     };
3244 
3245     if (left->CallSignatures().size() > right->CallSignatures().size()) {
3246         std::swap(left, right);
3247     }
3248 
3249     ArenaVector<checker::Signature *> intersection {ProgramAllocator()->Adapter()};
3250 
3251     for (const auto sig : left->CallSignatures()) {
3252         auto found = right->FindSpecificSignature(
3253             [sig, &sameSig](checker::Signature *otherSig) { return sameSig(sig, otherSig); });
3254         if (found != nullptr) {
3255             intersection.push_back(found);
3256         }
3257     }
3258 
3259     return CreateETSMethodType(left->Name(), std::move(intersection));
3260 }
3261 
3262 }  // namespace ark::es2panda::checker
3263