summaryrefslogtreecommitdiffstats
path: root/clang/lib/Sema/AnalysisBasedWarnings.cpp
diff options
context:
space:
mode:
authorErich Keane <erich.keane@intel.com>2017-06-23 20:22:19 +0000
committerErich Keane <erich.keane@intel.com>2017-06-23 20:22:19 +0000
commit89fe9c269a22425c7f567181cc529854206e5c5d (patch)
tree7d8586083a01137fbe1065037b6506782ac94533 /clang/lib/Sema/AnalysisBasedWarnings.cpp
parent6c3e41bbd3ac6eff9a16e1722427d67867b2194b (diff)
downloadbcm5719-llvm-89fe9c269a22425c7f567181cc529854206e5c5d.tar.gz
bcm5719-llvm-89fe9c269a22425c7f567181cc529854206e5c5d.zip
Emit warning when throw exception in destruct or dealloc functions which has a
(possible implicit) noexcept specifier Throwing in the destructor is not good (C++11 change try to not allow see below). But in reality, those codes are exist. C++11 [class.dtor]p3: A declaration of a destructor that does not have an exception-specification is implicitly considered to have the same exception specification as an implicit declaration. With this change, the application worked before may now run into runtime termination. My goal here is to emit a warning to provide only possible info to where the code may need to be changed. First there is no way, in compile time to identify the “throw” really throw out of the function. Things like the call which throw out… To keep this simple, when “throw” is seen, checking its enclosing function(only destructor and dealloc functions) with noexcept(true) specifier emit warning. Here is implementation detail: A new member function CheckCXXThrowInNonThrowingFunc is added for class Sema in Sema.h. It is used in the call to both BuildCXXThrow and TransformCXXThrowExpr. The function basic check if the enclosing function with non-throwing noexcept specifer, if so emit warning for it. The example of warning message like: k1.cpp:18:3: warning: ''~dependent_warn'' has a (possible implicit) non-throwing noexcept specifier. Throwing exception may cause termination. [-Wthrow-in-dtor] throw 1; ^ k1.cpp:43:30: note: in instantiation of member function 'dependent_warn<noexcept_fun>::~dependent_warn' requested here dependent_warn<noexcept_fun> f; // cause warning Differential Revision: https://reviews.llvm.org/D33333 llvm-svn: 306149
Diffstat (limited to 'clang/lib/Sema/AnalysisBasedWarnings.cpp')
-rw-r--r--clang/lib/Sema/AnalysisBasedWarnings.cpp150
1 files changed, 150 insertions, 0 deletions
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index db688b12cbc..934e13e72d0 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/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,
OpenPOWER on IntegriCloud