diff options
Diffstat (limited to 'lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r-- | lib/Sema/AnalysisBasedWarnings.cpp | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index db688b12cbcfe..934e13e72d056 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -279,6 +279,150 @@ static void checkRecursiveFunction(Sema &S, const FunctionDecl *FD, } //===----------------------------------------------------------------------===// +// Check for throw in a non-throwing function. +//===----------------------------------------------------------------------===// +enum ThrowState { + FoundNoPathForThrow, + FoundPathForThrow, + FoundPathWithNoThrowOutFunction, +}; + +static bool isThrowCaught(const CXXThrowExpr *Throw, + const CXXCatchStmt *Catch) { + const Type *ThrowType = nullptr; + if (Throw->getSubExpr()) + ThrowType = Throw->getSubExpr()->getType().getTypePtrOrNull(); + if (!ThrowType) + return false; + const Type *CaughtType = Catch->getCaughtType().getTypePtrOrNull(); + if (!CaughtType) + return true; + if (ThrowType->isReferenceType()) + ThrowType = ThrowType->castAs<ReferenceType>() + ->getPointeeType() + ->getUnqualifiedDesugaredType(); + if (CaughtType->isReferenceType()) + CaughtType = CaughtType->castAs<ReferenceType>() + ->getPointeeType() + ->getUnqualifiedDesugaredType(); + if (CaughtType == ThrowType) + return true; + const CXXRecordDecl *CaughtAsRecordType = + CaughtType->getPointeeCXXRecordDecl(); + const CXXRecordDecl *ThrowTypeAsRecordType = ThrowType->getAsCXXRecordDecl(); + if (CaughtAsRecordType && ThrowTypeAsRecordType) + return ThrowTypeAsRecordType->isDerivedFrom(CaughtAsRecordType); + return false; +} + +static bool isThrowCaughtByHandlers(const CXXThrowExpr *CE, + const CXXTryStmt *TryStmt) { + for (unsigned H = 0, E = TryStmt->getNumHandlers(); H < E; ++H) { + if (isThrowCaught(CE, TryStmt->getHandler(H))) + return true; + } + return false; +} + +static bool doesThrowEscapePath(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; + + OpLoc = CE->getThrowLoc(); + for (const auto &I : Block.succs()) { + if (!I.isReachable()) + continue; + if (const auto *Terminator = + dyn_cast_or_null<CXXTryStmt>(I->getTerminator())) + if (isThrowCaughtByHandlers(CE, Terminator)) + return false; + } + return true; + } + return false; +} + +static bool hasThrowOutNonThrowingFunc(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.back(); + Stack.pop_back(); + + unsigned ID = CurBlock->getBlockID(); + ThrowState CurState = States[ID]; + if (CurState == FoundPathWithNoThrowOutFunction) { + if (ExitID == ID) + continue; + + if (doesThrowEscapePath(*CurBlock, OpLoc)) + CurState = FoundPathForThrow; + } + + // 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, + const FunctionDecl *FD) { + if (!S.getSourceManager().isInSystemHeader(OpLoc)) { + S.Diag(OpLoc, diag::warn_throw_in_noexcept_func) << FD; + if (S.getLangOpts().CPlusPlus11 && + (isa<CXXDestructorDecl>(FD) || + FD->getDeclName().getCXXOverloadedOperator() == OO_Delete || + FD->getDeclName().getCXXOverloadedOperator() == OO_Array_Delete)) + S.Diag(FD->getLocation(), diag::note_throw_in_dtor); + else + S.Diag(FD->getLocation(), diag::note_throw_in_function); + } +} + +static void checkThrowInNonThrowingFunc(Sema &S, const FunctionDecl *FD, + AnalysisDeclContext &AC) { + CFG *BodyCFG = AC.getCFG(); + if (!BodyCFG) + return; + if (BodyCFG->getExit().pred_empty()) + return; + SourceLocation OpLoc; + if (hasThrowOutNonThrowingFunc(OpLoc, BodyCFG)) + EmitDiagForCXXThrowInNonThrowingFunc(S, OpLoc, FD); +} + +static bool isNoexcept(const FunctionDecl *FD) { + const auto *FPT = FD->getType()->castAs<FunctionProtoType>(); + if (FPT->getExceptionSpecType() != EST_None && + FPT->isNothrow(FD->getASTContext())) + return true; + return false; +} + +//===----------------------------------------------------------------------===// // Check for missing return value. //===----------------------------------------------------------------------===// @@ -2127,6 +2271,12 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, } } + // Check for throw out of non-throwing function. + if (!Diags.isIgnored(diag::warn_throw_in_noexcept_func, D->getLocStart())) + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (S.getLangOpts().CPlusPlus && isNoexcept(FD)) + checkThrowInNonThrowingFunc(S, FD, AC); + // If none of the previous checks caused a CFG build, trigger one here // for -Wtautological-overlap-compare if (!Diags.isIgnored(diag::warn_tautological_overlap_comparison, |