diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Analysis/ReachableCode.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/Analysis/ReachableCode.cpp | 107 |
1 files changed, 79 insertions, 28 deletions
diff --git a/contrib/llvm-project/clang/lib/Analysis/ReachableCode.cpp b/contrib/llvm-project/clang/lib/Analysis/ReachableCode.cpp index 221d137dadb8..acbe1470b389 100644 --- a/contrib/llvm-project/clang/lib/Analysis/ReachableCode.cpp +++ b/contrib/llvm-project/clang/lib/Analysis/ReachableCode.cpp @@ -12,10 +12,12 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/Analyses/ReachableCode.h" +#include "clang/AST/Attr.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ParentMap.h" +#include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/StmtCXX.h" #include "clang/Analysis/AnalysisDeclContext.h" #include "clang/Analysis/CFG.h" @@ -24,6 +26,7 @@ #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/SmallVector.h" +#include <optional> using namespace clang; @@ -73,7 +76,7 @@ static bool isBuiltinAssumeFalse(const CFGBlock *B, const Stmt *S, // (e.g. a CFGBlock containing only a goto). return false; } - if (Optional<CFGStmt> CS = B->back().getAs<CFGStmt>()) { + if (std::optional<CFGStmt> CS = B->back().getAs<CFGStmt>()) { if (const auto *CE = dyn_cast<CallExpr>(CS->getStmt())) { return CE->getCallee()->IgnoreCasts() == S && CE->isBuiltinAssumeFalse(C); } @@ -87,10 +90,8 @@ static bool isDeadReturn(const CFGBlock *B, const Stmt *S) { // block, or may be in a subsequent block because of destructors. const CFGBlock *Current = B; while (true) { - for (CFGBlock::const_reverse_iterator I = Current->rbegin(), - E = Current->rend(); - I != E; ++I) { - if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) { + for (const CFGElement &CE : llvm::reverse(*Current)) { + if (std::optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(CS->getStmt())) { if (RS == S) return true; @@ -220,14 +221,15 @@ static bool isConfigurationValue(const Stmt *S, return isConfigurationValue(cast<DeclRefExpr>(S)->getDecl(), PP); case Stmt::ObjCBoolLiteralExprClass: IgnoreYES_NO = true; - LLVM_FALLTHROUGH; + [[fallthrough]]; case Stmt::CXXBoolLiteralExprClass: case Stmt::IntegerLiteralClass: { const Expr *E = cast<Expr>(S); if (IncludeIntegers) { if (SilenceableCondVal && !SilenceableCondVal->getBegin().isValid()) *SilenceableCondVal = E->getSourceRange(); - return WrappedInParens || isExpandedFromConfigurationMacro(E, PP, IgnoreYES_NO); + return WrappedInParens || + isExpandedFromConfigurationMacro(E, PP, IgnoreYES_NO); } return false; } @@ -300,6 +302,12 @@ static bool shouldTreatSuccessorsAsReachable(const CFGBlock *B, if (isa<BinaryOperator>(Term)) { return isConfigurationValue(Term, PP); } + // Do not treat constexpr if statement successors as unreachable in warnings + // since the point of these statements is to determine branches at compile + // time. + if (const auto *IS = dyn_cast<IfStmt>(Term); + IS != nullptr && IS->isConstexpr()) + return true; } const Stmt *Cond = B->getTerminatorCondition(/* stripParens */ false); @@ -334,7 +342,7 @@ static unsigned scanFromBlock(const CFGBlock *Start, // This allows us to potentially uncover some "always unreachable" code // within the "sometimes unreachable" code. // Look at the successors and mark then reachable. - Optional<bool> TreatAllSuccessorsAsReachable; + std::optional<bool> TreatAllSuccessorsAsReachable; if (!IncludeSometimesUnreachableEdges) TreatAllSuccessorsAsReachable = false; @@ -346,13 +354,13 @@ static unsigned scanFromBlock(const CFGBlock *Start, if (!UB) break; - if (!TreatAllSuccessorsAsReachable.hasValue()) { + if (!TreatAllSuccessorsAsReachable) { assert(PP); TreatAllSuccessorsAsReachable = shouldTreatSuccessorsAsReachable(item, *PP); } - if (TreatAllSuccessorsAsReachable.getValue()) { + if (*TreatAllSuccessorsAsReachable) { B = UB; break; } @@ -446,26 +454,68 @@ bool DeadCodeScan::isDeadCodeRoot(const clang::CFGBlock *Block) { return isDeadRoot; } -static bool isValidDeadStmt(const Stmt *S) { +// Check if the given `DeadStmt` is a coroutine statement and is a substmt of +// the coroutine statement. `Block` is the CFGBlock containing the `DeadStmt`. +static bool isInCoroutineStmt(const Stmt *DeadStmt, const CFGBlock *Block) { + // The coroutine statement, co_return, co_await, or co_yield. + const Stmt *CoroStmt = nullptr; + // Find the first coroutine statement after the DeadStmt in the block. + bool AfterDeadStmt = false; + for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I != E; + ++I) + if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) { + const Stmt *S = CS->getStmt(); + if (S == DeadStmt) + AfterDeadStmt = true; + if (AfterDeadStmt && + // For simplicity, we only check simple coroutine statements. + (llvm::isa<CoreturnStmt>(S) || llvm::isa<CoroutineSuspendExpr>(S))) { + CoroStmt = S; + break; + } + } + if (!CoroStmt) + return false; + struct Checker : RecursiveASTVisitor<Checker> { + const Stmt *DeadStmt; + bool CoroutineSubStmt = false; + Checker(const Stmt *S) : DeadStmt(S) {} + bool VisitStmt(const Stmt *S) { + if (S == DeadStmt) + CoroutineSubStmt = true; + return true; + } + // Statements captured in the CFG can be implicit. + bool shouldVisitImplicitCode() const { return true; } + }; + Checker checker(DeadStmt); + checker.TraverseStmt(const_cast<Stmt *>(CoroStmt)); + return checker.CoroutineSubStmt; +} + +static bool isValidDeadStmt(const Stmt *S, const clang::CFGBlock *Block) { if (S->getBeginLoc().isInvalid()) return false; if (const BinaryOperator *BO = dyn_cast<BinaryOperator>(S)) return BO->getOpcode() != BO_Comma; - return true; + // Coroutine statements are never considered dead statements, because removing + // them may change the function semantic if it is the only coroutine statement + // of the coroutine. + return !isInCoroutineStmt(S, Block); } const Stmt *DeadCodeScan::findDeadCode(const clang::CFGBlock *Block) { for (CFGBlock::const_iterator I = Block->begin(), E = Block->end(); I!=E; ++I) - if (Optional<CFGStmt> CS = I->getAs<CFGStmt>()) { + if (std::optional<CFGStmt> CS = I->getAs<CFGStmt>()) { const Stmt *S = CS->getStmt(); - if (isValidDeadStmt(S)) + if (isValidDeadStmt(S, Block)) return S; } CFGTerminator T = Block->getTerminator(); if (T.isStmtBranch()) { const Stmt *S = T.getStmt(); - if (S && isValidDeadStmt(S)) + if (S && isValidDeadStmt(S, Block)) return S; } @@ -530,12 +580,11 @@ unsigned DeadCodeScan::scanBackwards(const clang::CFGBlock *Start, // earliest location. if (!DeferredLocs.empty()) { llvm::array_pod_sort(DeferredLocs.begin(), DeferredLocs.end(), SrcCmp); - for (DeferredLocsTy::iterator I = DeferredLocs.begin(), - E = DeferredLocs.end(); I != E; ++I) { - const CFGBlock *Block = I->first; + for (const auto &I : DeferredLocs) { + const CFGBlock *Block = I.first; if (Reachable[Block->getBlockID()]) continue; - reportDeadCode(Block, I->second, CB); + reportDeadCode(Block, I.second, CB); count += scanMaybeReachableFromBlock(Block, PP, Reachable); } } @@ -624,6 +673,10 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B, UK = reachable_code::UK_Return; } + const auto *AS = dyn_cast<AttributedStmt>(S); + bool HasFallThroughAttr = + AS && hasSpecificAttr<FallThroughAttr>(AS->getAttrs()); + SourceRange SilenceableCondVal; if (UK == reachable_code::UK_Other) { @@ -640,8 +693,9 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B, R2 = Inc->getSourceRange(); } - CB.HandleUnreachable(reachable_code::UK_Loop_Increment, - Loc, SourceRange(), SourceRange(Loc, Loc), R2); + CB.HandleUnreachable(reachable_code::UK_Loop_Increment, Loc, + SourceRange(), SourceRange(Loc, Loc), R2, + HasFallThroughAttr); return; } @@ -660,7 +714,7 @@ void DeadCodeScan::reportDeadCode(const CFGBlock *B, SourceRange R1, R2; SourceLocation Loc = GetUnreachableLoc(S, R1, R2); - CB.HandleUnreachable(UK, Loc, SilenceableCondVal, R1, R2); + CB.HandleUnreachable(UK, Loc, SilenceableCondVal, R1, R2, HasFallThroughAttr); } //===----------------------------------------------------------------------===// @@ -694,18 +748,15 @@ void FindUnreachableCode(AnalysisDeclContext &AC, Preprocessor &PP, // If there aren't explicit EH edges, we should include the 'try' dispatch // blocks as roots. if (!AC.getCFGBuildOptions().AddEHEdges) { - for (CFG::try_block_iterator I = cfg->try_blocks_begin(), - E = cfg->try_blocks_end() ; I != E; ++I) { - numReachable += scanMaybeReachableFromBlock(*I, PP, reachable); - } + for (const CFGBlock *B : cfg->try_blocks()) + numReachable += scanMaybeReachableFromBlock(B, PP, reachable); if (numReachable == cfg->getNumBlockIDs()) return; } // There are some unreachable blocks. We need to find the root blocks that // contain code that should be considered unreachable. - for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { - const CFGBlock *block = *I; + for (const CFGBlock *block : *cfg) { // A block may have been marked reachable during this loop. if (reachable[block->getBlockID()]) continue; |