[clang-tools-extra] [clang-tidy] Extend readability-else-after-return to remove else after calls to [[noreturn]] functions (PR #185202)
Berkay Sahin via cfe-commits
cfe-commits at lists.llvm.org
Sat Mar 7 09:14:13 PST 2026
https://github.com/berkaysahiin updated https://github.com/llvm/llvm-project/pull/185202
>From 9fd4db3e3ca72eccca786f26e58e4b67d5f0ecdc Mon Sep 17 00:00:00 2001
From: Berkay <berkaysahindev at gmail.com>
Date: Sat, 7 Mar 2026 19:37:50 +0300
Subject: [PATCH 1/2] [clang-tidy] extend readability-else-after-return to
remove else after calls to [[noreturn]] functions
---
.../readability/ElseAfterReturnCheck.cpp | 22 ++++++++++++++++++-
clang-tools-extra/docs/ReleaseNotes.rst | 9 ++++++--
.../readability/else-after-return.cpp | 12 ++++++++++
3 files changed, 40 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
index 7e93d619e2a9f..4160f0fcf1dc1 100644
--- a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
@@ -39,6 +39,23 @@ class PPConditionalCollector : public PPCallbacks {
const SourceManager &SM;
};
+bool isNoReturnStmt(const Stmt &Stmt) {
+ const auto *Call = dyn_cast<CallExpr>(&Stmt);
+ if (!Call)
+ return false;
+
+ const FunctionDecl *Func = Call->getDirectCallee();
+ if (!Func)
+ return false;
+
+ return Func->isNoReturn();
+}
+
+AST_MATCHER(Stmt, isNoReturnStmt) {
+ const Stmt &S = Node;
+ return isNoReturnStmt(S);
+}
+
AST_MATCHER_P(Stmt, stripLabelLikeStatements,
ast_matchers::internal::Matcher<Stmt>, InnerMatcher) {
const Stmt *S = Node.stripLabelLikeStatements();
@@ -174,7 +191,8 @@ void ElseAfterReturnCheck::registerPPCallbacks(const SourceManager &SM,
void ElseAfterReturnCheck::registerMatchers(MatchFinder *Finder) {
const auto InterruptsControlFlow = stmt(anyOf(
returnStmt().bind(InterruptingStr), continueStmt().bind(InterruptingStr),
- breakStmt().bind(InterruptingStr), cxxThrowExpr().bind(InterruptingStr)));
+ breakStmt().bind(InterruptingStr), cxxThrowExpr().bind(InterruptingStr),
+ stmt(isNoReturnStmt()).bind(InterruptingStr)));
const auto IfWithInterruptingThenElse =
ifStmt(unless(isConstexpr()), unless(isConsteval()),
@@ -237,6 +255,8 @@ static StringRef getControlFlowString(const Stmt &Stmt) {
return "break";
if (isa<CXXThrowExpr>(Stmt))
return "throw";
+ if (isNoReturnStmt(Stmt))
+ return "noreturn";
llvm_unreachable("Unknown control flow interrupter");
}
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index b0b4cd646c3bd..33437d2004079 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -295,8 +295,13 @@ Changes in existing checks
when a member expression has a non-identifier name.
- Improved :doc:`readability-else-after-return
- <clang-tidy/checks/readability/else-after-return>` check by fixing missed
- diagnostics when ``if`` statements appear in unbraced ``switch`` case labels.
+ <clang-tidy/checks/readability/else-after-return>` check:
+
+ - Fixed missed diagnostics when ``if`` statements appear in unbraced
+ ``switch`` case labels.
+
+ - Diagnose and remove redundant ``else`` branches after calls to
+ ``[[noreturn]]`` functions.
- Improved :doc:`readability-enum-initial-value
<clang-tidy/checks/readability/enum-initial-value>` check: the warning message
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return.cpp
index e987687a764cd..f0788dbf3221b 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/else-after-return.cpp
@@ -438,3 +438,15 @@ void testLabels(bool b) {
f(0);
}
}
+
+[[noreturn]] void noReturn();
+
+void testNoReturn() {
+ if (true) {
+ noReturn();
+ } else { // comment-28
+ // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: do not use 'else' after 'noreturn'
+ // CHECK-FIXES: {{^}} } // comment-28
+ f(0);
+ }
+}
>From 68d5017a4f90bd14c555b6d47684427dfcd0f73a Mon Sep 17 00:00:00 2001
From: Berkay <berkaysahindev at gmail.com>
Date: Sat, 7 Mar 2026 20:14:02 +0300
Subject: [PATCH 2/2] [clang-tidy] supress the linter
---
.../clang-tidy/readability/ElseAfterReturnCheck.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
index 4160f0fcf1dc1..d4cb863eb9645 100644
--- a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp
@@ -39,7 +39,7 @@ class PPConditionalCollector : public PPCallbacks {
const SourceManager &SM;
};
-bool isNoReturnStmt(const Stmt &Stmt) {
+bool isNoReturnStmt(const Stmt &Stmt) { // NOLINT
const auto *Call = dyn_cast<CallExpr>(&Stmt);
if (!Call)
return false;
More information about the cfe-commits
mailing list