https://github.com/snarang181 updated https://github.com/llvm/llvm-project/pull/145166
>From 359dfb1a835617a83ae13865817480e68aa67750 Mon Sep 17 00:00:00 2001 From: Samarth Narang <snar...@umass.edu> Date: Sat, 21 Jun 2025 08:42:00 -0400 Subject: [PATCH 1/4] Suppress noreturn warning if last statement in a function is a throw --- clang/lib/Sema/AnalysisBasedWarnings.cpp | 45 ++++++++++++++++++++ clang/test/SemaCXX/wreturn-always-throws.cpp | 22 ++++++++++ 2 files changed, 67 insertions(+) create mode 100644 clang/test/SemaCXX/wreturn-always-throws.cpp diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 2a107a36e24b4..85a5d99c710d5 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -626,6 +626,31 @@ struct CheckFallThroughDiagnostics { } // anonymous namespace +static bool isKnownToAlwaysThrow(const FunctionDecl *FD) { + if (!FD->hasBody()) + return false; + const Stmt *Body = FD->getBody(); + const Stmt *OnlyStmt = nullptr; + + if (const auto *Compound = dyn_cast<CompoundStmt>(Body)) { + if (Compound->size() != 1) + return false; // More than one statement, can't be known to always throw. + OnlyStmt = *Compound->body_begin(); + } else { + OnlyStmt = Body; + } + + // Unwrap ExprWithCleanups if necessary. + if (const auto *EWC = dyn_cast<ExprWithCleanups>(OnlyStmt)) { + OnlyStmt = EWC->getSubExpr(); + } + // Check if the only statement is a throw expression. + if (isa<CXXThrowExpr>(OnlyStmt)) { + return true; // Known to always throw. + } + return false; // Not known to always throw. +} + /// CheckFallThroughForBody - Check that we don't fall off the end of a /// function that should return a value. Check that we don't fall off the end /// of a noreturn function. We assume that functions and blocks not marked @@ -681,6 +706,26 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, if (CD.diag_FallThrough_HasNoReturn) S.Diag(RBrace, CD.diag_FallThrough_HasNoReturn) << CD.FunKind; } else if (!ReturnsVoid && CD.diag_FallThrough_ReturnsNonVoid) { + // If the final statement is a call to an always-throwing function, + // don't warn about the fall-through. + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + if (const auto *CS = dyn_cast<CompoundStmt>(Body)) { + if (!CS->body_empty()) { + const Stmt *LastStmt = CS->body_back(); + // Unwrap ExprWithCleanups if necessary. + if (const auto *EWC = dyn_cast<ExprWithCleanups>(LastStmt)) { + LastStmt = EWC->getSubExpr(); + } + if (const auto *CE = dyn_cast<CallExpr>(LastStmt)) { + if (const FunctionDecl *Callee = CE->getDirectCallee()) { + if (isKnownToAlwaysThrow(Callee)) { + return; // Don't warn about fall-through. + } + } + } + } + } + } bool NotInAllControlPaths = FallThroughType == MaybeFallThrough; S.Diag(RBrace, CD.diag_FallThrough_ReturnsNonVoid) << CD.FunKind << NotInAllControlPaths; diff --git a/clang/test/SemaCXX/wreturn-always-throws.cpp b/clang/test/SemaCXX/wreturn-always-throws.cpp new file mode 100644 index 0000000000000..0bbce0f5c1871 --- /dev/null +++ b/clang/test/SemaCXX/wreturn-always-throws.cpp @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -fexceptions -Wreturn-type -verify %s +// expected-no-diagnostics + +namespace std { + class string { + public: + string(const char*); // constructor for runtime_error + }; + class runtime_error { + public: + runtime_error(const string &); + }; +} + +void throwError(const std::string& msg) { + throw std::runtime_error(msg); +} + +int ensureZero(const int i) { + if (i == 0) return 0; + throwError("ERROR"); // no-warning +} >From b14827933344a1e408164c0b4eac8fa2ee5b5304 Mon Sep 17 00:00:00 2001 From: Samarth Narang <70980689+snarang...@users.noreply.github.com> Date: Sat, 21 Jun 2025 11:05:17 -0400 Subject: [PATCH 2/4] Update clang/lib/Sema/AnalysisBasedWarnings.cpp Co-authored-by: Copilot <175728472+copi...@users.noreply.github.com> --- clang/lib/Sema/AnalysisBasedWarnings.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 85a5d99c710d5..67e379fdd7d38 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -624,9 +624,7 @@ struct CheckFallThroughDiagnostics { } }; -} // anonymous namespace - -static bool isKnownToAlwaysThrow(const FunctionDecl *FD) { +bool isKnownToAlwaysThrow(const FunctionDecl *FD) { if (!FD->hasBody()) return false; const Stmt *Body = FD->getBody(); @@ -651,6 +649,7 @@ static bool isKnownToAlwaysThrow(const FunctionDecl *FD) { return false; // Not known to always throw. } +} // anonymous namespace /// CheckFallThroughForBody - Check that we don't fall off the end of a /// function that should return a value. Check that we don't fall off the end /// of a noreturn function. We assume that functions and blocks not marked >From 4340fecd20a1d45ca86d3c25e6bcf85e2af581de Mon Sep 17 00:00:00 2001 From: Samarth Narang <snar...@umass.edu> Date: Sat, 21 Jun 2025 11:29:58 -0400 Subject: [PATCH 3/4] Handle direct throws in functions. --- clang/lib/Sema/AnalysisBasedWarnings.cpp | 4 ++++ clang/test/SemaCXX/wreturn-always-throws.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 67e379fdd7d38..9502669612f74 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -722,6 +722,10 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, } } } + // Direct throw. + if (isa<CXXThrowExpr>(LastStmt)) { + return; // Don't warn about fall-through. + } } } } diff --git a/clang/test/SemaCXX/wreturn-always-throws.cpp b/clang/test/SemaCXX/wreturn-always-throws.cpp index 0bbce0f5c1871..5d2d2f0dffefe 100644 --- a/clang/test/SemaCXX/wreturn-always-throws.cpp +++ b/clang/test/SemaCXX/wreturn-always-throws.cpp @@ -20,3 +20,7 @@ int ensureZero(const int i) { if (i == 0) return 0; throwError("ERROR"); // no-warning } + +int alwaysThrows() { + throw std::runtime_error("This function always throws"); // no-warning +} >From df23c9ad2dc7cea31dbc0d93585312a1d24684db Mon Sep 17 00:00:00 2001 From: Samarth Narang <snar...@umass.edu> Date: Sat, 21 Jun 2025 12:34:41 -0400 Subject: [PATCH 4/4] Address review comments Add a note in the release notes about the fix for the no-return function warning --- clang/docs/ReleaseNotes.rst | 6 ++++ clang/lib/Sema/AnalysisBasedWarnings.cpp | 35 ++++++++++-------------- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 96477ef6ddc9a..060fcc04721e8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -642,6 +642,12 @@ Improvements to Clang's diagnostics #GH69470, #GH59391, #GH58172, #GH46215, #GH45915, #GH45891, #GH44490, #GH36703, #GH32903, #GH23312, #GH69874. +- Clang no longer warns about missing return statements (-Wreturn-type) + if the final statement of a non-void function is a `throw` expression + or a call to a function that is known to always throw. This avoids + false positives in code patterns where control flow is intentionally + terminated via exceptions. + Improvements to Clang's time-trace ---------------------------------- diff --git a/clang/lib/Sema/AnalysisBasedWarnings.cpp b/clang/lib/Sema/AnalysisBasedWarnings.cpp index 9502669612f74..ac9ba762adeb3 100644 --- a/clang/lib/Sema/AnalysisBasedWarnings.cpp +++ b/clang/lib/Sema/AnalysisBasedWarnings.cpp @@ -643,10 +643,7 @@ bool isKnownToAlwaysThrow(const FunctionDecl *FD) { OnlyStmt = EWC->getSubExpr(); } // Check if the only statement is a throw expression. - if (isa<CXXThrowExpr>(OnlyStmt)) { - return true; // Known to always throw. - } - return false; // Not known to always throw. + return isa<CXXThrowExpr>(OnlyStmt); } } // anonymous namespace @@ -708,25 +705,23 @@ static void CheckFallThroughForBody(Sema &S, const Decl *D, const Stmt *Body, // If the final statement is a call to an always-throwing function, // don't warn about the fall-through. if (const auto *FD = dyn_cast<FunctionDecl>(D)) { - if (const auto *CS = dyn_cast<CompoundStmt>(Body)) { - if (!CS->body_empty()) { - const Stmt *LastStmt = CS->body_back(); - // Unwrap ExprWithCleanups if necessary. - if (const auto *EWC = dyn_cast<ExprWithCleanups>(LastStmt)) { - LastStmt = EWC->getSubExpr(); - } - if (const auto *CE = dyn_cast<CallExpr>(LastStmt)) { - if (const FunctionDecl *Callee = CE->getDirectCallee()) { - if (isKnownToAlwaysThrow(Callee)) { - return; // Don't warn about fall-through. - } - } - } - // Direct throw. - if (isa<CXXThrowExpr>(LastStmt)) { + if (const auto *CS = dyn_cast<CompoundStmt>(Body); + CS && !CS->body_empty()) { + const Stmt *LastStmt = CS->body_back(); + // Unwrap ExprWithCleanups if necessary. + if (const auto *EWC = dyn_cast<ExprWithCleanups>(LastStmt)) { + LastStmt = EWC->getSubExpr(); + } + if (const auto *CE = dyn_cast<CallExpr>(LastStmt)) { + if (const FunctionDecl *Callee = CE->getDirectCallee(); + Callee && isKnownToAlwaysThrow(Callee)) { return; // Don't warn about fall-through. } } + // Direct throw. + if (isa<CXXThrowExpr>(LastStmt)) { + return; // Don't warn about fall-through. + } } } bool NotInAllControlPaths = FallThroughType == MaybeFallThrough; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits