blob: 646cea446c2cd7ee2ff7f29c219ac342958127c5 [file] [log] [blame]
// RUN: %clang_cc1 %s -fdelayed-template-parsing -fcxx-exceptions -fsyntax-only -Wexceptions -verify -fdeclspec -std=c++17
struct A_ShouldDiag {
~A_ShouldDiag(); // implicitly noexcept(true)
};
A_ShouldDiag::~A_ShouldDiag() { // expected-note {{destructor has a implicit non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but can still throw}}
}
struct B_ShouldDiag {
int i;
~B_ShouldDiag() noexcept(true) {} //no disg, no throw stmt
};
struct R_ShouldDiag : A_ShouldDiag {
B_ShouldDiag b;
~R_ShouldDiag() { // expected-note {{destructor has a implicit non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
__attribute__((nothrow)) R_ShouldDiag() {// expected-note {{function declared non-throwing here}}
throw 1;// expected-warning {{has a non-throwing exception specification but}}
}
void __attribute__((nothrow)) SomeThrow() {// expected-note {{function declared non-throwing here}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
void __declspec(nothrow) SomeDeclspecThrow() {// expected-note {{function declared non-throwing here}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
struct M_ShouldNotDiag {
B_ShouldDiag b;
~M_ShouldNotDiag() noexcept(false);
};
M_ShouldNotDiag::~M_ShouldNotDiag() noexcept(false) {
throw 1;
}
struct N_ShouldDiag {
B_ShouldDiag b;
~N_ShouldDiag(); //implicitly noexcept(true)
};
N_ShouldDiag::~N_ShouldDiag() { // expected-note {{destructor has a implicit non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
struct X_ShouldDiag {
B_ShouldDiag b;
~X_ShouldDiag() noexcept { // expected-note {{destructor has a non-throwing exception}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
struct Y_ShouldDiag : A_ShouldDiag {
~Y_ShouldDiag() noexcept(true) { // expected-note {{destructor has a non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
struct C_ShouldNotDiag {
int i;
~C_ShouldNotDiag() noexcept(false) {}
};
struct D_ShouldNotDiag {
C_ShouldNotDiag c;
~D_ShouldNotDiag() { //implicitly noexcept(false)
throw 1;
}
};
struct E_ShouldNotDiag {
C_ShouldNotDiag c;
~E_ShouldNotDiag(); //implicitly noexcept(false)
};
E_ShouldNotDiag::~E_ShouldNotDiag() //implicitly noexcept(false)
{
throw 1;
}
template <typename T>
class A1_ShouldDiag {
T b;
public:
~A1_ShouldDiag() { // expected-note {{destructor has a implicit non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
template <typename T>
struct B1_ShouldDiag {
T i;
~B1_ShouldDiag() noexcept(true) {}
};
template <typename T>
struct R1_ShouldDiag : A1_ShouldDiag<T> //expected-note {{in instantiation of member function}}
{
B1_ShouldDiag<T> b;
~R1_ShouldDiag() { // expected-note {{destructor has a implicit non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
template <typename T>
struct S1_ShouldDiag : A1_ShouldDiag<T> {
B1_ShouldDiag<T> b;
~S1_ShouldDiag() noexcept { // expected-note {{destructor has a non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
void operator delete(void *ptr) noexcept { // expected-note {{deallocator has a non-throwing exception specification}}
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 has a non-throwing exception specification}}
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 has a non-throwing exception specification}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
};
void foo() noexcept { //expected-note {{function declared non-throwing here}}
throw 1; // expected-warning {{has a non-throwing exception specification but}}
}
struct Throws {
~Throws() noexcept(false);
};
struct ShouldDiagnose {
Throws T;
~ShouldDiagnose() noexcept { //expected-note {{destructor has a non-throwing exception specification}}
throw; // expected-warning {{has a non-throwing exception specification but}}
}
};
struct ShouldNotDiagnose {
Throws T;
~ShouldNotDiagnose() {
throw;
}
};
void bar_ShouldNotDiag() noexcept {
try {
throw 1;
} catch (...) {
}
}
void f_ShouldNotDiag() noexcept {
try {
throw 12;
} catch (int) {
}
}
void g_ShouldNotDiag() noexcept {
try {
throw 12;
} catch (...) {
}
}
void h_ShouldDiag() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw 12; // expected-warning {{has a non-throwing exception specification but}}
} catch (const char *) {
}
}
void i_ShouldDiag() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw 12;
} catch (int) {
throw; // expected-warning {{has a non-throwing exception specification but}}
}
}
void j_ShouldDiag() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw 12;
} catch (int) {
throw "haha"; // expected-warning {{has a non-throwing exception specification but}}
}
}
void k_ShouldDiag() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw 12;
} catch (...) {
throw; // expected-warning {{has a non-throwing exception specification but}}
}
}
void loo_ShouldDiag(int i) noexcept { //expected-note {{function declared non-throwing here}}
if (i)
try {
throw 12;
} catch (int) {
throw "haha"; //expected-warning {{has a non-throwing exception specification but}}
}
i = 10;
}
void loo1_ShouldNotDiag() noexcept {
if (0)
throw 12;
}
void loo2_ShouldDiag() noexcept { //expected-note {{function declared non-throwing here}}
if (1)
throw 12; // expected-warning {{has a non-throwing exception specification but}}
}
struct S {};
void l_ShouldDiag() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw S{}; //expected-warning {{has a non-throwing exception specification but}}
} catch (S *s) {
}
}
void m_ShouldNotDiag() noexcept {
try {
const S &s = S{};
throw s;
} catch (S s) {
}
}
void n_ShouldNotDiag() noexcept {
try {
S s = S{};
throw s;
} catch (const S &s) {
}
}
// As seen in p34973, this should not throw the warning. If there is an active
// exception, catch(...) catches everything.
void o_ShouldNotDiag() noexcept {
try {
throw;
} catch (...) {
}
}
void p_ShouldNotDiag() noexcept {
// Don't warn here: it's possible that the user arranges to only call this
// when the active exception is of type 'int'.
try {
throw;
} catch (int){
}
}
void q_ShouldNotDiag() noexcept {
try {
throw;
} catch (int){
} catch (...){
}
}
#define NOEXCEPT noexcept
void with_macro() NOEXCEPT { //expected-note {{function declared non-throwing 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 {{function declared non-throwing here}}
throw 2; // expected-warning {{has a non-throwing exception specification but}}
} catch (char *) {
}
namespace derived {
struct B {};
struct D: B {};
void goodPlain() noexcept {
try {
throw D();
} catch (B) {}
}
void goodReference() noexcept {
try {
throw D();
} catch (B &) {}
}
void goodPointer() noexcept {
D d;
try {
throw &d;
} catch (B *) {}
}
void badPlain() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw B(); // expected-warning {{'badPlain' has a non-throwing exception specification but can still throw}}
} catch (D) {}
}
void badReference() noexcept { //expected-note {{function declared non-throwing here}}
try {
throw B(); // expected-warning {{'badReference' has a non-throwing exception specification but can still throw}}
} catch (D &) {}
}
void badPointer() noexcept { //expected-note {{function declared non-throwing here}}
B b;
try {
throw &b; // expected-warning {{'badPointer' has a non-throwing exception specification but can still throw}}
} catch (D *) {}
}
}
int main() {
R1_ShouldDiag<int> o; //expected-note {{in instantiation of member function}}
S1_ShouldDiag<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}}
ShouldDiagnose obj;
ShouldNotDiagnose obj1;
}
namespace ExceptionInNamespace {
namespace N {
struct E {};
}
void run() throw() {
try {
throw N::E();
} catch (const N::E &e) {
}
}
}
namespace HandlerSpecialCases {
struct A {};
using CA = const A;
struct B : A {};
using CB = const B;
struct AmbigBase {};
struct AmbigMiddle : AmbigBase {};
struct AmbigDerived : AmbigBase, AmbigMiddle {}; // expected-warning {{inaccessible}}
struct PrivateBase {};
struct PrivateDerived : private PrivateBase { friend void bad3() throw(); };
void good() throw() {
try { throw CA(); } catch (volatile A&) {}
try { throw B(); } catch (A&) {}
try { throw B(); } catch (const volatile A&) {}
try { throw CB(); } catch (A&) {}
try { throw (int*)0; } catch (void* const volatile) {}
try { throw (int*)0; } catch (void* const &) {}
try { throw (B*)0; } catch (A*) {}
try { throw (B*)0; } catch (A* const &) {}
try { throw (void(*)() noexcept)0; } catch (void (*)()) {}
try { throw (void(*)() noexcept)0; } catch (void (*const &)()) {}
try { throw (int**)0; } catch (const int * const*) {}
try { throw (int**)0; } catch (const int * const* const&) {}
try { throw nullptr; } catch (int*) {}
try { throw nullptr; } catch (int* const&) {}
}
void bad1() throw() { // expected-note {{here}}
try { throw A(); } catch (const B&) {} // expected-warning {{still throw}}
}
void bad2() throw() { // expected-note {{here}}
try { throw AmbigDerived(); } catch (const AmbigBase&) {} // expected-warning {{still throw}}
}
void bad3() throw() { // expected-note {{here}}
try { throw PrivateDerived(); } catch (const PrivateBase&) {} // expected-warning {{still throw}}
}
void bad4() throw() { // expected-note {{here}}
try { throw (int*)0; } catch (void* &) {} // expected-warning {{still throw}}
}
void bad5() throw() { // expected-note {{here}}
try { throw (int*)0; } catch (void* const volatile &) {} // expected-warning {{still throw}}
}
void bad6() throw() { // expected-note {{here}}
try { throw (int* volatile)0; } catch (void* const volatile &) {} // expected-warning {{still throw}}
}
void bad7() throw() { // expected-note {{here}}
try { throw (AmbigDerived*)0; } catch (AmbigBase*) {} // expected-warning {{still throw}}
}
void bad8() throw() { // expected-note {{here}}
try { throw (PrivateDerived*)0; } catch (PrivateBase*) {} // expected-warning {{still throw}}
}
void bad9() throw() { // expected-note {{here}}
try { throw (B*)0; } catch (A* &) {} // expected-warning {{still throw}}
}
void bad10() throw() { // expected-note {{here}}
try { throw (void(*)())0; } catch (void (*)() noexcept) {} // expected-warning {{still throw}}
}
void bad11() throw() { // expected-note {{here}}
try { throw (int**)0; } catch (const int **) {} // expected-warning {{still throw}}
}
void bad12() throw() { // expected-note {{here}}
try { throw nullptr; } catch (int) {} // expected-warning {{still throw}}
}
}
namespace NestedTry {
void f() noexcept {
try {
try {
throw 0;
} catch (float) {}
} catch (int) {}
}
struct A { [[noreturn]] ~A(); };
void g() noexcept { // expected-note {{here}}
try {
try {
throw 0; // expected-warning {{still throw}}
} catch (float) {}
} catch (const char*) {}
}
void h() noexcept { // expected-note {{here}}
try {
try {
throw 0;
} catch (float) {}
} catch (int) {
throw; // expected-warning {{still throw}}
}
}
// FIXME: Ideally, this should still warn; we can track which types are
// potentially thrown by the rethrow.
void i() noexcept {
try {
try {
throw 0;
} catch (int) {
throw;
}
} catch (float) {}
}
// FIXME: Ideally, this should not warn: the second catch block is
// unreachable.
void j() noexcept { // expected-note {{here}}
try {
try {
throw 0;
} catch (int) {}
} catch (float) {
throw; // expected-warning {{still throw}}
}
}
}