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

Reply via email to