From efcdb4a859e325b772024167553b6b8bfc794ac5 Mon Sep 17 00:00:00 2001 From: Jordan Rose Date: Tue, 1 Apr 2014 16:39:33 +0000 Subject: [PATCH] [analyzer] Fix a CFG printing bug. Also, add several destructor-related tests. Most of them don't work yet, but it's good to have them recorded. Patch by Alex McCarthy! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@205326 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Analysis/CFG.cpp | 2 + test/Analysis/dtor-cxx11.cpp | 15 + test/Analysis/dtor.cpp | 64 ++++ test/Analysis/temp-obj-dtors-cfg-output.cpp | 393 +++++++++++++++++++- test/Analysis/temporaries.cpp | 40 +- test/SemaCXX/return-noreturn.cpp | 8 + 6 files changed, 514 insertions(+), 8 deletions(-) create mode 100644 test/Analysis/dtor-cxx11.cpp diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 13ac92e86f..400c2025bc 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -3903,6 +3903,8 @@ static void print_block(raw_ostream &OS, const CFG* cfg, OS << " (EXIT)]\n"; else if (&B == cfg->getIndirectGotoBlock()) OS << " (INDIRECT GOTO DISPATCH)]\n"; + else if (B.hasNoReturnElement()) + OS << " (NORETURN)]\n"; else OS << "]\n"; diff --git a/test/Analysis/dtor-cxx11.cpp b/test/Analysis/dtor-cxx11.cpp new file mode 100644 index 0000000000..5c9024b8ed --- /dev/null +++ b/test/Analysis/dtor-cxx11.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -analyze -std=c++11 -analyzer-checker=core,unix.Malloc,debug.ExprInspection -analyzer-config cfg-temporary-dtors=true -Wno-null-dereference -verify %s +// expected-no-diagnostics + +#include "Inputs/system-header-simulator-cxx.h" + +namespace Cxx11BraceInit { + struct Foo { + ~Foo() {} + }; + + /* FIXME: Don't crash here. + void testInitializerList() { + for (Foo foo : {Foo(), Foo()}) {} + } */ +} diff --git a/test/Analysis/dtor.cpp b/test/Analysis/dtor.cpp index 11ce0d57ef..dbb950eda0 100644 --- a/test/Analysis/dtor.cpp +++ b/test/Analysis/dtor.cpp @@ -374,6 +374,64 @@ namespace LifetimeExtension { clang_analyzer_eval(SaveOnDestruct::lastOutput == 42); // expected-warning{{TRUE}} } + struct NRCheck { + bool bool_; + NRCheck():bool_(true) {} + ~NRCheck() __attribute__((noreturn)); + operator bool() const { return bool_; } + }; + + struct CheckAutoDestructor { + bool bool_; + CheckAutoDestructor():bool_(true) {} + operator bool() const { return bool_; } + }; + + struct CheckCustomDestructor { + bool bool_; + CheckCustomDestructor():bool_(true) {} + ~CheckCustomDestructor(); + operator bool() const { return bool_; } + }; + + bool testUnnamedNR() { + if (NRCheck()) + return true; + return false; + } + + bool testNamedNR() { + if (NRCheck c = NRCheck()) + return true; + return false; + } + + bool testUnnamedAutoDestructor() { + if (CheckAutoDestructor()) + return true; + return false; + } + + bool testNamedAutoDestructor() { + if (CheckAutoDestructor c = CheckAutoDestructor()) + return true; + return false; + } + + bool testUnnamedCustomDestructor() { + if (CheckCustomDestructor()) + return true; + return false; + } + + // This case used to cause an unexpected "Undefined or garbage value returned + // to caller" warning + bool testNamedCustomDestructor() { + if (CheckCustomDestructor c = CheckCustomDestructor()) + return true; + return false; + } + class VirtualDtorBase { public: int value; @@ -416,6 +474,12 @@ namespace NoReturn { f(&x); *x = 47; // no warning } + + void g2(int *x) { + if (! x) NR(); + // FIXME: this shouldn't cause a warning. + *x = 47; // expected-warning{{Dereference of null pointer}} + } } namespace PseudoDtor { diff --git a/test/Analysis/temp-obj-dtors-cfg-output.cpp b/test/Analysis/temp-obj-dtors-cfg-output.cpp index 9c460ed08d..64f1fa535a 100644 --- a/test/Analysis/temp-obj-dtors-cfg-output.cpp +++ b/test/Analysis/temp-obj-dtors-cfg-output.cpp @@ -51,6 +51,37 @@ void test_cond() { int b; } +struct C { + C():b_(true) {} + ~C() {} + + operator bool() { return b_; } + bool b_; +}; + +struct D { + D():b_(true) {} + + operator bool() { return b_; } + bool b_; +}; + +int test_cond_unnamed_custom_destructor() { + if (C()) { return 1; } else { return 0; } +} + +int test_cond_named_custom_destructor() { + if (C c = C()) { return 1; } else { return 0; } +} + +int test_cond_unnamed_auto_destructor() { + if (D()) { return 1; } else { return 0; } +} + +int test_cond_named_auto_destructor() { + if (D d = D()) { return 1; } else { return 0; } +} + void test_cond_cref() { const A& a = B() ? A() : A(B()); foo(B() ? A() : A(B())); @@ -125,6 +156,38 @@ void test_noreturn2() { int b; } +extern bool check(const NoReturn&); + +// PR16664 and PR18159 +int testConsistencyNestedSimple(bool value) { + if (value) { + if (!value || check(NoReturn())) { + return 1; + } + } + return 0; +} + +// PR16664 and PR18159 +int testConsistencyNestedComplex(bool value) { + if (value) { + if (!value || !value || check(NoReturn())) { + return 1; + } + } + return 0; +} + +// PR16664 and PR18159 +int testConsistencyNestedNormalReturn(bool value) { + if (value) { + if (!value || value || check(NoReturn())) { + return 1; + } + } + return 0; +} + // CHECK: [B1 (ENTRY)] // CHECK: Succs (1): B0 // CHECK: [B0 (EXIT)] @@ -470,6 +533,166 @@ void test_noreturn2() { // CHECK: Succs (2): B8 B9 // CHECK: [B0 (EXIT)] // CHECK: Preds (1): B1 +// CHECK: C() : b_(true) +// CHECK: [B2 (ENTRY)] +// CHECK: Succs (1): B1 +// CHECK: [B1] +// CHECK: 1: true +// CHECK: 2: b_([B1.1]) (Member initializer) +// CHECK: Preds (1): B2 +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (1): B1 +// CHECK: ~C() +// CHECK: [B1 (ENTRY)] +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (1): B1 +// CHECK: operator bool() +// CHECK: [B2 (ENTRY)] +// CHECK: Succs (1): B1 +// CHECK: [B1] +// CHECK: 1: this +// CHECK: 2: [B1.1]->b_ +// CHECK: 3: [B1.2] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: 4: return [B1.3]; +// CHECK: Preds (1): B2 +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (1): B1 +// CHECK: D() : b_(true) +// CHECK: [B2 (ENTRY)] +// CHECK: Succs (1): B1 +// CHECK: [B1] +// CHECK: 1: true +// CHECK: 2: b_([B1.1]) (Member initializer) +// CHECK: Preds (1): B2 +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (1): B1 +// CHECK: operator bool() +// CHECK: [B2 (ENTRY)] +// CHECK: Succs (1): B1 +// CHECK: [B1] +// CHECK: 1: this +// CHECK: 2: [B1.1]->b_ +// CHECK: 3: [B1.2] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: 4: return [B1.3]; +// CHECK: Preds (1): B2 +// CHECK: Succs (1): B0 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (1): B1 +// CHECK: int test_cond_unnamed_custom_destructor() +// CHECK: [B4 (ENTRY)] +// CHECK: Succs (1): B3 +// CHECK: [B1] +// CHECK: 1: 0 +// CHECK: 2: return [B1.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 1 +// CHECK: 2: return [B2.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: 1: C() (CXXConstructExpr, struct C) +// CHECK: 2: [B3.1] (BindTemporary) +// CHECK: 3: [B3.2].operator bool +// CHECK: 4: [B3.2] +// CHECK: 5: [B3.4] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CHECK: 6: ~C() (Temporary object destructor) +// CHECK: T: if [B3.5] +// CHECK: Preds (1): B4 +// CHECK: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (2): B1 B2 +// CHECK: int test_cond_named_custom_destructor() +// CHECK: [B5 (ENTRY)] +// CHECK: Succs (1): B4 +// CHECK: [B1] +// CHECK: 1: [B4.7].~C() (Implicit destructor) +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 0 +// CHECK: 2: return [B2.1]; +// CHECK: 3: [B4.7].~C() (Implicit destructor) +// CHECK: Preds (1): B4 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: 1: 1 +// CHECK: 2: return [B3.1]; +// CHECK: 3: [B4.7].~C() (Implicit destructor) +// CHECK: Preds (1): B4 +// CHECK: Succs (1): B0 +// CHECK: [B4] +// CHECK: 1: C() (CXXConstructExpr, struct C) +// CHECK: 2: [B4.1] (BindTemporary) +// CHECK: 3: [B4.2] (ImplicitCastExpr, NoOp, const struct C) +// CHECK: 4: [B4.3] +// CHECK: 5: [B4.4] (CXXConstructExpr, struct C) +// CHECK: 6: ~C() (Temporary object destructor) +// CHECK: 7: C c = C(); +// CHECK: 8: c +// CHECK: 9: [B4.8].operator bool +// CHECK: 10: [B4.8] +// CHECK: 11: [B4.10] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CHECK: T: if [B4.11] +// CHECK: Preds (1): B5 +// CHECK: Succs (2): B3 B2 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (3): B1 B2 B3 +// CHECK: int test_cond_unnamed_auto_destructor() +// CHECK: [B4 (ENTRY)] +// CHECK: Succs (1): B3 +// CHECK: [B1] +// CHECK: 1: 0 +// CHECK: 2: return [B1.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 1 +// CHECK: 2: return [B2.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: 1: D() (CXXConstructExpr, struct D) +// CHECK: 2: [B3.1].operator bool +// CHECK: 3: [B3.1] +// CHECK: 4: [B3.3] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CHECK: T: if [B3.4] +// CHECK: Preds (1): B4 +// CHECK: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (2): B1 B2 +// CHECK: int test_cond_named_auto_destructor() +// CHECK: [B4 (ENTRY)] +// CHECK: Succs (1): B3 +// CHECK: [B1] +// CHECK: 1: 0 +// CHECK: 2: return [B1.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 1 +// CHECK: 2: return [B2.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: 1: D() (CXXConstructExpr, struct D) +// CHECK: 2: [B3.1] (ImplicitCastExpr, NoOp, const struct D) +// CHECK: 3: [B3.2] +// CHECK: 4: [B3.3] (CXXConstructExpr, struct D) +// CHECK: 5: D d = D(); +// CHECK: 6: d +// CHECK: 7: [B3.6].operator bool +// CHECK: 8: [B3.6] +// CHECK: 9: [B3.8] (ImplicitCastExpr, UserDefinedConversion, _Bool) +// CHECK: T: if [B3.9] +// CHECK: Preds (1): B4 +// CHECK: Succs (2): B2 B1 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (2): B1 B2 // CHECK: [B14 (ENTRY)] // CHECK: Succs (1): B13 // CHECK: [B1] @@ -867,7 +1090,7 @@ void test_noreturn2() { // CHECK: [B1] // CHECK: 1: int b; // CHECK: Succs (1): B0 -// CHECK: [B2] +// CHECK: [B2 (NORETURN)] // CHECK: 1: int a; // CHECK: 2: NoReturn() (CXXConstructExpr, class NoReturn) // CHECK: 3: [B2.2] (BindTemporary) @@ -883,7 +1106,7 @@ void test_noreturn2() { // CHECK: [B1] // CHECK: 1: int b; // CHECK: Succs (1): B0 -// CHECK: [B2] +// CHECK: [B2 (NORETURN)] // CHECK: 1: int a; // CHECK: 2: NoReturn() (CXXConstructExpr, class NoReturn) // CHECK: 3: [B2.2] (BindTemporary) @@ -894,3 +1117,169 @@ void test_noreturn2() { // CHECK: Succs (1): B0 // CHECK: [B0 (EXIT)] // CHECK: Preds (2): B1 B2 +// CHECK: int testConsistencyNestedSimple(bool value) +// CHECK: [B9 (ENTRY)] +// CHECK: Succs (1): B8 +// CHECK: [B1] +// CHECK: 1: 0 +// CHECK: 2: return [B1.1]; +// CHECK: Preds (2): B3 B8 +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 1 +// CHECK: 2: return [B2.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: T: if [B5.1] +// CHECK: Preds (1): B5 +// CHECK: Succs (2): B2 B1 +// CHECK: [B4 (NORETURN)] +// CHECK: 1: ~NoReturn() (Temporary object destructor) +// CHECK: Preds (1): B5 +// CHECK: Succs (1): B0 +// CHECK: [B5] +// CHECK: 1: [B7.3] || [B6.7] +// CHECK: T: (Temp Dtor) [B7.3] || ... +// CHECK: Preds (2): B6 B7 +// CHECK: Succs (2): B3 B4 +// CHECK: [B6] +// CHECK: 1: check +// CHECK: 2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &)) +// CHECK: 3: NoReturn() (CXXConstructExpr, class NoReturn) +// CHECK: 4: [B6.3] (BindTemporary) +// CHECK: 5: [B6.4] (ImplicitCastExpr, NoOp, const class NoReturn) +// CHECK: 6: [B6.5] +// CHECK: 7: [B6.2]([B6.6]) +// CHECK: Preds (1): B7 +// CHECK: Succs (1): B5 +// CHECK: [B7] +// CHECK: 1: value +// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: 3: ![B7.2] +// CHECK: T: [B7.3] || ... +// CHECK: Preds (1): B8 +// CHECK: Succs (2): B5 B6 +// CHECK: [B8] +// CHECK: 1: value +// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: T: if [B8.2] +// CHECK: Preds (1): B9 +// CHECK: Succs (2): B7 B1 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (3): B1 B2 B4 +// CHECK: int testConsistencyNestedComplex(bool value) +// CHECK: [B10 (ENTRY)] +// CHECK: Succs (1): B9 +// CHECK: [B1] +// CHECK: 1: 0 +// CHECK: 2: return [B1.1]; +// CHECK: Preds (2): B3 B9 +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 1 +// CHECK: 2: return [B2.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: T: if [B5.1] +// CHECK: Preds (1): B5 +// CHECK: Succs (2): B2 B1 +// CHECK: [B4 (NORETURN)] +// CHECK: 1: ~NoReturn() (Temporary object destructor) +// CHECK: Preds (1): B5 +// CHECK: Succs (1): B0 +// CHECK: [B5] +// CHECK: 1: [B8.3] || [B7.3] || [B6.7] +// CHECK: T: (Temp Dtor) [B8.3] || [B7.3] || ... +// CHECK: Preds (3): B6 B7 B8 +// CHECK: Succs (2): B3 B4 +// CHECK: [B6] +// CHECK: 1: check +// CHECK: 2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &)) +// CHECK: 3: NoReturn() (CXXConstructExpr, class NoReturn) +// CHECK: 4: [B6.3] (BindTemporary) +// CHECK: 5: [B6.4] (ImplicitCastExpr, NoOp, const class NoReturn) +// CHECK: 6: [B6.5] +// CHECK: 7: [B6.2]([B6.6]) +// CHECK: Preds (1): B7 +// CHECK: Succs (1): B5 +// CHECK: [B7] +// CHECK: 1: value +// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: 3: ![B7.2] +// CHECK: T: [B8.3] || [B7.3] || ... +// CHECK: Preds (1): B8 +// CHECK: Succs (2): B5 B6 +// CHECK: [B8] +// CHECK: 1: value +// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: 3: ![B8.2] +// CHECK: T: [B8.3] || ... +// CHECK: Preds (1): B9 +// CHECK: Succs (2): B5 B7 +// CHECK: [B9] +// CHECK: 1: value +// CHECK: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: T: if [B9.2] +// CHECK: Preds (1): B10 +// CHECK: Succs (2): B8 B1 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (3): B1 B2 B4 +// CHECK: int testConsistencyNestedNormalReturn(bool value) +// CHECK: [B10 (ENTRY)] +// CHECK: Succs (1): B9 +// CHECK: [B1] +// CHECK: 1: 0 +// CHECK: 2: return [B1.1]; +// CHECK: Preds (2): B3 B9 +// CHECK: Succs (1): B0 +// CHECK: [B2] +// CHECK: 1: 1 +// CHECK: 2: return [B2.1]; +// CHECK: Preds (1): B3 +// CHECK: Succs (1): B0 +// CHECK: [B3] +// CHECK: T: if [B5.1] +// CHECK: Preds (1): B5 +// CHECK: Succs (2): B2 B1 +// CHECK: [B4 (NORETURN)] +// CHECK: 1: ~NoReturn() (Temporary object destructor) +// CHECK: Preds (1): B5 +// CHECK: Succs (1): B0 +// CHECK: [B5] +// CHECK: 1: [B8.3] || [B7.2] || [B6.7] +// CHECK: T: (Temp Dtor) [B8.3] || [B7.2] || ... +// CHECK: Preds (3): B6 B7 B8 +// CHECK: Succs (2): B3 B4 +// CHECK: [B6] +// CHECK: 1: check +// CHECK: 2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &)) +// CHECK: 3: NoReturn() (CXXConstructExpr, class NoReturn) +// CHECK: 4: [B6.3] (BindTemporary) +// CHECK: 5: [B6.4] (ImplicitCastExpr, NoOp, const class NoReturn) +// CHECK: 6: [B6.5] +// CHECK: 7: [B6.2]([B6.6]) +// CHECK: Preds (1): B7 +// CHECK: Succs (1): B5 +// CHECK: [B7] +// CHECK: 1: value +// CHECK: 2: [B7.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: T: [B8.3] || [B7.2] || ... +// CHECK: Preds (1): B8 +// CHECK: Succs (2): B5 B6 +// CHECK: [B8] +// CHECK: 1: value +// CHECK: 2: [B8.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: 3: ![B8.2] +// CHECK: T: [B8.3] || ... +// CHECK: Preds (1): B9 +// CHECK: Succs (2): B5 B7 +// CHECK: [B9] +// CHECK: 1: value +// CHECK: 2: [B9.1] (ImplicitCastExpr, LValueToRValue, _Bool) +// CHECK: T: if [B9.2] +// CHECK: Preds (1): B10 +// CHECK: Succs (2): B8 B1 +// CHECK: [B0 (EXIT)] +// CHECK: Preds (3): B1 B2 B4 diff --git a/test/Analysis/temporaries.cpp b/test/Analysis/temporaries.cpp index 6b49fcbddd..3bb88c4d4e 100644 --- a/test/Analysis/temporaries.cpp +++ b/test/Analysis/temporaries.cpp @@ -119,8 +119,8 @@ namespace destructors { extern bool check(const Dtor &); #ifndef TEMPORARY_DTORS - // FIXME: Don't crash here when tmp dtros are enabled. - // PR16664 and PR18159 + // FIXME: Don't assert here when tmp dtors are enabled. + // PR16664 and PR18159 if (coin() && (coin() || coin() || check(Dtor()))) { Dtor(); } @@ -170,10 +170,9 @@ namespace destructors { clang_analyzer_eval(true); // no warning, unreachable code } - /* - // PR16664 and PR18159 - FIXME: Don't crash here. + // PR16664 and PR18159 + FIXME: Don't assert here. void testConsistencyNested(int i) { extern bool compute(bool); @@ -193,6 +192,7 @@ namespace destructors { clang_analyzer_eval(true); // expected TRUE } + FIXME: This shouldn't cause a warning. if (compute(i == 5 && (i == 4 || i == 4 || compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) || @@ -200,7 +200,35 @@ namespace destructors { clang_analyzer_eval(true); // no warning, unreachable code } }*/ - + + // PR16664 and PR18159 + void testConsistencyNestedSimple(bool value) { + if (value) { + if (!value || check(NoReturnDtor())) { + clang_analyzer_eval(true); // no warning, unreachable code + } + } + } + + // PR16664 and PR18159 + void testConsistencyNestedComplex(bool value) { + if (value) { + if (!value || !value || check(NoReturnDtor())) { + // FIXME: This shouldn't cause a warning. + clang_analyzer_eval(true); // expected-warning{{TRUE}} + } + } + } + + // PR16664 and PR18159 + void testConsistencyNestedWarning(bool value) { + if (value) { + if (!value || value || check(NoReturnDtor())) { + clang_analyzer_eval(true); // expected-warning{{TRUE}} + } + } + } + #endif // TEMPORARY_DTORS } diff --git a/test/SemaCXX/return-noreturn.cpp b/test/SemaCXX/return-noreturn.cpp index 617de00895..16bb86cc21 100644 --- a/test/SemaCXX/return-noreturn.cpp +++ b/test/SemaCXX/return-noreturn.cpp @@ -40,6 +40,14 @@ namespace abort_struct_complex_cfgs { switch (x) default: L1: L2: case 4: { pr6884_abort_struct(); } } + // FIXME: detect noreturn destructors triggered by calls to delete. + int f7(int x) { + switch (x) default: L1: L2: case 4: { + pr6884_abort_struct *p = new pr6884_abort_struct(); + delete p; + } + } // expected-warning {{control reaches end of non-void function}} + // Test that these constructs work even when extraneous blocks are created // before and after the switch due to implicit destructors. int g1(int x) { -- 2.40.0