|
17 | 17 | #include "clang/AST/ExprCXX.h"
|
18 | 18 | #include "clang/AST/ExprObjC.h"
|
19 | 19 | #include "clang/AST/ParentMap.h"
|
| 20 | +#include "clang/AST/RecursiveASTVisitor.h" |
20 | 21 | #include "clang/AST/StmtCXX.h"
|
21 | 22 | #include "clang/Analysis/AnalysisDeclContext.h"
|
22 | 23 | #include "clang/Analysis/CFG.h"
|
@@ -60,6 +61,45 @@ static bool isTrivialDoWhile(const CFGBlock *B, const Stmt *S) {
|
60 | 61 | return false;
|
61 | 62 | }
|
62 | 63 |
|
| 64 | +// Check if the block starts with a coroutine statement and see if the given |
| 65 | +// unreachable 'S' is the substmt of the coroutine statement. |
| 66 | +// |
| 67 | +// We suppress the unreachable warning for cases where an unreachable code is |
| 68 | +// a substmt of the coroutine statement, becase removing it will change the |
| 69 | +// function semantic if this is the only coroutine statement of the coroutine. |
| 70 | +static bool isInCoroutineStmt(const CFGBlock *Block, const Stmt* S) { |
| 71 | + // The coroutine statement, co_return, co_await, or co_yield. |
| 72 | + const Stmt* CoroStmt = nullptr; |
| 73 | + // Find the first coroutine statement in the block. |
| 74 | + for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E; |
| 75 | + ++I) |
| 76 | + if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) { |
| 77 | + const Stmt *S = CS->getStmt(); |
| 78 | + if (llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S)) { |
| 79 | + CoroStmt = S ; |
| 80 | + break; |
| 81 | + } |
| 82 | + } |
| 83 | + if (!CoroStmt) |
| 84 | + return false; |
| 85 | + |
| 86 | + struct Checker : RecursiveASTVisitor<Checker> { |
| 87 | + const Stmt *StmtToCheck; |
| 88 | + bool CoroutineSubStmt = false; |
| 89 | + Checker(const Stmt *S) : StmtToCheck(S) {} |
| 90 | + bool VisitStmt(const Stmt *S) { |
| 91 | + if (S == StmtToCheck) |
| 92 | + CoroutineSubStmt = true; |
| 93 | + return true; |
| 94 | + } |
| 95 | + // The 'S' stmt captured in the CFG can be implicit. |
| 96 | + bool shouldVisitImplicitCode() const { return true; } |
| 97 | + }; |
| 98 | + Checker checker(S); |
| 99 | + checker.TraverseStmt(const_cast<Stmt *>(CoroStmt)); |
| 100 | + return checker.CoroutineSubStmt; |
| 101 | +} |
| 102 | + |
63 | 103 | static bool isBuiltinUnreachable(const Stmt *S) {
|
64 | 104 | if (const auto *DRE = dyn_cast<DeclRefExpr>(S))
|
65 | 105 | if (const auto *FDecl = dyn_cast<FunctionDecl>(DRE->getDecl()))
|
@@ -623,7 +663,7 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B,
|
623 | 663 | if (isa<BreakStmt>(S)) {
|
624 | 664 | UK = reachable_code::UK_Break;
|
625 | 665 | } else if (isTrivialDoWhile(B, S) || isBuiltinUnreachable(S) ||
|
626 |
| - isBuiltinAssumeFalse(B, S, C)) { |
| 666 | + isBuiltinAssumeFalse(B, S, C) || isInCoroutineStmt(B, S)) { |
627 | 667 | return;
|
628 | 668 | }
|
629 | 669 | else if (isDeadReturn(B, S)) {
|
|
0 commit comments