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