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 "aliveAnalyzer.h" 17#include <cstddef> 18 19#include "checker/types/ets/etsAsyncFuncReturnType.h" 20#include "ir/base/classDefinition.h" 21#include "ir/base/classProperty.h" 22#include "ir/base/methodDefinition.h" 23#include "ir/base/scriptFunction.h" 24#include "ir/statements/classDeclaration.h" 25#include "ir/statements/variableDeclaration.h" 26#include "ir/statements/doWhileStatement.h" 27#include "ir/statements/expressionStatement.h" 28#include "ir/statements/whileStatement.h" 29#include "ir/statements/forUpdateStatement.h" 30#include "ir/statements/labelledStatement.h" 31#include "ir/statements/forOfStatement.h" 32#include "ir/statements/blockStatement.h" 33#include "ir/statements/ifStatement.h" 34#include "ir/statements/switchStatement.h" 35#include "ir/statements/variableDeclarator.h" 36#include "ir/statements/throwStatement.h" 37#include "ir/statements/switchCaseStatement.h" 38#include "ir/statements/breakStatement.h" 39#include "ir/statements/continueStatement.h" 40#include "ir/statements/returnStatement.h" 41#include "ir/statements/tryStatement.h" 42#include "ir/expressions/callExpression.h" 43#include "ir/expressions/identifier.h" 44#include "ir/ets/etsNewClassInstanceExpression.h" 45#include "ir/ets/etsStructDeclaration.h" 46#include "ir/ts/tsInterfaceDeclaration.h" 47#include "checker/types/globalTypesHolder.h" 48#include "varbinder/variable.h" 49#include "varbinder/declaration.h" 50#include "checker/ETSchecker.h" 51#include "ir/base/catchClause.h" 52 53namespace ark::es2panda::checker { 54 55void AliveAnalyzer::AnalyzeNodes(const ir::AstNode *node) 56{ 57 node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); }); 58} 59 60void AliveAnalyzer::AnalyzeNode(const ir::AstNode *node) 61{ 62 if (node == nullptr) { 63 return; 64 } 65 66 switch (node->Type()) { 67 case ir::AstNodeType::EXPRESSION_STATEMENT: { 68 AnalyzeNode(node->AsExpressionStatement()->GetExpression()); 69 break; 70 } 71 case ir::AstNodeType::STRUCT_DECLARATION: { 72 AnalyzeStructDecl(node->AsETSStructDeclaration()); 73 break; 74 } 75 case ir::AstNodeType::CLASS_DECLARATION: { 76 AnalyzeClassDecl(node->AsClassDeclaration()); 77 break; 78 } 79 case ir::AstNodeType::METHOD_DEFINITION: { 80 AnalyzeMethodDef(node->AsMethodDefinition()); 81 break; 82 } 83 case ir::AstNodeType::VARIABLE_DECLARATION: { 84 AnalyzeVarDef(node->AsVariableDeclaration()); 85 break; 86 } 87 case ir::AstNodeType::BLOCK_STATEMENT: { 88 AnalyzeStats(node->AsBlockStatement()->Statements()); 89 break; 90 } 91 case ir::AstNodeType::DO_WHILE_STATEMENT: { 92 AnalyzeDoLoop(node->AsDoWhileStatement()); 93 break; 94 } 95 default: { 96 break; 97 } 98 } 99 100 // Helpers to reduce function size and pass code checker 101 AnalyzeNodeHelper1(node); 102 AnalyzeNodeHelper2(node); 103} 104 105// Helper function to reduce AnalyzeNode size and pass code checker 106void AliveAnalyzer::AnalyzeNodeHelper1(const ir::AstNode *node) 107{ 108 switch (node->Type()) { 109 case ir::AstNodeType::WHILE_STATEMENT: { 110 AnalyzeWhileLoop(node->AsWhileStatement()); 111 break; 112 } 113 case ir::AstNodeType::FOR_UPDATE_STATEMENT: { 114 AnalyzeForLoop(node->AsForUpdateStatement()); 115 break; 116 } 117 case ir::AstNodeType::FOR_OF_STATEMENT: { 118 AnalyzeForOfLoop(node->AsForOfStatement()); 119 break; 120 } 121 case ir::AstNodeType::IF_STATEMENT: { 122 AnalyzeIf(node->AsIfStatement()); 123 break; 124 } 125 case ir::AstNodeType::LABELLED_STATEMENT: { 126 AnalyzeLabelled(node->AsLabelledStatement()); 127 break; 128 } 129 case ir::AstNodeType::ETS_NEW_CLASS_INSTANCE_EXPRESSION: { 130 AnalyzeNewClass(node->AsETSNewClassInstanceExpression()); 131 break; 132 } 133 case ir::AstNodeType::CALL_EXPRESSION: { 134 AnalyzeCall(node->AsCallExpression()); 135 break; 136 } 137 case ir::AstNodeType::THROW_STATEMENT: { 138 AnalyzeThrow(node->AsThrowStatement()); 139 break; 140 } 141 case ir::AstNodeType::SWITCH_STATEMENT: { 142 AnalyzeSwitch(node->AsSwitchStatement()); 143 break; 144 } 145 default: { 146 break; 147 } 148 } 149} 150 151// Helper function to reduce AnalyzeNode size and pass code checker 152void AliveAnalyzer::AnalyzeNodeHelper2(const ir::AstNode *node) 153{ 154 switch (node->Type()) { 155 case ir::AstNodeType::TRY_STATEMENT: { 156 AnalyzeTry(node->AsTryStatement()); 157 break; 158 } 159 case ir::AstNodeType::BREAK_STATEMENT: { 160 AnalyzeBreak(node->AsBreakStatement()); 161 break; 162 } 163 case ir::AstNodeType::CONTINUE_STATEMENT: { 164 AnalyzeContinue(node->AsContinueStatement()); 165 break; 166 } 167 case ir::AstNodeType::RETURN_STATEMENT: { 168 AnalyzeReturn(node->AsReturnStatement()); 169 break; 170 } 171 default: { 172 break; 173 } 174 } 175} 176 177void AliveAnalyzer::AnalyzeDef(const ir::AstNode *node) 178{ 179 AnalyzeStat(node); 180 if (node != nullptr && node->IsClassStaticBlock() && status_ == LivenessStatus::DEAD) { 181 checker_->LogTypeError("Initializer must be able to complete normally.", node->Start()); 182 } 183} 184 185void AliveAnalyzer::AnalyzeStat(const ir::AstNode *node) 186{ 187 if (node == nullptr) { 188 return; 189 } 190 191 if (status_ == LivenessStatus::DEAD) { 192 checker_->LogTypeError("Unreachable statement.", node->Start()); 193 return; 194 } 195 196 if (node->IsClassStaticBlock()) { 197 AnalyzeNodes(node); 198 return; 199 } 200 201 AnalyzeNode(node); 202} 203 204void AliveAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats) 205{ 206 for (const auto *it : stats) { 207 AnalyzeStat(it); 208 } 209} 210 211void AliveAnalyzer::AnalyzeStructDecl(const ir::ETSStructDeclaration *structDecl) 212{ 213 for (const auto *it : structDecl->Definition()->Body()) { 214 AnalyzeNode(it); 215 } 216} 217 218void AliveAnalyzer::AnalyzeClassDecl(const ir::ClassDeclaration *classDecl) 219{ 220 LivenessStatus prevStatus = status_; 221 222 for (const auto *it : classDecl->Definition()->Body()) { 223 AnalyzeNode(it); 224 } 225 226 status_ = prevStatus; 227} 228 229void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef) 230{ 231 auto *func = methodDef->Function(); 232 233 if (func->Body() == nullptr || func->IsProxy()) { 234 return; 235 } 236 237 status_ = LivenessStatus::ALIVE; 238 AnalyzeStat(func->Body()); 239 ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType()); 240 const auto *returnType = methodDef->TsType()->AsETSFunctionType()->FindSignature(func)->ReturnType(); 241 const auto isVoid = returnType->IsETSVoidType() || returnType == checker_->GlobalVoidType(); 242 243 auto isPromiseVoid = false; 244 245 if (returnType->IsETSAsyncFuncReturnType()) { 246 const auto *asAsync = returnType->AsETSAsyncFuncReturnType(); 247 isPromiseVoid = asAsync->GetPromiseTypeArg() == checker_->GlobalETSUndefinedType(); 248 } 249 250 if (status_ == LivenessStatus::ALIVE && !isVoid && !isPromiseVoid && !checker_->IsAsyncImplMethod(methodDef)) { 251 if (!methodDef->Function()->HasReturnStatement()) { 252 checker_->LogTypeError("Function with a non void return type must return a value.", func->Id()->Start()); 253 ClearPendingExits(); 254 return; 255 } 256 257 checker_->LogTypeError("Not all code paths return a value.", func->Id()->Start()); 258 } 259 260 ClearPendingExits(); 261} 262 263void AliveAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef) 264{ 265 for (auto *it : varDef->Declarators()) { 266 if (it->Init() == nullptr) { 267 continue; 268 } 269 270 AnalyzeNode(it->Init()); 271 } 272} 273 274void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile) 275{ 276 SetOldPendingExits(PendingExits()); 277 AnalyzeStat(doWhile->Body()); 278 status_ = Or(status_, ResolveContinues(doWhile)); 279 AnalyzeNode(doWhile->Test()); 280 ASSERT(doWhile->Test()->TsType() && doWhile->Test()->TsType()->IsConditionalExprType()); 281 const auto exprRes = doWhile->Test()->TsType()->ResolveConditionExpr(); 282 status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || !std::get<1>(exprRes))); 283 status_ = Or(status_, ResolveBreaks(doWhile)); 284} 285 286void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt) 287{ 288 SetOldPendingExits(PendingExits()); 289 AnalyzeNode(whileStmt->Test()); 290 ASSERT(whileStmt->Test()->TsType() && whileStmt->Test()->TsType()->IsConditionalExprType()); 291 const auto exprRes = whileStmt->Test()->TsType()->ResolveConditionExpr(); 292 status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || std::get<1>(exprRes))); 293 AnalyzeStat(whileStmt->Body()); 294 status_ = Or(status_, ResolveContinues(whileStmt)); 295 status_ = Or(ResolveBreaks(whileStmt), From(!std::get<0>(exprRes) || !std::get<1>(exprRes))); 296} 297 298void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt) 299{ 300 AnalyzeNode(forStmt->Init()); 301 SetOldPendingExits(PendingExits()); 302 const Type *condType {}; 303 bool resolveType = false; 304 bool res = false; 305 306 if (forStmt->Test() != nullptr) { 307 AnalyzeNode(forStmt->Test()); 308 ASSERT(forStmt->Test()->TsType() && forStmt->Test()->TsType()->IsConditionalExprType()); 309 condType = forStmt->Test()->TsType(); 310 std::tie(resolveType, res) = forStmt->Test()->TsType()->ResolveConditionExpr(); 311 status_ = From(!resolveType || res); 312 } else { 313 status_ = LivenessStatus::ALIVE; 314 } 315 316 AnalyzeStat(forStmt->Body()); 317 status_ = Or(status_, ResolveContinues(forStmt)); 318 AnalyzeNode(forStmt->Update()); 319 status_ = Or(ResolveBreaks(forStmt), From(condType != nullptr && (!resolveType || !res))); 320} 321 322void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt) 323{ 324 // Note: iterator definition can be a reference to variable defined in outer scope! 325 if (forOfStmt->Left()->IsVariableDeclaration()) { 326 AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration()); 327 } else { 328 AnalyzeNode(forOfStmt->Left()); 329 } 330 AnalyzeNode(forOfStmt->Right()); 331 SetOldPendingExits(PendingExits()); 332 333 AnalyzeStat(forOfStmt->Body()); 334 status_ = Or(status_, ResolveContinues(forOfStmt)); 335 ResolveBreaks(forOfStmt); 336 status_ = LivenessStatus::ALIVE; 337} 338 339void AliveAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt) 340{ 341 AnalyzeNode(ifStmt->Test()); 342 AnalyzeStat(ifStmt->Consequent()); 343 if (ifStmt->Alternate() != nullptr) { 344 LivenessStatus prevStatus = status_; 345 status_ = LivenessStatus::ALIVE; 346 AnalyzeStat(ifStmt->Alternate()); 347 status_ = Or(status_, prevStatus); 348 } else { 349 status_ = LivenessStatus::ALIVE; 350 } 351} 352 353void AliveAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt) 354{ 355 SetOldPendingExits(PendingExits()); 356 AnalyzeStat(labelledStmt->Body()); 357 status_ = Or(status_, ResolveBreaks(labelledStmt)); 358} 359 360void AliveAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass) 361{ 362 for (const auto *it : newClass->GetArguments()) { 363 AnalyzeNode(it); 364 } 365 366 if (newClass->ClassDefinition() != nullptr) { 367 AnalyzeNode(newClass->ClassDefinition()); 368 } 369} 370 371void AliveAnalyzer::AnalyzeCall(const ir::CallExpression *callExpr) 372{ 373 AnalyzeNode(callExpr->Callee()); 374 for (const auto *it : callExpr->Arguments()) { 375 AnalyzeNode(it); 376 } 377 if (callExpr->Signature()->ReturnType() == checker_->GetGlobalTypesHolder()->GlobalBuiltinNeverType()) { 378 MarkDead(); 379 } 380} 381 382void AliveAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt) 383{ 384 AnalyzeNode(throwStmt->Argument()); 385 MarkDead(); 386} 387 388void AliveAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt) 389{ 390 SetOldPendingExits(PendingExits()); 391 392 AnalyzeNode(switchStmt->Discriminant()); 393 394 bool hasDefault = false; 395 for (std::size_t i = 0, size = switchStmt->Cases().size(); i < size; i++) { 396 const auto *caseClause = switchStmt->Cases()[i]; 397 status_ = LivenessStatus::ALIVE; 398 399 if (caseClause->Test() == nullptr) { 400 hasDefault = true; 401 } else { 402 AnalyzeNode(caseClause->Test()); 403 } 404 405 AnalyzeStats(caseClause->Consequent()); 406 407 if (status_ == LivenessStatus::ALIVE && !caseClause->Consequent().empty() && i < size - 1) { 408 // NOTE(user) Add lint categories and option to enable/disable compiler warnings 409 checker_->Warning("Possible fall-through into case", caseClause->Start()); 410 } 411 } 412 413 if (!hasDefault) { 414 status_ = LivenessStatus::ALIVE; 415 } 416 417 status_ = Or(status_, ResolveBreaks(switchStmt)); 418} 419 420void AliveAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt) 421{ 422 RecordExit(PendingExit(breakStmt)); 423} 424 425void AliveAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt) 426{ 427 RecordExit(PendingExit(contStmt)); 428} 429 430void AliveAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt) 431{ 432 AnalyzeNode(retStmt->Argument()); 433 RecordExit(PendingExit(retStmt)); 434} 435 436void AliveAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt) 437{ 438 status_ = LivenessStatus::ALIVE; 439 bool isAlive = false; 440 AnalyzeStats(tryStmt->Block()->Statements()); 441 442 if (status_ != LivenessStatus::DEAD) { 443 isAlive = true; 444 } 445 446 for (const auto &it : tryStmt->CatchClauses()) { 447 status_ = LivenessStatus::ALIVE; 448 AnalyzeStats(it->Body()->Statements()); 449 if (status_ == LivenessStatus::ALIVE) { 450 isAlive = true; 451 } 452 } 453 454 if (tryStmt->FinallyBlock() != nullptr) { 455 status_ = LivenessStatus::ALIVE; 456 AnalyzeStats(tryStmt->FinallyBlock()->Statements()); 457 const_cast<ir::TryStatement *>(tryStmt)->SetFinallyCanCompleteNormally(status_ == LivenessStatus::ALIVE); 458 if (status_ == LivenessStatus::DEAD) { 459 isAlive = false; 460 // NOTE(user) Add lint categories and option to enable/disable compiler warnings 461 checker_->Warning("Finally clause cannot complete normally", tryStmt->FinallyBlock()->Start()); 462 } 463 } 464 465 status_ = isAlive ? LivenessStatus::ALIVE : LivenessStatus::DEAD; 466} 467} // namespace ark::es2panda::checker 468