https://github.com/snarang181 updated https://github.com/llvm/llvm-project/pull/145166
>From 004e79ee8cccc56a965459ce0372de97f4a89d0c Mon Sep 17 00:00:00 2001 From: Samarth Narang <snar...@umass.edu> Date: Sat, 21 Jun 2025 08:42:00 -0400 Subject: [PATCH] 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 | 19 +++++++++ 2 files changed, 64 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..73f2f1085605a --- /dev/null +++ b/clang/test/SemaCXX/wreturn-always-throws.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -Wreturn-type -verify %s +// expected-no-diagnostics + +namespace std { + class runtime_error { + public: + runtime_error(const char *); + }; + class string {}; +} + +void throwError(const string& msg) { + throw std::runtime_error(msg); +} + +int ensureZero(const int i) { + if (i == 0) return 0; + throwError("ERROR"); // no-warning +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits