diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h index 0e696a978129..34b8a12f40e9 100644 --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -296,6 +296,12 @@ public: /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes. Stmt *IgnoreImplicit(); + const Stmt *stripLabelLikeStatements() const; + Stmt *stripLabelLikeStatements() { + return const_cast( + const_cast(this)->stripLabelLikeStatements()); + } + // Implement isa support. static bool classof(const Stmt *) { return true; } diff --git a/clang/lib/AST/Stmt.cpp b/clang/lib/AST/Stmt.cpp index 9e4be940110d..e7b87e4db679 100644 --- a/clang/lib/AST/Stmt.cpp +++ b/clang/lib/AST/Stmt.cpp @@ -97,6 +97,22 @@ Stmt *Stmt::IgnoreImplicit() { return s; } +/// \brief Strip off all label-like statements. +/// +/// This will strip off label statements, case statements, and default +/// statements recursively. +const Stmt *Stmt::stripLabelLikeStatements() const { + const Stmt *S = this; + while (true) { + if (const LabelStmt *LS = dyn_cast(S)) + S = LS->getSubStmt(); + else if (const SwitchCase *SC = dyn_cast(S)) + S = SC->getSubStmt(); + else + return S; + } +} + namespace { struct good {}; struct bad {}; diff --git a/clang/lib/Analysis/CFG.cpp b/clang/lib/Analysis/CFG.cpp index 393feffdadad..d385420a5677 100644 --- a/clang/lib/Analysis/CFG.cpp +++ b/clang/lib/Analysis/CFG.cpp @@ -723,9 +723,7 @@ void CFGBuilder::addLocalScopeForStmt(Stmt *S) { if (CompoundStmt *CS = dyn_cast(S)) { for (CompoundStmt::body_iterator BI = CS->body_begin(), BE = CS->body_end() ; BI != BE; ++BI) { - Stmt *SI = *BI; - if (LabelStmt *LS = dyn_cast(SI)) - SI = LS->getSubStmt(); + Stmt *SI = (*BI)->stripLabelLikeStatements(); if (DeclStmt *DS = dyn_cast(SI)) Scope = addLocalScopeForDeclStmt(DS, Scope); } @@ -734,9 +732,7 @@ void CFGBuilder::addLocalScopeForStmt(Stmt *S) { // For any other statement scope will be implicit and as such will be // interesting only for DeclStmt. - if (LabelStmt *LS = dyn_cast(S)) - S = LS->getSubStmt(); - if (DeclStmt *DS = dyn_cast(S)) + if (DeclStmt *DS = dyn_cast(S->stripLabelLikeStatements())) addLocalScopeForDeclStmt(DS); } diff --git a/clang/test/SemaCXX/return-noreturn.cpp b/clang/test/SemaCXX/return-noreturn.cpp index 53ed0d724527..1a10c2aa1687 100644 --- a/clang/test/SemaCXX/return-noreturn.cpp +++ b/clang/test/SemaCXX/return-noreturn.cpp @@ -8,23 +8,37 @@ struct pr6884_abort_struct { ~pr6884_abort_struct() __attribute__((noreturn)) { pr6884_abort(); } }; -int pr6884_f(int x) { - switch (x) { default: pr6884_abort(); } -} +// Ensure that destructors from objects are properly modeled in the CFG despite +// the presence of switches, case statements, labels, and blocks. These tests +// try to cover bugs reported in both PR6884 and PR10063. +namespace abort_struct_complex_cfgs { + int basic(int x) { + switch (x) { default: pr6884_abort(); } + } + int f1(int x) { + switch (x) default: pr6884_abort_struct(); + } + int f2(int x) { + switch (x) { default: pr6884_abort_struct(); } + } + int f2_positive(int x) { + switch (x) { default: ; } + } // expected-warning {{control reaches end of non-void function}} + int f3(int x) { + switch (x) { default: { pr6884_abort_struct(); } } + } + int f4(int x) { + switch (x) default: L1: L2: case 4: pr6884_abort_struct(); + } + int f5(int x) { + switch (x) default: L1: { L2: case 4: pr6884_abort_struct(); } + } + int f6(int x) { + switch (x) default: L1: L2: case 4: { pr6884_abort_struct(); } + } -int pr6884_g(int x) { - switch (x) { default: pr6884_abort_struct(); } -} - -int pr6884_g_positive(int x) { - switch (x) { default: ; } -} // expected-warning {{control reaches end of non-void function}} - -int pr6884_h(int x) { - switch (x) { - default: { - pr6884_abort_struct a; - } + int h(int x) { + switch (x) { default: { pr6884_abort_struct a; } } } }