diff --git a/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp b/clang-tools-extra/clang-tidy/readability/ElseAfterReturnCheck.cpp index 7e93d619e2a9f..d4cb863eb9645 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) { // NOLINT + const auto *Call = dyn_cast(&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, 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(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 - ` check by fixing missed - diagnostics when ``if`` statements appear in unbraced ``switch`` case labels. + ` 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 ` 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); + } +}