summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--clang/include/clang/Analysis/CFG.h3
-rw-r--r--clang/include/clang/Analysis/PathSensitive/AnalysisContext.h11
-rw-r--r--clang/lib/Analysis/AnalysisContext.cpp2
-rw-r--r--clang/lib/Analysis/CFG.cpp44
-rw-r--r--clang/lib/Sema/SemaChecking.cpp38
-rw-r--r--clang/lib/Sema/SemaDecl.cpp4
-rw-r--r--clang/test/SemaCXX/warn-unreachable.cpp35
7 files changed, 121 insertions, 16 deletions
diff --git a/clang/include/clang/Analysis/CFG.h b/clang/include/clang/Analysis/CFG.h
index fcfa6b7c957..e87784fd85e 100644
--- a/clang/include/clang/Analysis/CFG.h
+++ b/clang/include/clang/Analysis/CFG.h
@@ -133,7 +133,7 @@ class CFGBlock {
/// Label - An (optional) label that prefixes the executable
/// statements in the block. When this variable is non-NULL, it is
- /// either an instance of LabelStmt or SwitchCase.
+ /// either an instance of LabelStmt, SwitchCase or CXXCatchStmt.
Stmt *Label;
/// Terminator - The terminator for a basic block that
@@ -287,6 +287,7 @@ public:
/// buildCFG - Builds a CFG from an AST. The responsibility to free the
/// constructed CFG belongs to the caller.
static CFG* buildCFG(const Decl *D, Stmt* AST, ASTContext *C,
+ bool AddEHEdges = false,
bool AddScopes = false);
/// createBlock - Create a new block in the CFG. The CFG owns the block;
diff --git a/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h b/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h
index 63ba558e3d5..c82bb962fd1 100644
--- a/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h
+++ b/clang/include/clang/Analysis/PathSensitive/AnalysisContext.h
@@ -46,14 +46,21 @@ class AnalysisContext {
ParentMap *PM;
llvm::DenseMap<const BlockDecl*,void*> *ReferencedBlockVars;
llvm::BumpPtrAllocator A;
+ bool AddEHEdges;
public:
- AnalysisContext(const Decl *d) : D(d), cfg(0), liveness(0), PM(0),
- ReferencedBlockVars(0) {}
+ AnalysisContext(const Decl *d, bool addehedges = false)
+ : D(d), cfg(0), liveness(0), PM(0), ReferencedBlockVars(0),
+ AddEHEdges(addehedges) {}
~AnalysisContext();
ASTContext &getASTContext() { return D->getASTContext(); }
const Decl *getDecl() { return D; }
+ /// getAddEHEdges - Return true iff we are adding exceptional edges from
+ /// callExprs. If this is false, then try/catch statements and blocks
+ /// reachable from them can appear to be dead in the CFG, analysis passes must
+ /// cope with that.
+ bool getAddEHEdges() const { return AddEHEdges; }
Stmt *getBody();
CFG *getCFG();
ParentMap &getParentMap();
diff --git a/clang/lib/Analysis/AnalysisContext.cpp b/clang/lib/Analysis/AnalysisContext.cpp
index 1d5b4a18e98..ad9f6dd1941 100644
--- a/clang/lib/Analysis/AnalysisContext.cpp
+++ b/clang/lib/Analysis/AnalysisContext.cpp
@@ -55,7 +55,7 @@ const ImplicitParamDecl *AnalysisContext::getSelfDecl() const {
CFG *AnalysisContext::getCFG() {
if (!cfg)
- cfg = CFG::buildCFG(D, getBody(), &D->getASTContext());
+ cfg = CFG::buildCFG(D, getBody(), &D->getASTContext(), AddEHEdges);
return cfg;
}
diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp
index 57053b16589..ef3cdd88abe 100644
--- a/clang/lib/Analysis/CFG.cpp
+++ b/clang/lib/Analysis/CFG.cpp
@@ -94,7 +94,8 @@ public:
TryTerminatedBlock(NULL) {}
// buildCFG - Used by external clients to construct the CFG.
- CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, bool AddScopes);
+ CFG* buildCFG(const Decl *D, Stmt *Statement, ASTContext *C, bool AddEHEdges,
+ bool AddScopes);
private:
// Visitors to walk an AST and construct the CFG.
@@ -208,6 +209,11 @@ private:
}
bool badCFG;
+
+ // True iff EH edges on CallExprs should be added to the CFG.
+ bool AddEHEdges;
+
+ // True iff scope start and scope end notes should be added to the CFG.
bool AddScopes;
};
@@ -231,7 +237,7 @@ static VariableArrayType* FindVA(Type* t) {
/// transferred to the caller. If CFG construction fails, this method returns
/// NULL.
CFG* CFGBuilder::buildCFG(const Decl *D, Stmt* Statement, ASTContext* C,
- bool AddScopes) {
+ bool AddEHEdges, bool AddScopes) {
Context = C;
assert(cfg.get());
if (!Statement)
@@ -540,6 +546,22 @@ CFGBlock *CFGBuilder::VisitBreakStmt(BreakStmt *B) {
return Block;
}
+static bool CanThrow(Expr *E) {
+ QualType Ty = E->getType();
+ if (Ty->isFunctionPointerType())
+ Ty = Ty->getAs<PointerType>()->getPointeeType();
+ else if (Ty->isBlockPointerType())
+ Ty = Ty->getAs<BlockPointerType>()->getPointeeType();
+
+ const FunctionType *FT = Ty->getAs<FunctionType>();
+ if (FT) {
+ if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FT))
+ if (Proto->hasEmptyExceptionSpec())
+ return false;
+ }
+ return true;
+}
+
CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
// If this is a call to a no-return function, this stops the block here.
bool NoReturn = false;
@@ -547,21 +569,25 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
NoReturn = true;
}
- bool CanThrow = false;
+ bool AddEHEdge = false;
// Languages without exceptions are assumed to not throw.
if (Context->getLangOptions().Exceptions) {
- CanThrow = true;
+ if (AddEHEdges)
+ AddEHEdge = true;
}
if (FunctionDecl *FD = C->getDirectCallee()) {
if (FD->hasAttr<NoReturnAttr>())
NoReturn = true;
if (FD->hasAttr<NoThrowAttr>())
- CanThrow = false;
+ AddEHEdge = false;
}
- if (!NoReturn && !CanThrow)
+ if (!CanThrow(C->getCallee()))
+ AddEHEdge = false;
+
+ if (!NoReturn && !AddEHEdge)
return VisitStmt(C, asc);
if (Block) {
@@ -577,7 +603,7 @@ CFGBlock *CFGBuilder::VisitCallExpr(CallExpr *C, AddStmtChoice asc) {
// Wire this to the exit block directly.
AddSuccessor(Block, &cfg->getExit());
}
- if (CanThrow) {
+ if (AddEHEdge) {
// Add exceptional edges.
if (TryTerminatedBlock)
AddSuccessor(Block, TryTerminatedBlock);
@@ -1714,9 +1740,9 @@ CFGBlock* CFG::createBlock() {
/// buildCFG - Constructs a CFG from an AST. Ownership of the returned
/// CFG is returned to the caller.
CFG* CFG::buildCFG(const Decl *D, Stmt* Statement, ASTContext *C,
- bool AddScopes) {
+ bool AddEHEdges, bool AddScopes) {
CFGBuilder Builder;
- return Builder.buildCFG(D, Statement, C, AddScopes);
+ return Builder.buildCFG(D, Statement, C, AddEHEdges, AddScopes);
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index e61110f195f..9a6f950c245 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -2091,6 +2091,9 @@ static SourceLocation GetUnreachableLoc(CFGBlock &b) {
}
return b[1].getStmt()->getLocStart();
}
+ case Stmt::CXXTryStmtClass: {
+ return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc();
+ }
default: ;
}
return S->getLocStart();
@@ -2167,12 +2170,20 @@ void Sema::CheckUnreachable(AnalysisContext &AC) {
return;
llvm::SmallVector<SourceLocation, 24> lines;
+ bool AddEHEdges = AC.getAddEHEdges();
// First, give warnings for blocks with no predecessors, as they
// can't be part of a loop.
for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) {
CFGBlock &b = **I;
if (!live[b.getBlockID()]) {
if (b.pred_begin() == b.pred_end()) {
+ if (!AddEHEdges && b.getTerminator()
+ && isa<CXXTryStmt>(b.getTerminator())) {
+ // When not adding EH edges from calls, catch clauses
+ // can otherwise seem dead. Avoid noting them as dead.
+ count += MarkLive(&b, live);
+ continue;
+ }
SourceLocation c = GetUnreachableLoc(b);
if (!c.isValid()) {
// Blocks without a location can't produce a warning, so don't mark
@@ -2222,11 +2233,29 @@ Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) {
// FIXME: This should be NeverFallThrough
return NeverFallThroughOrReturn;
- // The CFG leaves in dead things, and we don't want to dead code paths to
+ // The CFG leaves in dead things, and we don't want the dead code paths to
// confuse us, so we mark all live things first.
std::queue<CFGBlock*> workq;
llvm::BitVector live(cfg->getNumBlockIDs());
- MarkLive(&cfg->getEntry(), live);
+ unsigned count = MarkLive(&cfg->getEntry(), live);
+
+ bool AddEHEdges = AC.getAddEHEdges();
+ if (!AddEHEdges && count != cfg->getNumBlockIDs())
+ // When there are things remaining dead, and we didn't add EH edges
+ // from CallExprs to the catch clauses, we have to go back and
+ // mark them as live.
+ for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) {
+ CFGBlock &b = **I;
+ if (!live[b.getBlockID()]) {
+ if (b.pred_begin() == b.pred_end()) {
+ if (b.getTerminator() && isa<CXXTryStmt>(b.getTerminator()))
+ // When not adding EH edges from calls, catch clauses
+ // can otherwise seem dead. Avoid noting them as dead.
+ count += MarkLive(&b, live);
+ continue;
+ }
+ }
+ }
// Now we know what is live, we check the live precessors of the exit block
// and look for fall through paths, being careful to ignore normal returns,
@@ -2243,6 +2272,11 @@ Sema::ControlFlowKind Sema::CheckFallThrough(AnalysisContext &AC) {
if (!live[B.getBlockID()])
continue;
if (B.size() == 0) {
+ if (B.getTerminator() && isa<CXXTryStmt>(B.getTerminator())) {
+ HasAbnormalEdge = true;
+ continue;
+ }
+
// A labeled empty statement, or the entry block...
HasPlainEdge = true;
continue;
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 1e1df069ea2..b6b3b5bee16 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -4102,7 +4102,9 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,
Decl *dcl = D.getAs<Decl>();
Stmt *Body = BodyArg.takeAs<Stmt>();
- AnalysisContext AC(dcl);
+ // Don't generate EH edges for CallExprs as we'd like to avoid the n^2
+ // explosion for destrutors that can result and the compile time hit.
+ AnalysisContext AC(dcl, false);
FunctionDecl *FD = 0;
FunctionTemplateDecl *FunTmpl = dyn_cast_or_null<FunctionTemplateDecl>(dcl);
if (FunTmpl)
diff --git a/clang/test/SemaCXX/warn-unreachable.cpp b/clang/test/SemaCXX/warn-unreachable.cpp
new file mode 100644
index 00000000000..13a82f4f183
--- /dev/null
+++ b/clang/test/SemaCXX/warn-unreachable.cpp
@@ -0,0 +1,35 @@
+// RUN: %clang %s -fsyntax-only -Xclang -verify -fblocks -Wunreachable-code -Wno-unused-value
+
+int live();
+int dead();
+int liveti() throw(int);
+int (*livetip)() throw(int);
+
+int test1() {
+ try {
+ live();
+ } catch (int i) {
+ live();
+ }
+ return 1;
+}
+
+void test2() {
+ try {
+ live();
+ } catch (int i) {
+ live();
+ }
+ try {
+ liveti();
+ } catch (int i) {
+ live();
+ }
+ try {
+ livetip();
+ } catch (int i) {
+ live();
+ }
+ throw 1;
+ dead(); // expected-warning {{will never be executed}}
+}
OpenPOWER on IntegriCloud