[clang] [Clang] Fix false-positive fallthrough warning (#230802) (PR #234848)
Adinesh Reddy via cfe-commits
cfe-commits at lists.llvm.org
Sun Oct 25 05:25:04 PDT 2026
https://github.com/Adinesh-Reddy updated https://github.com/llvm/llvm-project/pull/234848
>From 0ac141c1a3c92a11dba23021efddd9b1163edd0d Mon Sep 17 00:00:00 2001
From: Adinesh Reddy <cs24btech11026 at iith.ac.in>
Date: Fri, 23 Oct 2026 10:19:03 +0530
Subject: [PATCH 1/5] [Clang] Fix false-positive fallthrough warning (#230802)
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 14 ++++++++---
.../SemaCXX/switch-implicit-fallthrough.cpp | 24 +++++++++++++++++++
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 21af6ed5527ffc..718e45a9ccb572 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1276,11 +1276,13 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor {
continue; // Switch statement, good.
const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(P->getLabel());
- if (SW && SW->getSubStmt() == B.getLabel() && P->begin() == P->end())
+ if (SW && stripAttributedStmts(SW->getSubStmt()) == B.getLabel() &&
+ P->begin() == P->end())
continue; // Previous case label has no statements, good.
const LabelStmt *L = dyn_cast_or_null<LabelStmt>(P->getLabel());
- if (L && L->getSubStmt() == B.getLabel() && P->begin() == P->end())
+ if (L && stripAttributedStmts(L->getSubStmt()) == B.getLabel() &&
+ P->begin() == P->end())
continue; // Case label is preceded with a normal label, good.
if (!ReachableBlocks.count(P)) {
@@ -1355,6 +1357,12 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor {
private:
+ static const Stmt *stripAttributedStmts(const Stmt *S) {
+ while (const auto *AS = dyn_cast_or_null<AttributedStmt>(S))
+ S = AS->getSubStmt();
+ return S;
+ }
+
static const AttributedStmt *asFallThroughAttr(const Stmt *S) {
if (const AttributedStmt *AS = dyn_cast_or_null<AttributedStmt>(S)) {
if (hasSpecificAttr<FallThroughAttr>(AS->getAttrs()))
@@ -1373,7 +1381,7 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor {
// case X: {} case Y:
// case X: ; case Y:
if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
- if (!isa<SwitchCase>(SW->getSubStmt()))
+ if (!isa<SwitchCase>(stripAttributedStmts(SW->getSubStmt())))
return SW->getSubStmt();
return nullptr;
diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
index 750663717b66a4..5ca4eeb9ac9473 100644
--- a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
+++ b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
@@ -255,6 +255,30 @@ int fallthrough_covered_enums(Enum e) {
// Fallthrough annotations in local classes used to generate "fallthrough
// annotation does not directly precede switch label" warning.
+
+int fallthrough_attributed_labels(int n) {
+ switch (n) {
+ [[unlikely]] case 0:
+ [[unlikely]] case 1:
+ [[likely]] case 2:
+ case 3:
+ [[unlikely]] default:
+ return 1;
+ }
+ switch (n) {
+ case 4:
+ n++;
+ [[unlikely]] case 5: // 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++;
+ break;
+ [[unlikely]] case 6:
+ n++;
+ [[unlikely]] 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}}
+ return 2;
+ }
+ return n;
+}
+
void fallthrough_in_local_class() {
class C {
void f(int x) {
>From d9487e795c236ff30bac89686f302540f040d557 Mon Sep 17 00:00:00 2001
From: Adinesh Reddy <cs24btech11026 at iith.ac.in>
Date: Fri, 23 Oct 2026 15:07:00 +0530
Subject: [PATCH 2/5] Add fallthrough regression tests
---
clang/lib/Sema/AnalysisBasedWarnings.cpp | 2 +-
.../SemaCXX/switch-implicit-fallthrough.cpp | 70 ++++++++++++++++---
2 files changed, 62 insertions(+), 10 deletions(-)
diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp
index 718e45a9ccb572..41a8eb8a66129e 100644
--- a/clang/lib/Sema/AnalysisBasedWarnings.cpp
+++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp
@@ -1381,7 +1381,7 @@ class FallthroughMapper : public DynamicRecursiveASTVisitor {
// case X: {} case Y:
// case X: ; case Y:
if (const SwitchCase *SW = dyn_cast_or_null<SwitchCase>(B.getLabel()))
- if (!isa<SwitchCase>(stripAttributedStmts(SW->getSubStmt())))
+ if (!isa<SwitchCase>(SW->getSubStmt()))
return SW->getSubStmt();
return nullptr;
diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
index 5ca4eeb9ac9473..cb0e34d57d4a71 100644
--- a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
+++ b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
@@ -253,17 +253,16 @@ int fallthrough_covered_enums(Enum e) {
return n;
}
-// Fallthrough annotations in local classes used to generate "fallthrough
-// annotation does not directly precede switch label" warning.
-
+// Attributes on consecutive switch labels should not introduce false fallthrough.
int fallthrough_attributed_labels(int n) {
switch (n) {
- [[unlikely]] case 0:
- [[unlikely]] case 1:
- [[likely]] case 2:
- case 3:
- [[unlikely]] default:
- return 1;
+ [[unlikely]] case 0:
+ [[unlikely]] case 1:
+ [[likely]] case 2:
+ case 3:
+ return 0;
+ [[unlikely]] default:
+ return 1;
}
switch (n) {
case 4:
@@ -279,6 +278,59 @@ int fallthrough_attributed_labels(int n) {
return n;
}
+// Consecutive ordinary labels do not introduce fallthrough.
+int fallthrough_ordinary_labels(int n) {
+ switch (n) {
+ case 0:
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ default:
+ return 2;
+ }
+}
+
+// An explicit fallthrough annotation should suppress the warning.
+int fallthrough_explicit_annotation(int n) {
+ switch (n) {
+ case 0:
+ ++n;
+ [[clang::fallthrough]];
+ case 1:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+// Genuine fallthrough without an annotation should warn.
+int fallthrough_without_annotation(int n) {
+ switch (n) {
+ case 0:
+ ++n;
+ // expected-warning at +1 {{unannotated fall-through between switch labels}}
+ case 1: // expected-note 2 {{insert}}
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+// An attributed case label immediately follows an ordinary label.
+int fallthrough_label_attributed_case(int n) {
+ switch (n) {
+ case 0:
+ ordinary_label:
+ [[likely]] case 1:
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+// Fallthrough annotations in local classes used to generate "fallthrough
+// annotation does not directly precede switch label" warning.
void fallthrough_in_local_class() {
class C {
void f(int x) {
>From 6180dab32ae13718dc624adc3b906efa8bcd7ac8 Mon Sep 17 00:00:00 2001
From: Adinesh Reddy <cs24btech11026 at iith.ac.in>
Date: Fri, 23 Oct 2026 15:45:47 +0530
Subject: [PATCH 3/5] Preserve original fallthrough reproducer
---
clang/test/SemaCXX/switch-implicit-fallthrough.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
index cb0e34d57d4a71..c07f68f4fe8e58 100644
--- a/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
+++ b/clang/test/SemaCXX/switch-implicit-fallthrough.cpp
@@ -260,9 +260,9 @@ int fallthrough_attributed_labels(int n) {
[[unlikely]] case 1:
[[likely]] case 2:
case 3:
- return 0;
+ [[unlikely]] case 4:
[[unlikely]] default:
- return 1;
+ return -1;
}
switch (n) {
case 4:
>From 5e19877e17bb1f4d8a3cd8d2438a75d8c7d31faf Mon Sep 17 00:00:00 2001
From: Adinesh Reddy <cs24btech11026 at iith.ac.in>
Date: Sun, 25 Oct 2026 17:21:30 +0530
Subject: [PATCH 4/5] [Clang] Add release note for fallthrough warning fix
---
clang/docs/ReleaseNotes.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index 7d3f0eb702553d..a0af970d08aa6a 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -408,6 +408,8 @@ features cannot lower the translation-unit ABI level;
### Improvements to Clang's diagnostics
+- Clang no longer emits a false-positive `-Wimplicit-fallthrough` warning when consecutive switch labels share a body and are annotated with attributes like `[[unlikely]]`. (#GH230802)
+
- Fixed spurious `-Wimplicit-void-ptr-cast` warnings in C for parenthesized
null pointer macros such as `(NULL)`. (#GH171874)
>From 6a058b472ab3ae55bc624272321b922f25149478 Mon Sep 17 00:00:00 2001
From: Adinesh Reddy <cs24btech11026 at iith.ac.in>
Date: Sun, 25 Oct 2026 17:54:53 +0530
Subject: [PATCH 5/5] ReleaseNotes fix
---
clang/docs/ReleaseNotes.md | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index c7ab1f9e0a31f1..dabd0c3d241457 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -458,9 +458,9 @@ documentation](CIR/index.md) for more information regarding ClangIR's status and
### Improvements to Clang's diagnostics
-- Clang no longer emits a false-positive `-Wimplicit-fallthrough` warning
- when consecutive switch labels share a body and are annotated with attributes like
- `[[unlikely]]`. (#GH230802)
+- Fixed a false-positive `-Wimplicit-fallthrough` warning when
+ consecutive switch labels share a body and are annotated with
+ attributes like `[[unlikely]]`. (#GH230802)
- Fixed missing `-Wuninitialized-explicit-init` warnings for C file-scope
tentative definitions of structure types which contain a field marked with
More information about the cfe-commits
mailing list