//===----------------------------------------------------------------------===//
// Check for throw in a non-throwing function.
//===----------------------------------------------------------------------===//
-enum ThrowState {
- FoundNoPathForThrow,
- FoundPathForThrow,
- FoundPathWithNoThrowOutFunction,
-};
-static bool isThrowCaughtByHandlers(Sema &S,
- const CXXThrowExpr *CE,
- const CXXTryStmt *TryStmt) {
- for (unsigned H = 0, E = TryStmt->getNumHandlers(); H < E; ++H) {
- QualType Caught = TryStmt->getHandler(H)->getCaughtType();
- if (Caught.isNull() || // catch (...) catches everything
- (CE->getSubExpr() && // throw; is only caught by ...
- S.handlerCanCatch(Caught, CE->getSubExpr()->getType())))
- return true;
- }
- return false;
-}
+/// Determine whether an exception thrown by E, unwinding from ThrowBlock,
+/// can reach ExitBlock.
+static bool throwEscapes(Sema &S, const CXXThrowExpr *E, CFGBlock &ThrowBlock,
+ CFG *Body) {
+ SmallVector<CFGBlock *, 16> Stack;
+ llvm::BitVector Queued(Body->getNumBlockIDs());
-static bool doesThrowEscapePath(Sema &S, CFGBlock Block,
- SourceLocation &OpLoc) {
- for (const auto &B : Block) {
- if (B.getKind() != CFGElement::Statement)
- continue;
- const auto *CE = dyn_cast<CXXThrowExpr>(B.getAs<CFGStmt>()->getStmt());
- if (!CE)
- continue;
+ Stack.push_back(&ThrowBlock);
+ Queued[ThrowBlock.getBlockID()] = true;
- OpLoc = CE->getThrowLoc();
- for (const auto &I : Block.succs()) {
- if (!I.isReachable())
+ while (!Stack.empty()) {
+ CFGBlock &UnwindBlock = *Stack.back();
+ Stack.pop_back();
+
+ for (auto &Succ : UnwindBlock.succs()) {
+ if (!Succ.isReachable() || Queued[Succ->getBlockID()])
continue;
- if (const auto *Terminator =
- dyn_cast_or_null<CXXTryStmt>(I->getTerminator()))
- if (isThrowCaughtByHandlers(S, CE, Terminator))
- return false;
+
+ if (Succ->getBlockID() == Body->getExit().getBlockID())
+ return true;
+
+ if (auto *Catch =
+ dyn_cast_or_null<CXXCatchStmt>(Succ->getLabel())) {
+ QualType Caught = Catch->getCaughtType();
+ if (Caught.isNull() || // catch (...) catches everything
+ !E->getSubExpr() || // throw; is considered cuaght by any handler
+ S.handlerCanCatch(Caught, E->getSubExpr()->getType()))
+ // Exception doesn't escape via this path.
+ break;
+ } else {
+ Stack.push_back(Succ);
+ Queued[Succ->getBlockID()] = true;
+ }
}
- return true;
}
+
return false;
}
-static bool hasThrowOutNonThrowingFunc(Sema &S, SourceLocation &OpLoc,
- CFG *BodyCFG) {
- unsigned ExitID = BodyCFG->getExit().getBlockID();
-
- SmallVector<ThrowState, 16> States(BodyCFG->getNumBlockIDs(),
- FoundNoPathForThrow);
- States[BodyCFG->getEntry().getBlockID()] = FoundPathWithNoThrowOutFunction;
-
- SmallVector<CFGBlock *, 16> Stack;
- Stack.push_back(&BodyCFG->getEntry());
- while (!Stack.empty()) {
- CFGBlock *CurBlock = Stack.pop_back_val();
-
- unsigned ID = CurBlock->getBlockID();
- ThrowState CurState = States[ID];
- if (CurState == FoundPathWithNoThrowOutFunction) {
- if (ExitID == ID)
+static void visitReachableThrows(
+ CFG *BodyCFG,
+ llvm::function_ref<void(const CXXThrowExpr *, CFGBlock &)> Visit) {
+ llvm::BitVector Reachable(BodyCFG->getNumBlockIDs());
+ clang::reachable_code::ScanReachableFromBlock(&BodyCFG->getEntry(), Reachable);
+ for (CFGBlock *B : *BodyCFG) {
+ if (!Reachable[B->getBlockID()])
+ continue;
+ for (CFGElement &E : *B) {
+ Optional<CFGStmt> S = E.getAs<CFGStmt>();
+ if (!S)
continue;
-
- if (doesThrowEscapePath(S, *CurBlock, OpLoc))
- CurState = FoundPathForThrow;
+ if (auto *Throw = dyn_cast<CXXThrowExpr>(S->getStmt()))
+ Visit(Throw, *B);
}
-
- // Loop over successor blocks and add them to the Stack if their state
- // changes.
- for (const auto &I : CurBlock->succs())
- if (I.isReachable()) {
- unsigned NextID = I->getBlockID();
- if (NextID == ExitID && CurState == FoundPathForThrow) {
- States[NextID] = CurState;
- } else if (States[NextID] < CurState) {
- States[NextID] = CurState;
- Stack.push_back(I);
- }
- }
}
- // Return true if the exit node is reachable, and only reachable through
- // a throw expression.
- return States[ExitID] == FoundPathForThrow;
}
static void EmitDiagForCXXThrowInNonThrowingFunc(Sema &S, SourceLocation OpLoc,
if (BodyCFG->getExit().pred_empty())
return;
SourceLocation OpLoc;
- if (hasThrowOutNonThrowingFunc(S, OpLoc, BodyCFG))
- EmitDiagForCXXThrowInNonThrowingFunc(S, OpLoc, FD);
+ visitReachableThrows(BodyCFG, [&](const CXXThrowExpr *Throw, CFGBlock &Block) {
+ if (throwEscapes(S, Throw, Block, BodyCFG))
+ EmitDiagForCXXThrowInNonThrowingFunc(S, Throw->getThrowLoc(), FD);
+ });
}
static bool isNoexcept(const FunctionDecl *FD) {