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