diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -1087,6 +1087,31 @@ } } + // Does this CFGBlock only contain a single statement which is a return? + bool isEmptyCaseWithReturn(const CFGBlock *B) { + if (B->size() == 1 && B->succ_size() == 1) + if (Optional CS = B->front().getAs()) + if (const Stmt *S = CS->getStmt()) + if (isa(S)) + return true; + return false; + } + + bool shouldntWarnSucessors(const CFGBlock *B) { + return llvm::any_of(B->succs(), + [this](const CFGBlock::AdjacentBlock &Succ) { + if (const CFGBlock *S = Succ.getReachableBlock()) { + if (S->size()) + return isEmptyCaseWithReturn(S); + if (const Stmt *T = S->getTerminatorStmt()) + if (isa(T) || isa(T)) + return true; + return shouldntWarnSucessors(S); + } + return false; + }); + } + bool checkFallThroughIntoBlock(const CFGBlock &B, int &AnnotatedCnt, bool IsTemplateInstantiation) { assert(!ReachableBlocks.empty() && "ReachableBlocks empty"); @@ -1149,6 +1174,9 @@ continue; // Fallthrough annotation, good. } + if (shouldntWarnSucessors(P)) + continue; + if (!LastStmt) { // This block contains no executable statements. // Traverse its predecessors. std::copy(P->pred_begin(), P->pred_end(), diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough-blocks.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough-blocks.cpp --- a/clang/test/SemaCXX/switch-implicit-fallthrough-blocks.cpp +++ b/clang/test/SemaCXX/switch-implicit-fallthrough-blocks.cpp @@ -7,11 +7,17 @@ case 0: x++; [[clang::fallthrough]]; // no diagnostics + case 2: + x++; + case 3: // no diagnostics + break; case 1: x++; default: // \ expected-warning{{unannotated fall-through between switch labels}} \ + expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} \ expected-note{{insert 'break;' to avoid fall-through}} + x++; break; } }; diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp --- a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp +++ b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp @@ -33,7 +33,7 @@ } case 6: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} expected-note{{insert 'break;' to avoid fall-through}} n += 300; - case 66: // expected-warning{{unannotated fall-through between switch labels}} expected-note{{insert 'break;' to avoid fall-through}} + case 66: case 67: case 68: break; @@ -151,23 +151,23 @@ #define MY_CASE2(X, Y, U, V) case X: Y; case U: V int fallthrough_macro1(int n) { - MY_SWITCH(n, 13, n *= 2, 14, break) // expected-warning{{unannotated fall-through between switch labels}} + MY_SWITCH(n, 13, n *= 2, 14, break) switch (n + 1) { MY_CASE(33, n += 2); - MY_CASE(44, break); // expected-warning{{unannotated fall-through between switch labels}} - MY_CASE(55, n += 3); + MY_CASE(44, n += 3); // expected-warning{{unannotated fall-through between switch labels}} + MY_CASE(55, break); } switch (n + 3) { MY_CASE(333, return 333); - MY_CASE2(444, n += 44, 4444, break); // expected-warning{{unannotated fall-through between switch labels}} + MY_CASE2(444, n += 44, 4444, break); MY_CASE(555, n += 33); } - MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break)) // expected-warning{{unannotated fall-through between switch labels}} + MY_SWITCH2(n + 4, MY_CASE(17, n *= 3), MY_CASE(19, break)) - MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break)) // expected-warning{{unannotated fall-through between switch labels}} + MY_SWITCH2(n + 5, MY_CASE(21, break), MY_CASE2(23, n *= 7, 25, break)) return n; } @@ -237,9 +237,15 @@ [[clang::fallthrough]]; // no diagnostics case 1: x++; + case 2: // no diagnostics + break; + case 3: + x++; default: // \ expected-warning{{unannotated fall-through between switch labels}} \ + expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} \ expected-note{{insert 'break;' to avoid fall-through}} + x++; break; } } @@ -257,9 +263,15 @@ [[clang::fallthrough]]; // no diagnostics case 1: x++; + case 2: // no diagnostics + break; + case 3: + x++; default: // \ expected-warning{{unannotated fall-through between switch labels}} \ + expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} \ expected-note{{insert 'break;' to avoid fall-through}} + x++; break; } }; @@ -279,6 +291,106 @@ } } +void bar(void); + +// Should not warn. +void test0(int x) { + switch (x) { + case 0: + bar(); + default: + break; + } +} + +// should not warn. +void test1(int x) { + switch (x) { + case 0: + bar(); + default: + return; + } +} + +// should not warn. +void test2(int x) { + switch (x) { + case 0: + bar(); + default: + goto y; + } +y:; +} + +void test3(int x) { + switch (x) { + case 0: + bar(); + default: // \ + // expected-warning{{unannotated fall-through between switch labels}} \ + // expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} \ + // expected-note{{insert 'break;' to avoid fall-through}} + bar(); + break; + } +} + +// Should not warn. +int y; +void test4(int x) { + switch (x) { + case 0: + if (y) + bar(); + default: + break; + } +} + +// Should not warn. +void test5(int x) { + switch (x) { + case 0: + ++x; + case 1: + case 2: + case 3: + break; + } +} + +// Should not warn. +// TODO: GCC does not warn +void test6(int x) { + switch (x) { + case 0: + bar(); + default: // \ + // expected-warning{{unannotated fall-through between switch labels}} \ + // expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} \ + // expected-note{{insert 'break;' to avoid fall-through}} + ; + } + bar(); + bar(); +} + +// Should warn. +void test7(int x) { + switch (x) { + case 3: + ++x; + case 4: // \ + // expected-warning{{unannotated fall-through between switch labels}} \ + // expected-note{{insert '[[clang::fallthrough]];' to silence this warning}} \ + // expected-note{{insert 'break;' to avoid fall-through}} + do { + } while (1); + break; + } +} int fallthrough_placement_error(int n) { switch (n) { [[clang::fallthrough]]; // expected-warning{{fallthrough annotation in unreachable code}}