• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "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 
53 namespace ark::es2panda::checker {
54 
AnalyzeNodes(const ir::AstNode * node)55 void AliveAnalyzer::AnalyzeNodes(const ir::AstNode *node)
56 {
57     node->Iterate([this](auto *childNode) { AnalyzeNode(childNode); });
58 }
59 
AnalyzeNode(const ir::AstNode * node)60 void 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
AnalyzeNodeHelper1(const ir::AstNode * node)106 void 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
AnalyzeNodeHelper2(const ir::AstNode * node)152 void 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 
AnalyzeDef(const ir::AstNode * node)177 void AliveAnalyzer::AnalyzeDef(const ir::AstNode *node)
178 {
179     AnalyzeStat(node);
180     if (node != nullptr && node->IsClassStaticBlock() && status_ == LivenessStatus::DEAD) {
181         checker_->LogError(diagnostic::INIT_DOESNT_COMPLETE, {}, node->Start());
182     }
183 }
184 
AnalyzeStat(const ir::AstNode * node)185 void AliveAnalyzer::AnalyzeStat(const ir::AstNode *node)
186 {
187     if (node == nullptr) {
188         return;
189     }
190 
191     if (status_ == LivenessStatus::DEAD) {
192         checker_->LogDiagnostic(diagnostic::UNREACHABLE_STMT, node->Start());
193         return;
194     }
195 
196     if (node->IsClassStaticBlock()) {
197         AnalyzeNodes(node);
198         return;
199     }
200 
201     AnalyzeNode(node);
202 }
203 
AnalyzeStats(const ArenaVector<ir::Statement * > & stats)204 void AliveAnalyzer::AnalyzeStats(const ArenaVector<ir::Statement *> &stats)
205 {
206     for (const auto *it : stats) {
207         AnalyzeStat(it);
208     }
209 }
210 
AnalyzeStructDecl(const ir::ETSStructDeclaration * structDecl)211 void AliveAnalyzer::AnalyzeStructDecl(const ir::ETSStructDeclaration *structDecl)
212 {
213     for (const auto *it : structDecl->Definition()->Body()) {
214         AnalyzeNode(it);
215     }
216 }
217 
AnalyzeClassDecl(const ir::ClassDeclaration * classDecl)218 void 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 
AnalyzeMethodDef(const ir::MethodDefinition * methodDef)229 void AliveAnalyzer::AnalyzeMethodDef(const ir::MethodDefinition *methodDef)
230 {
231     for (ir::MethodDefinition *overload : methodDef->Overloads()) {
232         AnalyzeNode(overload);
233     }
234 
235     auto *func = methodDef->Function();
236 
237     ES2PANDA_ASSERT(func != nullptr);
238     if (func->Body() == nullptr || func->IsProxy()) {
239         return;
240     }
241 
242     status_ = LivenessStatus::ALIVE;
243     AnalyzeStat(func->Body());
244     ES2PANDA_ASSERT(methodDef->TsType() && methodDef->TsType()->IsETSFunctionType());
245     const auto *signature = methodDef->TsType()->AsETSFunctionType()->FindSignature(func);
246     ES2PANDA_ASSERT(signature != nullptr);
247     const auto *returnType = signature->ReturnType();
248     const auto isVoid = returnType->IsETSVoidType() || returnType == checker_->GlobalVoidType();
249 
250     auto isPromiseVoid = false;
251 
252     if (returnType->IsETSObjectType() &&
253         returnType->AsETSObjectType()->AssemblerName() == compiler::Signatures::BUILTIN_PROMISE) {
254         const auto *asAsync = returnType->AsETSObjectType();
255         isPromiseVoid = asAsync->TypeArguments().front() == checker_->GlobalETSUndefinedType() ||
256                         asAsync->TypeArguments().front() == checker_->GlobalVoidType();
257     }
258 
259     if (status_ == LivenessStatus::ALIVE && !isVoid && !isPromiseVoid) {
260         auto *fuction = methodDef->Function();
261         ES2PANDA_ASSERT(fuction != nullptr);
262         if (!fuction->HasReturnStatement()) {
263             if (!util::Helpers::IsAsyncMethod(methodDef)) {
264                 checker_->LogError(diagnostic::MISSING_RETURN_STMT, {}, func->Start());
265                 ClearPendingExits();
266             }
267             return;
268         }
269 
270         checker_->LogError(diagnostic::NONRETURNING_PATHS, {}, func->Start());
271     }
272 
273     ClearPendingExits();
274 }
275 
AnalyzeVarDef(const ir::VariableDeclaration * varDef)276 void AliveAnalyzer::AnalyzeVarDef(const ir::VariableDeclaration *varDef)
277 {
278     for (auto *it : varDef->Declarators()) {
279         if (it->Init() == nullptr) {
280             continue;
281         }
282 
283         AnalyzeNode(it->Init());
284     }
285 }
286 
AnalyzeDoLoop(const ir::DoWhileStatement * doWhile)287 void AliveAnalyzer::AnalyzeDoLoop(const ir::DoWhileStatement *doWhile)
288 {
289     SetOldPendingExits(PendingExits());
290     AnalyzeStat(doWhile->Body());
291     status_ = Or(status_, ResolveContinues(doWhile));
292     AnalyzeNode(doWhile->Test());
293     ES2PANDA_ASSERT(doWhile->Test()->TsType());
294     const auto exprRes = doWhile->Test()->TsType()->ResolveConditionExpr();
295     status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
296     status_ = Or(status_, ResolveBreaks(doWhile));
297 }
298 
AnalyzeWhileLoop(const ir::WhileStatement * whileStmt)299 void AliveAnalyzer::AnalyzeWhileLoop(const ir::WhileStatement *whileStmt)
300 {
301     SetOldPendingExits(PendingExits());
302     AnalyzeNode(whileStmt->Test());
303     ES2PANDA_ASSERT(whileStmt->Test()->TsType());
304     const auto exprRes = whileStmt->Test()->TsType()->ResolveConditionExpr();
305     status_ = And(status_, static_cast<LivenessStatus>(!std::get<0>(exprRes) || std::get<1>(exprRes)));
306     AnalyzeStat(whileStmt->Body());
307     status_ = Or(status_, ResolveContinues(whileStmt));
308     status_ = Or(ResolveBreaks(whileStmt), From(!std::get<0>(exprRes) || !std::get<1>(exprRes)));
309 }
310 
AnalyzeForLoop(const ir::ForUpdateStatement * forStmt)311 void AliveAnalyzer::AnalyzeForLoop(const ir::ForUpdateStatement *forStmt)
312 {
313     AnalyzeNode(forStmt->Init());
314     SetOldPendingExits(PendingExits());
315     const Type *condType {};
316     bool resolveType = false;
317     bool res = false;
318 
319     if (forStmt->Test() != nullptr) {
320         AnalyzeNode(forStmt->Test());
321         ES2PANDA_ASSERT(forStmt->Test()->TsType());
322         condType = forStmt->Test()->TsType();
323         std::tie(resolveType, res) = forStmt->Test()->TsType()->ResolveConditionExpr();
324         status_ = From(!resolveType || res);
325     } else {
326         status_ = LivenessStatus::ALIVE;
327     }
328 
329     AnalyzeStat(forStmt->Body());
330     status_ = Or(status_, ResolveContinues(forStmt));
331     AnalyzeNode(forStmt->Update());
332     status_ = Or(ResolveBreaks(forStmt), From(condType != nullptr && (!resolveType || !res)));
333 }
334 
AnalyzeForOfLoop(const ir::ForOfStatement * forOfStmt)335 void AliveAnalyzer::AnalyzeForOfLoop(const ir::ForOfStatement *forOfStmt)
336 {
337     //  Note: iterator definition can be a reference to variable defined in outer scope!
338     if (forOfStmt->Left()->IsVariableDeclaration()) {
339         AnalyzeVarDef(forOfStmt->Left()->AsVariableDeclaration());
340     } else {
341         AnalyzeNode(forOfStmt->Left());
342     }
343     AnalyzeNode(forOfStmt->Right());
344     SetOldPendingExits(PendingExits());
345 
346     AnalyzeStat(forOfStmt->Body());
347     status_ = Or(status_, ResolveContinues(forOfStmt));
348     ResolveBreaks(forOfStmt);
349     status_ = LivenessStatus::ALIVE;
350 }
351 
AnalyzeIf(const ir::IfStatement * ifStmt)352 void AliveAnalyzer::AnalyzeIf(const ir::IfStatement *ifStmt)
353 {
354     AnalyzeNode(ifStmt->Test());
355     AnalyzeStat(ifStmt->Consequent());
356     if (ifStmt->Alternate() != nullptr) {
357         LivenessStatus prevStatus = status_;
358         status_ = LivenessStatus::ALIVE;
359         AnalyzeStat(ifStmt->Alternate());
360         status_ = Or(status_, prevStatus);
361     } else {
362         status_ = LivenessStatus::ALIVE;
363     }
364 }
365 
AnalyzeLabelled(const ir::LabelledStatement * labelledStmt)366 void AliveAnalyzer::AnalyzeLabelled(const ir::LabelledStatement *labelledStmt)
367 {
368     SetOldPendingExits(PendingExits());
369     AnalyzeStat(labelledStmt->Body());
370     status_ = Or(status_, ResolveBreaks(labelledStmt));
371 }
372 
AnalyzeNewClass(const ir::ETSNewClassInstanceExpression * newClass)373 void AliveAnalyzer::AnalyzeNewClass(const ir::ETSNewClassInstanceExpression *newClass)
374 {
375     for (const auto *it : newClass->GetArguments()) {
376         AnalyzeNode(it);
377     }
378 }
379 
AnalyzeCall(const ir::CallExpression * callExpr)380 void AliveAnalyzer::AnalyzeCall(const ir::CallExpression *callExpr)
381 {
382     AnalyzeNode(callExpr->Callee());
383     for (const auto *it : callExpr->Arguments()) {
384         AnalyzeNode(it);
385     }
386     if (callExpr->Signature()->ReturnType() == checker_->GetGlobalTypesHolder()->GlobalETSNeverType()) {
387         MarkDead();
388     }
389 }
390 
AnalyzeThrow(const ir::ThrowStatement * throwStmt)391 void AliveAnalyzer::AnalyzeThrow(const ir::ThrowStatement *throwStmt)
392 {
393     AnalyzeNode(throwStmt->Argument());
394     MarkDead();
395 }
396 
AnalyzeSwitch(const ir::SwitchStatement * switchStmt)397 void AliveAnalyzer::AnalyzeSwitch(const ir::SwitchStatement *switchStmt)
398 {
399     SetOldPendingExits(PendingExits());
400 
401     AnalyzeNode(switchStmt->Discriminant());
402 
403     bool hasDefault = false;
404     for (std::size_t i = 0, size = switchStmt->Cases().size(); i < size; i++) {
405         const auto *caseClause = switchStmt->Cases()[i];
406         status_ = LivenessStatus::ALIVE;
407 
408         if (caseClause->Test() == nullptr) {
409             hasDefault = true;
410         } else {
411             AnalyzeNode(caseClause->Test());
412         }
413 
414         AnalyzeStats(caseClause->Consequent());
415 
416         if (status_ == LivenessStatus::ALIVE && !caseClause->Consequent().empty() && i < size - 1) {
417             // NOTE(user) Add lint categories and option to enable/disable compiler warnings
418             checker_->LogDiagnostic(diagnostic::MAYBE_FALLTHROUGH, caseClause->Start());
419         }
420     }
421 
422     if (!hasDefault) {
423         status_ = LivenessStatus::ALIVE;
424     }
425 
426     status_ = Or(status_, ResolveBreaks(switchStmt));
427 }
428 
AnalyzeBreak(const ir::BreakStatement * breakStmt)429 void AliveAnalyzer::AnalyzeBreak(const ir::BreakStatement *breakStmt)
430 {
431     RecordExit(PendingExit(breakStmt));
432 }
433 
AnalyzeContinue(const ir::ContinueStatement * contStmt)434 void AliveAnalyzer::AnalyzeContinue(const ir::ContinueStatement *contStmt)
435 {
436     RecordExit(PendingExit(contStmt));
437 }
438 
AnalyzeReturn(const ir::ReturnStatement * retStmt)439 void AliveAnalyzer::AnalyzeReturn(const ir::ReturnStatement *retStmt)
440 {
441     AnalyzeNode(retStmt->Argument());
442     RecordExit(PendingExit(retStmt));
443 }
444 
AnalyzeTry(const ir::TryStatement * tryStmt)445 void AliveAnalyzer::AnalyzeTry(const ir::TryStatement *tryStmt)
446 {
447     status_ = LivenessStatus::ALIVE;
448     bool isAlive = false;
449     AnalyzeStats(tryStmt->Block()->Statements());
450 
451     if (status_ != LivenessStatus::DEAD) {
452         isAlive = true;
453     }
454 
455     for (const auto &it : tryStmt->CatchClauses()) {
456         status_ = LivenessStatus::ALIVE;
457         AnalyzeStats(it->Body()->Statements());
458         if (status_ == LivenessStatus::ALIVE) {
459             isAlive = true;
460         }
461     }
462 
463     if (tryStmt->FinallyBlock() != nullptr) {
464         status_ = LivenessStatus::ALIVE;
465         AnalyzeStats(tryStmt->FinallyBlock()->Statements());
466         const_cast<ir::TryStatement *>(tryStmt)->SetFinallyCanCompleteNormally(status_ == LivenessStatus::ALIVE);
467         if (status_ == LivenessStatus::DEAD) {
468             isAlive = false;
469             // NOTE(user) Add lint categories and option to enable/disable compiler warnings
470             checker_->LogDiagnostic(diagnostic::FINALLY_CANT_COMPLETE, tryStmt->FinallyBlock()->Start());
471         }
472     }
473 
474     status_ = isAlive ? LivenessStatus::ALIVE : LivenessStatus::DEAD;
475 }
476 }  // namespace ark::es2panda::checker
477