-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Suppress noreturn warning if last statement in a function is a throw #145166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2c2324a
to
359dfb1
Compare
@llvm/pr-subscribers-clang Author: Samarth Narang (snarang181) ChangesFull diff: https://github.com/llvm/llvm-project/pull/145166.diff 2 Files Affected:
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
+}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR adds support to suppress “fall-through” return-type warnings when a function’s last statement always throws, along with a verifying test.
- Introduce
isKnownToAlwaysThrow
helper to detect single-statement throw functions - Update
CheckFallThroughForBody
to skip warnings for calls to those functions - Add a new test (
wreturn-always-throws.cpp
) to verify suppression
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
File | Description |
---|---|
clang/test/SemaCXX/wreturn-always-throws.cpp | New test covering a non-void function that ends in a throw |
clang/lib/Sema/AnalysisBasedWarnings.cpp | Helper and logic to suppress return-type warnings on throwers |
if (const auto *EWC = dyn_cast<ExprWithCleanups>(LastStmt)) { | ||
LastStmt = EWC->getSubExpr(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The unwrap of ExprWithCleanups
is duplicated in two places. Consider extracting a helper function to reduce duplication and centralize cleanup unwrapping logic.
if (const auto *EWC = dyn_cast<ExprWithCleanups>(LastStmt)) { | |
LastStmt = EWC->getSubExpr(); | |
} | |
LastStmt = unwrapExprWithCleanups(LastStmt); |
Copilot uses AI. Check for mistakes.
✅ With the latest revision this PR passed the C/C++ code formatter. |
1009d62
to
b148279
Compare
5e2fc5f
to
4340fec
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for the patch! Please add a release note in clang/docs/ReleaseNotes.rst
.
I'm not sure if this case should be handled:
if (condition) {
throw 1;
} else {
throw 2;
}
Maybe we need to open an issue for it.
@zwuis, added a description of the patch in the release notes and addressed your review comments. |
I feel like this is a solution that can grow out of hand very easily. IMO the better solution would be to have |
I agree, in principle I would expect Clang to be able to prove (in simple cases) that a function cannot return, and then treat it as-if they were marked as |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems reasonable to me, though I'd like to give other reviewers a chance. I think we need to be less optimistic in the release notes (clarifying that we are doing a very mild pattern match), else I think this is sensible.
df23c9a
to
cdb0568
Compare
3fd24e6
to
8cca1b0
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Want 1 more test and a preference on interface, else I'm pretty good here.
Add templated test case
Something went wrong here, since valid code started triggering the
Please fix or revert soon! |
A general comment about #144952 and this PR: any cross-function analysis like this one will only work on a translation unit level, which has obvious shortcomings (placing the implicitly noreturn function and its caller in different translation units will return the false positive diagnostic). I think, it may be better to explicitly mark functions |
@snarang181 friendly ping! |
Hi! I haven't been well and afk, I'll try to get to this as soon as possible. Thank you all for your comments. |
Just out of curiosity, could implementing this warning to be LTO only solve
this? Anything requiring whole-program analysis sounds like a task to be
done during LTO. IIRC GCC has some warnings that are only made possible by
LTO.
…On Tue, Jul 15, 2025 at 12:40 PM Alexander Kornienko < ***@***.***> wrote:
*alexfh* left a comment (llvm/llvm-project#145166)
<#145166 (comment)>
A general comment about #144952
<#144952> and this PR: any
cross-function analysis like this one will only work on a translation unit
level, which has obvious shortcomings (placing the implicitly noreturn
function and its caller in different translation units will return the
false positive diagnostic). I think, it may be better to explicitly mark
functions [[noreturn]] in the code rather than relying on the compiler to
derive this property.
—
Reply to this email directly, view it on GitHub
<#145166 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AHD2KHQTI6E6DZDNDTW2K433ITLDJAVCNFSM6AAAAAB72CXXLCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZTANZTGEYDGNRWGI>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
|
I don't think this would be the right direction. First of all, LTO is inherently a more expensive build mode, which (in my surroundings at least) is mostly used for releases, but not in the normal development cycle. Making this diagnostic LTO-only will shift the moment when the corresponding issues are found from development to release build, breaking the latter without showing up earlier. If the release builds are performed automatically on a certain schedule, this will be a very unfortunate moment for the first detection of these problems. I personally think that relying on explicit |
Oh, sad to hear that. Get well soon! |
@snarang181 how soon will you be able to address this? This diagnostic keeps being broken at HEAD, and we need to decide whether this change should be reverted until you have time to get back to this. I'll prepare a revert for now and will proceed with it, if I don't get a reply soon. |
@alexfh, I can start looking at this now. Is the proposed plan of action marking the functions |
There are two problems here:
I'd like @erichkeane and @AaronBallman to chime in on the second part - maybe this should be reconsidered. IMO, the motivating issue for this PR should be solved in the user code rather than in the compiler (#144952 (comment)). Now, getting back to the issue # 1: if you have a trivial fix for this, please go ahead and implement it (assuming the decision on # 2 is to leave this feature in Clang). If the fix is not obvious, I'd suggest to revert the commit and re-send it for review together with the fix. |
@alexfh, I have #150003 up as a patch for the problem you are seeing. |
… template specializations (#150003) This patch fixes incorrect behavior in Clang where [[noreturn]] (either spelled or inferred) was being inherited by explicit specializations of function templates or member function templates, even when those specializations returned normally. Follow up on llvm/llvm-project#145166
…pecializations (#150003) This patch fixes incorrect behavior in Clang where [[noreturn]] (either spelled or inferred) was being inherited by explicit specializations of function templates or member function templates, even when those specializations returned normally. Follow up on llvm/llvm-project#145166
…pecializations (llvm#150003) This patch fixes incorrect behavior in Clang where [[noreturn]] (either spelled or inferred) was being inherited by explicit specializations of function templates or member function templates, even when those specializations returned normally. Follow up on llvm#145166
…pecializations (llvm#150003) This patch fixes incorrect behavior in Clang where [[noreturn]] (either spelled or inferred) was being inherited by explicit specializations of function templates or member function templates, even when those specializations returned normally. Follow up on llvm#145166 (cherry picked from commit 22fef00)
… template specializations (#150003) This patch fixes incorrect behavior in Clang where [[noreturn]] (either spelled or inferred) was being inherited by explicit specializations of function templates or member function templates, even when those specializations returned normally. Follow up on llvm/llvm-project#145166 (cherry picked from commit 22fef00)
Fixes #144952