jyu2 updated this revision to Diff 102759.
jyu2 marked 13 inline comments as done.
https://reviews.llvm.org/D33333
Files:
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/AnalysisBasedWarnings.cpp
test/CXX/except/except.spec/p11.cpp
test/SemaCXX/warn-throw-out-noexcept-func.cpp
Index: lib/Sema/AnalysisBasedWarnings.cpp
===================================================================
--- lib/Sema/AnalysisBasedWarnings.cpp
+++ lib/Sema/AnalysisBasedWarnings.cpp
@@ -279,6 +279,152 @@
}
//===----------------------------------------------------------------------===//
+// Check for throw in a non-throwing function.
+//===----------------------------------------------------------------------===//
+enum ThrowState {
+ FoundNoPathForThrow,
+ FoundPathForThrow,
+ FoundPathWithNoThrowOutFunction,
+};
+
+static bool isThrowCaught(const CXXThrowExpr *Throw,
+ const CXXCatchStmt *Catch) {
+ const Type *CaughtType = Catch->getCaughtType().getTypePtrOrNull();
+ const Type *ThrowType = nullptr;
+ if (Throw->getSubExpr())
+ ThrowType = Throw->getSubExpr()->getType().getTypePtrOrNull();
+
+ if (ThrowType == nullptr)
+ return false;
+ if (ThrowType && ThrowType->isReferenceType())
+ ThrowType = ThrowType->castAs<ReferenceType>()
+ ->getPointeeType()
+ ->getUnqualifiedDesugaredType();
+ if (CaughtType == nullptr)
+ return true;
+ if (CaughtType && 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 CXXThrowExpr *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 CXXTryStmt *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(SourceLocation OpLoc, Sema &S,
+ 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(OpLoc, S, FD);
+}
+
+static bool isNoexcept(const FunctionDecl *FD) {
+ const FunctionProtoType *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 +2273,12 @@
}
}
+ // 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,
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -6341,6 +6341,15 @@
"cannot use '%0' with exceptions disabled">;
def err_objc_exceptions_disabled : Error<
"cannot use '%0' with Objective-C exceptions disabled">;
+def warn_throw_in_noexcept_func
+ : Warning<"%0 has a non-throwing exception specification but can still "
+ "throw, resulting in unexpected program termination">,
+ InGroup<Exceptions>;
+def note_throw_in_dtor
+ : Note<"destructor or deallocator has a (possibly implicit) non-throwing "
+ "excepton specification">;
+def note_throw_in_function
+ : Note<"non-throwing function declare here">;
def err_seh_try_outside_functions : Error<
"cannot use SEH '__try' in blocks, captured regions, or Obj-C method decls">;
def err_mixing_cxx_try_seh_try : Error<
Index: test/SemaCXX/warn-throw-out-noexcept-func.cpp
===================================================================
--- test/SemaCXX/warn-throw-out-noexcept-func.cpp
+++ test/SemaCXX/warn-throw-out-noexcept-func.cpp
@@ -0,0 +1,247 @@
+// RUN: %clang_cc1 %s -fdelayed-template-parsing -fcxx-exceptions -fexceptions -fsyntax-only -Wexceptions -verify -std=c++11
+struct A {
+ ~A();
+}; // implicitly noexcept(true)
+A::~A() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+struct B {
+ int i;
+ ~B() noexcept(true) {}
+};
+struct R : A {
+ B b;
+ ~R() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+
+struct M : A {
+ B b;
+ ~M() noexcept(false);
+};
+
+M::~M() noexcept(false) {
+ throw 1;
+}
+
+struct N : A {
+ B b;
+ ~N(); //implicitly noexcept(true)
+};
+
+N::~N() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+struct X : A {
+ B b;
+ ~X() noexcept { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+struct Y : A {
+ B b;
+ ~Y() noexcept(true) { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+struct C {
+ int i;
+ ~C() noexcept(false) {}
+};
+struct D : A {
+ C c;
+ ~D() { //implicitly noexcept(false)
+ throw 1;
+ }
+};
+struct E : A {
+ C c;
+ ~E(); //implicitly noexcept(false)
+};
+E::~E() //implicitly noexcept(false)
+{
+ throw 1;
+}
+
+template <typename T>
+class A1 {
+ T b;
+
+public:
+ ~A1() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+template <typename T>
+struct B1 {
+ T i;
+ ~B1() noexcept(true) {}
+};
+template <typename T>
+struct R1 : A1<T> //expected-note {{in instantiation of member function}}
+{
+ B1<T> b;
+ ~R1() { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+template <typename T>
+struct S1 : A1<T> {
+ B1<T> b;
+ ~S1() noexcept { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+void operator delete(void *ptr) noexcept { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+struct except_fun {
+ static const bool i = false;
+};
+struct noexcept_fun {
+ static const bool i = true;
+};
+template <typename T>
+struct dependent_warn {
+ ~dependent_warn() noexcept(T::i) {
+ throw 1;
+ }
+};
+template <typename T>
+struct dependent_warn_noexcept {
+ ~dependent_warn_noexcept() noexcept(T::i) { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+template <typename T>
+struct dependent_warn_both {
+ ~dependent_warn_both() noexcept(T::i) { // expected-note {{destructor or deallocator has a}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+ }
+};
+void foo() noexcept { //expected-note {{non-throwing function declare here}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+void bar() noexcept {
+ try {
+ throw 1;
+ } catch (...) {
+ }
+}
+void f() noexcept {
+ try {
+ throw 12;
+ } catch (int) {
+ }
+}
+void g() noexcept {
+ try {
+ throw 12;
+ } catch (...) {
+ }
+}
+
+void h() noexcept { //expected-note {{non-throwing function declare here}}
+ try {
+ throw 12; // expected-warning {{has a non-throwing exception specification but}}
+ } catch (const char *) {
+ }
+}
+
+void i() noexcept { //expected-note {{non-throwing function declare here}}
+ try {
+ throw 12;
+ } catch (int) {
+ throw; // expected-warning {{has a non-throwing exception specification but}}
+ }
+}
+void j() noexcept { //expected-note {{non-throwing function declare here}}
+ try {
+ throw 12;
+ } catch (int) {
+ throw "haha"; // expected-warning {{has a non-throwing exception specification but}}
+ }
+}
+
+void k() noexcept { //expected-note {{non-throwing function declare here}}
+ try {
+ throw 12;
+ } catch (...) {
+ throw; // expected-warning {{has a non-throwing exception specification but}}
+ }
+}
+
+void loo(int i) noexcept { //expected-note {{non-throwing function declare here}}
+ if (i)
+ try {
+ throw 12;
+ } catch (int) {
+ throw "haha"; //expected-warning {{has a non-throwing exception specification but}}
+ }
+ i = 10;
+}
+
+void loo1() noexcept {
+ if (0)
+ throw 12;
+}
+
+void loo2() noexcept { //expected-note {{non-throwing function declare here}}
+ if (1)
+ throw 12; // expected-warning {{has a non-throwing exception specification but}}
+}
+struct S {};
+
+void l() noexcept { //expected-note {{non-throwing function declare here}}
+ try {
+ throw S{}; //expected-warning {{has a non-throwing exception specification but}}
+ } catch (S *s) {
+ }
+}
+
+void m() noexcept {
+ try {
+ const S &s = S{};
+ throw s;
+ } catch (S s) {
+ }
+
+}
+void n() noexcept {
+ try {
+ S s = S{};
+ throw s;
+ } catch (const S &s) {
+ }
+}
+void o() noexcept { //expected-note {{non-throwing function declare here}}
+ try {
+ throw; //expected-warning {{has a non-throwing exception specification but}}
+ } catch (...) {
+ }
+}
+
+#define NOEXCEPT noexcept
+void with_macro() NOEXCEPT { //expected-note {{non-throwing function declare here}}
+ throw 1; // expected-warning {{has a non-throwing exception specification but}}
+}
+
+void with_try_block() try {
+ throw 2;
+} catch (...) {
+}
+
+void with_try_block1() noexcept try { //expected-note {{non-throwing function declare here}}
+ throw 2; // expected-warning {{has a non-throwing exception specification but}}
+} catch (char *) {
+}
+
+int main() {
+ R1<int> o; //expected-note {{in instantiation of member function}}
+ S1<int> b; //expected-note {{in instantiation of member function}}
+ dependent_warn<except_fun> f;
+ dependent_warn_noexcept<noexcept_fun> f1; //expected-note {{in instantiation of member function}}
+ dependent_warn_both<except_fun> f2;
+ dependent_warn_both<noexcept_fun> f3; //expected-note {{in instantiation of member function}}
+}
Index: test/CXX/except/except.spec/p11.cpp
===================================================================
--- test/CXX/except/except.spec/p11.cpp
+++ test/CXX/except/except.spec/p11.cpp
@@ -1,12 +1,11 @@
// RUN: %clang_cc1 -std=c++11 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
-// expected-no-diagnostics
// This is the "let the user shoot themselves in the foot" clause.
-void f() noexcept {
- throw 0; // no-error
+void f() noexcept { // expected-note {{non-throwing function declare here}}
+ throw 0; // expected-warning {{has a non-throwing exception specification but}}
}
-void g() throw() {
- throw 0; // no-error
+void g() throw() { // expected-note {{non-throwing function declare here}}
+ throw 0; // expected-warning {{has a non-throwing exception specification but}}
}
void h() throw(int) {
throw 0.0; // no-error
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits