• 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 "ETSAnalyzer.h"
17 
18 #include "checker/ETSchecker.h"
19 #include "compiler/lowering/util.h"
20 #include "generated/diagnostic.h"
21 #include "checker/types/globalTypesHolder.h"
22 #include "checker/types/ets/etsTupleType.h"
23 #include "evaluate/scopedDebugInfoPlugin.h"
24 #include "types/signature.h"
25 #include "compiler/lowering/ets/setJumpTarget.h"
26 #include "checker/types/ets/etsAsyncFuncReturnType.h"
27 #include "util/es2pandaMacros.h"
28 
29 #include <unordered_set>
30 
31 namespace ark::es2panda::checker {
32 
GetETSChecker() const33 ETSChecker *ETSAnalyzer::GetETSChecker() const
34 {
35     return static_cast<ETSChecker *>(GetChecker());
36 }
37 
38 // from base folder
Check(ir::CatchClause * st) const39 checker::Type *ETSAnalyzer::Check(ir::CatchClause *st) const
40 {
41     ETSChecker *checker = GetETSChecker();
42     checker::Type *exceptionType = checker->GlobalTypeError();
43 
44     if (st->Param() != nullptr) {
45         ES2PANDA_ASSERT(st->Param()->IsIdentifier());
46 
47         ir::Identifier *paramIdent = st->Param()->AsIdentifier();
48         if (!paramIdent->IsErrorPlaceHolder()) {
49             if (paramIdent->TypeAnnotation() != nullptr) {
50                 checker::Type *catchParamAnnotationType = paramIdent->TypeAnnotation()->GetType(checker);
51                 exceptionType = checker->CheckExceptionOrErrorType(catchParamAnnotationType, st->Param()->Start());
52             } else {
53                 exceptionType = checker->GlobalETSObjectType();
54             }
55             paramIdent->Variable()->SetTsType(exceptionType);
56         }
57         paramIdent->SetTsType(exceptionType);
58     } else {
59         ES2PANDA_ASSERT(checker->IsAnyError());
60     }
61 
62     const varbinder::Variable *catchVar = nullptr;
63     if (st->Param() != nullptr && st->Param()->IsIdentifier()) {
64         catchVar = st->Param()->AsIdentifier()->Variable();
65         ES2PANDA_ASSERT(catchVar != nullptr);
66         catchParamStack_.push_back(catchVar);
67     }
68 
69     st->Body()->Check(checker);
70 
71     if (catchVar != nullptr) {
72         catchParamStack_.pop_back();
73     }
74 
75     return st->SetTsType(exceptionType);
76 }
77 
Check(ir::ClassDefinition * node) const78 checker::Type *ETSAnalyzer::Check(ir::ClassDefinition *node) const
79 {
80     ETSChecker *checker = GetETSChecker();
81 
82     if (node->TsType() == nullptr) {
83         checker->BuildBasicClassProperties(node);
84     }
85 
86     if (!node->IsClassDefinitionChecked()) {
87         checker->CheckClassDefinition(node);
88     }
89 
90     return node->TsType();
91 }
92 
Check(ir::ClassProperty * st) const93 checker::Type *ETSAnalyzer::Check(ir::ClassProperty *st) const
94 {
95     if (st->TsType() != nullptr) {
96         return st->TsType();
97     }
98 
99     ES2PANDA_ASSERT(st->Id() != nullptr);
100 
101     ETSChecker *checker = GetETSChecker();
102 
103     if (st->Id()->Variable() == nullptr) {
104         auto ident = st->Id();
105         auto [decl, var] = checker->VarBinder()->NewVarDecl<varbinder::LetDecl>(
106             ident->Start(), compiler::GenName(checker->ProgramAllocator()).View());
107         var->SetScope(checker->VarBinder()->GetScope());
108         ident->SetVariable(var);
109         decl->BindNode(ident);
110         ident->SetTsType(var->SetTsType(checker->GlobalTypeError()));
111     }
112 
113     ES2PANDA_ASSERT(st->Id()->Variable() != nullptr);
114 
115     checker->CheckAnnotations(st->Annotations());
116     if (st->TypeAnnotation() != nullptr) {
117         st->TypeAnnotation()->Check(checker);
118     }
119 
120     checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
121                                               checker->Context().ContainingClass(),
122                                               checker->Context().ContainingSignature());
123 
124     if (st->IsStatic()) {
125         checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT);
126     }
127 
128     checker::Type *propertyType =
129         checker->CheckVariableDeclaration(st->Id(), st->TypeAnnotation(), st->Value(), st->Modifiers());
130 
131     propertyType = propertyType != nullptr ? propertyType : checker->GlobalTypeError();
132     st->SetTsType(propertyType);
133     if (st->IsDefinite() && st->TsType()->PossiblyETSNullish()) {
134         checker->LogError(diagnostic::LATE_INITIALIZATION_FIELD_HAS_INVALID_TYPE, st->TypeAnnotation()->Start());
135     }
136 
137     return propertyType;
138 }
139 
Check(ir::ClassStaticBlock * st) const140 checker::Type *ETSAnalyzer::Check(ir::ClassStaticBlock *st) const
141 {
142     ETSChecker *checker = GetETSChecker();
143 
144     if (checker->HasStatus(checker::CheckerStatus::INNER_CLASS)) {
145         checker->LogError(diagnostic::STATIC_INIT_IN_NESTED_CLASS, {}, st->Start());
146         st->SetTsType(checker->GlobalTypeError());
147         return st->TsType();
148     }
149 
150     auto *func = st->Function();
151     checker->BuildFunctionSignature(func);
152 
153     if (func->Signature() == nullptr) {
154         st->SetTsType(checker->GlobalTypeError());
155     } else {
156         st->SetTsType(checker->BuildMethodType(func));
157     }
158     checker::ScopeContext scopeCtx(checker, func->Scope());
159     checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
160                                               checker->Context().ContainingClass());
161     checker->AddStatus(checker::CheckerStatus::IN_STATIC_BLOCK | checker::CheckerStatus::IN_STATIC_CONTEXT);
162     func->Body()->Check(checker);
163     return st->TsType();
164 }
165 
166 // Satisfy the Chinese code checker
HandleNativeAndAsyncMethods(ETSChecker * checker,ir::MethodDefinition * node)167 static void HandleNativeAndAsyncMethods(ETSChecker *checker, ir::MethodDefinition *node)
168 {
169     auto *scriptFunc = node->Function();
170     ES2PANDA_ASSERT(scriptFunc != nullptr);
171     if (node->IsNative() && !node->IsConstructor() && !scriptFunc->IsSetter()) {
172         if (scriptFunc->ReturnTypeAnnotation() == nullptr) {
173             checker->LogError(diagnostic::NATIVE_WITHOUT_RETURN, {}, scriptFunc->Start());
174             node->SetTsType(checker->GlobalTypeError());
175         }
176     }
177 
178     if (util::Helpers::IsAsyncMethod(node)) {
179         if (scriptFunc->ReturnTypeAnnotation() != nullptr) {
180             auto *asyncFuncReturnType = scriptFunc->Signature()->ReturnType();
181 
182             if (!asyncFuncReturnType->IsETSObjectType() ||
183                 asyncFuncReturnType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) {
184                 checker->LogError(diagnostic::ASYNC_FUNCTION_RETURN_TYPE, {}, scriptFunc->Start());
185                 scriptFunc->Signature()->SetReturnType(checker->GlobalTypeError());
186                 return;
187             }
188         }
189     }
190 }
191 
192 //  Extacted from 'ETSAnalyzer::Check(ir::MethodDefinition *node)' to reduce its size
CheckMethodDefinitionHelper(ETSChecker * checker,ir::MethodDefinition * node)193 static checker::Type *CheckMethodDefinitionHelper(ETSChecker *checker, ir::MethodDefinition *node) noexcept
194 {
195     // NOTE(gogabr): temporary, until we have proper bridges, see #16485
196     // Don't check overriding for synthetic functional classes.
197     if ((node->Parent()->Modifiers() & ir::ModifierFlags::FUNCTIONAL) == 0) {
198         checker->CheckOverride(node->TsType()->AsETSFunctionType()->FindSignature(node->Function()));
199     }
200 
201     for (auto *overload : node->Overloads()) {
202         overload->Check(checker);
203     }
204 
205     return node->TsType();
206 }
207 
IsInitializerBlockTransfer(std::string_view str)208 static bool IsInitializerBlockTransfer(std::string_view str)
209 {
210     auto prefix = compiler::Signatures::INITIALIZER_BLOCK_INIT;
211     return str.size() >= prefix.size() && str.compare(0, prefix.size(), prefix) == 0;
212 }
213 
Check(ir::MethodDefinition * node) const214 checker::Type *ETSAnalyzer::Check(ir::MethodDefinition *node) const
215 {
216     ETSChecker *checker = GetETSChecker();
217     auto *scriptFunc = node->Function();
218 
219     // CC-OFFNXT(G.FMT.14-CPP) project code style
220     auto const returnErrorType = [checker, node]() -> checker::Type * {
221         node->SetTsType(checker->GlobalTypeError());
222         return node->TsType();
223     };
224 
225     if (scriptFunc == nullptr) {
226         checker->LogError(diagnostic::FUNC_EXPR_INVALID, {}, node->Start());
227         return returnErrorType();
228     }
229     checker->CheckAnnotations(scriptFunc->Annotations());
230     checker->CheckFunctionSignatureAnnotations(scriptFunc->Params(), scriptFunc->TypeParams(),
231                                                scriptFunc->ReturnTypeAnnotation());
232 
233     if (scriptFunc->IsProxy()) {
234         return ReturnTypeForStatement(node);
235     }
236 
237     ES2PANDA_ASSERT(!(scriptFunc->IsGetter() && scriptFunc->IsSetter()));
238     if (scriptFunc->IsGetter() || scriptFunc->IsSetter()) {
239         auto status = scriptFunc->IsGetter() ? CheckerStatus::IN_GETTER : CheckerStatus::IN_SETTER;
240         checker->AddStatus(status);
241     }
242 
243     // NOTE: aszilagyi. make it correctly check for open function not have body
244     if (!scriptFunc->HasBody() && !(node->IsAbstract() || node->IsNative() || node->IsDeclare() ||
245                                     checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) {
246         checker->LogError(diagnostic::FUNCTION_WITHOUT_BODY, {}, scriptFunc->Start());
247         return returnErrorType();
248     }
249 
250     if (CheckReturnTypeNecessity(node) && scriptFunc->ReturnTypeAnnotation() == nullptr) {
251         checker->LogError(diagnostic::MISSING_RETURN_TYPE, {}, scriptFunc->Start());
252         return returnErrorType();
253     }
254 
255     if (node->TsType() == nullptr) {
256         node->SetTsType(checker->BuildMethodSignature(node));
257     }
258 
259     if (IsInitializerBlockTransfer(scriptFunc->Id()->Name().Utf8())) {
260         checker->AddStatus(CheckerStatus::IN_STATIC_BLOCK);
261     }
262 
263     this->CheckMethodModifiers(node);
264     HandleNativeAndAsyncMethods(checker, node);
265     DoBodyTypeChecking(checker, node, scriptFunc);
266     CheckPredefinedMethodReturnType(checker, scriptFunc);
267     if (node->TsType()->IsTypeError()) {
268         return node->TsType();
269     }
270 
271     return CheckMethodDefinitionHelper(checker, node);
272 }
273 
CheckMethodModifiers(ir::MethodDefinition * node) const274 void ETSAnalyzer::CheckMethodModifiers(ir::MethodDefinition *node) const
275 {
276     ETSChecker *checker = GetETSChecker();
277     auto const notValidInAbstract = ir::ModifierFlags::NATIVE | ir::ModifierFlags::PRIVATE |
278                                     ir::ModifierFlags::OVERRIDE | ir::ModifierFlags::FINAL | ir::ModifierFlags::STATIC;
279 
280     if (node->IsAbstract() && (node->flags_ & notValidInAbstract) != 0U) {
281         checker->LogError(diagnostic::ABSTRACT_METHOD_INVALID_MODIFIER, {}, node->Start());
282         node->SetTsType(checker->GlobalTypeError());
283         return;
284     }
285 
286     if (node->Function() == nullptr) {
287         checker->LogError(diagnostic::FUNC_EXPR_INVALID, {}, node->Start());
288         node->SetTsType(checker->GlobalTypeError());
289         return;
290     }
291 
292     if ((node->IsAbstract() || (!node->Function()->HasBody() && !node->IsNative() && !node->IsDeclare())) &&
293         !(checker->HasStatus(checker::CheckerStatus::IN_ABSTRACT) ||
294           checker->HasStatus(checker::CheckerStatus::IN_INTERFACE))) {
295         checker->LogError(diagnostic::ABSTRACT_IN_CONCRETE, {}, node->Start());
296         node->SetTsType(checker->GlobalTypeError());
297     }
298 
299     auto const notValidInFinal = ir::ModifierFlags::ABSTRACT | ir::ModifierFlags::STATIC;
300 
301     if (node->IsFinal() && (node->flags_ & notValidInFinal) != 0U) {
302         checker->LogError(diagnostic::FINAL_METHOD_INVALID_MODIFIER, {}, node->Start());
303         node->SetTsType(checker->GlobalTypeError());
304     }
305 
306     auto const notValidInStatic = ir::ModifierFlags::ABSTRACT | ir::ModifierFlags::FINAL | ir::ModifierFlags::OVERRIDE;
307 
308     if (node->IsStatic() && (node->flags_ & notValidInStatic) != 0U) {
309         checker->LogError(diagnostic::STATIC_METHOD_INVALID_MODIFIER, {}, node->Start());
310         node->SetTsType(checker->GlobalTypeError());
311     }
312 }
313 
Check(ir::Property * expr) const314 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::Property *expr) const
315 {
316     ETSChecker *checker = GetETSChecker();
317     return checker->GlobalTypeError();
318 }
319 
Check(ir::SpreadElement * expr) const320 checker::Type *ETSAnalyzer::Check(ir::SpreadElement *expr) const
321 {
322     if (expr->TsType() != nullptr) {
323         return expr->TsType();
324     }
325 
326     ETSChecker *checker = GetETSChecker();
327     Type *exprType = expr->Argument()->Check(checker);
328 
329     if (exprType->IsETSResizableArrayType()) {
330         return expr->SetTsType(exprType->AsETSObjectType()->TypeArguments().front());
331     }
332 
333     if (!exprType->IsETSArrayType() && !exprType->IsETSTupleType()) {
334         if (!exprType->IsTypeError()) {
335             // Don't duplicate error messages for the same error
336             checker->LogError(diagnostic::SPREAD_OF_INVALID_TYPE, {exprType}, expr->Start());
337         }
338         return checker->InvalidateType(expr);
339     }
340 
341     checker::Type *const elementType = exprType->IsETSTupleType() ? exprType : checker->GetElementTypeOfArray(exprType);
342     return expr->SetTsType(elementType);
343 }
344 
Check(ir::TemplateElement * expr) const345 checker::Type *ETSAnalyzer::Check(ir::TemplateElement *expr) const
346 {
347     ETSChecker *checker = GetETSChecker();
348     expr->SetTsType(checker->CreateETSStringLiteralType(expr->Raw()));
349     return expr->TsType();
350 }
351 
Check(ir::ETSClassLiteral * expr) const352 checker::Type *ETSAnalyzer::Check(ir::ETSClassLiteral *expr) const
353 {
354     ETSChecker *checker = GetETSChecker();
355     auto *const literal = expr->Expr();
356 
357     checker->LogError(diagnostic::UNSUPPORTED_CLASS_LITERAL, {}, literal->Start());
358     expr->SetTsType(checker->GlobalTypeError());
359     return expr->TsType();
360 
361     auto exprType = literal->Check(checker);
362     if (exprType->IsETSVoidType()) {
363         checker->LogError(diagnostic::INVALID_DOT_CLASS, {}, literal->Start());
364         expr->SetTsType(checker->GlobalTypeError());
365         return expr->TsType();
366     }
367 
368     ArenaVector<checker::Type *> typeArgTypes(checker->ProgramAllocator()->Adapter());
369     typeArgTypes.push_back(exprType);  // NOTE: Box it if it's a primitive type
370 
371     checker::InstantiationContext ctx(checker, checker->GlobalBuiltinTypeType(), std::move(typeArgTypes),
372                                       expr->Range().start);
373     expr->SetTsType(ctx.Result());
374 
375     return expr->TsType();
376 }
377 
Check(ir::ETSFunctionType * node) const378 checker::Type *ETSAnalyzer::Check(ir::ETSFunctionType *node) const
379 {
380     if (node->TsType() != nullptr) {
381         return node->TsType();
382     }
383     ETSChecker *checker = GetETSChecker();
384     checker->CheckAnnotations(node->Annotations());
385     checker->CheckFunctionSignatureAnnotations(node->Params(), node->TypeParams(), node->ReturnType());
386 
387     auto *signatureInfo = checker->ComposeSignatureInfo(node->TypeParams(), node->Params());
388     auto *returnType = node->IsExtensionFunction() && node->ReturnType()->IsTSThisType()
389                            ? signatureInfo->params.front()->TsType()
390                            : checker->ComposeReturnType(node->ReturnType(), node->IsAsync());
391 
392     auto *const signature =
393         checker->CreateSignature(signatureInfo, returnType, node->Flags(), node->IsExtensionFunction());
394     if (signature == nullptr) {  // #23134
395         ES2PANDA_ASSERT(GetChecker()->IsAnyError());
396         return node->SetTsType(checker->GlobalTypeError());
397     }
398 
399     signature->SetOwner(checker->Context().ContainingClass());
400 
401     return node->SetTsType(checker->CreateETSArrowType(signature));
402 }
403 
404 template <typename T, typename = typename std::enable_if_t<std::is_base_of_v<ir::Expression, T>>>
CheckArrayElementType(ETSChecker * checker,T * newArrayInstanceExpr)405 static bool CheckArrayElementType(ETSChecker *checker, T *newArrayInstanceExpr)
406 {
407     ES2PANDA_ASSERT(checker != nullptr);
408     ES2PANDA_ASSERT(newArrayInstanceExpr != nullptr);
409 
410     checker::Type *elementType = newArrayInstanceExpr->TypeReference()->GetType(checker);
411     ES2PANDA_ASSERT(elementType != nullptr);
412     if (elementType->IsETSPrimitiveType()) {
413         return true;
414     }
415 
416     if (elementType->IsETSObjectType()) {
417         auto *calleeObj = elementType->AsETSObjectType();
418         const auto flags = checker::ETSObjectFlags::ABSTRACT | checker::ETSObjectFlags::INTERFACE;
419         if (!calleeObj->HasObjectFlag(flags)) {
420             // A workaround check for new Interface[...] in test cases
421             newArrayInstanceExpr->SetSignature(checker->CollectParameterlessConstructor(
422                 calleeObj->ConstructSignatures(), newArrayInstanceExpr->Start()));
423             checker->ValidateSignatureAccessibility(calleeObj, newArrayInstanceExpr->Signature(),
424                                                     newArrayInstanceExpr->Start());
425         } else {
426             checker->LogError(diagnostic::ABSTRACT_CLASS_AS_ARRAY_ELEMENT_TYPE, {}, newArrayInstanceExpr->Start());
427             return false;
428         }
429     } else {
430         if (!checker->Relation()->IsSupertypeOf(elementType, checker->GlobalETSUndefinedType()) &&
431             !checker->Relation()->IsIdenticalTo(checker->GetApparentType(elementType), elementType)) {
432             checker->LogError(diagnostic::TYPE_PARAMETER_AS_ARRAY_ELEMENT_TYPE, {}, newArrayInstanceExpr->Start());
433             return false;
434         }
435         if (!checker->Relation()->IsSupertypeOf(elementType, checker->GlobalETSUndefinedType())) {
436             checker->LogError(diagnostic::NON_SUPERTYPE_OF_UNDEFINED_AS_ARRAY_ELEMENT_TYPE, {},
437                               newArrayInstanceExpr->Start());
438             return false;
439         }
440     }
441     return true;
442 }
443 
NeedCreateETSResizableArrayType(ETSChecker * checker,Type * type)444 static bool NeedCreateETSResizableArrayType(ETSChecker *checker, Type *type)
445 {
446     return type == nullptr ||
447            checker->Relation()->IsSupertypeOf(type, checker->GetGlobalTypesHolder()->GlobalArrayBuiltinType());
448 }
449 
Check(ir::ETSNewArrayInstanceExpression * expr) const450 checker::Type *ETSAnalyzer::Check(ir::ETSNewArrayInstanceExpression *expr) const
451 {
452     ETSChecker *checker = GetETSChecker();
453 
454     auto *elementType = expr->TypeReference()->GetType(checker);
455     checker->ValidateArrayIndex(expr->Dimension(), true);
456 
457     CheckArrayElementType(checker, expr);
458     GetUnionPreferredType(expr, expr->GetPreferredType());
459 
460     auto *preferredType = expr->GetPreferredType();
461 
462     if (NeedCreateETSResizableArrayType(checker, expr->GetPreferredType()) ||
463         preferredType->IsETSResizableArrayType()) {
464         expr->SetTsType(checker->CreateETSResizableArrayType(elementType));
465     } else {
466         expr->SetTsType(checker->CreateETSArrayType(elementType));
467     }
468     if (expr->TsType()->IsETSArrayType()) {
469         checker->CreateBuiltinArraySignature(expr->TsType()->AsETSArrayType(), 1);
470     }
471 
472     return expr->TsType();
473 }
474 
CheckInstantiatedNewType(ETSChecker * checker,ir::ETSNewClassInstanceExpression * expr)475 static checker::Type *CheckInstantiatedNewType(ETSChecker *checker, ir::ETSNewClassInstanceExpression *expr)
476 {
477     checker::Type *calleeType = expr->GetTypeRef()->Check(checker);
478     if (calleeType->IsTypeError()) {
479         return checker->InvalidateType(expr->GetTypeRef());
480     }
481     if (calleeType->IsETSUnionType()) {
482         return checker->TypeError(expr->GetTypeRef(), diagnostic::UNION_NONCONSTRUCTIBLE, expr->Start());
483     }
484     if (!ir::ETSNewClassInstanceExpression::TypeIsAllowedForInstantiation(calleeType)) {
485         return checker->TypeError(expr->GetTypeRef(), diagnostic::CALLEE_NONCONSTRUCTIBLE, {calleeType}, expr->Start());
486     }
487     if (!calleeType->IsETSObjectType()) {
488         return checker->TypeError(expr->GetTypeRef(), diagnostic::EXPR_NONCONSTRUCTIBLE, {}, expr->Start());
489     }
490 
491     auto calleeObj = calleeType->AsETSObjectType();
492     if (calleeObj->HasObjectFlag(checker::ETSObjectFlags::ABSTRACT)) {
493         checker->LogError(diagnostic::ABSTRACT_INSTANTIATION, {calleeObj->Name()}, expr->Start());
494         return checker->GlobalTypeError();
495     }
496 
497     if (calleeObj->HasObjectFlag(checker::ETSObjectFlags::INTERFACE)) {
498         checker->LogError(diagnostic::INTERFACE_INSTANTIATION, {calleeObj->Name()}, expr->Start());
499         return checker->GlobalTypeError();
500     }
501 
502     if (calleeObj->HasObjectFlag(ETSObjectFlags::REQUIRED) &&
503         !expr->HasAstNodeFlags(ir::AstNodeFlags::ALLOW_REQUIRED_INSTANTIATION)) {
504         checker->LogError(diagnostic::NONLITERAL_INSTANTIATION, {}, expr->GetTypeRef()->Start());
505         return checker->GlobalTypeError();
506     }
507 
508     return calleeType;
509 }
510 
Check(ir::ETSNewClassInstanceExpression * expr) const511 checker::Type *ETSAnalyzer::Check(ir::ETSNewClassInstanceExpression *expr) const
512 {
513     if (expr->TsType() != nullptr) {
514         return expr->TsType();
515     }
516     ETSChecker *checker = GetETSChecker();
517     auto *calleeType = CheckInstantiatedNewType(checker, expr);
518     if (calleeType->IsTypeError()) {
519         return checker->InvalidateType(expr);
520     }
521     auto *calleeObj = calleeType->AsETSObjectType();
522     expr->SetTsType(calleeObj);
523 
524     if (calleeType->IsETSDynamicType() && !calleeType->AsETSDynamicType()->HasDecl()) {
525         auto lang = calleeType->AsETSDynamicType()->Language();
526         expr->SetSignature(checker->ResolveDynamicCallExpression(expr->GetTypeRef(), expr->GetArguments(), lang, true));
527     } else {
528         auto *signature = checker->ResolveConstructExpression(calleeObj, expr->GetArguments(), expr->Start());
529 
530         if (signature == nullptr) {
531             return checker->InvalidateType(expr);
532         }
533 
534         checker->CheckObjectLiteralArguments(signature, expr->GetArguments());
535 
536         checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start());
537 
538         if (calleeType->IsETSDynamicType()) {
539             ES2PANDA_ASSERT(signature->Function()->IsDynamic());
540             auto lang = calleeType->AsETSDynamicType()->Language();
541             expr->SetSignature(
542                 checker->ResolveDynamicCallExpression(expr->GetTypeRef(), signature->Params(), lang, true));
543         } else {
544             expr->SetSignature(signature);
545         }
546     }
547 
548     return expr->TsType();
549 }
550 
Check(ir::ETSNewMultiDimArrayInstanceExpression * expr) const551 checker::Type *ETSAnalyzer::Check(ir::ETSNewMultiDimArrayInstanceExpression *expr) const
552 {
553     ETSChecker *checker = GetETSChecker();
554 
555     CheckArrayElementType(checker, expr);
556     auto *elementType = expr->TypeReference()->GetType(checker);
557 
558     auto *fixedArrayType = elementType;
559     for (auto *dim : expr->Dimensions()) {
560         checker->ValidateArrayIndex(dim, true);
561         fixedArrayType = checker->CreateETSArrayType(fixedArrayType);
562     }
563     GetUnionPreferredType(expr, expr->GetPreferredType());
564 
565     auto *preferredType = expr->GetPreferredType();
566     if (NeedCreateETSResizableArrayType(checker, expr->GetPreferredType()) ||
567         preferredType->IsETSResizableArrayType()) {
568         expr->SetTsType(checker->CreateETSMultiDimResizableArrayType(elementType, expr->Dimensions().size()));
569     } else {
570         expr->SetTsType(fixedArrayType);
571     }
572 
573     if (expr->TsType()->IsETSArrayType()) {
574         expr->SetSignature(
575             checker->CreateBuiltinArraySignature(expr->TsType()->AsETSArrayType(), expr->Dimensions().size()));
576     }
577 
578     return expr->TsType();
579 }
580 
Check(ir::ETSPackageDeclaration * st) const581 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSPackageDeclaration *st) const
582 {
583     return ReturnTypeForStatement(st);
584 }
585 
Check(ir::ETSParameterExpression * expr) const586 checker::Type *ETSAnalyzer::Check(ir::ETSParameterExpression *expr) const
587 {
588     ETSChecker *checker = GetETSChecker();
589     if (expr->TsType() != nullptr) {
590         return expr->TsType();
591     }
592     ASSERT_PRINT(expr->Initializer() == nullptr, "default parameter was not lowered");
593 
594     if (expr->Ident()->TsType() != nullptr) {
595         expr->SetTsType(expr->Ident()->TsType());
596     } else if (expr->IsRestParameter()) {
597         expr->SetTsType(expr->RestParameter()->Check(checker));
598     } else {
599         expr->SetTsType(expr->Ident()->Check(checker));
600     }
601     ES2PANDA_ASSERT(!expr->IsOptional() ||
602                     checker->Relation()->IsSupertypeOf(expr->TsType(), checker->GlobalETSUndefinedType()));
603     return expr->TsType();
604 }
605 
Check(ir::ETSPrimitiveType * node) const606 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSPrimitiveType *node) const
607 {
608     ETSChecker *checker = GetETSChecker();
609     return node->GetType(checker);
610 }
611 
Check(ir::ETSStructDeclaration * node) const612 checker::Type *ETSAnalyzer::Check(ir::ETSStructDeclaration *node) const
613 {
614     ETSChecker *checker = GetETSChecker();
615     node->Definition()->Check(checker);
616     return ReturnTypeForStatement(node);
617 }
618 
Check(ir::ETSTypeReference * node) const619 checker::Type *ETSAnalyzer::Check(ir::ETSTypeReference *node) const
620 {
621     ETSChecker *checker = GetETSChecker();
622     checker->CheckAnnotations(node->Annotations());
623     return node->GetType(checker);
624 }
625 
Check(ir::ETSTypeReferencePart * node) const626 checker::Type *ETSAnalyzer::Check(ir::ETSTypeReferencePart *node) const
627 {
628     ETSChecker *checker = GetETSChecker();
629     return node->GetType(checker);
630 }
631 
Check(ir::ETSNonNullishTypeNode * node) const632 checker::Type *ETSAnalyzer::Check(ir::ETSNonNullishTypeNode *node) const
633 {
634     if (node->TsType() != nullptr) {
635         return node->TsType();
636     }
637     ETSChecker *checker = GetETSChecker();
638     checker::Type *originalType = node->GetTypeNode()->Check(checker);
639     if (!originalType->IsETSTypeParameter()) {
640         checker->LogError(diagnostic::ILLEGAL_NON_NULLISH_TYPE, {}, node->GetTypeNode()->Start());
641     }
642     return node->SetTsType(checker->GetNonNullishType(originalType));
643 }
644 
Check(ir::ETSNullType * node) const645 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNullType *node) const
646 {
647     ETSChecker *checker = GetETSChecker();
648     checker->CheckAnnotations(node->Annotations());
649     return node->SetTsType(checker->GlobalETSNullType());
650 }
651 
Check(ir::ETSUndefinedType * node) const652 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSUndefinedType *node) const
653 {
654     ETSChecker *checker = GetETSChecker();
655     checker->CheckAnnotations(node->Annotations());
656     return node->SetTsType(checker->GlobalETSUndefinedType());
657 }
658 
Check(ir::ETSNeverType * node) const659 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::ETSNeverType *node) const
660 {
661     ETSChecker *checker = GetETSChecker();
662     return checker->GlobalETSNeverType();
663 }
664 
Check(ir::ETSStringLiteralType * node) const665 checker::Type *ETSAnalyzer::Check(ir::ETSStringLiteralType *node) const
666 {
667     ETSChecker *checker = GetETSChecker();
668     checker->CheckAnnotations(node->Annotations());
669     return node->GetType(checker);
670 }
671 
Check(ir::ETSKeyofType * node) const672 checker::Type *ETSAnalyzer::Check(ir::ETSKeyofType *node) const
673 {
674     ETSChecker *checker = GetETSChecker();
675     return node->GetType(checker);
676 }
677 
678 // compile methods for EXPRESSIONS in alphabetical order
679 
GetPreferredType(ir::ArrayExpression * expr) const680 checker::Type *ETSAnalyzer::GetPreferredType(ir::ArrayExpression *expr) const
681 {
682     return expr->preferredType_;
683 }
684 
AddSpreadElementTypes(ETSChecker * checker,ir::SpreadElement * const element,ArenaVector<std::pair<Type *,ir::Expression * >> & elementTypes)685 static void AddSpreadElementTypes(ETSChecker *checker, ir::SpreadElement *const element,
686                                   ArenaVector<std::pair<Type *, ir::Expression *>> &elementTypes)
687 {
688     Type *const spreadType = element->Check(checker);
689 
690     if (spreadType->IsTypeError()) {
691         // error recovery
692         return;
693     }
694 
695     Type *const spreadArgumentType = element->Argument()->TsType();
696 
697     if (spreadArgumentType->IsETSTupleType()) {
698         for (Type *type : spreadArgumentType->AsETSTupleType()->GetTupleTypesList()) {
699             elementTypes.emplace_back(type, element);
700         }
701     } else if (spreadArgumentType->IsETSArrayType()) {
702         elementTypes.emplace_back(spreadArgumentType->AsETSArrayType()->ElementType(), element);
703     } else {
704         ES2PANDA_ASSERT(spreadArgumentType->IsETSResizableArrayType());
705         elementTypes.emplace_back(spreadArgumentType->AsETSObjectType()->TypeArguments().front(), element);
706     }
707 }
708 
ValidArrayExprSizeForTupleSize(ETSChecker * checker,Type * possibleTupleType,ir::Expression * possibleArrayExpr)709 static bool ValidArrayExprSizeForTupleSize(ETSChecker *checker, Type *possibleTupleType,
710                                            ir::Expression *possibleArrayExpr)
711 {
712     if (!possibleArrayExpr->IsArrayExpression() || !possibleTupleType->IsETSTupleType()) {
713         return true;
714     }
715 
716     return checker->IsArrayExprSizeValidForTuple(possibleArrayExpr->AsArrayExpression(),
717                                                  possibleTupleType->AsETSTupleType());
718 }
719 
GetElementTypes(ETSChecker * checker,ir::ArrayExpression * expr)720 static ArenaVector<std::pair<Type *, ir::Expression *>> GetElementTypes(ETSChecker *checker, ir::ArrayExpression *expr)
721 {
722     ArenaVector<std::pair<Type *, ir::Expression *>> elementTypes(checker->ProgramAllocator()->Adapter());
723 
724     for (std::size_t idx = 0; idx < expr->Elements().size(); ++idx) {
725         ir::Expression *const element = expr->Elements()[idx];
726 
727         if (element->IsSpreadElement()) {
728             AddSpreadElementTypes(checker, element->AsSpreadElement(), elementTypes);
729             continue;
730         }
731 
732         auto *const exprPreferredType = expr->GetPreferredType();
733 
734         if (expr->GetPreferredType()->IsETSTupleType() &&
735             idx < expr->GetPreferredType()->AsETSTupleType()->GetTupleSize() &&
736             !ValidArrayExprSizeForTupleSize(checker, exprPreferredType->AsETSTupleType()->GetTypeAtIndex(idx),
737                                             element)) {
738             elementTypes.emplace_back(checker->GlobalTypeError(), element);
739             continue;
740         }
741 
742         if (element->IsArrayExpression() || element->IsObjectExpression()) {
743             auto *const targetPreferredType = exprPreferredType->IsETSTupleType()
744                                                   ? exprPreferredType->AsETSTupleType()->GetTypeAtIndex(idx)
745                                                   : checker->GetElementTypeOfArray(exprPreferredType);
746             ETSChecker::SetPreferredTypeIfPossible(element, targetPreferredType);
747         }
748 
749         elementTypes.emplace_back(element->Check(checker), element);
750     }
751 
752     return elementTypes;
753 }
754 
GetArrayElementType(ETSChecker * checker,Type * preferredType)755 static Type *GetArrayElementType(ETSChecker *checker, Type *preferredType)
756 {
757     if (preferredType->IsETSArrayType()) {
758         return checker->GetNonConstantType(checker->GetElementTypeOfArray(preferredType));
759     }
760     ES2PANDA_ASSERT(preferredType->IsETSResizableArrayType());
761     return preferredType->AsETSResizableArrayType()->ElementType();
762 }
763 
CheckElement(ETSChecker * checker,Type * const preferredType,ArenaVector<std::pair<Type *,ir::Expression * >> arrayExprElementTypes,std::size_t idx)764 static bool CheckElement(ETSChecker *checker, Type *const preferredType,
765                          ArenaVector<std::pair<Type *, ir::Expression *>> arrayExprElementTypes, std::size_t idx)
766 {
767     auto [elementType, currentElement] = arrayExprElementTypes[idx];
768 
769     if (elementType->IsTypeError()) {
770         return true;
771     }
772 
773     Type *targetType = nullptr;
774 
775     if (preferredType->IsETSTupleType()) {
776         const auto *const tupleType = preferredType->AsETSTupleType();
777         if (tupleType->GetTupleSize() != arrayExprElementTypes.size()) {
778             return false;
779         }
780 
781         auto *const compareType = tupleType->GetTypeAtIndex(idx);
782         if (compareType == nullptr) {
783             checker->LogError(diagnostic::TUPLE_SIZE_MISMATCH, {tupleType->GetTupleSize()}, currentElement->Start());
784             return false;
785         }
786 
787         auto ctx = AssignmentContext(checker->Relation(), currentElement, elementType, compareType,
788                                      currentElement->Start(), std::nullopt, TypeRelationFlag::NO_THROW);
789         if (!ctx.IsAssignable()) {
790             checker->LogError(diagnostic::TUPLE_UNASSIGNABLE_ARRAY, {idx}, currentElement->Start());
791             return false;
792         }
793 
794         const CastingContext castCtx(
795             checker->Relation(), diagnostic::CAST_FAIL_UNREACHABLE, {},
796             CastingContext::ConstructorData {currentElement, compareType, checker->MaybeBoxType(compareType),
797                                              currentElement->Start(), TypeRelationFlag::NO_THROW});
798 
799         targetType = compareType;
800     } else {
801         targetType = GetArrayElementType(checker, preferredType);
802     }
803 
804     auto ctx = AssignmentContext(checker->Relation(), currentElement, elementType, targetType, currentElement->Start(),
805                                  {}, TypeRelationFlag::NO_THROW);
806     if (!ctx.IsAssignable()) {
807         checker->LogError(diagnostic::ARRAY_ELEMENT_INIT_TYPE_INCOMPAT, {idx, elementType, targetType},
808                           currentElement->Start());
809         return false;
810     }
811 
812     return true;
813 }
814 
InferPreferredTypeFromElements(ETSChecker * checker,ir::ArrayExpression * arrayExpr)815 static Type *InferPreferredTypeFromElements(ETSChecker *checker, ir::ArrayExpression *arrayExpr)
816 {
817     ArenaVector<Type *> arrayExpressionElementTypes(checker->ProgramAllocator()->Adapter());
818     for (auto *const element : arrayExpr->Elements()) {
819         auto *elementType = *element->Check(checker);
820         if (element->IsSpreadElement() && elementType->IsETSTupleType()) {
821             for (auto *typeFromTuple : elementType->AsETSTupleType()->GetTupleTypesList()) {
822                 arrayExpressionElementTypes.emplace_back(typeFromTuple);
823             }
824 
825             continue;
826         }
827 
828         if (element->IsSpreadElement() && elementType->IsETSArrayType()) {
829             elementType = elementType->AsETSArrayType()->ElementType();
830         }
831 
832         arrayExpressionElementTypes.emplace_back(elementType);
833     }
834 
835     // NOTE (smartin): fix union type normalization. Currently for primitive types like a 'char | char' type, it will be
836     // normalized to 'Char'. However it shouldn't be boxed, and be kept as 'char'. For a quick fix, if all types are
837     // primitive, then after making the union type, explicitly unbox it.
838     if (std::all_of(arrayExpressionElementTypes.begin(), arrayExpressionElementTypes.end(),
839                     [](Type *const typeOfElement) { return typeOfElement->IsETSPrimitiveType(); })) {
840         return checker->CreateETSResizableArrayType(checker->GetNonConstantType(
841             checker->MaybeUnboxType(checker->CreateETSUnionType(std::move(arrayExpressionElementTypes)))));
842     }
843 
844     // NOTE (smartin): optimize element access on constant array expressions (note is here, because the constant value
845     // will be present on the type)
846     return checker->CreateETSResizableArrayType(
847         checker->GetNonConstantType(checker->CreateETSUnionType(std::move(arrayExpressionElementTypes))));
848 }
849 
CheckArrayExpressionElements(ETSChecker * checker,ir::ArrayExpression * arrayExpr)850 static bool CheckArrayExpressionElements(ETSChecker *checker, ir::ArrayExpression *arrayExpr)
851 {
852     const ArenaVector<std::pair<Type *, ir::Expression *>> arrayExprElementTypes = GetElementTypes(checker, arrayExpr);
853 
854     bool allElementsAssignable = !std::any_of(arrayExprElementTypes.begin(), arrayExprElementTypes.end(),
855                                               [](auto &pair) { return pair.first->IsTypeError(); });
856 
857     for (std::size_t idx = 0; idx < arrayExprElementTypes.size(); ++idx) {
858         allElementsAssignable &= CheckElement(checker, arrayExpr->GetPreferredType(), arrayExprElementTypes, idx);
859     }
860 
861     return allElementsAssignable;
862 }
863 
IsPossibleArrayExpressionType(Type const * type)864 static bool IsPossibleArrayExpressionType(Type const *type)
865 {
866     return type->IsETSArrayType() || type->IsETSTupleType() || type->IsETSResizableArrayType();
867 }
868 
GetUnionPreferredType(ir::Expression * expr,Type * originalType) const869 void ETSAnalyzer::GetUnionPreferredType(ir::Expression *expr, Type *originalType) const
870 {
871     if (originalType == nullptr || !originalType->IsETSUnionType()) {
872         return;
873     }
874     checker::Type *preferredType = nullptr;
875     for (auto &type : originalType->AsETSUnionType()->ConstituentTypes()) {
876         if (IsPossibleArrayExpressionType(type)) {
877             if (preferredType != nullptr) {
878                 preferredType = nullptr;
879                 break;
880             }
881             preferredType = type;
882         }
883     }
884     if (expr->IsArrayExpression()) {
885         expr->AsArrayExpression()->SetPreferredType(preferredType);
886     } else if (expr->IsETSNewArrayInstanceExpression()) {
887         expr->AsETSNewArrayInstanceExpression()->SetPreferredType(preferredType);
888     } else if (expr->IsETSNewMultiDimArrayInstanceExpression()) {
889         expr->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(preferredType);
890     } else {
891         ES2PANDA_UNREACHABLE();
892     }
893 }
894 
Check(ir::ArrayExpression * expr) const895 checker::Type *ETSAnalyzer::Check(ir::ArrayExpression *expr) const
896 {
897     ETSChecker *checker = GetETSChecker();
898     if (expr->TsType() != nullptr) {
899         return expr->TsType();
900     }
901 
902     if (expr->GetPreferredType() != nullptr) {
903         if (expr->GetPreferredType()->IsETSTypeAliasType()) {
904             expr->SetPreferredType(expr->GetPreferredType()->AsETSTypeAliasType()->GetTargetType());
905         }
906 
907         if (expr->GetPreferredType()->IsETSUnionType()) {
908             GetUnionPreferredType(expr, expr->GetPreferredType());
909         }
910 
911         if (expr->GetPreferredType() != nullptr && !IsPossibleArrayExpressionType(expr->GetPreferredType())) {
912             expr->SetPreferredType(nullptr);
913         }
914     }
915 
916     if (!IsArrayExpressionValidInitializerForType(checker, expr->GetPreferredType())) {
917         checker->LogError(diagnostic::UNEXPECTED_ARRAY, {expr->GetPreferredType()}, expr->Start());
918         return checker->InvalidateType(expr);
919     }
920 
921     if (!expr->Elements().empty()) {
922         if (expr->GetPreferredType() == nullptr || expr->GetPreferredType() == checker->GlobalETSObjectType()) {
923             expr->SetPreferredType(InferPreferredTypeFromElements(checker, expr));
924         }
925 
926         if (!ValidArrayExprSizeForTupleSize(checker, expr->GetPreferredType(), expr) ||
927             !CheckArrayExpressionElements(checker, expr)) {
928             return checker->InvalidateType(expr);
929         }
930     }
931 
932     if (expr->GetPreferredType() == nullptr) {
933         return checker->TypeError(expr, diagnostic::UNRESOLVABLE_ARRAY, expr->Start());
934     }
935 
936     expr->SetTsType(expr->GetPreferredType());
937     if (!expr->GetPreferredType()->IsETSResizableArrayType() && !expr->TsType()->IsETSTupleType()) {
938         ES2PANDA_ASSERT(expr->TsType()->IsETSArrayType());
939         const auto *const arrayType = expr->TsType()->AsETSArrayType();
940         checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank());
941     }
942     return expr->TsType();
943 }
944 
TryInferPreferredType(ir::ArrowFunctionExpression * expr,checker::Type * preferredType,ETSChecker * checker)945 void TryInferPreferredType(ir::ArrowFunctionExpression *expr, checker::Type *preferredType, ETSChecker *checker)
946 {
947     if (!preferredType->IsETSUnionType()) {
948         if (preferredType->IsETSArrowType() &&
949             !preferredType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow().empty()) {
950             checker->TryInferTypeForLambdaTypeAlias(expr, preferredType->AsETSFunctionType());
951             checker->BuildFunctionSignature(expr->Function(), false);
952         }
953         return;
954     }
955 
956     for (auto &ct : preferredType->AsETSUnionType()->ConstituentTypes()) {
957         if (!ct->IsETSArrowType() || ct->AsETSFunctionType()->CallSignaturesOfMethodOrArrow().empty()) {
958             continue;
959         }
960         checker->TryInferTypeForLambdaTypeAlias(expr, ct->AsETSFunctionType());
961         checker->BuildFunctionSignature(expr->Function(), false);
962         if (expr->Function()->Signature() != nullptr) {
963             return;
964         }
965     }
966 }
967 
Check(ir::ArrowFunctionExpression * expr) const968 checker::Type *ETSAnalyzer::Check(ir::ArrowFunctionExpression *expr) const
969 {
970     ETSChecker *checker = GetETSChecker();
971     checker->CheckAnnotations(expr->Annotations());
972     if (expr->TsType() != nullptr) {
973         return expr->TsType();
974     }
975     checker::ScopeContext scopeCtx(checker, expr->Function()->Scope());
976 
977     if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD) && !expr->Function()->HasReceiver()) {
978         /*
979         example code:
980         ```
981             class A {
982                 prop:number
983             }
984             function method(this: A) {
985                 let a = () => {
986                     console.log(this.prop)
987                 }
988             }
989         ```
990         here the enclosing class of arrow function should be Class A
991         */
992         checker->Context().SetContainingClass(
993             checker->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable->TsType()->AsETSObjectType());
994     }
995 
996     auto lambdaSavedSmartCasts = checker->Context().CloneSmartCasts();
997     checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
998                                               checker->Context().ContainingClass());
999 
1000     if (expr->Parent()->IsCallExpression() && !expr->Function()->IsAsyncFunc()) {
1001         checker->Context().RestoreSmartCasts(lambdaSavedSmartCasts);
1002     }
1003 
1004     checker->AddStatus(checker::CheckerStatus::IN_LAMBDA);
1005     checker->Context().SetContainingLambda(expr);
1006 
1007     auto preferredType = expr->GetPreferredType();
1008     if (preferredType != nullptr) {
1009         TryInferPreferredType(expr, preferredType, checker);
1010     } else {
1011         checker->BuildFunctionSignature(expr->Function(), false);
1012     }
1013 
1014     if (expr->Function()->Signature() == nullptr) {
1015         return checker->InvalidateType(expr);
1016     }
1017 
1018     if (expr->Function()->HasReceiver()) {
1019         checker->AddStatus(checker::CheckerStatus::IN_EXTENSION_METHOD);
1020         CheckExtensionMethod(checker, expr->Function(), expr);
1021     }
1022     auto *signature = expr->Function()->Signature();
1023 
1024     checker->Context().SetContainingSignature(signature);
1025     expr->Function()->Body()->Check(checker);
1026 
1027     auto *funcType = checker->CreateETSArrowType(signature);
1028     checker->Context().SetContainingSignature(nullptr);
1029 
1030     if (expr->Function()->IsAsyncFunc()) {
1031         auto *retType = signature->ReturnType();
1032         if (!retType->IsETSObjectType() ||
1033             retType->AsETSObjectType()->GetOriginalBaseType() != checker->GlobalBuiltinPromiseType()) {
1034             checker->LogError(diagnostic::ASYNC_DOESNT_PROMISE, {}, expr->Function()->Start());
1035             expr->SetTsType(checker->GlobalTypeError());
1036             return expr->TsType();
1037         }
1038     }
1039     expr->SetTsType(funcType);
1040     return expr->TsType();
1041 }
1042 
IsInvalidArrayMemberAssignment(const ir::AssignmentExpression * const expr,ETSChecker * checker)1043 static bool IsInvalidArrayMemberAssignment(const ir::AssignmentExpression *const expr, ETSChecker *checker)
1044 {
1045     if (!expr->Left()->IsMemberExpression()) {
1046         return false;
1047     }
1048 
1049     const auto *const leftExpr = expr->Left()->AsMemberExpression();
1050     if (leftExpr->Object()->TsType()->IsETSArrayType() || leftExpr->Object()->TsType()->IsETSTupleType() ||
1051         leftExpr->Object()->TsType()->IsETSResizableArrayType()) {
1052         if (leftExpr->Object()->TsType()->IsETSArrayType() && leftExpr->Property()->IsIdentifier() &&
1053             leftExpr->Property()->AsIdentifier()->Name().Is("length")) {
1054             checker->LogError(diagnostic::ARRAY_LENGTH_MODIFICATION, {}, expr->Left()->Start());
1055             return true;
1056         }
1057 
1058         if (leftExpr->Object()->TsType()->HasTypeFlag(TypeFlag::READONLY)) {
1059             checker->LogError(diagnostic::READONLY_ARRAYLIKE_MODIFICATION, {}, expr->Left()->Start());
1060             return true;
1061         }
1062     }
1063 
1064     return false;
1065 }
1066 
GetSmartType(ir::AssignmentExpression * expr,checker::Type * leftType,checker::Type * rightType) const1067 checker::Type *ETSAnalyzer::GetSmartType(ir::AssignmentExpression *expr, checker::Type *leftType,
1068                                          checker::Type *rightType) const
1069 {
1070     ETSChecker *checker = GetETSChecker();
1071     checker::Type *smartType = leftType;
1072 
1073     if (expr->Left()->IsIdentifier() && expr->Target() != nullptr) {
1074         //  Now try to define the actual type of Identifier so that smart cast can be used in further checker
1075         //  processing
1076         smartType = checker->ResolveSmartType(rightType, leftType);
1077         auto const *const variable = expr->Target();
1078 
1079         //  Add/Remove/Modify smart cast for identifier
1080         //  (excluding the variables defined at top-level scope or captured in lambda-functions!)
1081         auto const *const variableScope = variable->GetScope();
1082         auto const topLevelVariable =
1083             variableScope != nullptr && (variableScope->IsGlobalScope() || (variableScope->Parent() != nullptr &&
1084                                                                             variableScope->Parent()->IsGlobalScope()));
1085         if (!topLevelVariable) {
1086             if (checker->Relation()->IsIdenticalTo(leftType, smartType)) {
1087                 checker->Context().RemoveSmartCast(variable);
1088             } else {
1089                 expr->Left()->SetTsType(smartType);
1090                 checker->Context().SetSmartCast(variable, smartType);
1091             }
1092         }
1093     }
1094     return smartType;
1095 }
1096 
Check(ir::AssignmentExpression * const expr) const1097 checker::Type *ETSAnalyzer::Check(ir::AssignmentExpression *const expr) const
1098 {
1099     if (expr->TsType() != nullptr) {
1100         return expr->TsType();
1101     }
1102 
1103     ETSChecker *checker = GetETSChecker();
1104 
1105     if (checker->HasStatus(CheckerStatus::IN_SETTER) && expr->Left()->IsMemberExpression()) {
1106         checker->WarnForEndlessLoopInGetterSetter(expr->Left()->AsMemberExpression());
1107     }
1108 
1109     const auto leftType = expr->Left()->Check(checker);
1110 
1111     if (IsInvalidArrayMemberAssignment(expr, checker)) {
1112         expr->SetTsType(checker->GlobalTypeError());
1113         return expr->TsType();
1114     }
1115 
1116     if (expr->Left()->IsIdentifier()) {
1117         expr->target_ = expr->Left()->AsIdentifier()->Variable();
1118     } else if (expr->Left()->IsMemberExpression()) {
1119         if (!expr->IsIgnoreConstAssign() &&
1120             expr->Left()->AsMemberExpression()->Object()->TsType()->HasTypeFlag(TypeFlag::READONLY)) {
1121             checker->LogError(diagnostic::READONLY_PROPERTY_REASSIGN, {}, expr->Left()->Start());
1122         }
1123         expr->target_ = expr->Left()->AsMemberExpression()->PropVar();
1124     } else {
1125         checker->LogError(diagnostic::ASSIGNMENT_INVALID_LHS, {}, expr->Left()->Start());
1126         expr->SetTsType(checker->GlobalTypeError());
1127         return expr->TsType();
1128     }
1129 
1130     if (expr->target_ != nullptr && !expr->IsIgnoreConstAssign()) {
1131         checker->ValidateUnaryOperatorOperand(expr->target_);
1132     }
1133 
1134     auto [rightType, relationNode] = CheckAssignmentExprOperatorType(expr, leftType);
1135     if (rightType->IsTypeError()) {
1136         return expr->SetTsType(leftType);
1137     }
1138 
1139     CastPossibleTupleOnRHS(checker, expr);
1140 
1141     checker::Type *smartType = rightType;
1142     if (!leftType->IsTypeError()) {
1143         if (const auto ctx = checker::AssignmentContext(checker->Relation(), relationNode, rightType, leftType,
1144                                                         expr->Right()->Start(),
1145                                                         {{diagnostic::INVALID_ASSIGNMNENT, {rightType, leftType}}});
1146             ctx.IsAssignable()) {
1147             smartType = GetSmartType(expr, leftType, rightType);
1148         }
1149     }
1150 
1151     return expr->SetTsType(smartType);
1152 }
1153 
HandleSubstitution(ETSChecker * checker,ir::AssignmentExpression * expr,Type * const leftType)1154 static checker::Type *HandleSubstitution(ETSChecker *checker, ir::AssignmentExpression *expr, Type *const leftType)
1155 {
1156     bool possibleInferredTypeOfArray = leftType->IsETSArrayType() || leftType->IsETSResizableArrayType() ||
1157                                        leftType->IsETSTupleType() || leftType->IsETSUnionType();
1158     if (expr->Right()->IsArrayExpression() && possibleInferredTypeOfArray) {
1159         checker->ModifyPreferredType(expr->Right()->AsArrayExpression(), leftType);
1160     }
1161 
1162     if (expr->Right()->IsETSNewArrayInstanceExpression()) {
1163         expr->Right()->AsETSNewArrayInstanceExpression()->SetPreferredType(leftType);
1164     }
1165 
1166     if (expr->Right()->IsETSNewMultiDimArrayInstanceExpression()) {
1167         expr->Right()->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(leftType);
1168     }
1169 
1170     if (expr->Right()->IsObjectExpression()) {
1171         expr->Right()->AsObjectExpression()->SetPreferredType(leftType);
1172     }
1173 
1174     if (expr->Right()->IsArrowFunctionExpression() && (leftType->IsETSArrowType() || leftType->IsETSUnionType())) {
1175         expr->Right()->AsArrowFunctionExpression()->SetPreferredType(leftType);
1176     }
1177 
1178     return expr->Right()->Check(checker);
1179 }
1180 
CheckAssignmentExprOperatorType(ir::AssignmentExpression * expr,Type * const leftType) const1181 std::tuple<Type *, ir::Expression *> ETSAnalyzer::CheckAssignmentExprOperatorType(ir::AssignmentExpression *expr,
1182                                                                                   Type *const leftType) const
1183 {
1184     ETSChecker *checker = GetETSChecker();
1185     checker::Type *sourceType {};
1186     ir::Expression *relationNode = expr->Right();
1187     switch (expr->OperatorType()) {
1188         case lexer::TokenType::PUNCTUATOR_MULTIPLY_EQUAL:
1189         case lexer::TokenType::PUNCTUATOR_EXPONENTIATION_EQUAL:
1190         case lexer::TokenType::PUNCTUATOR_DIVIDE_EQUAL:
1191         case lexer::TokenType::PUNCTUATOR_MOD_EQUAL:
1192         case lexer::TokenType::PUNCTUATOR_MINUS_EQUAL:
1193         case lexer::TokenType::PUNCTUATOR_LEFT_SHIFT_EQUAL:
1194         case lexer::TokenType::PUNCTUATOR_RIGHT_SHIFT_EQUAL:
1195         case lexer::TokenType::PUNCTUATOR_UNSIGNED_RIGHT_SHIFT_EQUAL:
1196         case lexer::TokenType::PUNCTUATOR_BITWISE_AND_EQUAL:
1197         case lexer::TokenType::PUNCTUATOR_BITWISE_XOR_EQUAL:
1198         case lexer::TokenType::PUNCTUATOR_BITWISE_OR_EQUAL:
1199         case lexer::TokenType::PUNCTUATOR_PLUS_EQUAL: {
1200             std::tie(std::ignore, expr->operationType_) = checker->CheckBinaryOperator(
1201                 expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start(), true);
1202 
1203             auto unboxedLeft = checker->MaybeUnboxInRelation(leftType);
1204             sourceType = unboxedLeft == nullptr ? leftType : unboxedLeft;
1205 
1206             relationNode = expr;
1207             break;
1208         }
1209         case lexer::TokenType::PUNCTUATOR_SUBSTITUTION: {
1210             sourceType = HandleSubstitution(checker, expr, leftType);
1211             break;
1212         }
1213         default: {
1214             ES2PANDA_UNREACHABLE();
1215             break;
1216         }
1217     }
1218 
1219     return {sourceType, relationNode};
1220 }
1221 
IsPromiseType(checker::Type * type,ETSChecker * checker)1222 static bool IsPromiseType(checker::Type *type, ETSChecker *checker)
1223 {
1224     return type->IsETSObjectType() &&
1225            type->AsETSObjectType()->GetOriginalBaseType() == checker->GlobalBuiltinPromiseType();
1226 }
1227 
Check(ir::AwaitExpression * expr) const1228 checker::Type *ETSAnalyzer::Check(ir::AwaitExpression *expr) const
1229 {
1230     ETSChecker *checker = GetETSChecker();
1231     if (expr->TsType() != nullptr) {
1232         return expr->TsType();
1233     }
1234 
1235     checker::Type *argType = checker->GetApparentType(expr->argument_->Check(checker));
1236     ES2PANDA_ASSERT(argType != nullptr);
1237     ArenaVector<Type *> awaitedTypes(checker->ProgramAllocator()->Adapter());
1238 
1239     if (argType->IsETSUnionType()) {
1240         for (Type *type : argType->AsETSUnionType()->ConstituentTypes()) {
1241             if (!IsPromiseType(type, checker)) {
1242                 return checker->TypeError(expr, diagnostic::AWAITED_NOT_PROMISE, expr->Argument()->Start());
1243             }
1244 
1245             Type *typeArg = type->AsETSObjectType()->TypeArguments().at(0);
1246             awaitedTypes.push_back(UnwrapPromiseType(typeArg));
1247         }
1248     } else {
1249         if (!IsPromiseType(argType, checker)) {
1250             return checker->TypeError(expr, diagnostic::AWAITED_NOT_PROMISE, expr->Argument()->Start());
1251         }
1252 
1253         Type *typeArg = argType->AsETSObjectType()->TypeArguments().at(0);
1254         awaitedTypes.push_back(UnwrapPromiseType(typeArg));
1255     }
1256 
1257     expr->SetTsType(argType->IsETSUnionType() ? checker->CreateETSUnionType(std::move(awaitedTypes)) : awaitedTypes[0]);
1258     return expr->TsType();
1259 }
1260 
UnwrapPromiseType(checker::Type * type) const1261 checker::Type *ETSAnalyzer::UnwrapPromiseType(checker::Type *type) const
1262 {
1263     ETSChecker *checker = GetETSChecker();
1264     checker::Type *promiseType = checker->GlobalBuiltinPromiseType();
1265     while (type->IsETSObjectType() && type->AsETSObjectType()->GetOriginalBaseType() == promiseType) {
1266         type = type->AsETSObjectType()->TypeArguments().at(0);
1267     }
1268     if (!type->IsETSUnionType()) {
1269         return type;
1270     }
1271     const auto &ctypes = type->AsETSUnionType()->ConstituentTypes();
1272     auto it = std::find_if(ctypes.begin(), ctypes.end(), [promiseType](checker::Type *t) {
1273         return t == promiseType || (t->IsETSObjectType() && t->AsETSObjectType()->GetBaseType() == promiseType);
1274     });
1275     if (it == ctypes.end()) {
1276         return type;
1277     }
1278     ArenaVector<Type *> newCTypes(ctypes);
1279     do {
1280         size_t index = it - ctypes.begin();
1281         newCTypes[index] = UnwrapPromiseType(ctypes[index]);
1282         ++it;
1283         it = std::find_if(it, ctypes.end(), [promiseType](checker::Type *t) {
1284             return t == promiseType || t->AsETSObjectType()->GetBaseType() == promiseType;
1285         });
1286     } while (it != ctypes.end());
1287     return checker->CreateETSUnionType(std::move(newCTypes));
1288 }
1289 
Check(ir::BinaryExpression * expr) const1290 checker::Type *ETSAnalyzer::Check(ir::BinaryExpression *expr) const
1291 {
1292     if (expr->TsType() != nullptr) {
1293         return expr->TsType();
1294     }
1295 
1296     ETSChecker *checker = GetETSChecker();
1297 
1298     bool inSmartExpr = false;
1299     if (!checker->Context().IsInTestExpression()) {
1300         switch (expr->OperatorType()) {
1301             case lexer::TokenType::KEYW_INSTANCEOF:
1302             case lexer::TokenType::PUNCTUATOR_EQUAL:
1303             case lexer::TokenType::PUNCTUATOR_NOT_EQUAL:
1304             case lexer::TokenType::PUNCTUATOR_STRICT_EQUAL:
1305             case lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL:
1306             case lexer::TokenType::PUNCTUATOR_LOGICAL_AND:
1307             case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
1308                 inSmartExpr = true;
1309                 SmartCastArray smartCasts = checker->Context().EnterTestExpression();
1310                 break;
1311             }
1312             default:
1313                 break;
1314         }
1315     }
1316 
1317     checker::Type *newTsType {nullptr};
1318     std::tie(newTsType, expr->operationType_) =
1319         checker->CheckBinaryOperator(expr->Left(), expr->Right(), expr, expr->OperatorType(), expr->Start());
1320     expr->SetTsType(newTsType);
1321 
1322     checker->Context().CheckBinarySmartCastCondition(expr);
1323 
1324     if (inSmartExpr) {
1325         checker->Context().ExitTestExpression();
1326     }
1327 
1328     return expr->TsType();
1329 }
1330 
Check(ir::BlockExpression * st) const1331 checker::Type *ETSAnalyzer::Check(ir::BlockExpression *st) const
1332 {
1333     if (st->TsType() != nullptr) {
1334         return st->TsType();
1335     }
1336 
1337     ETSChecker *checker = GetETSChecker();
1338     checker::ScopeContext scopeCtx(checker, st->Scope());
1339 
1340     // NOLINTNEXTLINE(modernize-loop-convert)
1341     for (std::size_t idx = 0; idx < st->Statements().size(); idx++) {
1342         st->Statements()[idx]->Check(checker);
1343     }
1344 
1345     auto lastStmt = st->Statements().back();
1346     ES2PANDA_ASSERT(lastStmt->IsExpressionStatement());
1347     st->SetTsType(lastStmt->AsExpressionStatement()->GetExpression()->TsType());
1348     return st->TsType();
1349 }
1350 
LambdaIsField(ir::CallExpression * expr)1351 static bool LambdaIsField(ir::CallExpression *expr)
1352 {
1353     if (!expr->Callee()->IsMemberExpression()) {
1354         return false;
1355     }
1356     auto *me = expr->Callee()->AsMemberExpression();
1357     return me->PropVar() != nullptr;
1358 }
1359 
ResolveSignature(ETSChecker * checker,ir::CallExpression * expr,checker::Type * calleeType) const1360 checker::Signature *ETSAnalyzer::ResolveSignature(ETSChecker *checker, ir::CallExpression *expr,
1361                                                   checker::Type *calleeType) const
1362 {
1363     if (calleeType->IsETSFunctionType() && calleeType->AsETSFunctionType()->HasHelperSignature() &&
1364         expr->Signature() != nullptr) {
1365         // Note: Only works when rechecking in DeclareOveloadLowering phase
1366         auto *helperSignature = calleeType->AsETSFunctionType()->GetHelperSignature();
1367         checker->LogDiagnostic(diagnostic::DUPLICATE_SIGS, {helperSignature->Function()->Id()->Name(), helperSignature},
1368                                expr->Start());
1369         checker->CreateOverloadSigContainer(helperSignature);
1370         return checker->ResolveCallExpressionAndTrailingLambda(checker->GetOverloadSigContainer(), expr, expr->Start());
1371     }
1372 
1373     if (calleeType->IsETSExtensionFuncHelperType()) {
1374         auto *signature =
1375             ResolveCallForETSExtensionFuncHelperType(calleeType->AsETSExtensionFuncHelperType(), checker, expr);
1376         GetChecker()->AsETSChecker()->UpdateDeclarationFromSignature(expr, signature);
1377         return signature;
1378     }
1379 
1380     // when a lambda with receiver is a class field or interface property,
1381     // then it can only be called like a lambda without receiver.
1382     if (checker->IsExtensionETSFunctionType(calleeType) && !LambdaIsField(expr)) {
1383         auto *signature = ResolveCallExtensionFunction(calleeType, checker, expr);
1384         if (signature != nullptr && signature->IsExtensionAccessor() &&
1385             !checker->HasStatus(CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK)) {
1386             checker->LogError(diagnostic::EXTENSION_ACCESSOR_INVALID_CALL, {}, expr->Start());
1387             return nullptr;
1388         }
1389         return signature;
1390     }
1391     auto &signatures = expr->IsETSConstructorCall() ? calleeType->AsETSObjectType()->ConstructSignatures()
1392                                                     : calleeType->AsETSFunctionType()->CallSignaturesOfMethodOrArrow();
1393 
1394     return checker->ResolveCallExpressionAndTrailingLambda(signatures, expr, expr->Start());
1395 }
1396 
GetCallExpressionCalleeObject(ETSChecker * checker,ir::CallExpression * expr,Type * calleeType)1397 static ETSObjectType *GetCallExpressionCalleeObject(ETSChecker *checker, ir::CallExpression *expr, Type *calleeType)
1398 {
1399     if (expr->IsETSConstructorCall()) {
1400         return calleeType->AsETSObjectType();
1401     }
1402     auto callee = expr->Callee();
1403     if (callee->IsMemberExpression()) {
1404         return callee->AsMemberExpression()->ObjType();
1405     }
1406     ES2PANDA_ASSERT(callee->IsIdentifier());
1407     return checker->Context().ContainingClass();
1408 }
1409 
GetReturnType(ir::CallExpression * expr,Type * calleeType) const1410 Type *ETSAnalyzer::GetReturnType(ir::CallExpression *expr, Type *calleeType) const
1411 {
1412     ETSChecker *checker = GetETSChecker();
1413 
1414     if (calleeType->IsTypeError()) {
1415         return checker->GlobalTypeError();
1416     }
1417 
1418     if (!calleeType->IsETSFunctionType() && !expr->IsETSConstructorCall() &&
1419         !calleeType->IsETSExtensionFuncHelperType()) {
1420         checker->LogError(diagnostic::NO_CALL_SIGNATURE, {calleeType}, expr->Start());
1421         return checker->GlobalTypeError();
1422     }
1423 
1424     Signature *const signature = ResolveSignature(checker, expr, calleeType);
1425     if (signature == nullptr) {
1426         return checker->GlobalTypeError();
1427     }
1428 
1429     checker->CheckObjectLiteralArguments(signature, expr->Arguments());
1430 
1431     if (calleeType->IsETSMethodType()) {
1432         ETSObjectType *calleeObj = GetCallExpressionCalleeObject(checker, expr, calleeType);
1433         checker->ValidateSignatureAccessibility(calleeObj, signature, expr->Start());
1434     }
1435 
1436     if (calleeType->IsETSMethodType() && signature->Function()->IsDynamic()) {
1437         ES2PANDA_ASSERT(signature->Function()->IsDynamic());
1438         auto lang = signature->Function()->Language();
1439         expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), signature->Params(), lang, false));
1440     } else {
1441         expr->SetSignature(signature);
1442     }
1443 
1444     // #22951: this type should not be encoded as a signature flag
1445     if (signature->HasSignatureFlag(SignatureFlags::THIS_RETURN_TYPE)) {
1446         return signature->HasSignatureFlag(SignatureFlags::EXTENSION_FUNCTION)
1447                    ? expr->Arguments()[0]->TsType()
1448                    : GetCallExpressionCalleeObject(checker, expr, calleeType);
1449     }
1450     return signature->ReturnType();
1451 }
1452 
CheckAbstractCall(ETSChecker * checker,ir::CallExpression * expr)1453 static void CheckAbstractCall(ETSChecker *checker, ir::CallExpression *expr)
1454 {
1455     if (expr->Callee()->IsMemberExpression()) {
1456         auto obj = expr->Callee()->AsMemberExpression()->Object();
1457         if (obj != nullptr && obj->IsSuperExpression()) {
1458             if ((expr->Signature() != nullptr) && (expr->Signature()->HasSignatureFlag(SignatureFlags::ABSTRACT))) {
1459                 checker->LogError(diagnostic::ABSTRACT_CALL, {}, expr->Start());
1460                 expr->SetTsType(checker->GlobalTypeError());
1461             }
1462         }
1463     }
1464 }
1465 
CheckCallee(ETSChecker * checker,ir::CallExpression * expr)1466 static void CheckCallee(ETSChecker *checker, ir::CallExpression *expr)
1467 {
1468     checker->CheckNonNullish(expr->Callee());
1469     if (expr->Callee()->IsMemberExpression() && expr->Callee()->AsMemberExpression()->Object() != nullptr &&
1470         expr->Callee()->AsMemberExpression()->Object()->TsType()->IsETSObjectType() &&
1471         expr->Callee()->AsMemberExpression()->Object()->TsType()->AsETSObjectType()->HasObjectFlag(
1472             ETSObjectFlags::READONLY)) {
1473         checker->LogError(diagnostic::READONLY_CALL, {}, expr->Start());
1474         expr->SetTsType(checker->GlobalTypeError());
1475     }
1476 }
1477 
1478 // Restore CheckerContext of the owner class if we want to perform checking
ReconstructOwnerClassContext(ETSChecker * checker,ETSObjectType * owner)1479 static checker::SavedCheckerContext ReconstructOwnerClassContext(ETSChecker *checker, ETSObjectType *owner)
1480 {
1481     if (owner == nullptr) {
1482         return SavedCheckerContext(checker, CheckerStatus::NO_OPTS, nullptr);
1483     }
1484     ES2PANDA_ASSERT(!owner->HasObjectFlag(ETSObjectFlags::ENUM));
1485     CheckerStatus const status =
1486         (owner->HasObjectFlag(ETSObjectFlags::CLASS) ? CheckerStatus::IN_CLASS : CheckerStatus::IN_INTERFACE) |
1487         (owner->HasObjectFlag(ETSObjectFlags::ABSTRACT) ? CheckerStatus::IN_ABSTRACT : CheckerStatus::NO_OPTS) |
1488         (owner->HasObjectFlag(ETSObjectFlags::INNER) ? CheckerStatus::INNER_CLASS : CheckerStatus::NO_OPTS) |
1489         (owner->GetDeclNode()->IsClassDefinition() && owner->GetDeclNode()->AsClassDefinition()->IsLocal()
1490              ? CheckerStatus::IN_LOCAL_CLASS
1491              : CheckerStatus::NO_OPTS);
1492 
1493     return SavedCheckerContext(checker, status, owner);
1494 }
1495 
GetCallExpressionReturnType(ir::CallExpression * expr,checker::Type * calleeType) const1496 checker::Type *ETSAnalyzer::GetCallExpressionReturnType(ir::CallExpression *expr, checker::Type *calleeType) const
1497 {
1498     ETSChecker *checker = GetETSChecker();
1499     checker::Type *returnType = nullptr;
1500     if (UNLIKELY(calleeType->IsETSDynamicType() && !calleeType->AsETSDynamicType()->HasDecl())) {
1501         // Trailing lambda for js function call is not supported, check the correctness of `foo() {}`
1502         checker->EnsureValidCurlyBrace(expr);
1503         auto lang = calleeType->AsETSDynamicType()->Language();
1504         expr->SetSignature(checker->ResolveDynamicCallExpression(expr->Callee(), expr->Arguments(), lang, false));
1505         returnType = expr->Signature()->ReturnType();
1506     } else {
1507         returnType = GetReturnType(expr, calleeType);
1508     }
1509 
1510     if (returnType->IsTypeError()) {
1511         return checker->GlobalTypeError();
1512     }
1513 
1514     auto *const signature = expr->Signature();
1515     if (signature->RestVar() != nullptr && signature->RestVar()->TsType()->IsETSArrayType()) {
1516         auto *elementType = signature->RestVar()->TsType()->AsETSArrayType()->ElementType();
1517         auto *const arrayType = checker->CreateETSArrayType(elementType)->AsETSArrayType();
1518         checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank());
1519     }
1520 
1521     if (!signature->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE) ||
1522         (signature->HasSignatureFlag(checker::SignatureFlags::CONSTRUCTOR))) {
1523         return returnType;
1524     }
1525 
1526     if (!signature->HasFunction()) {
1527         return checker->GlobalTypeError();
1528     }
1529 
1530     auto owner = const_cast<ETSObjectType *>(util::Helpers::GetContainingObjectType(signature->Function()));
1531     SavedCheckerContext savedCtx(ReconstructOwnerClassContext(checker, owner));
1532 
1533     ir::AstNode *methodDef = signature->Function();
1534     while (!methodDef->IsMethodDefinition()) {
1535         methodDef = methodDef->Parent();
1536         ES2PANDA_ASSERT(methodDef != nullptr);
1537     }
1538     ES2PANDA_ASSERT(methodDef->IsMethodDefinition());
1539     methodDef->Check(checker);
1540 
1541     if (!signature->Function()->HasBody()) {
1542         return signature->ReturnType();
1543     }
1544 
1545     if (signature->Function()->IsExternal()) {
1546         checker->VarBinder()->AsETSBinder()->ResolveReferencesForScopeWithContext(signature->Function()->Body(),
1547                                                                                   signature->Function()->Scope());
1548     }
1549     checker::ScopeContext scopeCtx(checker, signature->Function()->Body()->Scope());
1550     checker->CollectReturnStatements(signature->Function());
1551     return signature->ReturnType();
1552     // NOTE(vpukhov): #14902 substituted signature is not updated
1553 }
1554 
Check(ir::CallExpression * expr) const1555 checker::Type *ETSAnalyzer::Check(ir::CallExpression *expr) const
1556 {
1557     ETSChecker *checker = GetETSChecker();
1558     if (expr->TsType() != nullptr) {
1559         return expr->TsType();
1560     }
1561     ES2PANDA_ASSERT(!expr->IsOptional());
1562 
1563     auto *oldCallee = expr->Callee();
1564     checker::Type *calleeType = checker->GetApparentType(expr->Callee()->Check(checker));
1565     if (calleeType->IsTypeError()) {
1566         return checker->InvalidateType(expr);
1567     }
1568 
1569     if (expr->Callee() != oldCallee) {
1570         // If it is a static invoke, the callee will be transformed from an identifier to a member expression
1571         // Type check the callee again for member expression
1572         calleeType = checker->GetApparentType(expr->Callee()->Check(checker));
1573     }
1574 
1575     CheckCallee(checker, expr);
1576 
1577     checker::TypeStackElement tse(checker, expr, {{diagnostic::CYCLIC_CALLEE, {}}}, expr->Start());
1578     if (tse.HasTypeError()) {
1579         expr->SetTsType(checker->GlobalTypeError());
1580         return checker->GlobalTypeError();
1581     }
1582 
1583     checker::Type *const returnType = GetCallExpressionReturnType(expr, calleeType);
1584     expr->SetTsType(returnType);
1585     if (returnType->IsTypeError()) {
1586         return returnType;
1587     }
1588     if (calleeType->IsETSArrowType()) {
1589         expr->SetUncheckedType(checker->GuaranteedTypeForUncheckedCast(
1590             checker->GlobalETSAnyType(), checker->MaybeBoxType(expr->Signature()->ReturnType())));
1591     } else {
1592         expr->SetUncheckedType(checker->GuaranteedTypeForUncheckedCallReturn(expr->Signature()));
1593     }
1594 
1595     if (expr->UncheckedType() != nullptr) {
1596         ES2PANDA_ASSERT(expr->UncheckedType()->IsETSReferenceType());
1597         checker->ComputeApparentType(returnType);
1598     }
1599 
1600     if (returnType->IsTypeError()) {
1601         expr->SetTsType(returnType);
1602         return expr->TsType();
1603     }
1604 
1605     CheckVoidTypeExpression(checker, expr);
1606     CheckAbstractCall(checker, expr);
1607     return expr->TsType();
1608 }
1609 
Check(ir::ConditionalExpression * expr) const1610 checker::Type *ETSAnalyzer::Check(ir::ConditionalExpression *expr) const
1611 {
1612     if (expr->TsType() != nullptr) {
1613         return expr->TsType();
1614     }
1615 
1616     ETSChecker *const checker = GetETSChecker();
1617 
1618     SmartCastArray smartCasts = checker->Context().EnterTestExpression();
1619     checker->CheckTruthinessOfType(expr->Test());
1620     SmartCastTypes testedTypes = checker->Context().ExitTestExpression();
1621     if (testedTypes.has_value()) {
1622         for (auto [variable, consequentType, _] : *testedTypes) {
1623             checker->ApplySmartCast(variable, consequentType);
1624         }
1625     }
1626 
1627     auto *consequent = expr->Consequent();
1628     Type *consequentType = consequent->Check(checker);
1629 
1630     SmartCastArray consequentSmartCasts = checker->Context().CloneSmartCasts();
1631     checker->Context().RestoreSmartCasts(smartCasts);
1632 
1633     if (testedTypes.has_value()) {
1634         for (auto [variable, _, alternateType] : *testedTypes) {
1635             checker->ApplySmartCast(variable, alternateType);
1636         }
1637     }
1638 
1639     auto *alternate = expr->Alternate();
1640     Type *alternateType = alternate->Check(checker);
1641 
1642     // Here we need to combine types from consequent and alternate if blocks.
1643     checker->Context().CombineSmartCasts(consequentSmartCasts);
1644 
1645     if (checker->IsTypeIdenticalTo(consequentType, alternateType)) {
1646         expr->SetTsType(consequentType);
1647     } else {
1648         //  If possible and required update number literal type to the proper value (identical to left-side type)
1649         if (alternate->IsNumberLiteral() &&
1650             checker->AdjustNumberLiteralType(alternate->AsNumberLiteral(), alternateType, consequentType)) {
1651             expr->SetTsType(consequentType);
1652         } else if (consequent->IsNumberLiteral() &&
1653                    checker->AdjustNumberLiteralType(consequent->AsNumberLiteral(), consequentType, alternateType)) {
1654             expr->SetTsType(alternateType);
1655         } else {
1656             expr->SetTsType(checker->CreateETSUnionType({consequentType, alternateType}));
1657             if (expr->TsType()->IsETSReferenceType()) {
1658                 checker->MaybeBoxExpression(expr->Consequent());
1659                 checker->MaybeBoxExpression(expr->Alternate());
1660             }
1661         }
1662     }
1663 
1664     // Restore smart casts to initial state.
1665     checker->Context().RestoreSmartCasts(smartCasts);
1666 
1667     return expr->TsType();
1668 }
1669 
1670 // Convert method references to Arrow type if method is used as value
TransformTypeForMethodReference(ETSChecker * checker,ir::Expression * const use,Type * type)1671 static Type *TransformTypeForMethodReference(ETSChecker *checker, ir::Expression *const use, Type *type)
1672 {
1673     ES2PANDA_ASSERT(use->IsIdentifier() || use->IsMemberExpression());
1674     if (!type->IsETSMethodType()) {
1675         return type;
1676     }
1677     auto const getUseSite = [use]() {
1678         return use->IsIdentifier() ? use->Start() : use->AsMemberExpression()->Property()->Start();
1679     };
1680 
1681     ir::Expression *expr = use;
1682     while (expr->Parent()->IsMemberExpression() && expr->Parent()->AsMemberExpression()->Property() == expr) {
1683         expr = expr->Parent()->AsMemberExpression();
1684     }
1685     if (expr->Parent()->IsCallExpression() && expr->Parent()->AsCallExpression()->Callee() == expr) {
1686         return type;  // type is actually used as method
1687     }
1688 
1689     auto *const functionType = type->AsETSFunctionType();
1690     auto &signatures = functionType->CallSignatures();
1691 
1692     if (signatures.at(0)->HasSignatureFlag(SignatureFlags::PRIVATE)) {
1693         checker->LogError(diagnostic::PRIVATE_METHOD_AS_VALUE, getUseSite());
1694         return checker->GlobalTypeError();
1695     }
1696 
1697     auto it = signatures.begin();
1698     while (it != signatures.end()) {
1699         if ((*it)->HasSignatureFlag(SignatureFlags::ABSTRACT) &&
1700             !(*it)->Owner()->GetDeclNode()->IsTSInterfaceDeclaration()) {
1701             it = signatures.erase(it);
1702         } else {
1703             ++it;
1704         }
1705     }
1706 
1707     if (signatures.size() > 1U) {
1708         checker->LogError(diagnostic::OVERLOADED_METHOD_AS_VALUE, getUseSite());
1709         return checker->GlobalTypeError();
1710     }
1711     return type->AsETSFunctionType()->MethodToArrow(checker);
1712 }
1713 
Check(ir::Identifier * expr) const1714 checker::Type *ETSAnalyzer::Check(ir::Identifier *expr) const
1715 {
1716     if (expr->TsType() != nullptr) {
1717         return expr->TsType();
1718     }
1719 
1720     ETSChecker *checker = GetETSChecker();
1721 
1722     auto *identType = TransformTypeForMethodReference(checker, expr, checker->ResolveIdentifier(expr));
1723 
1724     if (expr->TsType() != nullptr && expr->TsType()->IsTypeError()) {
1725         return expr->TsType();
1726     }
1727     ES2PANDA_ASSERT(expr->Variable() != nullptr);
1728     if (expr->Parent() == nullptr || !expr->Parent()->IsAssignmentExpression() ||
1729         expr != expr->Parent()->AsAssignmentExpression()->Left()) {
1730         auto *const smartType = checker->Context().GetSmartCast(expr->Variable());
1731         if (smartType != nullptr) {
1732             identType = smartType;
1733         }
1734     }
1735 
1736     ES2PANDA_ASSERT(identType != nullptr);
1737     expr->SetTsType(identType);
1738     if (!identType->IsTypeError()) {
1739         checker->Context().CheckIdentifierSmartCastCondition(expr);
1740     }
1741     return expr->TsType();
1742 }
1743 
SearchReExportsType(ETSObjectType * baseType,ir::MemberExpression * expr,util::StringView & aliasName,ETSChecker * checker)1744 std::pair<checker::Type *, util::StringView> SearchReExportsType(ETSObjectType *baseType, ir::MemberExpression *expr,
1745                                                                  util::StringView &aliasName, ETSChecker *checker)
1746 {
1747     std::pair<ETSObjectType *, util::StringView> ret {};
1748 
1749     for (auto *const item : baseType->ReExports()) {
1750         auto name = item->GetReExportAliasValue(aliasName);
1751         if (name == aliasName && item->IsReExportHaveAliasValue(name)) {
1752             continue;
1753         }
1754 
1755         if (item->GetProperty(name, PropertySearchFlags::SEARCH_ALL) != nullptr) {
1756             if (ret.first != nullptr) {
1757                 checker->LogError(diagnostic::AMBIGUOUS_REFERENCE, {aliasName}, expr->Start());
1758                 expr->SetTsType(checker->GlobalTypeError());
1759                 return ret;
1760             }
1761             ret = {item, name};
1762         }
1763 
1764         if (auto reExportType = SearchReExportsType(item, expr, name, checker); reExportType.first != nullptr) {
1765             return reExportType;
1766         }
1767     }
1768 
1769     return ret;
1770 }
1771 
TypeErrorOnMissingProperty(ir::MemberExpression * expr,checker::Type * baseType,checker::ETSChecker * checker)1772 static void TypeErrorOnMissingProperty(ir::MemberExpression *expr, checker::Type *baseType,
1773                                        checker::ETSChecker *checker)
1774 {
1775     std::ignore = checker->TypeError(expr, diagnostic::PROPERTY_NONEXISTENT,
1776                                      {expr->Property()->AsIdentifier()->Name(), baseType}, expr->Object()->Start());
1777 }
1778 
ResolveMemberExpressionByBaseType(ETSChecker * checker,checker::Type * baseType,ir::MemberExpression * expr) const1779 checker::Type *ETSAnalyzer::ResolveMemberExpressionByBaseType(ETSChecker *checker, checker::Type *baseType,
1780                                                               ir::MemberExpression *expr) const
1781 {
1782     if (baseType->IsTypeError()) {
1783         return checker->InvalidateType(expr);
1784     }
1785 
1786     if (baseType->IsETSArrayType()) {
1787         if (expr->Property()->AsIdentifier()->Name().Is("length")) {
1788             return expr->AdjustType(checker, checker->GlobalIntType());
1789         }
1790 
1791         return expr->SetAndAdjustType(checker, checker->GlobalETSObjectType());
1792     }
1793 
1794     if (baseType->IsETSTupleType()) {
1795         return expr->SetAndAdjustType(checker, checker->GlobalETSObjectType());
1796     }
1797 
1798     if (baseType->IsETSFunctionType()) {
1799         return expr->SetAndAdjustType(checker, checker->GlobalBuiltinFunctionType());
1800     }
1801 
1802     if (baseType->IsETSObjectType()) {
1803         checker->ETSObjectTypeDeclNode(checker, baseType->AsETSObjectType());
1804         return expr->SetTsType(TransformTypeForMethodReference(
1805             checker, expr, expr->SetAndAdjustType(checker, baseType->AsETSObjectType())));
1806     }
1807 
1808     if (baseType->IsETSUnionType()) {
1809         return expr->AdjustType(checker, expr->CheckUnionMember(checker, baseType));
1810     }
1811 
1812     // NOTE(mshimenkov): temporary workaround to deliver 'primitives refactoring' patch
1813     // To be removed after complete refactoring
1814     if (baseType->IsETSPrimitiveType()) {
1815         static std::array<std::string_view, 7U> castMethods {{
1816             "toChar",
1817             "toByte",
1818             "toShort",
1819             "toInt",
1820             "toLong",
1821             "toFloat",
1822             "toDouble",
1823         }};
1824         auto method = expr->Property()->AsIdentifier()->Name().Utf8();
1825         auto res = std::find(castMethods.begin(), castMethods.end(), method);
1826         if (res != castMethods.end()) {
1827             auto type = checker->MaybeBoxType(baseType);
1828             expr->SetAstNodeFlags(ir::AstNodeFlags::TMP_CONVERT_PRIMITIVE_CAST_METHOD_CALL);
1829             checker->ETSObjectTypeDeclNode(checker, type->AsETSObjectType());
1830             return expr->SetTsType(TransformTypeForMethodReference(
1831                 checker, expr, expr->SetAndAdjustType(checker, type->AsETSObjectType())));
1832         }
1833     }
1834 
1835     TypeErrorOnMissingProperty(expr, baseType, checker);
1836     return expr->TsType();
1837 }
1838 
Check(ir::MemberExpression * expr) const1839 checker::Type *ETSAnalyzer::Check(ir::MemberExpression *expr) const
1840 {
1841     if (expr->TsType() != nullptr) {
1842         return expr->TsType();
1843     }
1844     ES2PANDA_ASSERT(!expr->IsOptional());
1845     ETSChecker *checker = GetETSChecker();
1846     auto *baseType = checker->GetNonConstantType(checker->GetApparentType(expr->Object()->Check(checker)));
1847     //  Note: don't use possible smart cast to null-like types.
1848     //        Such situation should be correctly resolved in the subsequent lowering.
1849     ES2PANDA_ASSERT(baseType != nullptr);
1850     if (baseType->DefinitelyETSNullish() && expr->Object()->IsIdentifier()) {
1851         baseType = expr->Object()->AsIdentifier()->Variable()->TsType();
1852     }
1853 
1854     if (baseType->IsETSObjectType() && !baseType->AsETSObjectType()->ReExports().empty() &&
1855         baseType->AsETSObjectType()->GetProperty(expr->Property()->AsIdentifier()->Name(),
1856                                                  PropertySearchFlags::SEARCH_ALL) == nullptr) {
1857         if (auto reExportType = SearchReExportsType(baseType->AsETSObjectType(), expr,
1858                                                     expr->Property()->AsIdentifier()->Name(), checker);
1859             reExportType.first != nullptr) {
1860             baseType = reExportType.first;
1861             expr->object_->AsIdentifier()->SetTsType(baseType);
1862             expr->property_->AsIdentifier()->SetName(reExportType.second);
1863         }
1864     }
1865     if (!checker->CheckNonNullish(expr->Object())) {
1866         auto *invalidType = checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK)
1867                                 ? checker->GlobalETSUnionUndefinedNull()
1868                                 : checker->InvalidateType(expr);
1869         return invalidType;
1870     }
1871 
1872     if (expr->IsComputed()) {
1873         return expr->AdjustType(checker, expr->CheckComputed(checker, baseType));
1874     }
1875 
1876     return ResolveMemberExpressionByBaseType(checker, baseType, expr);
1877 }
1878 
PreferredType(ir::ObjectExpression * expr) const1879 checker::Type *ETSAnalyzer::PreferredType(ir::ObjectExpression *expr) const
1880 {
1881     return expr->preferredType_;
1882 }
1883 
CheckDynamic(ir::ObjectExpression * expr) const1884 checker::Type *ETSAnalyzer::CheckDynamic(ir::ObjectExpression *expr) const
1885 {
1886     ETSChecker *checker = GetETSChecker();
1887     for (ir::Expression *propExpr : expr->Properties()) {
1888         ES2PANDA_ASSERT(propExpr->IsProperty());
1889         ir::Property *prop = propExpr->AsProperty();
1890         ir::Expression *value = prop->Value();
1891         value->Check(checker);
1892         ES2PANDA_ASSERT(value->TsType());
1893     }
1894 
1895     expr->SetTsType(expr->PreferredType());
1896     return expr->PreferredType();
1897 }
1898 
ValidatePreferredType(ETSChecker * checker,ir::ObjectExpression * expr)1899 static bool ValidatePreferredType(ETSChecker *checker, ir::ObjectExpression *expr)
1900 {
1901     auto preferredType = expr->PreferredType();
1902     if (preferredType == nullptr) {
1903         checker->LogError(diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start());
1904         return false;
1905     }
1906 
1907     if (preferredType->IsTypeError()) {
1908         //  Don't need to duplicate error message for a single error.
1909         return false;
1910     }
1911 
1912     if (!preferredType->IsETSObjectType()) {
1913         checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {preferredType}, expr->Start());
1914         return false;
1915     }
1916 
1917     return true;
1918 }
1919 
SetTypeforRecordProperties(const ir::ObjectExpression * expr,checker::ETSObjectType * objType,ETSChecker * checker)1920 static void SetTypeforRecordProperties(const ir::ObjectExpression *expr, checker::ETSObjectType *objType,
1921                                        ETSChecker *checker)
1922 {
1923     const auto &recordProperties = expr->Properties();
1924     auto typeArguments = objType->TypeArguments();
1925     auto *const valueType = typeArguments[1];  //  Record<K, V>  type arguments
1926 
1927     for (auto *const recordProperty : recordProperties) {
1928         ir::Expression *recordPropertyExpr = nullptr;
1929         if (recordProperty->IsProperty()) {
1930             recordPropertyExpr = recordProperty->AsProperty()->Value();
1931         } else if (recordProperty->IsSpreadElement()) {
1932             recordPropertyExpr = recordProperty->AsSpreadElement()->Argument();
1933         } else {
1934             ES2PANDA_UNREACHABLE();
1935         }
1936 
1937         ETSChecker::SetPreferredTypeIfPossible(recordPropertyExpr, valueType);
1938         recordPropertyExpr->Check(checker);
1939     }
1940 }
1941 
1942 // Helper to check for parameterless constructor
HasParameterlessConstructor(checker::ETSObjectType * objType,ETSChecker * checker,const lexer::SourcePosition & pos)1943 static bool HasParameterlessConstructor(checker::ETSObjectType *objType, ETSChecker *checker,
1944                                         const lexer::SourcePosition &pos)
1945 {
1946     for (checker::Signature *sig : objType->ConstructSignatures()) {
1947         if (sig->Params().empty()) {
1948             checker->ValidateSignatureAccessibility(objType, sig, pos);
1949             return true;
1950         }
1951     }
1952     return false;
1953 }
1954 
1955 // Helper to resolve property name from key expression
GetPropertyNameFromKey(ir::Expression * key)1956 static std::optional<util::StringView> GetPropertyNameFromKey(ir::Expression *key)
1957 {
1958     if (key->IsStringLiteral()) {
1959         return key->AsStringLiteral()->Str();
1960     }
1961     if (key->IsIdentifier()) {
1962         return key->AsIdentifier()->Name();
1963     }
1964     return std::nullopt;  // Indicates invalid key type
1965 }
1966 
1967 // Helper to determine property search flags based on object type
DetermineSearchFlagsForLiteral(checker::ETSObjectType * potentialObjType)1968 static checker::PropertySearchFlags DetermineSearchFlagsForLiteral(checker::ETSObjectType *potentialObjType)
1969 {
1970     if (potentialObjType->HasObjectFlag(checker::ETSObjectFlags::INTERFACE)) {
1971         return checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD |
1972                checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
1973                checker::PropertySearchFlags::SEARCH_INSTANCE_DECL | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
1974     }
1975     return checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | checker::PropertySearchFlags::SEARCH_IN_BASE |
1976            checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD;
1977 }
1978 
CheckSinglePropertyCompatibility(ir::Expression * propExpr,checker::ETSObjectType * potentialObjType)1979 static bool CheckSinglePropertyCompatibility(ir::Expression *propExpr, checker::ETSObjectType *potentialObjType)
1980 {
1981     if (!propExpr->IsProperty()) {
1982         return false;  // Not a key-value property
1983     }
1984     ir::Expression *key = propExpr->AsProperty()->Key();
1985 
1986     std::optional<util::StringView> optPname = GetPropertyNameFromKey(key);
1987     if (!optPname.has_value()) {
1988         return false;  // Invalid key type in literal
1989     }
1990     util::StringView pname = optPname.value();
1991 
1992     checker::PropertySearchFlags searchFlags = DetermineSearchFlagsForLiteral(potentialObjType);
1993 
1994     return potentialObjType->GetProperty(pname, searchFlags) != nullptr;
1995 }
1996 
CheckObjectLiteralCompatibility(ir::ObjectExpression * expr,checker::ETSObjectType * potentialObjType)1997 static bool CheckObjectLiteralCompatibility(ir::ObjectExpression *expr, checker::ETSObjectType *potentialObjType)
1998 {
1999     for (ir::Expression *propExpr : expr->Properties()) {
2000         if (!CheckSinglePropertyCompatibility(propExpr, potentialObjType)) {
2001             return false;
2002         }
2003     }
2004     return true;  // All properties found
2005 }
2006 
2007 // Helper to check if a property type indicates optionality (union with undefined)
IsPropertyTypeOptional(checker::Type * propertyType)2008 static bool IsPropertyTypeOptional(checker::Type *propertyType)
2009 {
2010     if (!propertyType->IsETSUnionType()) {
2011         return false;
2012     }
2013 
2014     auto *unionType = propertyType->AsETSUnionType();
2015     for (auto *constituentType : unionType->ConstituentTypes()) {
2016         if (constituentType->IsETSUndefinedType()) {
2017             return true;
2018         }
2019     }
2020     return false;
2021 }
2022 
2023 // Helper to check if a property has a default value in its declaration
HasPropertyDefaultValue(varbinder::LocalVariable * property)2024 static bool HasPropertyDefaultValue(varbinder::LocalVariable *property)
2025 {
2026     auto *decl = property->Declaration();
2027     if (decl == nullptr || decl->Node() == nullptr || !decl->Node()->IsClassProperty()) {
2028         return false;
2029     }
2030 
2031     auto *classProp = decl->Node()->AsClassProperty();
2032     return classProp->Value() != nullptr;
2033 }
2034 
2035 // Helper to check if a property is optional (flag or declaration)
IsPropertyOptional(varbinder::LocalVariable * property,checker::Type * propertyType)2036 static bool IsPropertyOptional(varbinder::LocalVariable *property, checker::Type *propertyType)
2037 {
2038     // Check if property is marked as optional
2039     if (property->HasFlag(varbinder::VariableFlags::OPTIONAL)) {
2040         return true;
2041     }
2042 
2043     // Check if property type includes undefined (indicating optional)
2044     if (IsPropertyTypeOptional(propertyType)) {
2045         return true;
2046     }
2047 
2048     // Check if declaration has optional modifier
2049     auto *decl = property->Declaration();
2050     if (decl != nullptr && decl->Node() != nullptr && decl->Node()->IsClassProperty()) {
2051         auto *classProp = decl->Node()->AsClassProperty();
2052         if (classProp->IsOptionalDeclaration()) {
2053             return true;
2054         }
2055     }
2056 
2057     return false;
2058 }
2059 
2060 // Helper to check if a method property is only getters/setters
IsMethodOnlyAccessors(checker::Type * propertyType)2061 static bool IsMethodOnlyAccessors(checker::Type *propertyType)
2062 {
2063     if (!propertyType->IsETSMethodType()) {
2064         return false;
2065     }
2066 
2067     auto methodType = propertyType->AsETSFunctionType();
2068     for (auto *sig : methodType->CallSignatures()) {
2069         if (!sig->HasSignatureFlag(checker::SignatureFlags::GETTER) &&
2070             !sig->HasSignatureFlag(checker::SignatureFlags::SETTER)) {
2071             // Regular method found
2072             return false;
2073         }
2074     }
2075     return true;
2076 }
2077 
2078 // Helper to check if an interface property is compatible with object literal property
IsInterfacePropertyCompatible(ir::Expression * propExpr,checker::ETSObjectType * interfaceType,ETSChecker * checker)2079 static bool IsInterfacePropertyCompatible(ir::Expression *propExpr, checker::ETSObjectType *interfaceType,
2080                                           ETSChecker *checker)
2081 {
2082     if (!propExpr->IsProperty()) {
2083         return false;
2084     }
2085 
2086     ir::Expression *key = propExpr->AsProperty()->Key();
2087     std::optional<util::StringView> optPname = GetPropertyNameFromKey(key);
2088     if (!optPname.has_value()) {
2089         return false;
2090     }
2091     util::StringView pname = optPname.value();
2092 
2093     // Check if property exists in interface
2094     varbinder::LocalVariable *property =
2095         interfaceType->GetProperty(pname, checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD |
2096                                               checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
2097                                               checker::PropertySearchFlags::SEARCH_INSTANCE_DECL |
2098                                               checker::PropertySearchFlags::SEARCH_IN_INTERFACES);
2099 
2100     if (property == nullptr) {
2101         return false;
2102     }
2103 
2104     auto *propertyType = checker->GetTypeOfVariable(property);
2105 
2106     // If it's a method type, it should only be getter/setter, not regular methods
2107     if (propertyType->IsETSMethodType()) {
2108         return IsMethodOnlyAccessors(propertyType);
2109     }
2110 
2111     return true;
2112 }
2113 
2114 // Helper to check if all required interface properties are satisfied
AreAllRequiredInterfacePropertiesSatisfied(ir::ObjectExpression * expr,checker::ETSObjectType * interfaceType,ETSChecker * checker)2115 static bool AreAllRequiredInterfacePropertiesSatisfied(ir::ObjectExpression *expr,
2116                                                        checker::ETSObjectType *interfaceType, ETSChecker *checker)
2117 {
2118     // Get all properties of the interface using GetAllProperties
2119     auto allProperties = interfaceType->GetAllProperties();
2120 
2121     // Create a set of property names provided in the object literal
2122     std::unordered_set<std::string> literalProperties;
2123     for (ir::Expression *propExpr : expr->Properties()) {
2124         if (propExpr->IsProperty()) {
2125             ir::Expression *key = propExpr->AsProperty()->Key();
2126             if (auto optPname = GetPropertyNameFromKey(key); optPname.has_value()) {
2127                 literalProperties.insert(std::string(optPname.value().Utf8()));
2128             }
2129         }
2130     }
2131 
2132     // Check if all literal properties exist in this interface
2133     for (const auto &litPropName : literalProperties) {
2134         bool found = false;
2135         for (auto *property : allProperties) {
2136             if (property->Name().Utf8() == litPropName) {
2137                 found = true;
2138                 break;
2139             }
2140         }
2141         if (!found) {
2142             return false;
2143         }
2144     }
2145 
2146     // Check that all required interface properties are satisfied
2147     for (auto *property : allProperties) {
2148         std::string propName(property->Name().Utf8());
2149         auto *propertyType = checker->GetTypeOfVariable(property);
2150 
2151         // Skip method types that aren't getters/setters (they make interface incompatible anyway)
2152         if (propertyType->IsETSMethodType()) {
2153             if (!IsMethodOnlyAccessors(propertyType)) {
2154                 // Regular methods not allowed
2155                 return false;
2156             }
2157         }
2158         // Check if this property is provided in the literal
2159         bool isInLiteral = literalProperties.find(propName) != literalProperties.end();
2160         if (!isInLiteral) {
2161             // Property not in literal - check if it's optional or has default value
2162             bool isOptional = IsPropertyOptional(property, propertyType);
2163             bool hasDefaultValue = HasPropertyDefaultValue(property);
2164             if (!isOptional && !hasDefaultValue) {
2165                 return false;
2166             }
2167         }
2168     }
2169 
2170     return true;  // All required properties are satisfied
2171 }
2172 
IsObjectTypeCompatibleWithLiteral(ETSChecker * checker,ir::ObjectExpression * expr,checker::ETSObjectType * potentialObjType)2173 static bool IsObjectTypeCompatibleWithLiteral(ETSChecker *checker, ir::ObjectExpression *expr,
2174                                               checker::ETSObjectType *potentialObjType)
2175 {
2176     // Split record/map types, class types and interfaces as requested by reviewer
2177 
2178     checker::ETSObjectType *originalBaseType = potentialObjType->GetOriginalBaseType();
2179     checker::GlobalTypesHolder *globalTypes = checker->GetGlobalTypesHolder();
2180 
2181     // Handle Record/Map types
2182     if (checker->IsTypeIdenticalTo(originalBaseType, globalTypes->GlobalMapBuiltinType()) ||
2183         checker->IsTypeIdenticalTo(originalBaseType, globalTypes->GlobalRecordBuiltinType())) {
2184         // Maps and Records are always compatible with object literals
2185         return true;
2186     }
2187 
2188     // Handle interface types
2189     if (potentialObjType->HasObjectFlag(checker::ETSObjectFlags::INTERFACE)) {
2190         // For interface types, check that all literal properties exist in the interface
2191         // and that interface has no regular methods (only getters/setters allowed)
2192 
2193         // For non-empty literals, check that all literal properties exist in interface
2194         // and all required interface properties are satisfied
2195         for (ir::Expression *propExpr : expr->Properties()) {
2196             if (!IsInterfacePropertyCompatible(propExpr, potentialObjType, checker)) {
2197                 return false;
2198             }
2199         }
2200 
2201         // Check all required interface properties are satisfied
2202         return AreAllRequiredInterfacePropertiesSatisfied(expr, potentialObjType, checker);
2203     }
2204 
2205     // Handle class types
2206     // For class types, you need to check:
2207     // - that there is a parameterless constructor, and
2208     // - that all fields/properties set in the object literal are present in the class
2209 
2210     if (!HasParameterlessConstructor(potentialObjType, checker, expr->Start())) {
2211         return false;
2212     }
2213 
2214     // Check that all properties in literal exist in class
2215     return CheckObjectLiteralCompatibility(expr, potentialObjType);
2216 }
2217 
ResolveUnionObjectTypeForObjectLiteral(ETSChecker * checker,ir::ObjectExpression * expr,checker::ETSUnionType * unionType)2218 checker::ETSObjectType *ResolveUnionObjectTypeForObjectLiteral(ETSChecker *checker, ir::ObjectExpression *expr,
2219                                                                checker::ETSUnionType *unionType)
2220 {
2221     std::vector<checker::ETSObjectType *> candidateObjectTypes;
2222     // Phase 1: Gather all ETSObjectTypes from the union
2223     for (auto *constituentType : unionType->ConstituentTypes()) {
2224         if (constituentType->IsETSObjectType()) {
2225             candidateObjectTypes.push_back(constituentType->AsETSObjectType());
2226         }
2227     }
2228 
2229     if (candidateObjectTypes.empty()) {
2230         // No ETSObjectTypes in the union at all
2231         checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, expr->Start());
2232         return nullptr;
2233     }
2234 
2235     std::vector<checker::ETSObjectType *> matchingObjectTypes;
2236     // Phase 2: Filter candidates using the helper function
2237     for (auto *potentialObjType : candidateObjectTypes) {
2238         if (IsObjectTypeCompatibleWithLiteral(checker, expr, potentialObjType)) {
2239             matchingObjectTypes.push_back(potentialObjType);
2240         }
2241     }
2242 
2243     // Phase 3: Decide based on number of matches
2244     if (matchingObjectTypes.size() == 1) {
2245         return matchingObjectTypes.front();
2246     }
2247     if (matchingObjectTypes.empty()) {
2248         // No candidate ETSObjectType from the union matched all properties
2249         checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, expr->Start());
2250         return nullptr;
2251     }
2252     // Ambiguous
2253     checker->LogError(diagnostic::AMBIGUOUS_REFERENCE, {expr->PreferredType()->ToString()}, expr->Start());
2254     return nullptr;
2255 }
2256 
ResolveObjectTypeFromPreferredType(ETSChecker * checker,ir::ObjectExpression * expr)2257 static checker::ETSObjectType *ResolveObjectTypeFromPreferredType(ETSChecker *checker, ir::ObjectExpression *expr)
2258 {
2259     // Assume not null, checked by caller in Check()
2260     checker::Type *preferredType = expr->PreferredType();
2261 
2262     if (preferredType->IsETSAsyncFuncReturnType()) {
2263         preferredType = preferredType->AsETSAsyncFuncReturnType()->GetPromiseTypeArg();
2264     }
2265 
2266     if (preferredType->IsETSUnionType()) {
2267         return ResolveUnionObjectTypeForObjectLiteral(checker, expr, preferredType->AsETSUnionType());
2268     }
2269 
2270     if (preferredType->IsETSObjectType()) {
2271         return preferredType->AsETSObjectType();
2272     }
2273 
2274     return nullptr;
2275 }
2276 
2277 // Helper to handle interface type objects
HandleInterfaceType(ETSChecker * checker,ir::ObjectExpression * expr,checker::ETSObjectType * objType)2278 static checker::Type *HandleInterfaceType(ETSChecker *checker, ir::ObjectExpression *expr,
2279                                           checker::ETSObjectType *objType)
2280 {
2281     auto *analyzer = static_cast<checker::ETSAnalyzer *>(checker->GetAnalyzer());
2282     analyzer->CheckObjectExprProps(
2283         expr, objType,
2284         checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD | checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
2285             checker::PropertySearchFlags::SEARCH_INSTANCE_DECL | checker::PropertySearchFlags::SEARCH_IN_INTERFACES);
2286     expr->SetTsType(objType);
2287     return objType;
2288 }
2289 
2290 // Helper to handle Record/Map types
HandleRecordOrMapType(ETSChecker * checker,ir::ObjectExpression * expr,checker::ETSObjectType * objType)2291 static checker::Type *HandleRecordOrMapType(ETSChecker *checker, ir::ObjectExpression *expr,
2292                                             checker::ETSObjectType *objType)
2293 {
2294     expr->SetTsType(objType);
2295     SetTypeforRecordProperties(expr, objType, checker);
2296     return objType;
2297 }
2298 
Check(ir::ObjectExpression * expr) const2299 checker::Type *ETSAnalyzer::Check(ir::ObjectExpression *expr) const
2300 {
2301     ETSChecker *checker = GetETSChecker();
2302     if (expr->TsType() != nullptr) {
2303         return expr->TsType();
2304     }
2305 
2306     if (expr->PreferredType() == nullptr) {
2307         checker->LogError(diagnostic::CLASS_COMPOSITE_UNKNOWN_TYPE, {}, expr->Start());
2308         expr->SetTsType(checker->GlobalTypeError());
2309         return expr->TsType();
2310     }
2311 
2312     if (!expr->PreferredType()->IsETSUnionType() && !expr->PreferredType()->IsETSDynamicType() &&
2313         !ValidatePreferredType(checker, expr)) {
2314         expr->SetTsType(checker->GlobalTypeError());
2315         return expr->TsType();
2316     }
2317 
2318     if (expr->PreferredType()->IsETSDynamicType() && !expr->PreferredType()->AsETSDynamicType()->HasDecl()) {
2319         return CheckDynamic(expr);
2320     }
2321 
2322     checker::ETSObjectType *objType = ResolveObjectTypeFromPreferredType(checker, expr);
2323 
2324     if (objType == nullptr) {
2325         if (!expr->PreferredType()->IsETSUnionType()) {
2326             checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_TARGET, {expr->PreferredType()}, expr->Start());
2327         }
2328         expr->SetTsType(checker->GlobalTypeError());
2329         return expr->TsType();
2330     }
2331 
2332     if (objType->HasObjectFlag(checker::ETSObjectFlags::INTERFACE)) {
2333         return HandleInterfaceType(checker, expr, objType);
2334     }
2335 
2336     checker::ETSObjectType *originalBaseObjType = objType->GetOriginalBaseType();
2337     checker::GlobalTypesHolder *globalTypes = checker->GetGlobalTypesHolder();
2338     if (checker->IsTypeIdenticalTo(originalBaseObjType, globalTypes->GlobalMapBuiltinType()) ||
2339         checker->IsTypeIdenticalTo(originalBaseObjType, globalTypes->GlobalRecordBuiltinType())) {
2340         return HandleRecordOrMapType(checker, expr, objType);
2341     }
2342 
2343     // If we reach here, objType is a class. It must have a parameterless constructor
2344     if (!HasParameterlessConstructor(objType, checker, expr->Start())) {
2345         expr->SetTsType(checker->TypeError(expr, diagnostic::NO_PARAMLESS_CTOR, {objType->Name()}, expr->Start()));
2346         return expr->TsType();
2347     }
2348 
2349     CheckObjectExprProps(expr, objType,
2350                          checker::PropertySearchFlags::SEARCH_INSTANCE_FIELD |
2351                              checker::PropertySearchFlags::SEARCH_IN_BASE |
2352                              checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD);
2353 
2354     expr->SetTsType(objType);
2355     return objType;
2356 }
2357 
CheckObjectExprProps(const ir::ObjectExpression * expr,checker::ETSObjectType * objectTypeForProperties,checker::PropertySearchFlags searchFlags) const2358 void ETSAnalyzer::CheckObjectExprProps(const ir::ObjectExpression *expr,
2359                                        checker::ETSObjectType *objectTypeForProperties,
2360                                        checker::PropertySearchFlags searchFlags) const
2361 {
2362     ETSChecker *checker = GetETSChecker();
2363     checker::ETSObjectType *objType = objectTypeForProperties;
2364 
2365     for (ir::Expression *propExpr : expr->Properties()) {
2366         if (!propExpr->IsProperty()) {
2367             checker->LogError(diagnostic::OBJECT_LITERAL_NOT_KV, {}, expr->Start());
2368             return;
2369         }
2370         ir::Expression *key = propExpr->AsProperty()->Key();
2371         ir::Expression *value = propExpr->AsProperty()->Value();
2372 
2373         util::StringView pname;
2374         if (key->IsStringLiteral()) {
2375             pname = key->AsStringLiteral()->Str();
2376         } else if (key->IsIdentifier()) {
2377             pname = key->AsIdentifier()->Name();
2378         } else {
2379             checker->LogError(diagnostic::CLASS_COMPOSITE_INVALID_KEY, {}, expr->Start());
2380             return;
2381         }
2382         varbinder::LocalVariable *lv = objType->GetProperty(pname, searchFlags);
2383         if (lv == nullptr) {
2384             checker->LogError(diagnostic::UNDEFINED_PROPERTY, {objType->Name(), pname}, propExpr->Start());
2385             return;
2386         }
2387         checker->ValidatePropertyAccess(lv, objType, propExpr->Start());
2388 
2389         if (key->IsIdentifier()) {
2390             key->AsIdentifier()->SetVariable(lv);
2391         }
2392 
2393         auto *propType = checker->GetTypeOfVariable(lv);
2394         if (propType->IsETSMethodType()) {
2395             checker->LogError(diagnostic::OBJECT_LITERAL_METHOD_KEY, {pname}, propExpr->Start());
2396             return;
2397         }
2398 
2399         ETSChecker::SetPreferredTypeIfPossible(value, propType);
2400         propExpr->SetTsType(propType);
2401         key->SetTsType(propType);
2402         value->SetTsType(value->Check(checker));
2403 
2404         checker::AssignmentContext(checker->Relation(), value, value->TsType(), propType, value->Start(),
2405                                    {{diagnostic::PROP_INCOMPAT, {value->TsType(), propType, pname}}});
2406     }
2407 
2408     if (objType->HasObjectFlag(ETSObjectFlags::REQUIRED)) {
2409         checker->ValidateObjectLiteralForRequiredType(objType, expr);
2410     }
2411 }
2412 
Check(ir::OpaqueTypeNode * expr) const2413 checker::Type *ETSAnalyzer::Check(ir::OpaqueTypeNode *expr) const
2414 {
2415     return expr->TsType();
2416 }
2417 
Check(ir::BrokenTypeNode * expr) const2418 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::BrokenTypeNode *expr) const
2419 {
2420     return GetETSChecker()->GlobalTypeError();
2421 }
2422 
Check(ir::SequenceExpression * expr) const2423 checker::Type *ETSAnalyzer::Check(ir::SequenceExpression *expr) const
2424 {
2425     ETSChecker *checker = GetETSChecker();
2426     if (expr->TsType() != nullptr) {
2427         return expr->TsType();
2428     }
2429 
2430     for (auto *it : expr->Sequence()) {
2431         it->Check(checker);
2432     }
2433     ES2PANDA_ASSERT(!expr->Sequence().empty());
2434     expr->SetTsType(expr->Sequence().back()->TsType());
2435     return expr->TsType();
2436 }
2437 
Check(ir::SuperExpression * expr) const2438 checker::Type *ETSAnalyzer::Check(ir::SuperExpression *expr) const
2439 {
2440     ETSChecker *checker = GetETSChecker();
2441     if (expr->TsType() != nullptr) {
2442         return expr->TsType();
2443     }
2444 
2445     expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass()->SuperType(), "super"));
2446     return expr->TsType();
2447 }
2448 
Check(ir::TemplateLiteral * expr) const2449 checker::Type *ETSAnalyzer::Check(ir::TemplateLiteral *expr) const
2450 {
2451     ETSChecker *checker = GetETSChecker();
2452 
2453     for (auto *it : expr->Expressions()) {
2454         it->Check(checker);
2455     }
2456 
2457     if (expr->TsType() != nullptr) {
2458         return expr->TsType();
2459     }
2460 
2461     if (expr->Quasis().size() != expr->Expressions().size() + 1U) {
2462         checker->LogError(diagnostic::TEMPLATE_COUNT_MISMATCH, {}, expr->Start());
2463         expr->SetTsType(checker->GlobalTypeError());
2464         return expr->TsType();
2465     }
2466 
2467     for (auto *it : expr->Quasis()) {
2468         it->Check(checker);
2469     }
2470 
2471     expr->SetTsType(checker->CreateETSStringLiteralType(expr->GetMultilineString()));
2472     return expr->TsType();
2473 }
2474 
Check(ir::ThisExpression * expr) const2475 checker::Type *ETSAnalyzer::Check(ir::ThisExpression *expr) const
2476 {
2477     ETSChecker *checker = GetETSChecker();
2478     if (expr->TsType() != nullptr) {
2479         return expr->TsType();
2480     }
2481 
2482     /*
2483     example code:
2484     ```
2485         class A {
2486             prop
2487         }
2488         function A.method() {
2489             let a = () => {
2490                 console.println(this.prop)
2491             }
2492         }
2493         is identical to
2494         function method(this: A) {
2495             let a = () => {
2496                 console.println(this.prop)
2497             }
2498         }
2499     ```
2500     here when "this" is used inside an extension function, we need to bind "this" to the first
2501     parameter(MANDATORY_PARAM_THIS), and capture the parameter's variable other than containing class's variable
2502     */
2503     auto *variable = checker->AsETSChecker()->Scope()->Find(varbinder::VarBinder::MANDATORY_PARAM_THIS).variable;
2504     if (checker->HasStatus(checker::CheckerStatus::IN_EXTENSION_METHOD)) {
2505         ES2PANDA_ASSERT(variable != nullptr);
2506         expr->SetTsType(variable->TsType());
2507     } else {
2508         expr->SetTsType(checker->CheckThisOrSuperAccess(expr, checker->Context().ContainingClass(), "this"));
2509     }
2510 
2511     return expr->TsType();
2512 }
2513 
2514 // Get string literal type as potential typeof result type with respect to spec p.7.17
GetTypeOfStringType(checker::Type * argType,ETSChecker * checker)2515 static checker::Type *GetTypeOfStringType(checker::Type *argType, ETSChecker *checker)
2516 {
2517     if (auto unboxed = checker->MaybeUnboxType(argType); unboxed->IsETSPrimitiveType()) {
2518         switch (checker->TypeKind(unboxed)) {
2519             case TypeFlag::ETS_BOOLEAN:
2520                 return checker->CreateETSStringLiteralType("boolean");
2521             case TypeFlag::BYTE:
2522             case TypeFlag::CHAR:
2523             case TypeFlag::SHORT:
2524             case TypeFlag::INT:
2525             case TypeFlag::LONG:
2526             case TypeFlag::FLOAT:
2527             case TypeFlag::DOUBLE:
2528                 return checker->CreateETSStringLiteralType("number");
2529             default:
2530                 ES2PANDA_UNREACHABLE();
2531         }
2532     }
2533     if (argType->IsETSUndefinedType()) {
2534         return checker->CreateETSStringLiteralType("undefined");
2535     }
2536     if (argType->IsETSArrayType() || argType->IsETSNullType() || argType->IsETSResizableArrayType()) {
2537         return checker->CreateETSStringLiteralType("object");
2538     }
2539     if (argType->IsETSStringType()) {
2540         return checker->CreateETSStringLiteralType("string");
2541     }
2542     if (argType->IsETSBigIntType()) {
2543         return checker->CreateETSStringLiteralType("bigint");
2544     }
2545     if (argType->IsETSFunctionType()) {
2546         return checker->CreateETSStringLiteralType("function");
2547     }
2548     if (argType->IsETSIntEnumType()) {
2549         return checker->CreateETSStringLiteralType("number");
2550     }
2551     if (argType->IsETSStringEnumType()) {
2552         return checker->CreateETSStringLiteralType("string");
2553     }
2554     return checker->GlobalBuiltinETSStringType();
2555 }
2556 
ComputeTypeOfType(ETSChecker * checker,checker::Type * argType)2557 static checker::Type *ComputeTypeOfType(ETSChecker *checker, checker::Type *argType)
2558 {
2559     checker::Type *ret = nullptr;
2560     ArenaVector<checker::Type *> types(checker->ProgramAllocator()->Adapter());
2561     if (argType->IsETSUnionType()) {
2562         for (auto *it : argType->AsETSUnionType()->ConstituentTypes()) {
2563             checker::Type *elType = ComputeTypeOfType(checker, it);
2564             types.push_back(elType);
2565         }
2566         ret = checker->CreateETSUnionType(std::move(types));
2567     } else {
2568         ret = GetTypeOfStringType(argType, checker);
2569     }
2570     return ret;
2571 }
2572 
Check(ir::TypeofExpression * expr) const2573 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::TypeofExpression *expr) const
2574 {
2575     ETSChecker *checker = GetETSChecker();
2576     if (expr->TsType() != nullptr) {
2577         return expr->TsType();
2578     }
2579 
2580     expr->Argument()->Check(checker);
2581     expr->SetTsType(ComputeTypeOfType(checker, expr->Argument()->TsType()));
2582     return expr->TsType();
2583 }
2584 
Check(ir::UnaryExpression * expr) const2585 checker::Type *ETSAnalyzer::Check(ir::UnaryExpression *expr) const
2586 {
2587     ETSChecker *checker = GetETSChecker();
2588 
2589     if (expr->TsType() != nullptr) {
2590         return expr->TsType();
2591     }
2592 
2593     auto argType = expr->argument_->Check(checker);
2594     const auto isCondExpr = expr->OperatorType() == lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK;
2595     checker::Type *operandType = checker->ApplyUnaryOperatorPromotion(argType, true, true, isCondExpr);
2596     auto unboxedOperandType =
2597         isCondExpr ? checker->MaybeUnboxConditionalInRelation(argType) : checker->MaybeUnboxInRelation(argType);
2598 
2599     if (argType != nullptr && argType->IsETSBigIntType() && argType->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL)) {
2600         switch (expr->OperatorType()) {
2601             case lexer::TokenType::PUNCTUATOR_MINUS: {
2602                 checker::Type *type = checker->CreateETSBigIntLiteralType(argType->AsETSBigIntType()->GetValue());
2603                 ES2PANDA_ASSERT(type != nullptr);
2604                 // We do not need this const anymore as we are negating the bigint object in runtime
2605                 type->RemoveTypeFlag(checker::TypeFlag::CONSTANT);
2606                 expr->argument_->SetTsType(type);
2607                 expr->SetTsType(type);
2608                 return expr->TsType();
2609             }
2610             default:
2611                 // Handled below
2612                 // NOTE(kkonsw): handle other unary operators for bigint literals
2613                 break;
2614         }
2615     }
2616 
2617     if (argType != nullptr && argType->IsETSBigIntType()) {
2618         switch (expr->OperatorType()) {
2619             case lexer::TokenType::PUNCTUATOR_MINUS:
2620             case lexer::TokenType::PUNCTUATOR_PLUS:
2621             case lexer::TokenType::PUNCTUATOR_TILDE: {
2622                 expr->SetTsType(argType);
2623                 return expr->TsType();
2624             }
2625             default:
2626                 break;
2627         }
2628     }
2629 
2630     if ((argType != nullptr) && argType->IsETSObjectType() && (unboxedOperandType != nullptr) &&
2631         unboxedOperandType->IsETSPrimitiveType()) {
2632         expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedOperandType));
2633     }
2634 
2635     SetTsTypeForUnaryExpression(checker, expr, operandType);
2636 
2637     checker->Context().CheckUnarySmartCastCondition(expr);
2638 
2639     return expr->TsType();
2640 }
2641 
Check(ir::UpdateExpression * expr) const2642 checker::Type *ETSAnalyzer::Check(ir::UpdateExpression *expr) const
2643 {
2644     ETSChecker *checker = GetETSChecker();
2645     if (expr->TsType() != nullptr) {
2646         return expr->TsType();
2647     }
2648 
2649     checker::Type *operandType = expr->argument_->Check(checker);
2650     if (operandType->IsTypeError()) {
2651         return checker->InvalidateType(expr);
2652     }
2653 
2654     if (expr->Argument()->IsIdentifier()) {
2655         checker->ValidateUnaryOperatorOperand(expr->Argument()->AsIdentifier()->Variable());
2656     } else if (expr->Argument()->IsTSAsExpression()) {
2657         if (auto *const asExprVar = expr->Argument()->AsTSAsExpression()->Variable(); asExprVar != nullptr) {
2658             checker->ValidateUnaryOperatorOperand(asExprVar);
2659         }
2660     } else if (expr->Argument()->IsTSNonNullExpression()) {
2661         if (auto *const nonNullExprVar = expr->Argument()->AsTSNonNullExpression()->Variable();
2662             nonNullExprVar != nullptr) {
2663             checker->ValidateUnaryOperatorOperand(nonNullExprVar);
2664         }
2665     } else if (expr->Argument()->IsMemberExpression()) {
2666         varbinder::LocalVariable *propVar = expr->argument_->AsMemberExpression()->PropVar();
2667         if (propVar != nullptr) {
2668             checker->ValidateUnaryOperatorOperand(propVar);
2669         }
2670     } else {
2671         ES2PANDA_ASSERT(checker->IsAnyError());
2672         expr->Argument()->SetTsType(checker->GlobalTypeError());
2673         return expr->SetTsType(checker->GlobalTypeError());
2674     }
2675 
2676     if (operandType->IsETSBigIntType()) {
2677         return expr->SetTsType(operandType);
2678     }
2679 
2680     auto unboxedType = checker->MaybeUnboxInRelation(operandType);
2681     if (unboxedType == nullptr || !unboxedType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
2682         checker->LogError(diagnostic::OPERAND_NOT_NUMERIC, {}, expr->Argument()->Start());
2683         return expr->SetTsType(checker->GlobalTypeError());
2684     }
2685 
2686     if (operandType->IsETSObjectType()) {
2687         expr->Argument()->AddBoxingUnboxingFlags(checker->GetUnboxingFlag(unboxedType) |
2688                                                  checker->GetBoxingFlag(unboxedType));
2689     }
2690 
2691     return expr->SetTsType(operandType);
2692 }
2693 
2694 // compile methods for LITERAL EXPRESSIONS in alphabetical order
Check(ir::BigIntLiteral * expr) const2695 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::BigIntLiteral *expr) const
2696 {
2697     ETSChecker *checker = GetETSChecker();
2698     expr->SetTsType(checker->CreateETSBigIntLiteralType(expr->Str()));
2699     return expr->TsType();
2700 }
2701 
Check(ir::BooleanLiteral * expr) const2702 checker::Type *ETSAnalyzer::Check(ir::BooleanLiteral *expr) const
2703 {
2704     ETSChecker *checker = GetETSChecker();
2705     if (expr->TsType() == nullptr) {
2706         expr->SetTsType(checker->CreateETSBooleanType(expr->Value()));
2707     }
2708     return expr->TsType();
2709 }
2710 
Check(ir::CharLiteral * expr) const2711 checker::Type *ETSAnalyzer::Check(ir::CharLiteral *expr) const
2712 {
2713     ETSChecker *checker = GetETSChecker();
2714     if (expr->TsType() == nullptr) {
2715         expr->SetTsType(checker->ProgramAllocator()->New<checker::CharType>(expr->Char()));
2716     }
2717     return expr->TsType();
2718 }
2719 
Check(ir::NullLiteral * expr) const2720 checker::Type *ETSAnalyzer::Check(ir::NullLiteral *expr) const
2721 {
2722     ETSChecker *checker = GetETSChecker();
2723     if (expr->TsType() == nullptr) {
2724         expr->SetTsType(checker->GlobalETSNullType());
2725     }
2726     return expr->TsType();
2727 }
2728 
Check(ir::NumberLiteral * expr) const2729 checker::Type *ETSAnalyzer::Check(ir::NumberLiteral *expr) const
2730 {
2731     ETSChecker *checker = GetETSChecker();
2732     if (expr->Number().IsInt()) {
2733         expr->SetTsType(checker->CreateIntType(expr->Number().GetInt()));
2734         return expr->TsType();
2735     }
2736 
2737     if (expr->Number().IsLong()) {
2738         expr->SetTsType(checker->CreateLongType(expr->Number().GetLong()));
2739         return expr->TsType();
2740     }
2741 
2742     if (expr->Number().IsFloat()) {
2743         expr->SetTsType(checker->CreateFloatType(expr->Number().GetFloat()));
2744         return expr->TsType();
2745     }
2746 
2747     expr->SetTsType(checker->CreateDoubleType(expr->Number().GetDouble()));
2748     return expr->TsType();
2749 }
2750 
Check(ir::StringLiteral * expr) const2751 checker::Type *ETSAnalyzer::Check(ir::StringLiteral *expr) const
2752 {
2753     ETSChecker *checker = GetETSChecker();
2754     if (expr->TsType() == nullptr) {
2755         expr->SetTsType(checker->CreateETSStringLiteralType(expr->Str()));
2756     }
2757     return expr->TsType();
2758 }
2759 
Check(ir::ImportDeclaration * st) const2760 checker::Type *ETSAnalyzer::Check(ir::ImportDeclaration *st) const
2761 {
2762     ETSChecker *checker = GetETSChecker();
2763     checker::Type *type = nullptr;
2764     for (auto *spec : st->Specifiers()) {
2765         if (spec->IsImportNamespaceSpecifier()) {
2766             type = spec->AsImportNamespaceSpecifier()->Check(checker);
2767         }
2768     }
2769 
2770     return type;
2771 }
2772 
Check(ir::ImportNamespaceSpecifier * st) const2773 checker::Type *ETSAnalyzer::Check(ir::ImportNamespaceSpecifier *st) const
2774 {
2775     ETSChecker *checker = GetETSChecker();
2776     if (st->Local()->Name().Empty()) {
2777         return ReturnTypeForStatement(st);
2778     }
2779 
2780     if (st->Local()->AsIdentifier()->TsType() != nullptr) {
2781         return st->Local()->TsType();
2782     }
2783 
2784     ir::ETSImportDeclaration *importDecl = nullptr;
2785     if (st->Parent()->IsETSImportDeclaration()) {
2786         importDecl = st->Parent()->AsETSImportDeclaration();
2787     } else if (st->Parent()->IsETSReExportDeclaration()) {
2788         importDecl = st->Parent()->AsETSReExportDeclaration()->GetETSImportDeclarations();
2789     } else {
2790         ES2PANDA_UNREACHABLE();
2791     }
2792 
2793     if (importDecl->IsPureDynamic()) {
2794         auto *type = checker->GlobalBuiltinDynamicType(importDecl->Language());
2795         checker->SetrModuleObjectTsType(st->Local(), type);
2796         return type;
2797     }
2798 
2799     return checker->GetImportSpecifierObjectType(importDecl, st->Local()->AsIdentifier());
2800 }
2801 
2802 // compile methods for STATEMENTS in alphabetical order
Check(ir::AssertStatement * st) const2803 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::AssertStatement *st) const
2804 {
2805     ES2PANDA_UNREACHABLE();
2806 }
2807 
Check(ir::BlockStatement * st) const2808 checker::Type *ETSAnalyzer::Check(ir::BlockStatement *st) const
2809 {
2810     ETSChecker *checker = GetETSChecker();
2811     checker::ScopeContext scopeCtx(checker, st->Scope());
2812 
2813     // Iterator type checking of statements is modified to index type, to allow modifying the statement list during
2814     // checking without invalidating the iterator
2815     //---- Don't modify this to iterator, as it may break things during checking
2816     for (std::size_t idx = 0; idx < st->Statements().size(); ++idx) {
2817         auto *stmt = st->Statements()[idx];
2818         stmt->Check(checker);
2819 
2820         //  NOTE! Processing of trailing blocks was moved here so that smart casts could be applied correctly
2821         if (auto const tb = st->trailingBlocks_.find(stmt); tb != st->trailingBlocks_.end()) {
2822             auto *const trailingBlock = tb->second;
2823             trailingBlock->Check(checker);
2824             st->Statements().emplace(std::next(st->Statements().begin() + idx), trailingBlock);
2825             ++idx;
2826         }
2827     }
2828     if (UNLIKELY(checker->GetDebugInfoPlugin() != nullptr)) {
2829         // Compilation in eval-mode might require to create additional statements.
2830         // In this case, they must be created after iteration through statements ends.
2831         checker->GetDebugInfoPlugin()->AddPrologueEpilogue(st);
2832     }
2833 
2834     auto const *const scope = st->Scope();
2835     if (scope == nullptr) {
2836         return ReturnTypeForStatement(st);
2837     }
2838 
2839     //  Remove possible smart casts for variables declared in inner scope:
2840     if (scope->IsFunctionScope() && st->Parent()->Parent()->Parent()->IsMethodDefinition()) {
2841         // When exiting method definition, just clear all smart casts
2842         checker->Context().ClearSmartCasts();
2843     } else if (!scope->IsGlobalScope()) {
2844         // otherwise only check inner declarations
2845         for (auto const *const decl : scope->Decls()) {
2846             if (decl->IsLetOrConstDecl() && decl->Node() != nullptr && decl->Node()->IsIdentifier()) {
2847                 checker->Context().RemoveSmartCast(decl->Node()->AsIdentifier()->Variable());
2848             }
2849         }
2850     }
2851 
2852     // Note: Guarantee all the const property need to be initialized in initializer block is initialized.
2853     if (st->IsETSModule() && st->AsETSModule()->Program()->IsPackage() &&
2854         (checker->Context().Status() & checker::CheckerStatus::IN_EXTERNAL) == 0) {
2855         CheckAllConstPropertyInitialized(checker, st->AsETSModule());
2856     }
2857     return ReturnTypeForStatement(st);
2858 }
2859 
Check(ir::BreakStatement * st) const2860 checker::Type *ETSAnalyzer::Check(ir::BreakStatement *st) const
2861 {
2862     ETSChecker *checker = GetETSChecker();
2863 
2864     if (!st->HasTarget()) {
2865         compiler::SetJumpTargetPhase setJumpTarget;
2866         setJumpTarget.FindJumpTarget(checker->VarBinder()->GetContext(), st);
2867     }
2868 
2869     if (st->Target() == nullptr) {
2870         return checker->GlobalTypeError();
2871     }
2872 
2873     checker->Context().OnBreakStatement(st);
2874     return ReturnTypeForStatement(st);
2875 }
2876 
Check(ir::ClassDeclaration * st) const2877 checker::Type *ETSAnalyzer::Check(ir::ClassDeclaration *st) const
2878 {
2879     ETSChecker *checker = GetETSChecker();
2880     st->Definition()->Check(checker);
2881     return ReturnTypeForStatement(st);
2882 }
2883 
Check(ir::AnnotationDeclaration * st) const2884 checker::Type *ETSAnalyzer::Check(ir::AnnotationDeclaration *st) const
2885 {
2886     if (st->Expr()->TsType() != nullptr) {
2887         return ReturnTypeForStatement(st);
2888     }
2889     ETSChecker *checker = GetETSChecker();
2890     st->Expr()->Check(checker);
2891 
2892     for (auto *anno : st->Annotations()) {
2893         checker->CheckStandardAnnotation(anno);
2894         anno->Check(checker);
2895     }
2896 
2897     ScopeContext scopeCtx(checker, st->Scope());
2898     for (auto *it : st->Properties()) {
2899         auto *property = it->AsClassProperty();
2900         if (checker::Type *propertyType = property->Check(checker); !propertyType->IsTypeError()) {
2901             checker->CheckAnnotationPropertyType(property);
2902         }
2903     }
2904 
2905     auto baseName = st->GetBaseName();
2906     if (!baseName->IsErrorPlaceHolder()) {
2907         if (baseName->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) {
2908             auto *annoDecl = baseName->Variable()->Declaration()->Node()->AsAnnotationDeclaration();
2909             if (annoDecl != st && annoDecl->IsDeclare()) {
2910                 checker->CheckAmbientAnnotation(st, annoDecl);
2911             }
2912         }
2913     }
2914 
2915     return ReturnTypeForStatement(st);
2916 }
2917 
Check(ir::AnnotationUsage * st) const2918 checker::Type *ETSAnalyzer::Check(ir::AnnotationUsage *st) const
2919 {
2920     if (st->Expr()->TsType() != nullptr) {
2921         return ReturnTypeForStatement(st);
2922     }
2923     ETSChecker *checker = GetETSChecker();
2924     st->Expr()->Check(checker);
2925 
2926     if (st->GetBaseName()->Variable() == nullptr ||
2927         !st->GetBaseName()->Variable()->Declaration()->Node()->IsAnnotationDeclaration()) {
2928         checker->LogError(diagnostic::NOT_AN_ANNOTATION, {st->GetBaseName()->Name()}, st->GetBaseName()->Start());
2929         return ReturnTypeForStatement(st);
2930     }
2931 
2932     auto *annoDecl = st->GetBaseName()->Variable()->Declaration()->Node()->AsAnnotationDeclaration();
2933     annoDecl->Check(checker);
2934 
2935     ArenaUnorderedMap<util::StringView, ir::ClassProperty *> fieldMap {checker->ProgramAllocator()->Adapter()};
2936     for (auto *it : annoDecl->Properties()) {
2937         auto *field = it->AsClassProperty();
2938         ES2PANDA_ASSERT(field != nullptr);
2939         auto *id = field->Id();
2940         ES2PANDA_ASSERT(id != nullptr);
2941         fieldMap.insert(std::make_pair(id->Name(), field));
2942     }
2943 
2944     if (annoDecl->Properties().size() < st->Properties().size()) {
2945         checker->LogError(diagnostic::ANNOTATION_ARG_COUNT_MISMATCH, {}, st->Start());
2946         return ReturnTypeForStatement(st);
2947     }
2948 
2949     if (st->Properties().size() == 1 && st->Properties().at(0)->AsClassProperty()->Id() != nullptr &&
2950         st->Properties().at(0)->AsClassProperty()->Id()->Name() == compiler::Signatures::ANNOTATION_KEY_VALUE) {
2951         checker->CheckSinglePropertyAnnotation(st, annoDecl);
2952         fieldMap.clear();
2953     } else {
2954         checker->CheckMultiplePropertiesAnnotation(st, st->GetBaseName()->Name(), fieldMap);
2955     }
2956 
2957     checker->ProcessRequiredFields(fieldMap, st, checker);
2958     return ReturnTypeForStatement(st);
2959 }
2960 
Check(ir::ContinueStatement * st) const2961 checker::Type *ETSAnalyzer::Check(ir::ContinueStatement *st) const
2962 {
2963     ETSChecker *checker = GetETSChecker();
2964 
2965     if (!st->HasTarget()) {
2966         compiler::SetJumpTargetPhase setJumpTarget;
2967         setJumpTarget.FindJumpTarget(checker->VarBinder()->GetContext(), st);
2968     }
2969 
2970     if (st->Target() == nullptr) {
2971         return checker->GlobalTypeError();
2972     }
2973 
2974     checker->AddStatus(CheckerStatus::MEET_CONTINUE);
2975     return ReturnTypeForStatement(st);
2976 }
2977 
Check(ir::DoWhileStatement * st) const2978 checker::Type *ETSAnalyzer::Check(ir::DoWhileStatement *st) const
2979 {
2980     ETSChecker *checker = GetETSChecker();
2981     checker::ScopeContext scopeCtx(checker, st->Scope());
2982 
2983     //  NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
2984     auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt);
2985 
2986     checker->CheckTruthinessOfType(st->Test());
2987     st->Body()->Check(checker);
2988 
2989     checker->Context().ExitLoop(smartCasts, clearFlag, st);
2990     return ReturnTypeForStatement(st);
2991 }
2992 
Check(ir::EmptyStatement * st) const2993 checker::Type *ETSAnalyzer::Check([[maybe_unused]] ir::EmptyStatement *st) const
2994 {
2995     return ReturnTypeForStatement(st);
2996 }
2997 
Check(ir::ExpressionStatement * st) const2998 checker::Type *ETSAnalyzer::Check(ir::ExpressionStatement *st) const
2999 {
3000     ETSChecker *checker = GetETSChecker();
3001     return st->GetExpression()->Check(checker);
3002 }
3003 
ValidateAndProcessIteratorType(ETSChecker * checker,Type * elemType,ir::ForOfStatement * const st)3004 static bool ValidateAndProcessIteratorType(ETSChecker *checker, Type *elemType, ir::ForOfStatement *const st)
3005 {
3006     checker::Type *iterType = GetIteratorType(checker, elemType, st->Left());
3007     if (iterType->IsTypeError()) {
3008         return false;
3009     }
3010 
3011     const auto ident = st->Left()->IsVariableDeclaration()
3012                            ? st->Left()->AsVariableDeclaration()->Declarators().front()->Id()->AsIdentifier()
3013                            : st->Left()->AsIdentifier();
3014     auto *const relation = checker->Relation();
3015     relation->SetFlags(checker::TypeRelationFlag::ASSIGNMENT_CONTEXT);
3016     relation->SetNode(ident);
3017     if (auto ctx = checker::AssignmentContext(checker->Relation(), ident, elemType, iterType, ident->Start(),
3018                                               std::nullopt, TypeRelationFlag::NO_THROW);
3019         !ctx.IsAssignable()) {
3020         checker->LogError(diagnostic::ITERATOR_ELEMENT_TYPE_MISMATCH, {elemType, iterType}, st->Start());
3021         return false;
3022     }
3023 
3024     relation->SetNode(nullptr);
3025     relation->SetFlags(checker::TypeRelationFlag::NONE);
3026 
3027     const auto variable = ident->Variable();
3028     if (variable != nullptr) {
3029         // Set smart type for variable of 'for-of' statement
3030         const auto smartType = checker->ResolveSmartType(elemType, variable->TsType());
3031         checker->Context().SetSmartCast(variable, smartType);
3032     }
3033 
3034     return true;
3035 }
3036 
Check(ir::ForOfStatement * const st) const3037 checker::Type *ETSAnalyzer::Check(ir::ForOfStatement *const st) const
3038 {
3039     ETSChecker *checker = GetETSChecker();
3040     checker::ScopeContext scopeCtx(checker, st->Scope());
3041 
3042     //  NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
3043     auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt);
3044 
3045     checker::Type *const exprType = st->Right()->Check(checker);
3046     if (exprType == nullptr) {
3047         checker->LogError(diagnostic::FOROF_CANT_INFER_SOURCE, {}, st->Right()->Start());
3048         return checker->GlobalTypeError();
3049     }
3050 
3051     checker::Type *elemType = checker->GlobalTypeError();
3052 
3053     if (exprType->IsETSStringType()) {
3054         elemType = checker->GlobalBuiltinETSStringType();
3055     } else if (exprType->IsETSArrayType() || exprType->IsETSResizableArrayType()) {
3056         elemType = checker->GetElementTypeOfArray(exprType);
3057     } else if (exprType->IsETSObjectType() || exprType->IsETSUnionType() || exprType->IsETSTypeParameter()) {
3058         elemType = st->CheckIteratorMethod(checker);
3059     }
3060 
3061     if (elemType == checker->GlobalTypeError()) {
3062         checker->LogError(diagnostic::FOROF_SOURCE_NONITERABLE, {}, st->Right()->Start());
3063         return checker->GlobalTypeError();
3064     }
3065 
3066     st->Left()->Check(checker);
3067 
3068     if (!ValidateAndProcessIteratorType(checker, elemType, st)) {
3069         return checker->GlobalTypeError();
3070     };
3071 
3072     st->Body()->Check(checker);
3073 
3074     checker->Context().ExitLoop(smartCasts, clearFlag, st);
3075     return ReturnTypeForStatement(st);
3076 }
3077 
Check(ir::ForUpdateStatement * st) const3078 checker::Type *ETSAnalyzer::Check(ir::ForUpdateStatement *st) const
3079 {
3080     ETSChecker *checker = GetETSChecker();
3081     checker::ScopeContext scopeCtx(checker, st->Scope());
3082 
3083     //  NOTE: Smart casts are not processed correctly within the loops now, thus clear them at this point.
3084     auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, std::nullopt);
3085 
3086     if (st->Init() != nullptr) {
3087         st->Init()->Check(checker);
3088     }
3089 
3090     if (st->Test() != nullptr) {
3091         checker->CheckTruthinessOfType(st->Test());
3092     }
3093 
3094     if (st->Update() != nullptr) {
3095         st->Update()->Check(checker);
3096     }
3097 
3098     st->Body()->Check(checker);
3099 
3100     checker->Context().ExitLoop(smartCasts, clearFlag, st);
3101     return ReturnTypeForStatement(st);
3102 }
3103 
Check(ir::IfStatement * st) const3104 checker::Type *ETSAnalyzer::Check(ir::IfStatement *st) const
3105 {
3106     ETSChecker *const checker = GetETSChecker();
3107 
3108     SmartCastArray smartCasts = checker->Context().EnterTestExpression();
3109     checker->CheckTruthinessOfType(st->Test());
3110     SmartCastTypes testedTypes = checker->Context().ExitTestExpression();
3111     if (testedTypes.has_value()) {
3112         for (auto [variable, consequentType, _] : *testedTypes) {
3113             checker->ApplySmartCast(variable, consequentType);
3114         }
3115     }
3116 
3117     checker->Context().EnterPath();
3118     st->Consequent()->Check(checker);
3119     bool const consequentTerminated = checker->Context().ExitPath();
3120     SmartCastArray consequentSmartCasts = checker->Context().CloneSmartCasts();
3121 
3122     // Restore smart casts to initial state.
3123     checker->Context().RestoreSmartCasts(smartCasts);
3124     //  Apply the alternate smart casts
3125     if (testedTypes.has_value()) {
3126         for (auto [variable, _, alternateType] : *testedTypes) {
3127             checker->ApplySmartCast(variable, alternateType);
3128         }
3129     }
3130 
3131     if (st->Alternate() != nullptr) {
3132         checker->Context().EnterPath();
3133         st->Alternate()->Check(checker);
3134         bool const alternateTerminated = checker->Context().ExitPath();
3135         if (alternateTerminated) {
3136             if (!consequentTerminated) {
3137                 // Here we need to restore types from consequent if block.
3138                 checker->Context().RestoreSmartCasts(consequentSmartCasts);
3139             } else {
3140                 // Here we need to restore initial smart types.
3141                 checker->Context().RestoreSmartCasts(smartCasts);
3142             }
3143         } else if (!consequentTerminated) {
3144             // Here we need to combine types from consequent and alternate if blocks.
3145             checker->Context().CombineSmartCasts(consequentSmartCasts);
3146         }
3147     } else {
3148         if (!consequentTerminated) {
3149             // Here we need to combine types from consequent if block and initial.
3150             checker->Context().CombineSmartCasts(consequentSmartCasts);
3151         }
3152     }
3153 
3154     return ReturnTypeForStatement(st);
3155 }
3156 
Check(ir::LabelledStatement * st) const3157 checker::Type *ETSAnalyzer::Check(ir::LabelledStatement *st) const
3158 {
3159     ETSChecker *checker = GetETSChecker();
3160     st->body_->Check(checker);
3161     return ReturnTypeForStatement(st);
3162 }
3163 
CheckIsValidReturnTypeAnnotation(ir::ReturnStatement * st,ir::ScriptFunction * containingFunc,ir::TypeNode * returnTypeAnnotation,ETSChecker * checker)3164 static bool CheckIsValidReturnTypeAnnotation(ir::ReturnStatement *st, ir::ScriptFunction *containingFunc,
3165                                              ir::TypeNode *returnTypeAnnotation, ETSChecker *checker)
3166 {
3167     // check valid `this` type as return type
3168     if (containingFunc->GetPreferredReturnType() != nullptr || !returnTypeAnnotation->IsTSThisType()) {
3169         return true;
3170     }
3171 
3172     // only extension function and class method could return `this`;
3173     bool inValidNormalFuncReturnThisType = st->Argument() == nullptr || !st->Argument()->IsThisExpression();
3174     bool inValidExtensionFuncReturnThisType =
3175         !containingFunc->HasReceiver() ||
3176         (containingFunc->HasReceiver() && (st->Argument() == nullptr || !st->Argument()->IsIdentifier() ||
3177                                            !st->Argument()->AsIdentifier()->IsReceiver()));
3178     if (inValidNormalFuncReturnThisType && inValidExtensionFuncReturnThisType) {
3179         checker->LogError(diagnostic::RETURN_THIS_OUTSIDE_METHOD, {}, st->Start());
3180         return false;
3181     }
3182 
3183     return true;
3184 }
3185 
3186 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
CheckInferredFunctionReturnType(ir::ReturnStatement * st,ir::ScriptFunction * containingFunc,checker::Type * & funcReturnType,ir::TypeNode * returnTypeAnnotation,ETSChecker * checker) const3187 bool ETSAnalyzer::CheckInferredFunctionReturnType(ir::ReturnStatement *st, ir::ScriptFunction *containingFunc,
3188                                                   checker::Type *&funcReturnType, ir::TypeNode *returnTypeAnnotation,
3189                                                   ETSChecker *checker) const
3190 {
3191     if (!CheckIsValidReturnTypeAnnotation(st, containingFunc, returnTypeAnnotation, checker)) {
3192         return false;
3193     }
3194 
3195     if (containingFunc->ReturnTypeAnnotation() != nullptr) {
3196         if (containingFunc->IsAsyncFunc()) {
3197             auto *promiseType = containingFunc->ReturnTypeAnnotation()->GetType(checker);
3198             ES2PANDA_ASSERT(promiseType != nullptr);
3199             if (!promiseType->IsETSObjectType() || promiseType->AsETSObjectType()->TypeArguments().size() != 1) {
3200                 return false;
3201             }
3202             funcReturnType = checker->CreateETSAsyncFuncReturnTypeFromPromiseType(promiseType->AsETSObjectType());
3203         } else {
3204             funcReturnType = containingFunc->ReturnTypeAnnotation()->GetType(checker);
3205         }
3206     } else {
3207         funcReturnType = containingFunc->GetPreferredReturnType();
3208     }
3209 
3210     // Case when function's return type is defined explicitly:
3211     if (st->argument_ == nullptr) {
3212         ES2PANDA_ASSERT(funcReturnType != nullptr);
3213         if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType() &&
3214             !funcReturnType->IsETSAsyncFuncReturnType()) {
3215             checker->LogError(diagnostic::RETURN_WITHOUT_VALUE, {}, st->Start());
3216             return false;
3217         }
3218         funcReturnType = checker->GlobalVoidType();
3219     } else {
3220         const auto name = containingFunc->Scope()->InternalName().Mutf8();
3221         if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) {
3222             return false;
3223         }
3224 
3225         if (st->argument_->IsObjectExpression()) {
3226             st->argument_->AsObjectExpression()->SetPreferredType(funcReturnType);
3227         }
3228         if (st->argument_->IsMemberExpression()) {
3229             checker->SetArrayPreferredTypeForNestedMemberExpressions(st->argument_->AsMemberExpression(),
3230                                                                      funcReturnType);
3231         }
3232 
3233         if (st->argument_->IsArrayExpression()) {
3234             st->argument_->AsArrayExpression()->SetPreferredType(funcReturnType);
3235         }
3236 
3237         if (st->argument_->IsETSNewArrayInstanceExpression()) {
3238             st->argument_->AsETSNewArrayInstanceExpression()->SetPreferredType(funcReturnType);
3239         }
3240 
3241         if (st->argument_->IsETSNewMultiDimArrayInstanceExpression()) {
3242             st->argument_->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(funcReturnType);
3243         }
3244 
3245         checker::Type *argumentType = st->argument_->Check(checker);
3246         return CheckReturnType(checker, funcReturnType, argumentType, st->argument_, containingFunc);
3247     }
3248     return true;
3249 }
3250 
GetFunctionReturnType(ir::ReturnStatement * st,ir::ScriptFunction * containingFunc) const3251 checker::Type *ETSAnalyzer::GetFunctionReturnType(ir::ReturnStatement *st, ir::ScriptFunction *containingFunc) const
3252 {
3253     ES2PANDA_ASSERT(containingFunc->ReturnTypeAnnotation() != nullptr ||
3254                     containingFunc->Signature()->ReturnType() != nullptr ||
3255                     containingFunc->GetPreferredReturnType() != nullptr);
3256 
3257     ETSChecker *checker = GetETSChecker();
3258     checker::Type *funcReturnType = nullptr;
3259 
3260     if (auto *const returnTypeAnnotation = containingFunc->ReturnTypeAnnotation();
3261         returnTypeAnnotation != nullptr || containingFunc->GetPreferredReturnType() != nullptr) {
3262         if (!CheckInferredFunctionReturnType(st, containingFunc, funcReturnType, returnTypeAnnotation, checker)) {
3263             return checker->GlobalTypeError();
3264         }
3265     } else {
3266         //  Case when function's return type should be inferred from return statement(s):
3267         if (containingFunc->Signature()->HasSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE)) {
3268             InferReturnType(checker, containingFunc, funcReturnType,
3269                             st->argument_);  // This removes the NEED_RETURN_TYPE flag, so only the first return
3270                                              // statement going to land here...
3271         } else {
3272             //  All subsequent return statements:
3273             ProcessReturnStatements(checker, containingFunc, funcReturnType, st,
3274                                     st->argument_);  // and the remaining return statements will get processed here.
3275         }
3276     }
3277 
3278     if ((st->argument_ != nullptr) && st->argument_->IsArrayExpression() && funcReturnType->IsArrayType()) {
3279         checker->ModifyPreferredType(st->argument_->AsArrayExpression(), funcReturnType);
3280         st->argument_->Check(checker);
3281     }
3282 
3283     return funcReturnType;
3284 }
3285 
Check(ir::ReturnStatement * st) const3286 checker::Type *ETSAnalyzer::Check(ir::ReturnStatement *st) const
3287 {
3288     ETSChecker *checker = GetETSChecker();
3289 
3290     ir::AstNode *ancestor = util::Helpers::FindAncestorGivenByType(st, ir::AstNodeType::SCRIPT_FUNCTION);
3291     ES2PANDA_ASSERT(ancestor && ancestor->IsScriptFunction());
3292 
3293     ES2PANDA_ASSERT(ancestor != nullptr);
3294     auto *containingFunc = ancestor->AsScriptFunction();
3295     containingFunc->AddFlag(ir::ScriptFunctionFlags::HAS_RETURN);
3296 
3297     if (containingFunc->Signature() == nullptr) {
3298         ES2PANDA_ASSERT(checker->IsAnyError());
3299         return ReturnTypeForStatement(st);
3300     }
3301 
3302     checker->AddStatus(CheckerStatus::MEET_RETURN);
3303 
3304     if (containingFunc->IsConstructor()) {
3305         if (st->argument_ != nullptr) {
3306             checker->LogError(diagnostic::NON_VOID_RETURN_IN_CONSTRUCTOR, {}, st->Start());
3307             return checker->GlobalTypeError();
3308         }
3309         return ReturnTypeForStatement(st);
3310     }
3311 
3312     st->returnType_ = GetFunctionReturnType(st, containingFunc);
3313 
3314     if (containingFunc->ReturnTypeAnnotation() == nullptr) {
3315         containingFunc->AddReturnStatement(st);
3316     }
3317 
3318     return ReturnTypeForStatement(st);
3319 }
3320 
Check(ir::SwitchStatement * st) const3321 checker::Type *ETSAnalyzer::Check(ir::SwitchStatement *st) const
3322 {
3323     ETSChecker *checker = GetETSChecker();
3324     checker::ScopeContext scopeCtx(checker, st->Scope());
3325     checker::SavedTypeRelationFlagsContext savedTypeRelationFlagCtx(checker->Relation(),
3326                                                                     checker::TypeRelationFlag::NONE);
3327 
3328     auto *comparedExprType = checker->CheckSwitchDiscriminant(st->Discriminant());
3329     auto unboxedDiscType = (st->Discriminant()->GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U
3330                                ? checker->MaybeUnboxInRelation(comparedExprType)
3331                                : comparedExprType;
3332 
3333     SmartCastArray smartCasts = checker->Context().CloneSmartCasts();
3334     bool hasDefaultCase = false;
3335 
3336     for (auto &it : st->Cases()) {
3337         checker->Context().EnterPath();
3338         it->CheckAndTestCase(checker, comparedExprType, unboxedDiscType, st->Discriminant(), hasDefaultCase);
3339         bool const caseTerminated = checker->Context().ExitPath();
3340 
3341         if (it != st->Cases().back()) {
3342             if (!caseTerminated) {
3343                 checker->Context().CombineSmartCasts(smartCasts);
3344             } else {
3345                 checker->Context().RestoreSmartCasts(smartCasts);
3346             }
3347         } else {
3348             if (!caseTerminated) {
3349                 //  if the recent switch case isn't terminated in any way, copy actual smart casts to the array of
3350                 //  smart casts for the other case blocks so that it can be processed in unified way
3351                 checker->Context().AddBreakSmartCasts(st, checker->Context().CloneSmartCasts());
3352             }
3353             checker->Context().ClearSmartCasts();
3354         }
3355     }
3356 
3357     // If default case is absent initial smart casts should be also applied here
3358     if (!hasDefaultCase) {
3359         checker->Context().AddBreakSmartCasts(st, std::move(smartCasts));
3360     }
3361 
3362     // Combine smart casts from all [non-terminated] case blocks with 'break'
3363     checker->Context().CombineBreakSmartCasts(st);
3364 
3365     checker->CheckForSameSwitchCases(st->Cases());
3366     return ReturnTypeForStatement(st);
3367 }
3368 
Check(ir::ThrowStatement * st) const3369 checker::Type *ETSAnalyzer::Check(ir::ThrowStatement *st) const
3370 {
3371     ETSChecker *checker = GetETSChecker();
3372     const auto *arg = st->argument_;
3373     checker::Type *argType = st->argument_->Check(checker);
3374 
3375     bool isRethrow = false;
3376     if (arg->IsIdentifier() && !catchParamStack_.empty()) {
3377         const varbinder::Variable *sym = arg->AsIdentifier()->Variable();
3378         ES2PANDA_ASSERT(sym != nullptr);
3379         if (!catchParamStack_.empty() && sym == catchParamStack_.back()) {
3380             isRethrow = true;
3381         }
3382     }
3383     if (!isRethrow && !argType->IsTypeError()) {
3384         checker->CheckExceptionOrErrorType(argType, st->Start());
3385     }
3386 
3387     checker->AddStatus(CheckerStatus::MEET_THROW);
3388     return ReturnTypeForStatement(st);
3389 }
3390 
Check(ir::TryStatement * st) const3391 checker::Type *ETSAnalyzer::Check(ir::TryStatement *st) const
3392 {
3393     ETSChecker *checker = GetETSChecker();
3394     std::vector<checker::ETSObjectType *> exceptions {};
3395 
3396     std::vector<SmartCastArray> casts {};
3397     auto smartCasts = checker->Context().CheckTryBlock(*st->Block());
3398     st->Block()->Check(checker);
3399 
3400     bool defaultCatchFound = false;
3401     for (auto *catchClause : st->CatchClauses()) {
3402         if (defaultCatchFound) {
3403             checker->LogError(diagnostic::CATCH_DEFAULT_NOT_LAST, {}, catchClause->Start());
3404             return checker->GlobalTypeError();
3405         }
3406 
3407         checker->Context().RestoreSmartCasts(smartCasts);
3408 
3409         if (auto const exceptionType = catchClause->Check(checker); !exceptionType->IsTypeError()) {
3410             auto *clauseType = exceptionType->AsETSObjectType();
3411             checker->CheckExceptionClauseType(exceptions, catchClause, clauseType);
3412             exceptions.emplace_back(clauseType);
3413         }
3414 
3415         defaultCatchFound = catchClause->IsDefaultCatchClause();
3416 
3417         casts.emplace_back(checker->Context().CloneSmartCasts());
3418     }
3419 
3420     checker->Context().RestoreSmartCasts(smartCasts);
3421     if (!casts.empty()) {
3422         for (auto const &cast : casts) {
3423             checker->Context().CombineSmartCasts(cast);
3424         }
3425     }
3426 
3427     if (st->HasFinalizer()) {
3428         st->FinallyBlock()->Check(checker);
3429     }
3430 
3431     return ReturnTypeForStatement(st);
3432 }
3433 
Check(ir::VariableDeclarator * st) const3434 checker::Type *ETSAnalyzer::Check(ir::VariableDeclarator *st) const
3435 {
3436     if (st->TsType() != nullptr) {
3437         return st->TsType();
3438     }
3439 
3440     ETSChecker *checker = GetETSChecker();
3441     ES2PANDA_ASSERT(st->Id()->IsIdentifier());
3442     auto *const ident = st->Id()->AsIdentifier();
3443     ir::ModifierFlags flags = ir::ModifierFlags::NONE;
3444 
3445     if (ident->Parent()->Parent()->AsVariableDeclaration()->Kind() ==
3446         ir::VariableDeclaration::VariableDeclarationKind::CONST) {
3447         flags |= ir::ModifierFlags::CONST;
3448     }
3449 
3450     if (ident->IsOptionalDeclaration()) {
3451         flags |= ir::ModifierFlags::OPTIONAL;
3452     }
3453 
3454     // Processing possible parser errors
3455     if (ident->Variable() == nullptr) {
3456         ident->Check(checker);
3457     }
3458     auto *const variableType = checker->CheckVariableDeclaration(ident, ident->TypeAnnotation(), st->Init(), flags);
3459 
3460     //  Now try to define the actual type of Identifier so that smart cast can be used in further checker processing
3461     //  NOTE: T_S and K_o_t_l_i_n don't act in such way, but we can try - why not? :)
3462     auto *smartType = variableType;
3463     if (auto *const initType = st->Init() != nullptr ? st->Init()->TsType() : nullptr; initType != nullptr) {
3464         smartType = checker->ResolveSmartType(initType, variableType);
3465         //  Set smart type for identifier if it differs from annotated type
3466         //  Top-level and captured variables are not processed here!
3467         if (!checker->Relation()->IsIdenticalTo(variableType, smartType)) {
3468             ident->SetTsType(smartType);
3469             checker->Context().SetSmartCast(ident->Variable(), smartType);
3470         }
3471     }
3472 
3473     return st->SetTsType(smartType);
3474 }
3475 
Check(ir::VariableDeclaration * st) const3476 checker::Type *ETSAnalyzer::Check(ir::VariableDeclaration *st) const
3477 {
3478     ETSChecker *checker = GetETSChecker();
3479 
3480     checker->CheckAnnotations(st->Annotations());
3481 
3482     for (auto *it : st->Declarators()) {
3483         it->Check(checker);
3484     }
3485 
3486     return ReturnTypeForStatement(st);
3487 }
3488 
Check(ir::WhileStatement * st) const3489 checker::Type *ETSAnalyzer::Check(ir::WhileStatement *st) const
3490 {
3491     ETSChecker *checker = GetETSChecker();
3492     checker::ScopeContext scopeCtx(checker, st->Scope());
3493 
3494     // Invalidate smart cast for variables in the test condition, that will be reassigned in the loop body
3495     const auto reassignedVars = checker->Context().GetReassignedVariablesInNode(st->Body());
3496     for (const auto &[var, _] : reassignedVars) {
3497         checker->Context().RemoveSmartCast(var);
3498     }
3499 
3500     SmartCastArray savedSmartCasts = checker->Context().EnterTestExpression();
3501     checker->CheckTruthinessOfType(st->Test());
3502     SmartCastTypes testedTypes = checker->Context().ExitTestExpression();
3503     if (testedTypes.has_value()) {
3504         for (auto [variable, consequentType, _] : *testedTypes) {
3505             checker->ApplySmartCast(variable, consequentType);
3506         }
3507     }
3508 
3509     auto [smartCasts, clearFlag] = checker->Context().EnterLoop(*st, testedTypes);
3510     st->Body()->Check(checker);
3511     checker->Context().ExitLoop(savedSmartCasts, clearFlag, st);
3512     return ReturnTypeForStatement(st);
3513 }
3514 
Check(ir::TSArrayType * node) const3515 checker::Type *ETSAnalyzer::Check(ir::TSArrayType *node) const
3516 {
3517     ETSChecker *checker = GetETSChecker();
3518     checker->CheckAnnotations(node->Annotations());
3519     node->elementType_->Check(checker);
3520     node->SetTsType(node->GetType(checker));
3521 
3522     const auto *arrayType = node->TsType()->AsETSArrayType();
3523     checker->CreateBuiltinArraySignature(arrayType, arrayType->Rank());
3524     return node->TsType();
3525 }
3526 
Check(ir::TSAsExpression * expr) const3527 checker::Type *ETSAnalyzer::Check(ir::TSAsExpression *expr) const
3528 {
3529     ETSChecker *checker = GetETSChecker();
3530 
3531     if (expr->TsType() != nullptr) {
3532         return expr->TsType();
3533     }
3534 
3535     checker->CheckAnnotations(expr->TypeAnnotation()->Annotations());
3536     auto *const targetType = expr->TypeAnnotation()->AsTypeNode()->GetType(checker);
3537     ES2PANDA_ASSERT(targetType != nullptr);
3538     if (targetType->IsTypeError()) {
3539         return checker->InvalidateType(expr);
3540     }
3541 
3542     ETSChecker::SetPreferredTypeIfPossible(expr->Expr(), targetType);
3543 
3544     auto const sourceType = expr->Expr()->Check(checker);
3545     if (sourceType->IsTypeError()) {
3546         return checker->InvalidateType(expr);
3547     }
3548 
3549     // NOTE(vpukhov): #20510 lowering
3550     if (targetType->IsETSPrimitiveType() && sourceType->IsETSReferenceType()) {
3551         auto *const boxedTargetType = checker->MaybeBoxInRelation(targetType);
3552         if (!checker->Relation()->IsIdenticalTo(sourceType, boxedTargetType)) {
3553             expr->Expr()->AddAstNodeFlags(ir::AstNodeFlags::CHECKCAST);
3554         }
3555     }
3556 
3557     if (sourceType->DefinitelyETSNullish() && !targetType->PossiblyETSNullish()) {
3558         return checker->TypeError(expr, diagnostic::NULLISH_CAST_TO_NONNULLISH, expr->Start());
3559     }
3560 
3561     const checker::CastingContext ctx(
3562         checker->Relation(), diagnostic::INVALID_CAST, {sourceType, targetType},
3563         checker::CastingContext::ConstructorData {expr->Expr(), sourceType, targetType, expr->Expr()->Start()});
3564 
3565     if (sourceType->IsETSDynamicType() && targetType->IsLambdaObject()) {
3566         // NOTE: itrubachev. change targetType to created lambdaobject type.
3567         // Now targetType is not changed, only construct signature is added to it
3568         checker->BuildLambdaObjectClass(targetType->AsETSObjectType(),
3569                                         expr->TypeAnnotation()->AsETSFunctionType()->ReturnType());
3570     }
3571     expr->isUncheckedCast_ = ctx.UncheckedCast();
3572 
3573     // Make sure the array type symbol gets created for the assembler to be able to emit checkcast.
3574     // Because it might not exist, if this particular array type was never created explicitly.
3575     if (!expr->isUncheckedCast_ && targetType->IsETSArrayType()) {
3576         const auto *const targetArrayType = targetType->AsETSArrayType();
3577         checker->CreateBuiltinArraySignature(targetArrayType, targetArrayType->Rank());
3578     }
3579 
3580     if (targetType == checker->GetGlobalTypesHolder()->GlobalETSNeverType()) {
3581         return checker->TypeError(expr, diagnostic::CAST_TO_NEVER, expr->Start());
3582     }
3583 
3584     checker->ComputeApparentType(targetType);
3585     expr->SetTsType(targetType);
3586     return expr->TsType();
3587 }
3588 
Check(ir::TSEnumDeclaration * st) const3589 checker::Type *ETSAnalyzer::Check(ir::TSEnumDeclaration *st) const
3590 {
3591     // Some invalid TSEnumDeclaration will not be transformed to class.
3592     return ReturnTypeForStatement(st);
3593 }
3594 
Check(ir::TSInterfaceDeclaration * st) const3595 checker::Type *ETSAnalyzer::Check(ir::TSInterfaceDeclaration *st) const
3596 {
3597     if (st->TsType() != nullptr) {
3598         return st->TsType();
3599     }
3600 
3601     ETSChecker *checker = GetETSChecker();
3602     auto *stmtType = checker->BuildBasicInterfaceProperties(st);
3603     ES2PANDA_ASSERT(stmtType != nullptr);
3604 
3605     if (stmtType->IsTypeError()) {
3606         return st->SetTsType(stmtType);
3607     }
3608 
3609     auto *interfaceType = stmtType->AsETSObjectType();
3610     checker->CheckInterfaceAnnotations(st);
3611 
3612     interfaceType->SetSuperType(checker->GlobalETSObjectType());
3613     checker->CheckInvokeMethodsLegitimacy(interfaceType);
3614 
3615     st->SetTsType(interfaceType);
3616 
3617     checker::ScopeContext scopeCtx(checker, st->Scope());
3618     auto savedContext = checker::SavedCheckerContext(checker, checker::CheckerStatus::IN_INTERFACE, interfaceType);
3619 
3620     for (auto *it : st->Body()->Body()) {
3621         it->Check(checker);
3622     }
3623     return st->TsType();
3624 }
3625 
Check(ir::TSNonNullExpression * expr) const3626 checker::Type *ETSAnalyzer::Check(ir::TSNonNullExpression *expr) const
3627 {
3628     if (expr->TsType() != nullptr) {
3629         return expr->TsType();
3630     }
3631     ETSChecker *checker = GetETSChecker();
3632     auto exprType = expr->expr_->Check(checker);
3633     //  If the actual [smart] type is definitely 'null' or 'undefined' then probably CTE should be thrown.
3634     //  Anyway we'll definitely obtain NullPointerError at runtime.
3635     if (exprType->DefinitelyETSNullish()) {
3636         checker->LogDiagnostic(diagnostic::NULLISH_OPERAND, expr->Expr()->Start());
3637 
3638         if (expr->expr_->IsIdentifier()) {
3639             ES2PANDA_ASSERT(expr->expr_->AsIdentifier()->Variable() != nullptr);
3640             auto originalType = expr->expr_->AsIdentifier()->Variable()->TsType();
3641             if (originalType != nullptr) {
3642                 expr->SetTsType(checker->GetNonNullishType(originalType));
3643             }
3644         }
3645     }
3646 
3647     if (expr->TsType() == nullptr) {
3648         expr->SetTsType(checker->GetNonNullishType(exprType));
3649     }
3650     expr->SetOriginalType(expr->TsType());
3651     return expr->TsType();
3652 }
3653 
Check(ir::TSQualifiedName * expr) const3654 checker::Type *ETSAnalyzer::Check(ir::TSQualifiedName *expr) const
3655 {
3656     ETSChecker *checker = GetETSChecker();
3657     checker::Type *baseType = expr->Left()->Check(checker);
3658     if (baseType->IsETSObjectType()) {
3659         // clang-format off
3660         auto searchName = expr->Right()->Name();
3661         // clang-format on
3662         // NOTE (oeotvos) This should be done differently in the follow-up patch.
3663         if (searchName.Empty()) {
3664             searchName = expr->Right()->Name();
3665         }
3666         varbinder::Variable *prop =
3667             baseType->AsETSObjectType()->GetProperty(searchName, checker::PropertySearchFlags::SEARCH_DECL);
3668         // NOTE(dslynko): in debugger evaluation mode must lazily generate module's properties here.
3669         if (prop == nullptr) {
3670             checker->LogError(diagnostic::NONEXISTENT_TYPE, {expr->Right()->Name()}, expr->Right()->Start());
3671             return checker->GlobalTypeError();
3672         }
3673 
3674         checker->ValidateNamespaceProperty(prop, baseType->AsETSObjectType(), expr->Right());
3675         expr->Right()->SetVariable(prop);
3676         return checker->GetTypeOfVariable(prop);
3677     }
3678 
3679     checker->LogError(diagnostic::NONEXISTENT_TYPE, {expr->Right()->Name()}, expr->Right()->Start());
3680     return checker->GlobalTypeError();
3681 }
3682 
Check(ir::TSTypeAliasDeclaration * st) const3683 checker::Type *ETSAnalyzer::Check(ir::TSTypeAliasDeclaration *st) const
3684 {
3685     ETSChecker *checker = GetETSChecker();
3686     auto checkerContext = SavedCheckerContext(checker, CheckerStatus::NO_OPTS, checker->Context().ContainingClass());
3687 
3688     checker->CheckAnnotations(st->Annotations());
3689 
3690     if (st->TypeParams() == nullptr) {
3691         const checker::SavedTypeRelationFlagsContext savedFlagsCtx(
3692             checker->Relation(), checker::TypeRelationFlag::NO_THROW_GENERIC_TYPEALIAS);
3693 
3694         if (st->TypeAnnotation()->TsType() == nullptr) {
3695             st->TypeAnnotation()->Check(checker);
3696         }
3697 
3698         return ReturnTypeForStatement(st);
3699     }
3700 
3701     if (st->TypeParameterTypes().empty()) {
3702         auto [typeParamTypes, ok] = checker->CreateUnconstrainedTypeParameters(st->TypeParams());
3703         st->SetTypeParameterTypes(std::move(typeParamTypes));
3704         if (ok) {
3705             checker->AssignTypeParameterConstraints(st->TypeParams());
3706         }
3707     }
3708 
3709     const checker::SavedTypeRelationFlagsContext savedFlagsCtx(checker->Relation(),
3710                                                                checker::TypeRelationFlag::NO_THROW_GENERIC_TYPEALIAS);
3711 
3712     if (st->TypeAnnotation()->TsType() == nullptr) {
3713         st->TypeAnnotation()->Check(checker);
3714     }
3715 
3716     return ReturnTypeForStatement(st);
3717 }
3718 
ReturnTypeForStatement(const ir::Statement * const st) const3719 checker::Type *ETSAnalyzer::ReturnTypeForStatement([[maybe_unused]] const ir::Statement *const st) const
3720 {
3721     ES2PANDA_ASSERT(st->IsStatement());
3722     return nullptr;
3723 }
3724 
3725 }  // namespace ark::es2panda::checker
3726