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