diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Checker/UnreachableCodeChecker.cpp | 123 |
1 files changed, 92 insertions, 31 deletions
diff --git a/lib/Checker/UnreachableCodeChecker.cpp b/lib/Checker/UnreachableCodeChecker.cpp index 9ee3d02d26..e95e6a832d 100644 --- a/lib/Checker/UnreachableCodeChecker.cpp +++ b/lib/Checker/UnreachableCodeChecker.cpp @@ -13,13 +13,14 @@ // A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp //===----------------------------------------------------------------------===// +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" #include "clang/Checker/PathSensitive/CheckerVisitor.h" #include "clang/Checker/PathSensitive/ExplodedGraph.h" #include "clang/Checker/PathSensitive/SVals.h" +#include "clang/Checker/PathSensitive/CheckerHelpers.h" #include "clang/Checker/BugReporter/BugReporter.h" #include "GRExprEngineExperimentalChecks.h" -#include "clang/AST/StmtCXX.h" -#include "clang/Basic/Builtins.h" #include "llvm/ADT/SmallPtrSet.h" // The number of CFGBlock pointers we want to reserve memory for. This is used @@ -35,9 +36,13 @@ public: void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, bool hasWorkRemaining); private: + typedef bool (*ExplodedNodeHeuristic)(const ExplodedNode &EN); + static SourceLocation GetUnreachableLoc(const CFGBlock &b, SourceRange &R); void FindUnreachableEntryPoints(const CFGBlock *CB); void MarkSuccessorsReachable(const CFGBlock *CB); + static const Expr *getConditon(const Stmt *S); + static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); llvm::SmallPtrSet<const CFGBlock*, DEFAULT_CFGBLOCKS> reachable; llvm::SmallPtrSet<const CFGBlock*, DEFAULT_CFGBLOCKS> visited; @@ -61,14 +66,18 @@ void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, return; CFG *C = 0; + ParentMap *PM = 0; // Iterate over ExplodedGraph for (ExplodedGraph::node_iterator I = G.nodes_begin(); I != G.nodes_end(); ++I) { const ProgramPoint &P = I->getLocation(); + const LocationContext *LC = P.getLocationContext(); // Save the CFG if we don't have it already if (!C) - C = P.getLocationContext()->getCFG(); + C = LC->getCFG(); + if (!PM) + PM = &LC->getParentMap(); if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) { const CFGBlock *CB = BE->getBlock(); @@ -76,8 +85,8 @@ void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, } } - // Bail out if we didn't get the CFG - if (!C) + // Bail out if we didn't get the CFG or the ParentMap. + if (!C || !PM) return; ASTContext &Ctx = B.getContext(); @@ -86,34 +95,39 @@ void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) { const CFGBlock *CB = *I; // Check if the block is unreachable - if (!reachable.count(CB)) { - // Find the entry points for this block - FindUnreachableEntryPoints(CB); - // This block may have been pruned; check if we still want to report it - if (reachable.count(CB)) - continue; - - // We found a statement that wasn't covered - SourceRange S; - SourceLocation SL = GetUnreachableLoc(*CB, S); - if (S.getBegin().isMacroID() || S.getEnd().isMacroID() || S.isInvalid() - || SL.isInvalid()) - continue; - - // Special case for __builtin_unreachable. - // FIXME: This should be extended to include other unreachable markers, - // such as llvm_unreachable. - if (!CB->empty()) { - const Stmt *First = CB->front(); - if (const CallExpr *CE = dyn_cast<CallExpr>(First)) { - if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) - continue; - } + if (reachable.count(CB)) + continue; + + // Find the entry points for this block + FindUnreachableEntryPoints(CB); + + // This block may have been pruned; check if we still want to report it + if (reachable.count(CB)) + continue; + + // Check for false positives + if (CB->size() > 0 && isInvalidPath(CB, *PM)) + continue; + + // We found a statement that wasn't covered + SourceRange S; + SourceLocation SL = GetUnreachableLoc(*CB, S); + if (S.isInvalid() || SL.isInvalid()) + continue; + + // Special case for __builtin_unreachable. + // FIXME: This should be extended to include other unreachable markers, + // such as llvm_unreachable. + if (!CB->empty()) { + const Stmt *First = CB->front(); + if (const CallExpr *CE = dyn_cast<CallExpr>(First)) { + if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) + continue; } - - B.EmitBasicReport("Unreachable code", "This statement is never executed", - SL, S); } + + B.EmitBasicReport("Unreachable code", "This statement is never executed", + SL, S); } } @@ -156,3 +170,50 @@ SourceLocation UnreachableCodeChecker::GetUnreachableLoc(const CFGBlock &b, R = S->getSourceRange(); return S->getLocStart(); } + +// Returns the Expr* condition if this is a conditional statement, or 0 +// otherwise +const Expr *UnreachableCodeChecker::getConditon(const Stmt *S) { + if (const IfStmt *IS = dyn_cast<IfStmt>(S)) { + return IS->getCond(); + } + else if (const SwitchStmt *SS = dyn_cast<SwitchStmt>(S)) { + return SS->getCond(); + } + else if (const WhileStmt *WS = dyn_cast<WhileStmt>(S)) { + return WS->getCond(); + } + else if (const DoStmt *DS = dyn_cast<DoStmt>(S)) { + return DS->getCond(); + } + else if (const ForStmt *FS = dyn_cast<ForStmt>(S)) { + return FS->getCond(); + } + else if (const ConditionalOperator *CO = dyn_cast<ConditionalOperator>(S)) { + return CO->getCond(); + } + + return 0; +} + +// Traverse the predecessor Stmt*s from this CFGBlock to find any signs of a +// path that is a false positive. +bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, + const ParentMap &PM) { + + // Start at the end of the block and work up the path. + const Stmt *S = CB->back().getStmt(); + while (S) { + // Find any false positives in the conditions on this path. + if (const Expr *cond = getConditon(S)) { + if (containsMacro(cond) || containsEnum(cond) + || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) + || containsStmt<SizeOfAlignOfExpr>(cond)) + return true; + } + // Get the previous statement. + S = PM.getParent(S); + } + + return false; +} |