aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Checker/UnreachableCodeChecker.cpp123
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;
+}