1 /*
2 * Copyright (c) 2021-2024 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 "ETSAnalyzerHelpers.h"
17 #include "checker/types/ets/etsAsyncFuncReturnType.h"
18
19 namespace ark::es2panda::checker {
CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker * checker,checker::ETSObjectType * objType,ir::ScriptFunction * extensionFunc,checker::Signature * signature)20 void CheckExtensionIsShadowedInCurrentClassOrInterface(checker::ETSChecker *checker, checker::ETSObjectType *objType,
21 ir::ScriptFunction *extensionFunc, checker::Signature *signature)
22 {
23 const auto methodName = extensionFunc->Id()->Name();
24 // Only check if there are class and interfaces' instance methods which would shadow instance extension method
25 auto *const variable = objType->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(methodName);
26 if (variable == nullptr) {
27 return;
28 }
29
30 const auto *const funcType = variable->TsType()->AsETSFunctionType();
31 for (auto *funcSignature : funcType->CallSignatures()) {
32 signature->SetReturnType(funcSignature->ReturnType());
33 if (!checker->Relation()->IsCompatibleTo(signature, funcSignature)) {
34 continue;
35 }
36
37 checker->ReportWarning({"extension is shadowed by a instance member function '", funcType->Name(),
38 funcSignature, "' in class ", objType->Name()},
39 extensionFunc->Body()->Start());
40 return;
41 }
42 }
43
CheckExtensionIsShadowedByMethod(checker::ETSChecker * checker,checker::ETSObjectType * objType,ir::ScriptFunction * extensionFunc,checker::Signature * signature)44 void CheckExtensionIsShadowedByMethod(checker::ETSChecker *checker, checker::ETSObjectType *objType,
45 ir::ScriptFunction *extensionFunc, checker::Signature *signature)
46 {
47 if (objType == nullptr) {
48 return;
49 }
50
51 CheckExtensionIsShadowedInCurrentClassOrInterface(checker, objType, extensionFunc, signature);
52
53 for (auto *interface : objType->Interfaces()) {
54 CheckExtensionIsShadowedByMethod(checker, interface, extensionFunc, signature);
55 }
56
57 CheckExtensionIsShadowedByMethod(checker, objType->SuperType(), extensionFunc, signature);
58 }
59
ReplaceThisInExtensionMethod(checker::ETSChecker * checker,ir::ScriptFunction * extensionFunc)60 static void ReplaceThisInExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc)
61 {
62 ASSERT(!extensionFunc->Params().empty());
63 ASSERT(extensionFunc->Params()[0]->AsETSParameterExpression()->Ident()->Name() ==
64 varbinder::TypedBinder::MANDATORY_PARAM_THIS);
65 auto thisVariable = extensionFunc->Params()[0]->Variable();
66 extensionFunc->Body()->TransformChildrenRecursively(
67 [=](ir::AstNode *ast) {
68 if (ast->IsThisExpression()) {
69 auto *thisParam = checker->Allocator()->New<ir::Identifier>(
70 varbinder::TypedBinder::MANDATORY_PARAM_THIS, checker->Allocator());
71 thisParam->SetParent(ast->Parent());
72 thisParam->SetVariable(thisVariable);
73 return static_cast<ir::AstNode *>(thisParam);
74 }
75 return ast;
76 },
77 "replace-this-in-extension-method");
78 }
79
CheckExtensionMethod(checker::ETSChecker * checker,ir::ScriptFunction * extensionFunc,ir::MethodDefinition * node)80 void CheckExtensionMethod(checker::ETSChecker *checker, ir::ScriptFunction *extensionFunc, ir::MethodDefinition *node)
81 {
82 auto *const thisType = extensionFunc->Signature()->Params()[0]->TsType();
83
84 // "Extension Functions" are only allowed for classes, interfaces, and arrays.
85 if (thisType->IsETSArrayType() ||
86 (thisType->IsETSObjectType() &&
87 (thisType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::CLASS) ||
88 thisType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE)))) {
89 // Skip for arrays (array does not contain a class definition) and checked class definition.
90 if (!thisType->IsETSArrayType() && thisType->Variable()->Declaration()->Node()->IsClassDefinition() &&
91 !thisType->Variable()->Declaration()->Node()->AsClassDefinition()->IsClassDefinitionChecked()) {
92 thisType->Variable()->Declaration()->Node()->Check(checker);
93 }
94
95 // NOTE(gogabr): should be done in a lowering
96 ReplaceThisInExtensionMethod(checker, extensionFunc);
97
98 checker::SignatureInfo *originalExtensionSigInfo = checker->Allocator()->New<checker::SignatureInfo>(
99 extensionFunc->Signature()->GetSignatureInfo(), checker->Allocator());
100 originalExtensionSigInfo->minArgCount -= 1;
101 originalExtensionSigInfo->params.erase(originalExtensionSigInfo->params.begin());
102 checker::Signature *originalExtensionSigature =
103 checker->CreateSignature(originalExtensionSigInfo, extensionFunc->Signature()->ReturnType(), extensionFunc);
104
105 // The shadowing check is only relevant for classes and interfaces,
106 // since for arrays there are no other ways to declare a method other than "Extension Functions".
107 if (thisType->IsETSObjectType()) {
108 CheckExtensionIsShadowedByMethod(checker, thisType->AsETSObjectType(), extensionFunc,
109 originalExtensionSigature);
110 }
111 } else {
112 checker->LogTypeError("Extension function can only defined for class, interface or array.", node->Start());
113 }
114 }
115
DoBodyTypeChecking(ETSChecker * checker,ir::MethodDefinition * node,ir::ScriptFunction * scriptFunc)116 void DoBodyTypeChecking(ETSChecker *checker, ir::MethodDefinition *node, ir::ScriptFunction *scriptFunc)
117 {
118 if (scriptFunc->HasBody() && (node->IsNative() || node->IsAbstract() || node->IsDeclare())) {
119 checker->LogTypeError("Native, Abstract and Declare methods cannot have body.", scriptFunc->Body()->Start());
120 }
121
122 if (!scriptFunc->IsAsyncFunc() && scriptFunc->HasBody() &&
123 (!scriptFunc->IsExternal() || scriptFunc->IsExternalOverload())) {
124 checker::ScopeContext scopeCtx(checker, scriptFunc->Scope());
125 checker::SavedCheckerContext savedContext(checker, checker->Context().Status(),
126 checker->Context().ContainingClass());
127 checker->Context().SetContainingSignature(checker->GetSignatureFromMethodDefinition(node));
128
129 if (node->IsStatic() && !node->IsConstructor() &&
130 !checker->Context().ContainingClass()->HasObjectFlag(checker::ETSObjectFlags::GLOBAL)) {
131 checker->AddStatus(checker::CheckerStatus::IN_STATIC_CONTEXT);
132 }
133
134 if (node->IsConstructor()) {
135 checker->AddStatus(checker::CheckerStatus::IN_CONSTRUCTOR);
136 }
137
138 if (node->IsExtensionMethod()) {
139 CheckExtensionMethod(checker, scriptFunc, node);
140 }
141
142 scriptFunc->Body()->Check(checker);
143
144 if (scriptFunc->ReturnTypeAnnotation() == nullptr) {
145 if (scriptFunc->IsAsyncImplFunc()) {
146 ComposeAsyncImplFuncReturnType(checker, scriptFunc);
147 }
148
149 for (auto &returnStatement : scriptFunc->ReturnStatements()) {
150 returnStatement->SetReturnType(checker, scriptFunc->Signature()->ReturnType());
151 }
152 }
153
154 checker->Context().SetContainingSignature(nullptr);
155 }
156 }
157
ComposeAsyncImplFuncReturnType(ETSChecker * checker,ir::ScriptFunction * scriptFunc)158 void ComposeAsyncImplFuncReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)
159 {
160 auto const promiseType = checker->CreatePromiseOf(scriptFunc->Signature()->ReturnType());
161
162 auto *objectId =
163 checker->AllocNode<ir::Identifier>(compiler::Signatures::BUILTIN_OBJECT_CLASS, checker->Allocator());
164 checker->VarBinder()->AsETSBinder()->LookupTypeReference(objectId, false);
165 auto *returnType = checker->AllocNode<ir::ETSTypeReference>(
166 checker->AllocNode<ir::ETSTypeReferencePart>(objectId, nullptr, nullptr));
167 objectId->SetParent(returnType->Part());
168 returnType->Part()->SetParent(returnType);
169 returnType->SetTsType(
170 checker->Allocator()->New<ETSAsyncFuncReturnType>(checker->Allocator(), checker->Relation(), promiseType));
171 returnType->Check(checker);
172 scriptFunc->Signature()->SetReturnType(returnType->TsType());
173 }
174
ComposeAsyncImplMethod(ETSChecker * checker,ir::MethodDefinition * node)175 void ComposeAsyncImplMethod(ETSChecker *checker, ir::MethodDefinition *node)
176 {
177 auto *classDef = checker->FindAncestorGivenByType(node, ir::AstNodeType::CLASS_DEFINITION)->AsClassDefinition();
178 auto *scriptFunc = node->Function();
179 ir::MethodDefinition *implMethod = checker->CreateAsyncProxy(node, classDef);
180
181 implMethod->Check(checker);
182 node->SetAsyncPairMethod(implMethod);
183
184 if (scriptFunc->Signature()->HasSignatureFlag(SignatureFlags::NEED_RETURN_TYPE)) {
185 node->Function()->Signature()->SetReturnType(
186 implMethod->Function()->Signature()->ReturnType()->AsETSAsyncFuncReturnType()->PromiseType());
187 scriptFunc->Signature()->RemoveSignatureFlag(SignatureFlags::NEED_RETURN_TYPE);
188 }
189
190 if (node->Function()->IsOverload()) {
191 auto *baseOverloadImplMethod = node->BaseOverloadMethod()->AsyncPairMethod();
192 implMethod->Function()->Id()->SetVariable(baseOverloadImplMethod->Function()->Id()->Variable());
193 baseOverloadImplMethod->AddOverload(implMethod);
194 } else {
195 classDef->Body().push_back(implMethod);
196 }
197 }
198
CheckPredefinedMethodReturnType(ETSChecker * checker,ir::ScriptFunction * scriptFunc)199 void CheckPredefinedMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc)
200 {
201 auto const &position = scriptFunc->Start();
202
203 if (scriptFunc->IsSetter() && (scriptFunc->Signature()->ReturnType() != checker->GlobalVoidType())) {
204 checker->LogTypeError("Setter must have void return type", position);
205 }
206
207 if (scriptFunc->IsGetter() && (scriptFunc->Signature()->ReturnType() == checker->GlobalVoidType())) {
208 checker->LogTypeError("Getter must return a value", position);
209 }
210
211 auto const name = scriptFunc->Id()->Name();
212 auto const methodName = std::string {ir::PREDEFINED_METHOD} + std::string {name.Utf8()};
213
214 if (name.Is(compiler::Signatures::GET_INDEX_METHOD)) {
215 if (scriptFunc->Signature()->ReturnType() == checker->GlobalVoidType()) {
216 checker->LogTypeError(methodName + "' shouldn't have void return type.", position);
217 }
218 } else if (name.Is(compiler::Signatures::SET_INDEX_METHOD)) {
219 if (scriptFunc->Signature()->ReturnType() != checker->GlobalVoidType()) {
220 checker->LogTypeError(methodName + "' should have void return type.", position);
221 }
222 } else if (name.Is(compiler::Signatures::ITERATOR_METHOD)) {
223 CheckIteratorMethodReturnType(checker, scriptFunc, position, methodName);
224 }
225 }
226
HasIteratorInterface(ETSObjectType const * const objectType)227 static bool HasIteratorInterface(ETSObjectType const *const objectType)
228 {
229 auto const hasIteratorInterfaceImpl = [](ETSObjectType const *const checkType,
230 auto &&iteratorInterfaceImpl) -> bool {
231 if (checkType->Name().Is(ir::ITERATOR_INTERFACE_NAME)) {
232 return true;
233 }
234 for (const auto *const interface : checkType->Interfaces()) {
235 if (iteratorInterfaceImpl(interface, iteratorInterfaceImpl)) {
236 return true;
237 }
238 }
239 return false;
240 };
241
242 return hasIteratorInterfaceImpl(objectType, hasIteratorInterfaceImpl);
243 }
244
CheckIteratorMethodReturnType(ETSChecker * checker,ir::ScriptFunction * scriptFunc,const lexer::SourcePosition & position,const std::string & methodName)245 void CheckIteratorMethodReturnType(ETSChecker *checker, ir::ScriptFunction *scriptFunc,
246 const lexer::SourcePosition &position, const std::string &methodName)
247 {
248 const auto *returnType = scriptFunc->Signature()->ReturnType();
249
250 if (returnType == nullptr) {
251 checker->LogTypeError(methodName + "' doesn't have return type.", position);
252 }
253
254 if (returnType->IsETSTypeParameter()) {
255 returnType = checker->GetApparentType(returnType->AsETSTypeParameter()->GetConstraintType());
256 }
257
258 if (returnType->IsETSObjectType() && HasIteratorInterface(returnType->AsETSObjectType())) {
259 return;
260 }
261
262 while (returnType->IsETSObjectType() && returnType->AsETSObjectType()->SuperType() != nullptr) {
263 returnType = returnType->AsETSObjectType()->SuperType();
264 if (returnType->IsETSObjectType() && HasIteratorInterface(returnType->AsETSObjectType())) {
265 return;
266 }
267 }
268
269 checker->LogTypeError(
270 {"The return type of '", scriptFunc->Id()->Name(), "' must be a type that implements Iterator interface."},
271 position);
272 }
273
InitAnonymousLambdaCallee(checker::ETSChecker * checker,ir::Expression * callee,checker::Type * calleeType)274 checker::Type *InitAnonymousLambdaCallee(checker::ETSChecker *checker, ir::Expression *callee,
275 checker::Type *calleeType)
276 {
277 auto *const arrowFunc = callee->AsArrowFunctionExpression()->Function();
278
279 ArenaVector<ir::Expression *> params {checker->Allocator()->Adapter()};
280 checker->CopyParams(arrowFunc->Params(), params);
281 checker::Type *funcReturnType = nullptr;
282
283 auto *typeAnnotation = arrowFunc->ReturnTypeAnnotation();
284 if (typeAnnotation != nullptr) {
285 typeAnnotation = typeAnnotation->Clone(checker->Allocator(), nullptr);
286 typeAnnotation->SetTsType(arrowFunc->ReturnTypeAnnotation()->TsType());
287 } else if (arrowFunc->Signature()->ReturnType() != nullptr) {
288 auto newTypeAnnotation = callee->AsArrowFunctionExpression()->CreateTypeAnnotation(checker);
289 typeAnnotation = arrowFunc->ReturnTypeAnnotation();
290 funcReturnType = newTypeAnnotation->GetType(checker);
291 }
292
293 auto signature = ir::FunctionSignature(nullptr, std::move(params), typeAnnotation);
294 auto *funcType = checker->AllocNode<ir::ETSFunctionType>(std::move(signature), ir::ScriptFunctionFlags::NONE);
295
296 funcType->SetScope(arrowFunc->Scope()->AsFunctionScope()->ParamScope());
297 auto *const funcIface = typeAnnotation != nullptr ? funcType->Check(checker) : funcReturnType;
298 checker->Relation()->SetNode(callee);
299 checker->Relation()->IsAssignableTo(calleeType, funcIface);
300 return funcIface;
301 }
302
ResolveCallExtensionFunction(checker::ETSFunctionType * functionType,checker::ETSChecker * checker,ir::CallExpression * expr)303 checker::Signature *ResolveCallExtensionFunction(checker::ETSFunctionType *functionType, checker::ETSChecker *checker,
304 ir::CallExpression *expr)
305 {
306 auto *memberExpr = expr->Callee()->AsMemberExpression();
307 expr->Arguments().insert(expr->Arguments().begin(), memberExpr->Object());
308 auto *signature =
309 checker->ResolveCallExpressionAndTrailingLambda(functionType->CallSignatures(), expr, expr->Start());
310 if (signature == nullptr) {
311 return nullptr;
312 }
313 if (!signature->Function()->IsExtensionMethod()) {
314 checker->LogTypeError({"Property '", memberExpr->Property()->AsIdentifier()->Name(),
315 "' does not exist on type '", memberExpr->ObjType()->Name(), "'"},
316 memberExpr->Property()->Start());
317 }
318 expr->SetSignature(signature);
319 expr->SetCallee(memberExpr->Property());
320 memberExpr->Property()->AsIdentifier()->SetParent(expr);
321 expr->Arguments()[0]->SetParent(expr);
322 checker->HandleUpdatedCallExpressionNode(expr);
323 // Set TsType for new Callee(original member expression's Object)
324 expr->Callee()->Check(checker);
325 return signature;
326 }
327
ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType * type,checker::ETSChecker * checker,ir::CallExpression * expr)328 checker::Signature *ResolveCallForETSExtensionFuncHelperType(checker::ETSExtensionFuncHelperType *type,
329 checker::ETSChecker *checker, ir::CallExpression *expr)
330 {
331 checker::Signature *signature = checker->ResolveCallExpressionAndTrailingLambda(
332 type->ClassMethodType()->CallSignatures(), expr, expr->Start(), checker::TypeRelationFlag::NO_THROW);
333
334 if (signature != nullptr) {
335 if (expr->Callee()->IsMemberExpression()) {
336 auto memberExpr = expr->Callee()->AsMemberExpression();
337 auto var = type->ClassMethodType()->Variable();
338 memberExpr->Property()->AsIdentifier()->SetVariable(var);
339 }
340
341 return signature;
342 }
343
344 return ResolveCallExtensionFunction(type->ExtensionMethodType(), checker, expr);
345 }
346
GetUnionTypeSignatures(ETSChecker * checker,checker::ETSUnionType * etsUnionType)347 ArenaVector<checker::Signature *> GetUnionTypeSignatures(ETSChecker *checker, checker::ETSUnionType *etsUnionType)
348 {
349 ArenaVector<checker::Signature *> callSignatures(checker->Allocator()->Adapter());
350
351 for (auto *constituentType : etsUnionType->ConstituentTypes()) {
352 if (constituentType->IsETSObjectType() &&
353 constituentType->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE)) {
354 ArenaVector<checker::Signature *> tmpCallSignatures(checker->Allocator()->Adapter());
355 tmpCallSignatures =
356 constituentType->AsETSObjectType()
357 ->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME)
358 ->TsType()
359 ->AsETSFunctionType()
360 ->CallSignatures();
361 callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
362 }
363 if (constituentType->IsETSFunctionType()) {
364 ArenaVector<checker::Signature *> tmpCallSignatures(checker->Allocator()->Adapter());
365 tmpCallSignatures = constituentType->AsETSFunctionType()->CallSignatures();
366 callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
367 }
368 if (constituentType->IsETSUnionType()) {
369 ArenaVector<checker::Signature *> tmpCallSignatures(checker->Allocator()->Adapter());
370 tmpCallSignatures = GetUnionTypeSignatures(checker, constituentType->AsETSUnionType());
371 callSignatures.insert(callSignatures.end(), tmpCallSignatures.begin(), tmpCallSignatures.end());
372 }
373 }
374
375 return callSignatures;
376 }
377
ChooseSignatures(ETSChecker * checker,checker::Type * calleeType,bool isConstructorCall,bool isFunctionalInterface,bool isUnionTypeWithFunctionalInterface)378 ArenaVector<checker::Signature *> &ChooseSignatures(ETSChecker *checker, checker::Type *calleeType,
379 bool isConstructorCall, bool isFunctionalInterface,
380 bool isUnionTypeWithFunctionalInterface)
381 {
382 static ArenaVector<checker::Signature *> unionSignatures(checker->Allocator()->Adapter());
383 unionSignatures.clear();
384 if (isConstructorCall) {
385 return calleeType->AsETSObjectType()->ConstructSignatures();
386 }
387 if (isFunctionalInterface) {
388 return calleeType->AsETSObjectType()
389 ->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME)
390 ->TsType()
391 ->AsETSFunctionType()
392 ->CallSignatures();
393 }
394 if (isUnionTypeWithFunctionalInterface) {
395 unionSignatures = GetUnionTypeSignatures(checker, calleeType->AsETSUnionType());
396 return unionSignatures;
397 }
398 return calleeType->AsETSFunctionType()->CallSignatures();
399 }
400
ChooseCalleeObj(ETSChecker * checker,ir::CallExpression * expr,checker::Type * calleeType,bool isConstructorCall)401 checker::ETSObjectType *ChooseCalleeObj(ETSChecker *checker, ir::CallExpression *expr, checker::Type *calleeType,
402 bool isConstructorCall)
403 {
404 if (isConstructorCall) {
405 return calleeType->AsETSObjectType();
406 }
407 if (expr->Callee()->IsIdentifier()) {
408 return checker->Context().ContainingClass();
409 }
410 ASSERT(expr->Callee()->IsMemberExpression());
411 return expr->Callee()->AsMemberExpression()->ObjType();
412 }
413
ProcessExclamationMark(ETSChecker * checker,ir::UnaryExpression * expr,checker::Type * operandType)414 void ProcessExclamationMark(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)
415 {
416 if (checker->IsNullLikeOrVoidExpression(expr->Argument())) {
417 auto tsType = checker->CreateETSBooleanType(true);
418 tsType->AddTypeFlag(checker::TypeFlag::CONSTANT);
419 expr->SetTsType(tsType);
420 return;
421 }
422
423 if (operandType == nullptr || !operandType->IsConditionalExprType()) {
424 checker->LogTypeError("Bad operand type, the type of the operand must be boolean type.",
425 expr->Argument()->Start());
426 expr->SetTsType(checker->GlobalTypeError());
427 return;
428 }
429
430 auto exprRes = operandType->ResolveConditionExpr();
431 if (std::get<0>(exprRes)) {
432 auto tsType = checker->CreateETSBooleanType(!std::get<1>(exprRes));
433 tsType->AddTypeFlag(checker::TypeFlag::CONSTANT);
434 expr->SetTsType(tsType);
435 return;
436 }
437
438 expr->SetTsType(checker->GlobalETSBooleanType());
439 }
440
SetTsTypeForUnaryExpression(ETSChecker * checker,ir::UnaryExpression * expr,checker::Type * operandType)441 void SetTsTypeForUnaryExpression(ETSChecker *checker, ir::UnaryExpression *expr, checker::Type *operandType)
442 {
443 switch (expr->OperatorType()) {
444 case lexer::TokenType::PUNCTUATOR_MINUS:
445 case lexer::TokenType::PUNCTUATOR_PLUS: {
446 if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
447 checker->LogTypeError("Bad operand type, the type of the operand must be numeric type.",
448 expr->Argument()->Start());
449 expr->SetTsType(checker->GlobalTypeError());
450 break;
451 }
452
453 if (operandType->HasTypeFlag(checker::TypeFlag::CONSTANT) &&
454 expr->OperatorType() == lexer::TokenType::PUNCTUATOR_MINUS) {
455 expr->SetTsType(checker->NegateNumericType(operandType, expr));
456 break;
457 }
458
459 expr->SetTsType(operandType);
460 break;
461 }
462 case lexer::TokenType::PUNCTUATOR_TILDE: {
463 if (operandType == nullptr || !operandType->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
464 checker->LogTypeError("Bad operand type, the type of the operand must be numeric type.",
465 expr->Argument()->Start());
466 expr->SetTsType(checker->GlobalTypeError());
467 break;
468 }
469
470 if (operandType->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
471 expr->SetTsType(checker->BitwiseNegateNumericType(operandType, expr));
472 break;
473 }
474
475 expr->SetTsType(checker->SelectGlobalIntegerTypeForNumeric(operandType));
476 break;
477 }
478 case lexer::TokenType::PUNCTUATOR_EXCLAMATION_MARK: {
479 ProcessExclamationMark(checker, expr, operandType);
480 break;
481 }
482 case lexer::TokenType::PUNCTUATOR_DOLLAR_DOLLAR: {
483 expr->SetTsType(expr->Argument()->TsType());
484 break;
485 }
486 default: {
487 UNREACHABLE();
488 break;
489 }
490 }
491 }
492
CreateSyntheticType(ETSChecker * checker,util::StringView const & syntheticName,checker::ETSObjectType * lastObjectType,ir::Identifier * id)493 checker::ETSObjectType *CreateSyntheticType(ETSChecker *checker, util::StringView const &syntheticName,
494 checker::ETSObjectType *lastObjectType, ir::Identifier *id)
495 {
496 auto *syntheticObjType = checker->Allocator()->New<checker::ETSObjectType>(
497 checker->Allocator(), syntheticName, syntheticName,
498 std::make_tuple(id, checker::ETSObjectFlags::NO_OPTS, checker->Relation()));
499
500 auto *classDecl = checker->Allocator()->New<varbinder::ClassDecl>(syntheticName);
501 varbinder::LocalVariable *var =
502 checker->Allocator()->New<varbinder::LocalVariable>(classDecl, varbinder::VariableFlags::CLASS);
503 var->SetTsType(syntheticObjType);
504 lastObjectType->AddProperty<checker::PropertyType::STATIC_FIELD>(var);
505 syntheticObjType->SetEnclosingType(lastObjectType);
506 return syntheticObjType;
507 }
508
509 // NOLINTBEGIN(modernize-avoid-c-arrays)
510 static constexpr char const INVALID_CONST_ASSIGNMENT[] = "Cannot assign a value to a constant variable ";
511 static constexpr char const INVALID_READONLY_ASSIGNMENT[] = "Cannot assign a value to a readonly variable ";
512 static constexpr char const ITERATOR_TYPE_ABSENT[] = "Cannot obtain iterator type in 'for-of' statement.";
513 // NOLINTEND(modernize-avoid-c-arrays)
514
GetIteratorType(ETSChecker * checker,checker::Type * elemType,ir::AstNode * left)515 checker::Type *GetIteratorType(ETSChecker *checker, checker::Type *elemType, ir::AstNode *left)
516 {
517 // Just to avoid extra nested level(s)
518 // CC-OFFNXT(G.FMT.14-CPP) project code style
519 auto const getIterType = [checker, elemType](ir::VariableDeclarator *const declarator) -> checker::Type * {
520 if (declarator->TsType() == nullptr) {
521 if (auto *resolved = checker->FindVariableInFunctionScope(declarator->Id()->AsIdentifier()->Name(),
522 varbinder::ResolveBindingOptions::ALL_NON_TYPE);
523 resolved != nullptr) {
524 resolved->SetTsType(elemType);
525 return elemType;
526 }
527 } else {
528 return declarator->TsType();
529 }
530 return checker->GlobalTypeError();
531 };
532
533 checker::Type *iterType = nullptr;
534 if (left->IsIdentifier()) {
535 if (auto *const variable = left->AsIdentifier()->Variable(); variable != nullptr) {
536 auto *decl = variable->Declaration();
537 if (decl->IsConstDecl() || decl->IsReadonlyDecl()) {
538 std::string_view errorMsg =
539 decl->IsConstDecl() ? INVALID_CONST_ASSIGNMENT : INVALID_READONLY_ASSIGNMENT;
540 checker->LogTypeError({errorMsg, variable->Name()}, decl->Node()->Start());
541 }
542 }
543 iterType = left->AsIdentifier()->TsType();
544 } else if (left->IsVariableDeclaration()) {
545 if (auto const &declarators = left->AsVariableDeclaration()->Declarators(); !declarators.empty()) {
546 iterType = getIterType(declarators.front());
547 }
548 }
549
550 if (iterType == nullptr) {
551 checker->LogTypeError(ITERATOR_TYPE_ABSENT, left->Start());
552 return checker->GlobalTypeError();
553 }
554 return iterType;
555 }
556
CheckArgumentVoidType(checker::Type * & funcReturnType,ETSChecker * checker,const std::string & name,ir::ReturnStatement * st)557 bool CheckArgumentVoidType(checker::Type *&funcReturnType, ETSChecker *checker, const std::string &name,
558 ir::ReturnStatement *st)
559 {
560 if (name.find(compiler::Signatures::ETS_MAIN_WITH_MANGLE_BEGIN) != std::string::npos) {
561 if (!funcReturnType->IsETSVoidType() && !funcReturnType->IsIntType()) {
562 checker->LogTypeError("Bad return type, main enable only void or int type.", st->Start());
563 }
564 }
565 return true;
566 }
567
CheckReturnType(ETSChecker * checker,checker::Type * funcReturnType,checker::Type * argumentType,ir::Expression * stArgument,bool isAsync)568 bool CheckReturnType(ETSChecker *checker, checker::Type *funcReturnType, checker::Type *argumentType,
569 ir::Expression *stArgument, bool isAsync)
570 {
571 if (funcReturnType->IsETSVoidType() || funcReturnType == checker->GlobalVoidType()) {
572 if (argumentType != checker->GlobalVoidType()) {
573 checker->LogTypeError("Unexpected return value, enclosing method return type is void.",
574 stArgument->Start());
575 return false;
576 }
577 if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType,
578 stArgument->Start(), {},
579 checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
580 // CC-OFFNXT(G.FMT.02) project code style
581 .IsAssignable()) {
582 checker->LogTypeError({"Return statement type is not compatible with the enclosing method's return type."},
583 stArgument->Start());
584 return false;
585 }
586 return true;
587 }
588
589 if (isAsync && funcReturnType->IsETSObjectType() &&
590 funcReturnType->AsETSObjectType()->GetOriginalBaseType() == checker->GlobalBuiltinPromiseType()) {
591 auto promiseArg = funcReturnType->AsETSObjectType()->TypeArguments()[0];
592 checker::AssignmentContext(checker->Relation(), stArgument, argumentType, promiseArg, stArgument->Start(), {},
593 checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW);
594 if (checker->Relation()->IsTrue()) {
595 return true;
596 }
597 }
598
599 const Type *targetType = checker->TryGettingFunctionTypeFromInvokeFunction(funcReturnType);
600 const Type *sourceType = checker->TryGettingFunctionTypeFromInvokeFunction(argumentType);
601 if (!checker::AssignmentContext(checker->Relation(), stArgument, argumentType, funcReturnType, stArgument->Start(),
602 {}, checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
603 // CC-OFFNXT(G.FMT.02) project code style
604 .IsAssignable()) {
605 checker->LogTypeError(
606 {"Type '", sourceType, "' is not compatible with the enclosing method's return type '", targetType, "'"},
607 stArgument->Start());
608 return false;
609 }
610 return true;
611 }
612
InferReturnType(ETSChecker * checker,ir::ScriptFunction * containingFunc,checker::Type * & funcReturnType,ir::Expression * stArgument)613 void InferReturnType(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType,
614 ir::Expression *stArgument)
615 {
616 // First (or single) return statement in the function:
617 funcReturnType =
618 stArgument == nullptr ? checker->GlobalVoidType() : checker->GetNonConstantType(stArgument->Check(checker));
619 /*
620 when st_argment is ArrowFunctionExpression, need infer type for st_argment
621 example code:
622 ```
623 return () => {}
624 ```
625 */
626 if (stArgument != nullptr && stArgument->IsArrowFunctionExpression()) {
627 auto arrowFunc = stArgument->AsArrowFunctionExpression();
628 auto typeAnnotation = arrowFunc->CreateTypeAnnotation(checker);
629
630 auto *argumentType = arrowFunc->TsType();
631 funcReturnType = typeAnnotation->GetType(checker);
632
633 const Type *sourceType = checker->TryGettingFunctionTypeFromInvokeFunction(argumentType);
634 const Type *targetType = checker->TryGettingFunctionTypeFromInvokeFunction(funcReturnType);
635
636 if (!checker::AssignmentContext(checker->Relation(), arrowFunc, argumentType, funcReturnType,
637 stArgument->Start(), {},
638 checker::TypeRelationFlag::DIRECT_RETURN | checker::TypeRelationFlag::NO_THROW)
639 // CC-OFFNXT(G.FMT.02) project code style
640 .IsAssignable()) {
641 checker->LogTypeError({"Type '", sourceType,
642 "' is not compatible with the enclosing method's return type '", targetType, "'"},
643 stArgument->Start());
644 funcReturnType = checker->GlobalTypeError();
645 return;
646 }
647 }
648
649 containingFunc->Signature()->SetReturnType(funcReturnType);
650 containingFunc->Signature()->RemoveSignatureFlag(checker::SignatureFlags::NEED_RETURN_TYPE);
651 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
652 checker->VarBinder()->AsETSBinder()->BuildFunctionName(containingFunc);
653
654 if (stArgument != nullptr && stArgument->IsObjectExpression()) {
655 stArgument->AsObjectExpression()->SetPreferredType(funcReturnType);
656 }
657 }
658
ProcessReturnStatements(ETSChecker * checker,ir::ScriptFunction * containingFunc,checker::Type * & funcReturnType,ir::ReturnStatement * st,ir::Expression * stArgument)659 void ProcessReturnStatements(ETSChecker *checker, ir::ScriptFunction *containingFunc, checker::Type *&funcReturnType,
660 ir::ReturnStatement *st, ir::Expression *stArgument)
661 {
662 funcReturnType = containingFunc->Signature()->ReturnType();
663
664 if (stArgument == nullptr) {
665 // previous return statement(s) have value
666 if (!funcReturnType->IsETSVoidType() && funcReturnType != checker->GlobalVoidType()) {
667 checker->LogTypeError("All return statements in the function should be empty or have a value.",
668 st->Start());
669 return;
670 }
671 } else {
672 if (stArgument->IsObjectExpression()) {
673 stArgument->AsObjectExpression()->SetPreferredType(funcReturnType);
674 }
675
676 if (stArgument->IsMemberExpression()) {
677 checker->SetArrayPreferredTypeForNestedMemberExpressions(stArgument->AsMemberExpression(), funcReturnType);
678 }
679
680 checker::Type *argumentType = checker->GetNonConstantType(stArgument->Check(checker));
681
682 // previous return statement(s) don't have any value
683 if (funcReturnType->IsETSVoidType() && !argumentType->IsETSVoidType()) {
684 checker->LogTypeError("All return statements in the function should be empty or have a value.",
685 stArgument->Start());
686 return;
687 }
688
689 const auto name = containingFunc->Scope()->InternalName().Mutf8();
690 if (!CheckArgumentVoidType(funcReturnType, checker, name, st)) {
691 return;
692 }
693
694 auto *const relation = checker->Relation();
695 relation->SetNode(stArgument);
696
697 if (!relation->IsIdenticalTo(funcReturnType, argumentType)) {
698 checker->ResolveReturnStatement(funcReturnType, argumentType, containingFunc, st);
699 }
700
701 relation->SetNode(nullptr);
702 relation->SetFlags(checker::TypeRelationFlag::NONE);
703 }
704 }
705
CreateOptionalSignaturesForFunctionalType(ETSChecker * checker,ir::ETSFunctionType * node,ETSObjectType * genericInterfaceType,size_t optionalParameterIndex)706 ETSObjectType *CreateOptionalSignaturesForFunctionalType(ETSChecker *checker, ir::ETSFunctionType *node,
707 ETSObjectType *genericInterfaceType,
708 size_t optionalParameterIndex)
709 {
710 auto substitution = checker->NewSubstitution();
711 const auto ¶ms = node->Params();
712 auto returnType = node->ReturnType()->GetType(checker);
713
714 for (size_t i = 0; i < optionalParameterIndex; i++) {
715 checker::ETSChecker::EmplaceSubstituted(
716 substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
717 InstantiateBoxedPrimitiveType(checker, params[i],
718 params[i]->AsETSParameterExpression()->TypeAnnotation()->GetType(checker)));
719 }
720
721 for (size_t i = optionalParameterIndex; i < params.size(); i++) {
722 checker::ETSChecker::EmplaceSubstituted(
723 substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
724 CreateParamTypeWithDefaultParam(checker, params[i]));
725 }
726
727 checker::ETSChecker::EmplaceSubstituted(
728 substitution,
729 genericInterfaceType->TypeArguments()[genericInterfaceType->TypeArguments().size() - 1]
730 ->AsETSTypeParameter()
731 ->GetOriginal(),
732 InstantiateBoxedPrimitiveType(checker, node->ReturnType(), returnType));
733
734 return genericInterfaceType->Substitute(checker->Relation(), substitution)->AsETSObjectType();
735 }
736
CreateInterfaceTypeForETSFunctionType(ETSChecker * checker,ir::ETSFunctionType * node,ETSObjectType * genericInterfaceType)737 ETSObjectType *CreateInterfaceTypeForETSFunctionType(ETSChecker *checker, ir::ETSFunctionType *node,
738 ETSObjectType *genericInterfaceType)
739 {
740 auto substitution = checker->NewSubstitution();
741 size_t i = 0;
742 if (auto const ¶ms = node->Params(); params.size() < checker->GlobalBuiltinFunctionTypeVariadicThreshold()) {
743 for (; i < params.size(); i++) {
744 checker::ETSChecker::EmplaceSubstituted(
745 substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
746 InstantiateBoxedPrimitiveType(
747 checker, params[i], params[i]->AsETSParameterExpression()->TypeAnnotation()->GetType(checker)));
748 }
749 }
750
751 checker::ETSChecker::EmplaceSubstituted(
752 substitution, genericInterfaceType->TypeArguments()[i]->AsETSTypeParameter()->GetOriginal(),
753 InstantiateBoxedPrimitiveType(checker, node->ReturnType(), node->ReturnType()->GetType(checker)));
754 return genericInterfaceType->Substitute(checker->Relation(), substitution)->AsETSObjectType();
755 }
756
CreateParamTypeWithDefaultParam(ETSChecker * checker,ir::Expression * param)757 Type *CreateParamTypeWithDefaultParam(ETSChecker *checker, ir::Expression *param)
758 {
759 if (!param->AsETSParameterExpression()->IsDefault()) {
760 checker->LogTypeError({"Expected initializer for ", param->AsETSParameterExpression()->Ident()->Name()},
761 param->Start());
762 }
763
764 ArenaVector<Type *> types(checker->Allocator()->Adapter());
765 types.push_back(InstantiateBoxedPrimitiveType(
766 checker, param, param->AsETSParameterExpression()->TypeAnnotation()->GetType(checker)));
767
768 if (param->AsETSParameterExpression()->Initializer()->IsUndefinedLiteral()) {
769 types.push_back(checker->GlobalETSUndefinedType());
770 }
771
772 return checker->CreateETSUnionType(Span<Type *const>(types));
773 }
774
InstantiateBoxedPrimitiveType(ETSChecker * checker,ir::Expression * param,Type * paramType)775 Type *InstantiateBoxedPrimitiveType(ETSChecker *checker, ir::Expression *param, Type *paramType)
776 {
777 if (paramType->IsETSReferenceType()) {
778 return paramType;
779 }
780
781 auto node = checker->Relation()->GetNode();
782 checker->Relation()->SetNode(param);
783 auto boxedTypeArg = checker->MaybeBoxInRelation(paramType);
784 paramType = boxedTypeArg->Instantiate(checker->Allocator(), checker->Relation(), checker->GetGlobalTypesHolder());
785 checker->Relation()->SetNode(node);
786 ASSERT(paramType->IsETSReferenceType());
787
788 return paramType;
789 }
790 } // namespace ark::es2panda::checker
791