• 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/checker.h"
17 #include "checker/ets/narrowingWideningConverter.h"
18 #include "checker/types/globalTypesHolder.h"
19 #include "checker/types/ets/etsObjectType.h"
20 #include "checker/types/ets/etsPartialTypeParameter.h"
21 #include "ir/base/catchClause.h"
22 #include "ir/base/scriptFunction.h"
23 #include "ir/base/classProperty.h"
24 #include "ir/base/methodDefinition.h"
25 #include "ir/statements/variableDeclarator.h"
26 #include "ir/statements/switchCaseStatement.h"
27 #include "ir/expressions/identifier.h"
28 #include "ir/expressions/callExpression.h"
29 #include "ir/expressions/memberExpression.h"
30 #include "ir/expressions/arrowFunctionExpression.h"
31 #include "ir/statements/labelledStatement.h"
32 #include "ir/statements/tryStatement.h"
33 #include "ir/ets/etsNewClassInstanceExpression.h"
34 #include "ir/ets/etsParameterExpression.h"
35 #include "ir/ts/tsTypeAliasDeclaration.h"
36 #include "ir/ts/tsEnumMember.h"
37 #include "ir/ts/tsTypeParameter.h"
38 #include "ir/ets/etsUnionType.h"
39 #include "ir/ets/etsTuple.h"
40 #include "varbinder/declaration.h"
41 #include "checker/ETSchecker.h"
42 #include "varbinder/ETSBinder.h"
43 #include "checker/ets/typeRelationContext.h"
44 #include "checker/ets/boxingConverter.h"
45 #include "checker/ets/unboxingConverter.h"
46 #include "util/helpers.h"
47 #include "generated/diagnostic.h"
48 
49 namespace ark::es2panda::checker {
50 
CheckTruthinessOfType(ir::Expression * expr)51 void ETSChecker::CheckTruthinessOfType(ir::Expression *expr)
52 {
53     auto const testType = expr->Check(this);
54     auto *const conditionType = MaybeUnboxConditionalInRelation(testType);
55 
56     expr->SetTsType(conditionType);
57 
58     if (conditionType == nullptr) {
59         return;
60     }
61 
62     if (conditionType->IsETSVoidType()) {
63         LogError(diagnostic::VOID_IN_LOGIC, {}, expr->Start());
64         return;
65     }
66 
67     if (conditionType->IsETSPrimitiveType()) {
68         FlagExpressionWithUnboxing(testType, conditionType, expr);
69     }
70 
71     // For T_S compatibility
72     if (conditionType->IsETSEnumType()) {
73         expr->AddAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
74     }
75 }
76 
CheckNonNullish(ir::Expression const * expr)77 bool ETSChecker::CheckNonNullish(ir::Expression const *expr)
78 {
79     if (!expr->TsType()->PossiblyETSNullish()) {
80         return true;
81     }
82 
83     if (HasStatus(checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK)) {
84         return false;
85     }
86 
87     LogError(diagnostic::POSSIBLY_NULLISH, {}, expr->Start());
88     return false;
89 }
90 
GetNonNullishType(Type * type)91 Type *ETSChecker::GetNonNullishType(Type *type)
92 {
93     if (type->DefinitelyNotETSNullish()) {
94         return type;
95     }
96     if (type->IsETSAnyType()) {
97         return type;
98     }
99     if (type->IsETSTypeParameter()) {
100         return ProgramAllocator()->New<ETSNonNullishType>(type->AsETSTypeParameter());
101     }
102     if (type->IsETSPartialTypeParameter()) {
103         return type->AsETSPartialTypeParameter()->GetUnderlying();
104     }
105     if (type->IsETSNullType() || type->IsETSUndefinedType()) {
106         return GetGlobalTypesHolder()->GlobalETSNeverType();
107     }
108 
109     ArenaVector<Type *> copied(ProgramAllocator()->Adapter());
110     for (auto const &t : type->AsETSUnionType()->ConstituentTypes()) {
111         if (t->IsETSNullType() || t->IsETSUndefinedType()) {
112             continue;
113         }
114         copied.push_back(GetNonNullishType(t));
115     }
116     return copied.empty() ? GetGlobalTypesHolder()->GlobalETSNeverType() : CreateETSUnionType(std::move(copied));
117 }
118 
RemoveNullType(Type * const type)119 Type *ETSChecker::RemoveNullType(Type *const type)
120 {
121     if (type->IsETSAnyType() || type->DefinitelyNotETSNullish() || type->IsETSUndefinedType()) {
122         return type;
123     }
124 
125     if (type->IsETSTypeParameter()) {
126         // Strict equality produces incorrect NonNullish types #21526
127         return type;
128     }
129 
130     if (type->IsETSNullType()) {
131         return GetGlobalTypesHolder()->GlobalETSNeverType();
132     }
133 
134     ES2PANDA_ASSERT(type->IsETSUnionType());
135     ArenaVector<Type *> copiedTypes(ProgramAllocator()->Adapter());
136 
137     for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) {
138         if (!constituentType->IsETSNullType()) {
139             copiedTypes.push_back(RemoveNullType(constituentType));
140         }
141     }
142 
143     return copiedTypes.empty() ? GetGlobalTypesHolder()->GlobalETSNeverType()
144                                : CreateETSUnionType(std::move(copiedTypes));
145 }
146 
RemoveUndefinedType(Type * const type)147 Type *ETSChecker::RemoveUndefinedType(Type *const type)
148 {
149     if (type->IsETSAnyType() || type->DefinitelyNotETSNullish() || type->IsETSNullType()) {
150         return type;
151     }
152 
153     if (type->IsETSTypeParameter()) {
154         // Strict equality produces incorrect NonNullish types #21526
155         return type;
156     }
157 
158     if (type->IsETSUndefinedType()) {
159         return GetGlobalTypesHolder()->GlobalETSNeverType();
160     }
161 
162     ES2PANDA_ASSERT(type->IsETSUnionType());
163     ArenaVector<Type *> copiedTypes(ProgramAllocator()->Adapter());
164 
165     for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) {
166         if (!constituentType->IsETSUndefinedType()) {
167             copiedTypes.push_back(RemoveUndefinedType(constituentType));
168         }
169     }
170 
171     return copiedTypes.empty() ? GetGlobalTypesHolder()->GlobalETSNeverType()
172                                : CreateETSUnionType(std::move(copiedTypes));
173 }
174 
RemoveNullishTypes(Type * type)175 std::pair<Type *, Type *> ETSChecker::RemoveNullishTypes(Type *type)
176 {
177     if (type->DefinitelyNotETSNullish()) {
178         return {GetGlobalTypesHolder()->GlobalETSNeverType(), type};
179     }
180 
181     if (type->IsETSAnyType()) {
182         return {type, type};
183     }
184 
185     if (type->IsETSTypeParameter()) {
186         return {GetGlobalTypesHolder()->GlobalETSUnionUndefinedNull(),
187                 ProgramAllocator()->New<ETSNonNullishType>(type->AsETSTypeParameter())};
188     }
189 
190     if (type->IsETSUndefinedType() || type->IsETSNullType()) {
191         return {type, GetGlobalTypesHolder()->GlobalETSNeverType()};
192     }
193 
194     ES2PANDA_ASSERT(type->IsETSUnionType());
195     ArenaVector<Type *> nullishTypes(ProgramAllocator()->Adapter());
196     ArenaVector<Type *> notNullishTypes(ProgramAllocator()->Adapter());
197 
198     for (auto *constituentType : type->AsETSUnionType()->ConstituentTypes()) {
199         if (constituentType->IsETSUndefinedType() || constituentType->IsETSNullType()) {
200             nullishTypes.push_back(constituentType);
201         } else {
202             notNullishTypes.push_back(
203                 !constituentType->IsETSTypeParameter()
204                     ? constituentType
205                     : ProgramAllocator()->New<ETSNonNullishType>(constituentType->AsETSTypeParameter()));
206         }
207     }
208 
209     Type *nullishType = nullishTypes.empty() ? GetGlobalTypesHolder()->GlobalETSNeverType()
210                                              : CreateETSUnionType(std::move(nullishTypes));
211     Type *notNullishType = notNullishTypes.empty() ? GetGlobalTypesHolder()->GlobalETSNeverType()
212                                                    : CreateETSUnionType(std::move(notNullishTypes));
213     return {nullishType, notNullishType};
214 }
215 
216 // NOTE(vpukhov): can be implemented with relation if etscompiler will support it
217 template <typename Pred, typename Trv>
MatchConstituentOrConstraint(const Type * type,Pred const & pred,Trv const & trv)218 static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred, Trv const &trv)
219 {
220     auto const traverse = [&pred, &trv](const Type *ttype) {
221         return MatchConstituentOrConstraint<Pred, Trv>(ttype, pred, trv);
222     };
223     if (pred(type)) {
224         return true;
225     }
226     if (!trv(type)) {
227         return false;
228     }
229     if (type->IsETSUnionType()) {
230         for (auto const &ctype : type->AsETSUnionType()->ConstituentTypes()) {
231             if (traverse(ctype)) {
232                 return true;
233             }
234         }
235         return false;
236     }
237     if (type->IsETSTypeParameter()) {
238         return traverse(type->AsETSTypeParameter()->GetConstraintType());
239     }
240     if (type->IsETSNonNullishType()) {
241         auto tparam = type->AsETSNonNullishType()->GetUnderlying();
242         return traverse(tparam->GetConstraintType());
243     }
244     if (type->IsETSPartialTypeParameter()) {
245         auto tparam = type->AsETSPartialTypeParameter()->GetUnderlying();
246         return traverse(tparam->GetConstraintType());
247     }
248     return false;
249 }
250 
251 template <typename Pred>
MatchConstituentOrConstraint(const Type * type,Pred const & pred)252 static bool MatchConstituentOrConstraint(const Type *type, Pred const &pred)
253 {
254     return MatchConstituentOrConstraint(type, pred, []([[maybe_unused]] const Type *t) { return true; });
255 }
256 
PossiblyETSNull() const257 bool Type::PossiblyETSNull() const
258 {
259     return MatchConstituentOrConstraint(
260         this, [](const Type *t) { return t->IsETSAnyType() || t->IsETSNullType(); },
261         [](const Type *t) { return !t->IsETSNonNullishType(); });
262 }
263 
PossiblyETSUndefined() const264 bool Type::PossiblyETSUndefined() const
265 {
266     return MatchConstituentOrConstraint(
267         this, [](const Type *t) { return t->IsETSAnyType() || t->IsETSUndefinedType(); },
268         [](const Type *t) { return !t->IsETSNonNullishType(); });
269 }
270 
PossiblyETSNullish() const271 bool Type::PossiblyETSNullish() const
272 {
273     return MatchConstituentOrConstraint(
274         this, [](const Type *t) { return t->IsETSAnyType() || t->IsETSNullType() || t->IsETSUndefinedType(); },
275         [](const Type *t) { return !t->IsETSNonNullishType(); });
276 }
277 
DefinitelyETSNullish() const278 bool Type::DefinitelyETSNullish() const
279 {
280     return !MatchConstituentOrConstraint(
281         this,
282         [](const Type *t) {
283             return !(t->IsTypeParameter() || t->IsETSUnionType() || t->IsETSNullType() || t->IsETSUndefinedType());
284         },
285         [](const Type *t) { return !t->IsETSNonNullishType(); });
286 }
287 
DefinitelyNotETSNullish() const288 bool Type::DefinitelyNotETSNullish() const
289 {
290     return !PossiblyETSNullish();
291 }
292 
PossiblyETSString() const293 bool Type::PossiblyETSString() const
294 {
295     return MatchConstituentOrConstraint(this, [](const Type *t) {
296         return t->IsETSAnyType() || t->IsETSStringType() ||
297                (t->IsETSObjectType() && t->AsETSObjectType()->IsGlobalETSObjectType());
298     });
299 }
300 
IsValueTypedObjectType(ETSObjectType const * t)301 static bool IsValueTypedObjectType(ETSObjectType const *t)
302 {
303     ETSObjectFlags flags = ETSObjectFlags::FUNCTIONAL_REFERENCE | ETSObjectFlags::VALUE_TYPED |
304                            ETSObjectFlags::ENUM_OBJECT | ETSObjectFlags::FUNCTIONAL;
305     return t->IsGlobalETSObjectType() || t->HasObjectFlag(flags);
306 }
307 
PossiblyETSValueTyped() const308 bool Type::PossiblyETSValueTyped() const
309 {
310     return MatchConstituentOrConstraint(this,
311                                         [](const Type *t) { return t->IsETSNullType() || t->IsETSUndefinedType(); }) ||
312            PossiblyETSValueTypedExceptNullish();
313 }
314 
PossiblyETSValueTypedExceptNullish() const315 bool Type::PossiblyETSValueTypedExceptNullish() const
316 {
317     return MatchConstituentOrConstraint(this, [](const Type *t) {
318         return t->IsETSAnyType() || t->IsETSFunctionType() ||
319                (t->IsETSObjectType() && IsValueTypedObjectType(t->AsETSObjectType()));
320     });
321 }
322 
IsETSArrowType() const323 bool Type::IsETSArrowType() const
324 {
325     // Arrow types (in form `(p: A) => B`) will be isolated from Methods
326     return IsETSFunctionType() && !IsETSMethodType();
327 }
328 
IsETSMethodType() const329 bool Type::IsETSMethodType() const
330 {
331     return HasTypeFlag(TypeFlag::ETS_METHOD);
332 }
333 
IsSaneETSReferenceType(Type const * type)334 [[maybe_unused]] static bool IsSaneETSReferenceType(Type const *type)
335 {
336     static constexpr TypeFlag ETS_SANE_REFERENCE_TYPE =
337         TypeFlag::TYPE_ERROR | TypeFlag::ETS_NULL | TypeFlag::ETS_UNDEFINED | TypeFlag::ETS_OBJECT |
338         TypeFlag::ETS_TYPE_PARAMETER | TypeFlag::WILDCARD | TypeFlag::ETS_NONNULLISH |
339         TypeFlag::ETS_REQUIRED_TYPE_PARAMETER | TypeFlag::ETS_ANY | TypeFlag::ETS_NEVER | TypeFlag::ETS_UNION |
340         TypeFlag::ETS_ARRAY | TypeFlag::FUNCTION | TypeFlag::ETS_PARTIAL_TYPE_PARAMETER | TypeFlag::ETS_TUPLE |
341         TypeFlag::ETS_ENUM | TypeFlag::ETS_READONLY;
342 
343     // Issues
344     if (type->IsETSVoidType()) {  // NOTE(vpukhov): #19701 void refactoring
345         return true;
346     }
347     if (type->IsETSTypeAliasType()) {  // NOTE(vpukhov): #20561
348         return true;
349     }
350     if (type->IsNeverType()) {  // NOTE(vpukhov): #20562 We use ets/never and ts/never simultaneously
351         ES2PANDA_UNREACHABLE();
352     }
353     return type->HasTypeFlag(ETS_SANE_REFERENCE_TYPE);
354 }
355 
IsETSPrimitiveType() const356 bool Type::IsETSPrimitiveType() const
357 {
358     static constexpr TypeFlag ETS_PRIMITIVE = TypeFlag::ETS_NUMERIC | TypeFlag::CHAR | TypeFlag::ETS_BOOLEAN;
359 
360     // Do not modify
361     ES2PANDA_ASSERT(!HasTypeFlag(ETS_PRIMITIVE) == IsSaneETSReferenceType(this));
362     return HasTypeFlag(ETS_PRIMITIVE);
363 }
364 
IsETSPrimitiveOrEnumType() const365 bool Type::IsETSPrimitiveOrEnumType() const
366 {
367     return IsETSPrimitiveType() || IsETSEnumType();
368 }
369 
IsETSReferenceType() const370 bool Type::IsETSReferenceType() const
371 {
372     // Do not modify
373     return !IsETSPrimitiveType();
374 }
375 
IsETSUnboxableObject() const376 bool Type::IsETSUnboxableObject() const
377 {
378     return IsETSObjectType() && AsETSObjectType()->HasObjectFlag(ETSObjectFlags::UNBOXABLE_TYPE);
379 }
380 
IsConstantExpression(ir::Expression * expr,Type * type)381 bool ETSChecker::IsConstantExpression(ir::Expression *expr, Type *type)
382 {
383     return (type->HasTypeFlag(TypeFlag::CONSTANT) && (expr->IsIdentifier() || expr->IsMemberExpression()));
384 }
385 
GetNonConstantType(Type * type)386 Type *ETSChecker::GetNonConstantType(Type *type)
387 {
388     ES2PANDA_ASSERT(type != nullptr);
389     if (type->IsETSStringType()) {
390         return GlobalBuiltinETSStringType();
391     }
392 
393     if (type->IsETSBigIntType()) {
394         return GlobalETSBigIntType();
395     }
396 
397     if (type->IsETSUnionType()) {
398         return CreateETSUnionType(ETSUnionType::GetNonConstantTypes(this, type->AsETSUnionType()->ConstituentTypes()));
399     }
400 
401     if (!type->IsETSPrimitiveType()) {
402         return type;
403     }
404 
405     if (type->HasTypeFlag(TypeFlag::LONG)) {
406         return GlobalLongType();
407     }
408 
409     if (type->HasTypeFlag(TypeFlag::BYTE)) {
410         return GlobalByteType();
411     }
412 
413     if (type->HasTypeFlag(TypeFlag::SHORT)) {
414         return GlobalShortType();
415     }
416 
417     if (type->HasTypeFlag(TypeFlag::CHAR)) {
418         return GlobalCharType();
419     }
420 
421     if (type->HasTypeFlag(TypeFlag::INT)) {
422         return GlobalIntType();
423     }
424 
425     if (type->HasTypeFlag(TypeFlag::FLOAT)) {
426         return GlobalFloatType();
427     }
428 
429     if (type->HasTypeFlag(TypeFlag::DOUBLE)) {
430         return GlobalDoubleType();
431     }
432 
433     if (type->IsETSBooleanType()) {
434         return GlobalETSBooleanType();
435     }
436     return type;
437 }
438 
GetTypeOfSetterGetter(varbinder::Variable * const var)439 Type *ETSChecker::GetTypeOfSetterGetter(varbinder::Variable *const var)
440 {
441     auto *propType = var->TsType()->AsETSFunctionType();
442     if (propType->HasTypeFlag(checker::TypeFlag::GETTER)) {
443         auto *getter = propType->FindGetter();
444         ES2PANDA_ASSERT(getter != nullptr);
445         return getter->ReturnType();
446     }
447 
448     if (propType->FindSetter()->Params().empty()) {
449         var->SetTsType(GlobalTypeError());
450         return GlobalTypeError();
451     }
452 
453     if (propType->FindSetter()->Params().empty()) {
454         var->SetTsType(GlobalTypeError());
455         return GlobalTypeError();
456     }
457 
458     return propType->FindSetter()->Params()[0]->TsType();
459 }
460 
IterateInVariableContext(varbinder::Variable * const var)461 void ETSChecker::IterateInVariableContext(varbinder::Variable *const var)
462 {
463     // Before computing the given variables type, we have to make a new checker context frame so that the checking is
464     // done in the proper context, and have to enter the scope where the given variable is declared, so reference
465     // resolution works properly
466     auto *iter = var->Declaration()->Node()->Parent();
467     while (iter != nullptr) {
468         if (iter->IsMethodDefinition()) {
469             auto *methodDef = iter->AsMethodDefinition();
470             ES2PANDA_ASSERT(methodDef->TsType());
471             auto *func = methodDef->Function();
472             ES2PANDA_ASSERT(func != nullptr);
473             Context().SetContainingSignature(func->Signature());
474         } else if (iter->IsClassDefinition()) {
475             auto *classDef = iter->AsClassDefinition();
476             Type *containingClass {};
477 
478             if (classDef->TsType() == nullptr) {
479                 containingClass = BuildBasicClassProperties(classDef);
480                 ResolveDeclaredMembersOfObject(containingClass->AsETSObjectType());
481             } else {
482                 containingClass = classDef->TsType()->AsETSObjectType();
483             }
484 
485             ES2PANDA_ASSERT(classDef->TsType());
486             if (!containingClass->IsTypeError()) {
487                 Context().SetContainingClass(containingClass->AsETSObjectType());
488             }
489         }
490 
491         iter = iter->Parent();
492     }
493 }
494 
GetTypeFromVarLikeVariableDeclaration(ETSChecker * checker,varbinder::Variable * const var)495 static Type *GetTypeFromVarLikeVariableDeclaration(ETSChecker *checker, varbinder::Variable *const var)
496 {
497     if (var->TsType() != nullptr) {
498         return var->TsType();
499     }
500 
501     auto *declNode = var->Declaration()->Node();
502     if (var->Declaration()->Node()->IsIdentifier()) {
503         declNode = declNode->Parent();
504     }
505     TypeStackElement tse(
506         checker, var->Declaration(),
507         // OHOS CC thinks initializer lists are statement blocks...
508         // CC-OFFNXT(G.FMT.03-CPP) project code style
509         {{diagnostic::CIRCULAR_DEPENDENCY, util::DiagnosticMessageParams {var->Declaration()->Name()}}},
510         declNode->Start());
511     if (tse.HasTypeError()) {
512         var->SetTsType(checker->GlobalTypeError());
513         return checker->GlobalTypeError();
514     }
515     return declNode->Check(checker);
516 }
517 
GetTypeFromVariableDeclaration(varbinder::Variable * const var)518 Type *ETSChecker::GetTypeFromVariableDeclaration(varbinder::Variable *const var)
519 {
520     Type *variableType = nullptr;
521 
522     switch (var->Declaration()->Type()) {
523         case varbinder::DeclType::CLASS: {
524             auto *classDef = var->Declaration()->Node()->AsClassDefinition();
525             BuildBasicClassProperties(classDef);
526             variableType = classDef->TsType();
527             break;
528         }
529         case varbinder::DeclType::CONST:
530             [[fallthrough]];
531         case varbinder::DeclType::READONLY:
532             [[fallthrough]];
533         case varbinder::DeclType::LET:
534             [[fallthrough]];
535         case varbinder::DeclType::VAR: {
536             variableType = GetTypeFromVarLikeVariableDeclaration(this, var);
537             break;
538         }
539 
540         case varbinder::DeclType::FUNC:
541             [[fallthrough]];
542         case varbinder::DeclType::IMPORT:
543             variableType = var->Declaration()->Node()->Check(this);
544             break;
545 
546         case varbinder::DeclType::TYPE_ALIAS:
547             variableType = GetTypeFromTypeAliasReference(var);
548             break;
549 
550         case varbinder::DeclType::INTERFACE:
551             variableType = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration());
552             break;
553 
554         case varbinder::DeclType::ANNOTATIONUSAGE:
555             [[fallthrough]];
556         case varbinder::DeclType::ANNOTATIONDECL:
557             break;
558 
559         default:
560             ES2PANDA_ASSERT(IsAnyError());
561             break;
562     }
563 
564     return variableType != nullptr ? variableType : GlobalTypeError();
565 }
566 
GetTypeOfVariable(varbinder::Variable * const var)567 Type *ETSChecker::GetTypeOfVariable(varbinder::Variable *const var)
568 {
569     if (IsVariableExtensionAccessor(var)) {
570         return var->TsType();
571     }
572 
573     if (IsVariableGetterSetter(var)) {
574         return GetTypeOfSetterGetter(var);
575     }
576 
577     if (var->TsType() != nullptr) {
578         return var->TsType();
579     }
580 
581     // NOTE: kbaladurin. forbid usage of imported entities as types without declarations
582     if (VarBinder()->AsETSBinder()->IsDynamicModuleVariable(var)) {
583         auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(var);
584         ES2PANDA_ASSERT(importData != nullptr);
585         if (importData->import->IsPureDynamic()) {
586             return GlobalBuiltinDynamicType(importData->import->Language());
587         }
588     }
589 
590     checker::SavedCheckerContext savedContext(this, CheckerStatus::NO_OPTS);
591     checker::ScopeContext scopeCtx(this, var->GetScope());
592     IterateInVariableContext(var);
593 
594     return GetTypeFromVariableDeclaration(var);
595 }
596 
597 // Determine if unchecked cast is needed and yield guaranteed source type
GuaranteedTypeForUncheckedCast(Type * base,Type * substituted)598 Type *ETSChecker::GuaranteedTypeForUncheckedCast(Type *base, Type *substituted)
599 {
600     // Apparent type acts as effective representation for type.
601     //  For T extends SomeClass|undefined
602     //  Apparent(Int|T|null) is Int|SomeClass|undefined|null
603     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
604     auto *appBase = GetApparentType(base);
605     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
606     auto *appSubst = GetApparentType(substituted);
607     // Base is supertype of Substituted AND Substituted is supertype of Base
608     return Relation()->IsIdenticalTo(appSubst, appBase) ? nullptr : appBase;
609 }
610 
611 // Determine if substituted property access requires cast from erased type
GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable * const prop)612 Type *ETSChecker::GuaranteedTypeForUncheckedPropertyAccess(varbinder::Variable *const prop)
613 {
614     if (IsVariableStatic(prop)) {
615         return nullptr;
616     }
617 
618     if (prop->TsType() != nullptr && prop->TsType()->IsTypeError()) {
619         return nullptr;
620     }
621 
622     if (IsVariableGetterSetter(prop)) {
623         auto *method = prop->TsType()->AsETSFunctionType();
624         if (!method->HasTypeFlag(checker::TypeFlag::GETTER)) {
625             return nullptr;
626         }
627         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
628         return GuaranteedTypeForUncheckedCallReturn(method->FindGetter());
629     }
630     // NOTE(vpukhov): mark ETSDynamicType properties
631     if (prop->Declaration() == nullptr || prop->Declaration()->Node() == nullptr) {
632         return nullptr;
633     }
634 
635     switch (auto node = prop->Declaration()->Node(); node->Type()) {
636         case ir::AstNodeType::CLASS_PROPERTY: {
637             auto *id = node->AsClassProperty()->Id();
638             ES2PANDA_ASSERT(id != nullptr);
639             auto baseProp = id->Variable();
640             if (baseProp == prop) {
641                 return nullptr;
642             }
643             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
644             return GuaranteedTypeForUncheckedCast(GetTypeOfVariable(baseProp), GetTypeOfVariable(prop));
645         }
646         case ir::AstNodeType::METHOD_DEFINITION:
647         case ir::AstNodeType::CLASS_DEFINITION:
648             return GetTypeOfVariable(prop);
649         case ir::AstNodeType::TS_ENUM_DECLARATION:
650             return nullptr;
651         default:
652             ES2PANDA_UNREACHABLE();
653     }
654 }
655 
656 // Determine if substituted method cast requires cast from erased type
GuaranteedTypeForUncheckedCallReturn(Signature * sig)657 Type *ETSChecker::GuaranteedTypeForUncheckedCallReturn(Signature *sig)
658 {
659     ES2PANDA_ASSERT(sig != nullptr);
660     ES2PANDA_ASSERT(sig->HasFunction());
661     ES2PANDA_ASSERT(sig);
662     if (sig->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) {
663         return sig->ReturnType();
664     }
665     auto *const baseSig = sig->Function() != nullptr ? sig->Function()->Signature() : nullptr;
666     if (baseSig == nullptr || baseSig == sig) {
667         return nullptr;
668     }
669     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
670     return GuaranteedTypeForUncheckedCast(MaybeBoxType(baseSig->ReturnType()), MaybeBoxType(sig->ReturnType()));
671 }
672 
ResolveUnionUncheckedType(ArenaVector<checker::Type * > && apparentTypes)673 Type *ETSChecker::ResolveUnionUncheckedType(ArenaVector<checker::Type *> &&apparentTypes)
674 {
675     if (apparentTypes.empty()) {
676         return nullptr;
677     }
678     auto *unionType = CreateETSUnionType(std::move(apparentTypes));
679     ES2PANDA_ASSERT(unionType != nullptr);
680     if (unionType->IsETSUnionType()) {
681         checker::Type *typeLUB = unionType->AsETSUnionType()->GetAssemblerLUB();
682         return typeLUB;
683     }
684     // Is case of single apparent type, just return itself
685     return unionType;
686 }
687 
GuaranteedTypeForUnionFieldAccess(ir::MemberExpression * memberExpression,ETSUnionType * etsUnionType)688 Type *ETSChecker::GuaranteedTypeForUnionFieldAccess(ir::MemberExpression *memberExpression, ETSUnionType *etsUnionType)
689 {
690     const auto &types = etsUnionType->ConstituentTypes();
691     ArenaVector<checker::Type *> apparentTypes {ProgramAllocator()->Adapter()};
692     const auto *prop = memberExpression->Property();
693     if (!prop->IsIdentifier() && !prop->IsStringLiteral()) {
694         return GlobalTypeError();
695     }
696     const auto &propertyName = prop->IsIdentifier() ? prop->AsIdentifier()->Name() : prop->AsStringLiteral()->Str();
697     for (auto *type : types) {
698         auto searchFlags = PropertySearchFlags::SEARCH_FIELD | PropertySearchFlags::SEARCH_METHOD |
699                            PropertySearchFlags::SEARCH_IN_BASE;
700         if (!type->IsETSObjectType()) {
701             return nullptr;
702         }
703         auto *fieldVar = type->AsETSObjectType()->GetProperty(propertyName, searchFlags);
704         if (fieldVar == nullptr) {
705             return nullptr;
706         }
707         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
708         auto *fieldType = GuaranteedTypeForUncheckedPropertyAccess(fieldVar);
709         if (fieldType != nullptr) {
710             apparentTypes.push_back(fieldType);
711         }
712     }
713     return ResolveUnionUncheckedType(std::move(apparentTypes));
714 }
715 
IsAllowedTypeAliasRecursion(const ir::TSTypeAliasDeclaration * typeAliasNode,std::unordered_set<const ir::TSTypeAliasDeclaration * > & typeAliases)716 bool ETSChecker::IsAllowedTypeAliasRecursion(const ir::TSTypeAliasDeclaration *typeAliasNode,
717                                              std::unordered_set<const ir::TSTypeAliasDeclaration *> &typeAliases)
718 {
719     bool isAllowedRerursiveType = true;
720 
721     RecursionPreserver<const ir::TSTypeAliasDeclaration> recursionPreserver(typeAliases, typeAliasNode);
722 
723     if (*recursionPreserver) {
724         return false;
725     }
726 
727     auto typeAliasDeclarationCheck = [this, &typeAliases](ir::ETSTypeReferencePart *part) {
728         if (!part->Name()->IsIdentifier()) {
729             return false;
730         }
731 
732         if (part->Name()->Variable() == nullptr) {
733             return true;
734         }
735 
736         auto const *const decl = part->Name()->Variable()->Declaration();
737         if (auto const *const node = decl->Node(); node != nullptr && node->IsTSTypeAliasDeclaration()) {
738             return IsAllowedTypeAliasRecursion(node->AsTSTypeAliasDeclaration(), typeAliases);
739         }
740 
741         return true;
742     };
743 
744     if (typeAliasNode->TypeAnnotation()->IsETSTypeReference()) {
745         isAllowedRerursiveType &=
746             typeAliasDeclarationCheck(typeAliasNode->TypeAnnotation()->AsETSTypeReference()->Part());
747     }
748 
749     if (isAllowedRerursiveType && typeAliasNode->TypeAnnotation()->IsETSUnionType()) {
750         for (auto &type : typeAliasNode->TypeAnnotation()->AsETSUnionType()->Types()) {
751             if (type->IsETSTypeReference()) {
752                 isAllowedRerursiveType &= typeAliasDeclarationCheck(type->AsETSTypeReference()->Part());
753             }
754         }
755     }
756 
757     return isAllowedRerursiveType;
758 }
759 
GetTypeFromTypeAliasReference(varbinder::Variable * var)760 Type *ETSChecker::GetTypeFromTypeAliasReference(varbinder::Variable *var)
761 {
762     if (var->TsType() != nullptr) {
763         return var->TsType();
764     }
765 
766     auto *const aliasTypeNode = var->Declaration()->Node()->AsTSTypeAliasDeclaration();
767     std::unordered_set<const ir::TSTypeAliasDeclaration *> typeAliases;
768     auto isAllowedRecursion = IsAllowedTypeAliasRecursion(aliasTypeNode, typeAliases);
769 
770     TypeStackElement tse(this, aliasTypeNode, {{diagnostic::CYCLIC_ALIAS}}, aliasTypeNode->Start(), isAllowedRecursion);
771 
772     if (tse.HasTypeError()) {
773         var->SetTsType(GlobalTypeError());
774         return GlobalTypeError();
775     }
776 
777     auto *typeAliasType = tse.GetElementType();
778 
779     if (typeAliasType != nullptr) {
780         typeAliasType->AsETSTypeAliasType()->SetRecursive();
781         return typeAliasType;
782     }
783 
784     typeAliasType = CreateETSTypeAliasType(aliasTypeNode->Id()->Name(), aliasTypeNode);
785     if (aliasTypeNode->TypeParams() != nullptr) {
786         auto [typeParamTypes, ok] = CreateUnconstrainedTypeParameters(aliasTypeNode->TypeParams());
787         ES2PANDA_ASSERT(typeAliasType != nullptr);
788         typeAliasType->AsETSTypeAliasType()->SetTypeArguments(std::move(typeParamTypes));
789         if (ok) {
790             AssignTypeParameterConstraints(aliasTypeNode->TypeParams());
791         }
792     }
793     tse.SetElementType(typeAliasType);
794 
795     aliasTypeNode->Check(this);
796     Type *targetType = aliasTypeNode->TypeAnnotation()->GetType(this);
797     typeAliasType->AsETSTypeAliasType()->SetTargetType(targetType);
798 
799     var->SetTsType(targetType);
800     return targetType;
801 }
802 
GetTypeFromInterfaceReference(varbinder::Variable * var)803 Type *ETSChecker::GetTypeFromInterfaceReference(varbinder::Variable *var)
804 {
805     if (var->TsType() != nullptr) {
806         return var->TsType();
807     }
808 
809     CheckerStatus status = CheckerStatus::IN_STATIC_CONTEXT;
810     status &= this->Context().Status();
811     this->Context().Status() &= ~CheckerStatus::IN_STATIC_CONTEXT;
812 
813     auto *interfaceType = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration());
814     var->SetTsType(interfaceType);
815 
816     this->Context().Status() |= status;
817     return interfaceType;
818 }
819 
GetTypeFromClassReference(varbinder::Variable * var)820 Type *ETSChecker::GetTypeFromClassReference(varbinder::Variable *var)
821 {
822     if (var->TsType() != nullptr) {
823         return var->TsType();
824     }
825 
826     auto classDef = var->Declaration()->Node()->AsClassDefinition();
827 
828     CheckerStatus status = CheckerStatus::IN_STATIC_CONTEXT;
829     status &= this->Context().Status();
830     this->Context().Status() &= ~CheckerStatus::IN_STATIC_CONTEXT;
831 
832     auto *classType = BuildBasicClassProperties(classDef);
833     var->SetTsType(classType);
834     this->Context().Status() |= status;
835     return classType;
836 }
837 
GetTypeFromTypeParameterReference(varbinder::LocalVariable * var,const lexer::SourcePosition & pos)838 Type *ETSChecker::GetTypeFromTypeParameterReference(varbinder::LocalVariable *var, const lexer::SourcePosition &pos)
839 {
840     ES2PANDA_ASSERT(var->Declaration()->Node()->IsTSTypeParameter());
841     if ((var->Declaration()->Node()->AsTSTypeParameter()->Parent()->Parent()->IsClassDefinition() ||
842          var->Declaration()->Node()->AsTSTypeParameter()->Parent()->Parent()->IsTSInterfaceDeclaration()) &&
843         HasStatus(CheckerStatus::IN_STATIC_CONTEXT)) {
844         return TypeError(var, diagnostic::STATIC_REF_TO_NONSTATIC, {var->Name()}, pos);
845     }
846 
847     return var->TsType();
848 }
849 
CheckAmbientAnnotationFieldInitializer(ir::Expression * init,ir::Expression * expected)850 bool ETSChecker::CheckAmbientAnnotationFieldInitializer(ir::Expression *init, ir::Expression *expected)
851 {
852     if (init->Type() != expected->Type()) {
853         return false;
854     }
855 
856     switch (init->Type()) {
857         case ir::AstNodeType::NUMBER_LITERAL:
858         case ir::AstNodeType::BOOLEAN_LITERAL:
859         case ir::AstNodeType::STRING_LITERAL:
860         case ir::AstNodeType::ARRAY_EXPRESSION:
861         case ir::AstNodeType::MEMBER_EXPRESSION:
862         case ir::AstNodeType::UNARY_EXPRESSION: {
863             if (CheckAmbientAnnotationFieldInitializerValue(init, expected)) {
864                 break;
865             }
866             LogError(diagnostic::AMBIENT_ANNOT_FIELD_INIT_MISMATCH, {}, init->Start());
867             return false;
868         }
869         default:
870             ES2PANDA_UNREACHABLE();
871     }
872 
873     return true;
874 }
875 
IsValidateUnaryExpression(lexer::TokenType operatorType)876 static bool IsValidateUnaryExpression(lexer::TokenType operatorType)
877 {
878     return operatorType == lexer::TokenType::PUNCTUATOR_PLUS || operatorType == lexer::TokenType::PUNCTUATOR_MINUS;
879 }
880 
CheckAmbientAnnotationFieldInitializerValue(ir::Expression * init,ir::Expression * expected)881 bool ETSChecker::CheckAmbientAnnotationFieldInitializerValue(ir::Expression *init, ir::Expression *expected)
882 {
883     switch (init->Type()) {
884         case ir::AstNodeType::NUMBER_LITERAL: {
885             return init->AsNumberLiteral()->Number().GetDouble() == expected->AsNumberLiteral()->Number().GetDouble();
886         }
887         case ir::AstNodeType::BOOLEAN_LITERAL: {
888             return init->AsBooleanLiteral()->Value() == expected->AsBooleanLiteral()->Value();
889         }
890         case ir::AstNodeType::STRING_LITERAL: {
891             return init->AsStringLiteral()->Str() == expected->AsStringLiteral()->Str();
892         }
893         case ir::AstNodeType::ARRAY_EXPRESSION: {
894             const auto &elements = init->AsArrayExpression()->Elements();
895             const auto &expectedElements = expected->AsArrayExpression()->Elements();
896             if (elements.size() != expectedElements.size()) {
897                 return false;
898             }
899             for (size_t i = 0; i < elements.size(); ++i) {
900                 if (!CheckAmbientAnnotationFieldInitializer(elements[i], expectedElements[i])) {
901                     return false;
902                 }
903             }
904             return true;
905         }
906         case ir::AstNodeType::MEMBER_EXPRESSION: {
907             if (!Relation()->IsIdenticalTo(init->TsType(), expected->TsType())) {
908                 return false;
909             }
910             auto elem = init->AsMemberExpression()->Property()->AsIdentifier()->Name();
911             auto expectedElem = expected->AsMemberExpression()->Property()->AsIdentifier()->Name();
912             return elem == expectedElem;
913         }
914         case ir::AstNodeType::UNARY_EXPRESSION: {
915             if (!IsValidateUnaryExpression(init->AsUnaryExpression()->OperatorType()) ||
916                 !IsValidateUnaryExpression(expected->AsUnaryExpression()->OperatorType())) {
917                 LogError(diagnostic::ILLEGAL_UNARY_OP, {}, init->Start());
918                 return false;
919             }
920             if (init->AsUnaryExpression()->OperatorType() != expected->AsUnaryExpression()->OperatorType()) {
921                 return false;
922             }
923             return CheckAmbientAnnotationFieldInitializer(init->AsUnaryExpression()->Argument(),
924                                                           expected->AsUnaryExpression()->Argument());
925         }
926         default:
927             ES2PANDA_UNREACHABLE();
928     }
929 }
930 
CheckAmbientAnnotation(ir::AnnotationDeclaration * annoImpl,ir::AnnotationDeclaration * annoDecl)931 void ETSChecker::CheckAmbientAnnotation(ir::AnnotationDeclaration *annoImpl, ir::AnnotationDeclaration *annoDecl)
932 {
933     std::unordered_map<util::StringView, ir::ClassProperty *> fieldMap;
934 
935     for (auto *prop : annoDecl->Properties()) {
936         auto *field = prop->AsClassProperty();
937         ES2PANDA_ASSERT(field->Id() != nullptr);
938         fieldMap[field->Id()->Name()] = field;
939     }
940 
941     for (auto *prop : annoImpl->Properties()) {
942         auto *field = prop->AsClassProperty();
943         auto *id = field->Id();
944         ES2PANDA_ASSERT(id != nullptr);
945         auto fieldName = id->Name();
946         auto fieldDeclIter = fieldMap.find(fieldName);
947         if (fieldDeclIter == fieldMap.end()) {
948             LogError(diagnostic::AMBIENT_ANNOT_IMPL_OF_UNDEFINED_FIELD, {fieldName, annoDecl->GetBaseName()->Name()},
949                      field->Start());
950             continue;
951         }
952 
953         auto *fieldDecl = fieldDeclIter->second;
954         fieldDecl->Check(this);
955         if (!Relation()->IsIdenticalTo(field->TsType(), fieldDecl->TsType())) {
956             LogError(diagnostic::AMBIENT_ANNOT_FIELD_TYPE_MISMATCH, {fieldName, annoDecl->GetBaseName()->Name()},
957                      field->TypeAnnotation()->Start());
958         }
959 
960         bool hasValueMismatch = (field->Value() == nullptr) != (fieldDecl->Value() == nullptr);
961         bool initializerInvalid = field->Value() != nullptr && fieldDecl->Value() != nullptr &&
962                                   !CheckAmbientAnnotationFieldInitializer(field->Value(), fieldDecl->Value());
963         if (hasValueMismatch || initializerInvalid) {
964             LogError(diagnostic::AMBIENT_ANNOT_FIELD_MISMATCH, {fieldName, annoDecl->GetBaseName()->Name()},
965                      field->Start());
966         }
967         fieldMap.erase(fieldDeclIter);
968     }
969 
970     for (auto it : fieldMap) {
971         LogError(diagnostic::AMBIENT_ANNOT_FIELD_MISSING_IMPL,
972                  {it.second->Key()->AsIdentifier()->Name(), annoDecl->GetBaseName()->Name()}, annoImpl->Start());
973     }
974 }
975 
CheckFunctionSignatureAnnotations(const ArenaVector<ir::Expression * > & params,ir::TSTypeParameterDeclaration * typeParams,ir::TypeNode * returnTypeAnnotation)976 void ETSChecker::CheckFunctionSignatureAnnotations(const ArenaVector<ir::Expression *> &params,
977                                                    ir::TSTypeParameterDeclaration *typeParams,
978                                                    ir::TypeNode *returnTypeAnnotation)
979 {
980     for (auto *param : params) {
981         if (param->IsETSParameterExpression()) {
982             CheckAnnotations(param->AsETSParameterExpression()->Annotations());
983             if (param->AsETSParameterExpression()->TypeAnnotation() != nullptr) {
984                 param->AsETSParameterExpression()->TypeAnnotation()->Check(this);
985             }
986         }
987     }
988 
989     if (typeParams != nullptr) {
990         for (auto *typeParam : typeParams->Params()) {
991             CheckAnnotations(typeParam->Annotations());
992         }
993     }
994 
995     if (returnTypeAnnotation != nullptr) {
996         ValidateThisUsage(returnTypeAnnotation);
997         CheckAnnotations(returnTypeAnnotation->Annotations());
998     }
999 }
1000 
CheckAndLogInvalidThisUsage(const ir::TypeNode * type,const diagnostic::DiagnosticKind & diagnostic)1001 bool ETSChecker::CheckAndLogInvalidThisUsage(const ir::TypeNode *type, const diagnostic::DiagnosticKind &diagnostic)
1002 {
1003     if (type->IsTSThisType()) {
1004         LogError(diagnostic, {}, type->Start());
1005         return true;
1006     }
1007     return false;
1008 }
1009 
IsFixedArray(ir::ETSTypeReferencePart * part)1010 bool ETSChecker::IsFixedArray(ir::ETSTypeReferencePart *part)
1011 {
1012     return part->Name()->IsIdentifier() &&
1013            part->Name()->AsIdentifier()->Name().Mutf8() == compiler::Signatures::FIXED_ARRAY_TYPE_NAME;
1014 }
1015 
ValidateThisUsage(const ir::TypeNode * returnTypeAnnotation)1016 void ETSChecker::ValidateThisUsage(const ir::TypeNode *returnTypeAnnotation)
1017 {
1018     if (returnTypeAnnotation->IsETSUnionType()) {
1019         auto types = returnTypeAnnotation->AsETSUnionType()->Types();
1020         for (auto type : types) {
1021             if (CheckAndLogInvalidThisUsage(type, diagnostic::NOT_ALLOWED_THIS_IN_UNION_TYPE)) {
1022                 return;
1023             }
1024             ValidateThisUsage(type);
1025         }
1026         return;
1027     }
1028     if (returnTypeAnnotation->IsETSTuple()) {
1029         auto types = returnTypeAnnotation->AsETSTuple()->GetTupleTypeAnnotationsList();
1030         for (auto type : types) {
1031             if (CheckAndLogInvalidThisUsage(type, diagnostic::NOT_ALLOWED_THIS_IN_TUPLE_TYPE)) {
1032                 return;
1033             }
1034             ValidateThisUsage(type);
1035         }
1036         return;
1037     }
1038     if (returnTypeAnnotation->IsETSTypeReference() &&
1039         IsFixedArray(returnTypeAnnotation->AsETSTypeReference()->Part()) &&
1040         returnTypeAnnotation->AsETSTypeReference()->Part()->TypeParams() != nullptr) {
1041         auto elementType = returnTypeAnnotation->AsETSTypeReference()->Part()->TypeParams()->Params()[0];
1042         if (CheckAndLogInvalidThisUsage(elementType, diagnostic::NOT_ALLOWED_THIS_IN_ARRAY_TYPE)) {
1043             return;
1044         }
1045         ValidateThisUsage(elementType);
1046         return;
1047     }
1048 }
1049 
CheckAnnotations(const ArenaVector<ir::AnnotationUsage * > & annotations)1050 void ETSChecker::CheckAnnotations(const ArenaVector<ir::AnnotationUsage *> &annotations)
1051 {
1052     if (annotations.empty()) {
1053         return;
1054     }
1055     std::unordered_set<util::StringView> seenAnnotations;
1056     for (const auto &anno : annotations) {
1057         anno->Check(this);
1058         CheckAnnotationRetention(anno);
1059         auto annoName = anno->GetBaseName()->Name();
1060         if (seenAnnotations.find(annoName) != seenAnnotations.end()) {
1061             LogError(diagnostic::ANNOT_DUPLICATE, {annoName}, anno->Start());
1062         }
1063         seenAnnotations.insert(annoName);
1064     }
1065 }
1066 
IsValidSourceRetentionUsage(ir::AnnotationUsage * anno,ir::AnnotationDeclaration * annoDecl)1067 static bool IsValidSourceRetentionUsage(ir::AnnotationUsage *anno, ir::AnnotationDeclaration *annoDecl)
1068 {
1069     bool isTransformedClassProperty = anno->Parent()->IsClassProperty() &&
1070                                       anno->Parent()->Parent()->IsClassDefinition() &&
1071                                       anno->Parent()->Parent()->AsClassDefinition()->IsModule();
1072     return (!anno->Parent()->IsClassDefinition() && !anno->Parent()->IsScriptFunction() &&
1073             !anno->Parent()->IsETSModule() && !anno->Parent()->IsTSInterfaceDeclaration() &&
1074             !anno->Parent()->IsETSParameterExpression() && !anno->Parent()->IsClassProperty() &&
1075             !annoDecl->IsSourceRetention()) ||
1076            (isTransformedClassProperty && !annoDecl->IsSourceRetention());
1077 }
1078 
CheckAnnotationRetention(ir::AnnotationUsage * anno)1079 void ETSChecker::CheckAnnotationRetention(ir::AnnotationUsage *anno)
1080 {
1081     if (anno->GetBaseName()->Name().Mutf8() == compiler::Signatures::BUILTIN_RETENTION &&
1082         !anno->Parent()->IsAnnotationDeclaration()) {
1083         LogError(diagnostic::INVALID_ANNOTATION_RETENTION, {}, anno->Start());
1084         return;
1085     }
1086     if (anno->GetBaseName()->Variable() == nullptr ||
1087         !anno->GetBaseName()->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) {
1088         return;
1089     }
1090     auto *annoDecl = anno->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration();
1091     annoDecl->Check(this);
1092     if (IsValidSourceRetentionUsage(anno, annoDecl)) {
1093         LogError(diagnostic::ANNOTATION_ON_LAMBDA_LOCAL_TYPE, {}, anno->Start());
1094     }
1095 }
1096 
HandleAnnotationRetention(ir::AnnotationUsage * anno,ir::AnnotationDeclaration * annoDecl)1097 void ETSChecker::HandleAnnotationRetention(ir::AnnotationUsage *anno, ir::AnnotationDeclaration *annoDecl)
1098 {
1099     if (anno->Properties().size() != 1) {
1100         return;
1101     }
1102     const auto value = anno->Properties()[0]->AsClassProperty()->Value();
1103     if (value->IsStringLiteral()) {
1104         const auto policyStr = value->AsStringLiteral()->Str().Mutf8();
1105         if (policyStr == compiler::Signatures::SOURCE_POLICY) {
1106             annoDecl->SetSourceRetention();
1107             return;
1108         }
1109         if (policyStr == compiler::Signatures::BYTECODE_POLICY) {
1110             annoDecl->SetBytecodeRetention();
1111             return;
1112         }
1113         if (policyStr == compiler::Signatures::RUNTIME_POLICY) {
1114             annoDecl->SetRuntimeRetention();
1115             return;
1116         }
1117     }
1118     LogError(diagnostic::ANNOTATION_POLICY_INVALID, {}, anno->Properties()[0]->Start());
1119 }
1120 
CheckStandardAnnotation(ir::AnnotationUsage * anno)1121 void ETSChecker::CheckStandardAnnotation(ir::AnnotationUsage *anno)
1122 {
1123     if (anno->GetBaseName()->Variable() == nullptr || IsTypeError(anno->GetBaseName()->TsType())) {
1124         return;
1125     }
1126     ES2PANDA_ASSERT(anno->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration() != nullptr);
1127     auto *annoDecl = anno->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration();
1128     auto annoName = annoDecl->InternalName().Mutf8();
1129     if (annoName.rfind(compiler::Signatures::STD_ANNOTATIONS) != 0) {
1130         LogError(diagnostic::STANDARD_ANNOTATION_REQUIRED, {}, anno->Start());
1131     }
1132     if (annoName == compiler::Signatures::STD_ANNOTATIONS_RETENTION) {
1133         HandleAnnotationRetention(anno, anno->Parent()->AsAnnotationDeclaration());
1134     }
1135 }
1136 
CheckAnnotationPropertyType(ir::ClassProperty * property)1137 void ETSChecker::CheckAnnotationPropertyType(ir::ClassProperty *property)
1138 {
1139     // typeAnnotation check
1140     if (!ValidateAnnotationPropertyType(property->TsType())) {
1141         LogError(diagnostic::ANNOT_FIELD_INVALID_TYPE, {}, property->Start());
1142     }
1143 
1144     // The type of the Initializer has been check in the parser,
1145     // except for the enumeration type, because it is a member expression,
1146     // so here is an additional check to the enumeration type.
1147     if (property->Value() != nullptr &&
1148         ((property->Value()->IsMemberExpression() && !property->TsType()->IsETSEnumType()) ||
1149          property->Value()->IsIdentifier())) {
1150         LogError(diagnostic::ANNOTATION_FIELD_NONLITERAL, {}, property->Value()->Start());
1151     }
1152 }
1153 
CheckSinglePropertyAnnotation(ir::AnnotationUsage * st,ir::AnnotationDeclaration * annoDecl)1154 void ETSChecker::CheckSinglePropertyAnnotation(ir::AnnotationUsage *st, ir::AnnotationDeclaration *annoDecl)
1155 {
1156     auto *param = st->Properties().at(0)->AsClassProperty();
1157     if (annoDecl->Properties().size() > 1) {
1158         LogError(diagnostic::ANNOT_MULTIPLE_FIELD, {st->GetBaseName()->Name()}, st->Start());
1159     }
1160     auto singleField = annoDecl->Properties().at(0)->AsClassProperty();
1161     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1162     auto clone = singleField->TypeAnnotation()->Clone(ProgramAllocator(), param);
1163     param->SetTypeAnnotation(clone);
1164     ScopeContext scopeCtx(this, st->Scope());
1165     param->Check(this);
1166     CheckAnnotationPropertyType(param);
1167 }
1168 
ProcessRequiredFields(ArenaUnorderedMap<util::StringView,ir::ClassProperty * > & fieldMap,ir::AnnotationUsage * st,ETSChecker * checker) const1169 void ETSChecker::ProcessRequiredFields(ArenaUnorderedMap<util::StringView, ir::ClassProperty *> &fieldMap,
1170                                        ir::AnnotationUsage *st, ETSChecker *checker) const
1171 {
1172     for (const auto &entry : fieldMap) {
1173         if (entry.second->Value() == nullptr) {
1174             checker->LogError(diagnostic::ANNOT_FIELD_NO_VAL, {entry.first}, st->Start());
1175             continue;
1176         }
1177         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1178         auto *clone = entry.second->Clone(checker->ProgramAllocator(), st);
1179         st->AddProperty(clone);
1180     }
1181 }
1182 
CheckMultiplePropertiesAnnotation(ir::AnnotationUsage * st,util::StringView const & baseName,ArenaUnorderedMap<util::StringView,ir::ClassProperty * > & fieldMap)1183 void ETSChecker::CheckMultiplePropertiesAnnotation(ir::AnnotationUsage *st, util::StringView const &baseName,
1184                                                    ArenaUnorderedMap<util::StringView, ir::ClassProperty *> &fieldMap)
1185 {
1186     for (auto *it : st->Properties()) {
1187         auto *param = it->AsClassProperty();
1188         auto *id = param->Id();
1189         ES2PANDA_ASSERT(id != nullptr);
1190         auto result = fieldMap.find(id->Name());
1191         if (result == fieldMap.end()) {
1192             LogError(diagnostic::ANNOT_PROP_UNDEFINED, {id->Name(), baseName}, param->Start());
1193             continue;
1194         }
1195 
1196         if (result->second == nullptr || result->second->TypeAnnotation() == nullptr) {
1197             continue;
1198         }
1199 
1200         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1201         auto clone = result->second->TypeAnnotation()->Clone(ProgramAllocator(), param);
1202         param->SetTypeAnnotation(clone);
1203         ScopeContext scopeCtx(this, st->Scope());
1204         param->Check(this);
1205         CheckAnnotationPropertyType(param);
1206         fieldMap.erase(result);
1207     }
1208 }
1209 
MaybeUnboxInRelation(Type * type)1210 Type *ETSChecker::MaybeUnboxInRelation(Type *type)
1211 {
1212     if (type == nullptr) {
1213         return nullptr;
1214     }
1215 
1216     if (type->IsETSPrimitiveType()) {
1217         return type;
1218     }
1219 
1220     if (!type->IsETSUnboxableObject()) {
1221         return nullptr;
1222     }
1223 
1224     auto savedResult = Relation()->IsTrue();
1225     Relation()->Result(false);
1226 
1227     UnboxingConverter converter = UnboxingConverter(AsETSChecker(), Relation(), type, type);
1228     Relation()->Result(savedResult);
1229     return converter.Result();
1230 }
1231 
MaybeUnboxConditionalInRelation(Type * const objectType)1232 Type *ETSChecker::MaybeUnboxConditionalInRelation(Type *const objectType)
1233 {
1234     if (objectType != nullptr && objectType->IsTypeError()) {
1235         return objectType;
1236     }
1237 
1238     if (objectType == nullptr) {
1239         return nullptr;
1240     }
1241 
1242     if (auto *unboxed = MaybeUnboxInRelation(objectType); unboxed != nullptr) {
1243         return unboxed;
1244     }
1245 
1246     return objectType;
1247 }
1248 
MaybeBoxInRelation(Type * objectType)1249 Type *ETSChecker::MaybeBoxInRelation(Type *objectType)
1250 {
1251     if (objectType == nullptr) {
1252         return nullptr;
1253     }
1254 
1255     if (objectType->IsETSUnboxableObject()) {
1256         return objectType;
1257     }
1258 
1259     if (!objectType->IsETSPrimitiveType()) {
1260         return nullptr;
1261     }
1262 
1263     auto savedResult = Relation()->IsTrue();
1264     Relation()->Result(false);
1265 
1266     BoxingConverter converter = BoxingConverter(AsETSChecker(), Relation(), objectType,
1267                                                 Checker::GetGlobalTypesHolder()->GlobalIntegerBuiltinType());
1268     Relation()->Result(savedResult);
1269     return converter.Result();
1270 }
1271 
MaybeBoxType(Type * type) const1272 Type *ETSChecker::MaybeBoxType(Type *type) const
1273 {
1274     ES2PANDA_ASSERT(type != nullptr);
1275     return type->IsETSPrimitiveType() ? BoxingConverter::Convert(this, type) : type;
1276 }
1277 
MaybeUnboxType(Type * type) const1278 Type *ETSChecker::MaybeUnboxType(Type *type) const
1279 {
1280     ES2PANDA_ASSERT(type != nullptr);
1281     return type->IsETSUnboxableObject() ? UnboxingConverter::Convert(this, type->AsETSObjectType()) : type;
1282 }
1283 
MaybeBoxType(Type const * type) const1284 Type const *ETSChecker::MaybeBoxType(Type const *type) const
1285 {
1286     return MaybeBoxType(const_cast<Type *>(type));
1287 }
1288 
MaybeUnboxType(Type const * type) const1289 Type const *ETSChecker::MaybeUnboxType(Type const *type) const
1290 {
1291     return MaybeUnboxType(const_cast<Type *>(type));
1292 }
1293 
GetBoxingFlag(Type * const boxingType)1294 ir::BoxingUnboxingFlags ETSChecker::GetBoxingFlag(Type *const boxingType)
1295 {
1296     auto typeKind = TypeKind(MaybeUnboxInRelation(boxingType));
1297     switch (typeKind) {
1298         case TypeFlag::ETS_BOOLEAN:
1299             return ir::BoxingUnboxingFlags::BOX_TO_BOOLEAN;
1300         case TypeFlag::BYTE:
1301             return ir::BoxingUnboxingFlags::BOX_TO_BYTE;
1302         case TypeFlag::CHAR:
1303             return ir::BoxingUnboxingFlags::BOX_TO_CHAR;
1304         case TypeFlag::SHORT:
1305             return ir::BoxingUnboxingFlags::BOX_TO_SHORT;
1306         case TypeFlag::INT:
1307             return ir::BoxingUnboxingFlags::BOX_TO_INT;
1308         case TypeFlag::LONG:
1309             return ir::BoxingUnboxingFlags::BOX_TO_LONG;
1310         case TypeFlag::FLOAT:
1311             return ir::BoxingUnboxingFlags::BOX_TO_FLOAT;
1312         case TypeFlag::DOUBLE:
1313             return ir::BoxingUnboxingFlags::BOX_TO_DOUBLE;
1314         default:
1315             ES2PANDA_UNREACHABLE();
1316     }
1317 }
1318 
GetUnboxingFlag(Type const * const unboxingType) const1319 ir::BoxingUnboxingFlags ETSChecker::GetUnboxingFlag(Type const *const unboxingType) const
1320 {
1321     auto typeKind = TypeKind(unboxingType);
1322     switch (typeKind) {
1323         case TypeFlag::ETS_BOOLEAN:
1324             return ir::BoxingUnboxingFlags::UNBOX_TO_BOOLEAN;
1325         case TypeFlag::BYTE:
1326             return ir::BoxingUnboxingFlags::UNBOX_TO_BYTE;
1327         case TypeFlag::CHAR:
1328             return ir::BoxingUnboxingFlags::UNBOX_TO_CHAR;
1329         case TypeFlag::SHORT:
1330             return ir::BoxingUnboxingFlags::UNBOX_TO_SHORT;
1331         case TypeFlag::INT:
1332             return ir::BoxingUnboxingFlags::UNBOX_TO_INT;
1333         case TypeFlag::LONG:
1334             return ir::BoxingUnboxingFlags::UNBOX_TO_LONG;
1335         case TypeFlag::FLOAT:
1336             return ir::BoxingUnboxingFlags::UNBOX_TO_FLOAT;
1337         case TypeFlag::DOUBLE:
1338             return ir::BoxingUnboxingFlags::UNBOX_TO_DOUBLE;
1339         default:
1340             ES2PANDA_UNREACHABLE();
1341     }
1342 }
1343 
MaybeAddBoxingFlagInRelation(TypeRelation * relation,Type * target)1344 void ETSChecker::MaybeAddBoxingFlagInRelation(TypeRelation *relation, Type *target)
1345 {
1346     auto boxingResult = MaybeBoxInRelation(target);
1347     if ((boxingResult != nullptr) && !relation->OnlyCheckBoxingUnboxing()) {
1348         relation->GetNode()->RemoveBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOXING_FLAG);
1349         relation->GetNode()->AddBoxingUnboxingFlags(GetBoxingFlag(boxingResult));
1350         relation->Result(true);
1351     }
1352 }
1353 
MaybeAddUnboxingFlagInRelation(TypeRelation * relation,Type * source,Type * self)1354 void ETSChecker::MaybeAddUnboxingFlagInRelation(TypeRelation *relation, Type *source, Type *self)
1355 {
1356     auto unboxingResult = UnboxingConverter(this, relation, source, self).Result();
1357     if ((unboxingResult != nullptr) && relation->IsTrue() && !relation->OnlyCheckBoxingUnboxing()) {
1358         relation->GetNode()->AddBoxingUnboxingFlags(GetUnboxingFlag(unboxingResult));
1359     }
1360 }
1361 
CheckUnboxedTypeWidenable(TypeRelation * relation,Type * target,Type * self)1362 void ETSChecker::CheckUnboxedTypeWidenable(TypeRelation *relation, Type *target, Type *self)
1363 {
1364     checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(
1365         relation, TypeRelationFlag::ONLY_CHECK_WIDENING |
1366                       (relation->ApplyNarrowing() ? TypeRelationFlag::NARROWING : TypeRelationFlag::NONE));
1367     // NOTE: vpukhov. handle union type
1368     auto unboxedType = MaybeUnboxInRelation(target);
1369     if (unboxedType == nullptr) {
1370         return;
1371     }
1372     NarrowingWideningConverter(this, relation, unboxedType, self);
1373     if (!relation->IsTrue()) {
1374         relation->Result(relation->IsAssignableTo(self, unboxedType));
1375     }
1376 }
1377 
CheckUnboxedTypesAssignable(TypeRelation * relation,Type * source,Type * target)1378 void ETSChecker::CheckUnboxedTypesAssignable(TypeRelation *relation, Type *source, Type *target)
1379 {
1380     auto *unboxedSourceType = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(source);
1381     auto *unboxedTargetType = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(target);
1382     if (unboxedSourceType == nullptr || unboxedTargetType == nullptr) {
1383         return;
1384     }
1385     relation->IsAssignableTo(unboxedSourceType, unboxedTargetType);
1386     if (relation->IsTrue()) {
1387         relation->GetNode()->AddBoxingUnboxingFlags(
1388             relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxedSourceType));
1389     }
1390 }
1391 
CheckBoxedSourceTypeAssignable(TypeRelation * relation,Type * source,Type * target)1392 void ETSChecker::CheckBoxedSourceTypeAssignable(TypeRelation *relation, Type *source, Type *target)
1393 {
1394     ES2PANDA_ASSERT(relation != nullptr);
1395     checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(
1396         relation, (relation->ApplyWidening() ? TypeRelationFlag::WIDENING : TypeRelationFlag::NONE) |
1397                       (relation->ApplyNarrowing() ? TypeRelationFlag::NARROWING : TypeRelationFlag::NONE) |
1398                       (relation->OnlyCheckBoxingUnboxing() ? TypeRelationFlag::ONLY_CHECK_BOXING_UNBOXING
1399                                                            : TypeRelationFlag::NONE));
1400 
1401     auto *boxedSourceType = relation->GetChecker()->AsETSChecker()->MaybeBoxInRelation(source);
1402     if (boxedSourceType == nullptr) {
1403         return;
1404     }
1405     ES2PANDA_ASSERT(target != nullptr);
1406     // Do not box primitive in case of cast to dynamic types
1407     if (target->IsETSDynamicType()) {
1408         return;
1409     }
1410     relation->IsAssignableTo(boxedSourceType, target);
1411     if (relation->IsTrue()) {
1412         MaybeAddBoxingFlagInRelation(relation, boxedSourceType);
1413     } else {
1414         auto unboxedTargetType = MaybeUnboxInRelation(target);
1415         if (unboxedTargetType == nullptr) {
1416             return;
1417         }
1418         NarrowingWideningConverter(this, relation, unboxedTargetType, source);
1419         if (relation->IsTrue()) {
1420             MaybeAddBoxingFlagInRelation(relation, target);
1421         }
1422     }
1423 }
1424 
CheckUnboxedSourceTypeWithWideningAssignable(TypeRelation * relation,Type * source,Type * target)1425 void ETSChecker::CheckUnboxedSourceTypeWithWideningAssignable(TypeRelation *relation, Type *source, Type *target)
1426 {
1427     auto *unboxedSourceType = relation->GetChecker()->AsETSChecker()->MaybeUnboxInRelation(source);
1428     if (unboxedSourceType == nullptr) {
1429         return;
1430     }
1431     relation->IsAssignableTo(unboxedSourceType, target);
1432     if (!relation->IsTrue() && relation->ApplyWidening()) {
1433         relation->GetChecker()->AsETSChecker()->CheckUnboxedTypeWidenable(relation, target, unboxedSourceType);
1434     }
1435     if (!relation->OnlyCheckBoxingUnboxing()) {
1436         relation->GetNode()->AddBoxingUnboxingFlags(
1437             relation->GetChecker()->AsETSChecker()->GetUnboxingFlag(unboxedSourceType));
1438     }
1439 }
1440 
1441 // #22952: optional arrow leftovers
CheckLambdaAssignable(ir::Expression * param,ir::ScriptFunction * lambda)1442 bool ETSChecker::CheckLambdaAssignable(ir::Expression *param, ir::ScriptFunction *lambda)
1443 {
1444     ES2PANDA_ASSERT(param->IsETSParameterExpression());
1445     ir::AstNode *typeAnn = param->AsETSParameterExpression()->Ident()->TypeAnnotation();
1446     if (typeAnn == nullptr) {
1447         return false;
1448     }
1449     if (typeAnn->IsETSTypeReference() && !typeAnn->AsETSTypeReference()->TsType()->IsETSArrayType() &&
1450         !typeAnn->AsETSTypeReference()->TsType()->IsETSAnyType()) {
1451         typeAnn = util::Helpers::DerefETSTypeReference(typeAnn);
1452     }
1453 
1454     if (!typeAnn->IsETSFunctionType()) {
1455         // the surrounding function is made so we can *bypass* the typecheck in the "inference" context,
1456         // however the body of the function has to be checked in any case
1457         if (typeAnn->IsETSUnionType()) {
1458             return CheckLambdaAssignableUnion(typeAnn, lambda);
1459         }
1460 
1461         Type *paramType = param->AsETSParameterExpression()->Ident()->TsType();
1462         if (Relation()->IsSupertypeOf(paramType, GlobalBuiltinFunctionType())) {
1463             lambda->Parent()->Check(this);
1464             return true;
1465         }
1466         return false;
1467     }
1468 
1469     ir::ETSFunctionType *calleeType = typeAnn->AsETSFunctionType();
1470     return lambda->Params().size() <= calleeType->Params().size();
1471 }
1472 
CheckLambdaInfer(ir::AstNode * typeAnnotation,ir::ArrowFunctionExpression * const arrowFuncExpr,Type * const subParameterType)1473 bool ETSChecker::CheckLambdaInfer(ir::AstNode *typeAnnotation, ir::ArrowFunctionExpression *const arrowFuncExpr,
1474                                   Type *const subParameterType)
1475 {
1476     if (typeAnnotation->IsETSTypeReference()) {
1477         typeAnnotation = util::Helpers::DerefETSTypeReference(typeAnnotation);
1478     }
1479 
1480     if (!typeAnnotation->IsETSFunctionType()) {
1481         return false;
1482     }
1483 
1484     ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
1485     auto calleeType = typeAnnotation->AsETSFunctionType();
1486     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1487     InferTypesForLambda(lambda, calleeType, subParameterType->AsETSFunctionType()->ArrowSignature());
1488 
1489     return true;
1490 }
1491 
CheckLambdaTypeAnnotation(ir::ETSParameterExpression * param,ir::ArrowFunctionExpression * const arrowFuncExpr,Type * const parameterType,TypeRelationFlag flags)1492 bool ETSChecker::CheckLambdaTypeAnnotation(ir::ETSParameterExpression *param,
1493                                            ir::ArrowFunctionExpression *const arrowFuncExpr, Type *const parameterType,
1494                                            TypeRelationFlag flags)
1495 {
1496     ir::AstNode *typeAnnotation = param->Ident()->TypeAnnotation();
1497     if (typeAnnotation->IsETSTypeReference()) {
1498         typeAnnotation = util::Helpers::DerefETSTypeReference(typeAnnotation);
1499     }
1500     auto checkInvocable = [&arrowFuncExpr, &parameterType, this](TypeRelationFlag functionFlags) {
1501         Type *const argumentType = arrowFuncExpr->Check(this);
1502         functionFlags |= TypeRelationFlag::NO_THROW;
1503 
1504         checker::InvocationContext invocationCtx(Relation(), arrowFuncExpr, argumentType, parameterType,
1505                                                  arrowFuncExpr->Start(), std::nullopt, functionFlags);
1506         return invocationCtx.IsInvocable();
1507     };
1508 
1509     //  process `single` type as usual.
1510     if (!typeAnnotation->IsETSUnionType()) {
1511         // #22952: infer optional parameter heuristics
1512         auto nonNullishParam = param->IsOptional() ? GetNonNullishType(parameterType) : parameterType;
1513         ES2PANDA_ASSERT(nonNullishParam != nullptr);
1514         if (!nonNullishParam->IsETSFunctionType()) {
1515             return true;
1516         }
1517         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1518         return CheckLambdaInfer(typeAnnotation, arrowFuncExpr, nonNullishParam) && checkInvocable(flags);
1519     }
1520 
1521     // Preserve actual lambda types
1522     ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
1523     ArenaVector<ir::TypeNode *> lambdaParamTypes {ProgramAllocator()->Adapter()};
1524     for (auto *const lambdaParam : lambda->Params()) {
1525         lambdaParamTypes.emplace_back(lambdaParam->AsETSParameterExpression()->Ident()->TypeAnnotation());
1526     }
1527     auto *const lambdaReturnTypeAnnotation = lambda->ReturnTypeAnnotation();
1528 
1529     if (!parameterType->IsETSUnionType() || parameterType->AsETSUnionType()->ConstituentTypes().size() !=
1530                                                 typeAnnotation->AsETSUnionType()->Types().size()) {
1531         Type *const argumentType = arrowFuncExpr->Check(this);
1532         return Relation()->IsSupertypeOf(parameterType, argumentType);
1533     }
1534 
1535     const auto typeAnnsOfUnion = typeAnnotation->AsETSUnionType()->Types();
1536     const auto typeParamOfUnion = parameterType->AsETSUnionType()->ConstituentTypes();
1537     for (size_t ix = 0; ix < typeAnnsOfUnion.size(); ++ix) {
1538         auto *typeNode = typeAnnsOfUnion[ix];
1539         auto *paramNode = typeParamOfUnion[ix];
1540         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1541         if (CheckLambdaInfer(typeNode, arrowFuncExpr, paramNode) && checkInvocable(flags)) {
1542             return true;
1543         }
1544 
1545         //  Restore inferring lambda types:
1546         for (std::size_t i = 0U; i < lambda->Params().size(); ++i) {
1547             if (lambdaParamTypes[i] == nullptr) {
1548                 lambda->Params()[i]->AsETSParameterExpression()->Ident()->SetTsTypeAnnotation(nullptr);
1549             }
1550         }
1551         if (lambdaReturnTypeAnnotation == nullptr) {
1552             lambda->SetReturnTypeAnnotation(nullptr);
1553         }
1554     }
1555 
1556     return false;
1557 }
1558 
ResolveLambdaArgumentType(Signature * signature,ir::Expression * argument,size_t paramPosition,size_t argumentPosition,TypeRelationFlag resolutionFlags)1559 bool ETSChecker::ResolveLambdaArgumentType(Signature *signature, ir::Expression *argument, size_t paramPosition,
1560                                            size_t argumentPosition, TypeRelationFlag resolutionFlags)
1561 {
1562     if (!argument->IsArrowFunctionExpression()) {
1563         return true;
1564     }
1565 
1566     auto arrowFuncExpr = argument->AsArrowFunctionExpression();
1567     bool typeValid = true;
1568     ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
1569     if (!NeedTypeInference(lambda)) {
1570         return typeValid;
1571     }
1572 
1573     arrowFuncExpr->SetTsType(nullptr);
1574     auto *const param =
1575         signature->GetSignatureInfo()->params[paramPosition]->Declaration()->Node()->AsETSParameterExpression();
1576     Type *const parameterType = signature->Params()[paramPosition]->TsType();
1577 
1578     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1579     bool rc = CheckLambdaTypeAnnotation(param, arrowFuncExpr, parameterType, resolutionFlags);
1580     if (!rc) {
1581         if ((resolutionFlags & TypeRelationFlag::NO_THROW) == 0) {
1582             Type *const argumentType = arrowFuncExpr->Check(this);
1583             LogError(diagnostic::TYPE_MISMATCH_AT_IDX, {argumentType, parameterType, argumentPosition + 1},
1584                      arrowFuncExpr->Start());
1585         }
1586         rc = false;
1587     } else if ((lambda->Signature() != nullptr) && !lambda->HasReturnStatement()) {
1588         //  Need to check void return type here if there are no return statement(s) in the body.
1589         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1590         if (!AssignmentContext(Relation(), ProgramAllocNode<ir::Identifier>(ProgramAllocator()), GlobalVoidType(),
1591                                lambda->Signature()->ReturnType(), lambda->Start(), std::nullopt,
1592                                checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
1593                  .IsAssignable()) {  // CC-OFF(G.FMT.02-CPP) project code style
1594             LogError(diagnostic::ARROW_TYPE_MISMATCH, {GlobalVoidType(), lambda->Signature()->ReturnType()},
1595                      lambda->Body()->Start());
1596             rc = false;
1597         }
1598     }
1599 
1600     typeValid &= rc;
1601 
1602     return typeValid;
1603 }
1604 
TrailingLambdaTypeInference(Signature * signature,const ArenaVector<ir::Expression * > & arguments)1605 bool ETSChecker::TrailingLambdaTypeInference(Signature *signature, const ArenaVector<ir::Expression *> &arguments)
1606 {
1607     if (arguments.empty() || signature->GetSignatureInfo()->params.empty()) {
1608         return false;
1609     }
1610     ES2PANDA_ASSERT(arguments.back()->IsArrowFunctionExpression());
1611     const size_t lastParamPos = signature->GetSignatureInfo()->params.size() - 1;
1612     const size_t lastArgPos = arguments.size() - 1;
1613     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1614     return ResolveLambdaArgumentType(signature, arguments.back(), lastParamPos, lastArgPos, TypeRelationFlag::NONE);
1615 }
1616 
TypeInference(Signature * signature,const ArenaVector<ir::Expression * > & arguments,TypeRelationFlag inferenceFlags)1617 bool ETSChecker::TypeInference(Signature *signature, const ArenaVector<ir::Expression *> &arguments,
1618                                TypeRelationFlag inferenceFlags)
1619 {
1620     bool typeConsistent = true;
1621     auto const argumentCount = arguments.size();
1622     auto const minArity = std::min(signature->ArgCount(), argumentCount);
1623 
1624     for (size_t idx = 0U; idx < minArity; ++idx) {
1625         auto const &argument = arguments[idx];
1626 
1627         if (idx == argumentCount - 1 && (inferenceFlags & TypeRelationFlag::NO_CHECK_TRAILING_LAMBDA) != 0) {
1628             continue;
1629         }
1630         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1631         const bool valid = ResolveLambdaArgumentType(signature, argument, idx, idx, inferenceFlags);
1632         typeConsistent &= valid;
1633     }
1634 
1635     return typeConsistent;
1636 }
1637 
1638 // #22951 requires complete refactoring
IsExtensionETSFunctionType(const checker::Type * type)1639 bool ETSChecker::IsExtensionETSFunctionType(const checker::Type *type)
1640 {
1641     if (type == nullptr || (!type->IsETSFunctionType() && !type->IsETSObjectType())) {
1642         return false;
1643     }
1644 
1645     if (type->IsETSObjectType()) {
1646         return type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::EXTENSION_FUNCTION);
1647     }
1648 
1649     if (type->IsETSArrowType()) {
1650         return type->AsETSFunctionType()->ArrowSignature()->IsExtensionFunction();
1651     }
1652 
1653     return type->AsETSFunctionType()->IsExtensionFunctionType();
1654 }
1655 
1656 // #22951 requires complete refactoring
IsExtensionAccessorFunctionType(const checker::Type * type)1657 bool ETSChecker::IsExtensionAccessorFunctionType(const checker::Type *type)
1658 {
1659     if (type == nullptr || !type->IsETSFunctionType()) {
1660         return false;
1661     }
1662 
1663     if (type->IsETSArrowType()) {
1664         return type->AsETSFunctionType()->ArrowSignature()->IsExtensionAccessor();
1665     }
1666 
1667     return type->AsETSFunctionType()->IsExtensionAccessorType();
1668 }
1669 
CheckExceptionClauseType(const std::vector<checker::ETSObjectType * > & exceptions,ir::CatchClause * catchClause,checker::Type * clauseType)1670 void ETSChecker::CheckExceptionClauseType(const std::vector<checker::ETSObjectType *> &exceptions,
1671                                           ir::CatchClause *catchClause, checker::Type *clauseType)
1672 {
1673     for (auto *exception : exceptions) {
1674         this->Relation()->IsIdenticalTo(clauseType, exception);
1675         if (this->Relation()->IsTrue()) {
1676             LogError(diagnostic::EXCEPTION_REDECLARATION, {}, catchClause->Start());
1677         }
1678     }
1679 }
1680 
1681 }  // namespace ark::es2panda::checker
1682