[clang] [Clang][Sema] Add -Wredundant-defer to diagnose redundant `_Defer`s (PR #225748)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 23 06:12:08 PDT 2026
https://github.com/Expertcoderz updated https://github.com/llvm/llvm-project/pull/225748
>From 730360dc5a0f855d996f12b7f8ab2f8bdf5ebff1 Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 08:22:44 +0000
Subject: [PATCH 1/2] [Clang][Sema] Add -Wredundant-defer to diagnose redundant
`_Defer`s
This PR adds a new `-Wredundant-defer` diagnostic to have Clang
emit warnings for superfluous uses of `_Defer`, where the
substatement is always executed at the point of defer.
With `-Wredundant-defer`, `_Defer` statements that occur in these
positions will trigger the warning:
- as the last statement of a compound statement or function
- as the body of an `if`/`else` statement
- as the body of a `for`/`while` loop
- immediately preceding a `break` or `continue` statement
- as the substatement of another `_Defer`
This helps to detect mistakes and some potential misuses of `_Defer`.
---
clang/docs/ReleaseNotes.md | 5 +
clang/include/clang/Basic/DiagnosticGroups.td | 2 +
.../clang/Basic/DiagnosticSemaKinds.td | 4 +
clang/lib/Sema/SemaStmt.cpp | 41 ++++-
clang/test/Parser/defer-ts.c | 4 +-
clang/test/Sema/warn-redundant-defer.c | 151 ++++++++++++++++++
6 files changed, 203 insertions(+), 4 deletions(-)
create mode 100644 clang/test/Sema/warn-redundant-defer.c
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index e5da258b9950a3..a0126b0785d59d 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -518,6 +518,11 @@ features cannot lower the translation-unit ABI level;
- Improve Clang diagnoses when unary `__imag` operator with non-complex type operand is used as lvalue. (GH222383)
+- Added `-Wredundant-defer` to diagnose redundant uses of the `_Defer`
+ keyword, such as when deferring the last statement of a block; when
+ used as the body of a conditional; or when it immediately precedes
+ a `break`/`continue` statement.
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 1da7698944b24b..4b35e4247e2c4c 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -345,6 +345,8 @@ def EmptyBody : DiagGroup<"empty-body">;
def Exceptions : DiagGroup<"exceptions">;
def DeclarationAfterStatement : DiagGroup<"declaration-after-statement">;
+def RedundantDefer : DiagGroup<"redundant-defer">;
+
def GNUEmptyStruct : DiagGroup<"gnu-empty-struct">;
def ExtraTokens : DiagGroup<"extra-tokens">;
def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">;
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 36a18473f4d4cc..17a15406d003aa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11342,6 +11342,10 @@ def warn_empty_defer_body : Warning<
def note_empty_body_on_separate_line : Note<
"put the semicolon on a separate line to silence this warning">;
+def warn_redundant_defer: Warning<
+ "redundant use of defer; the deferred statement is executed immediately">,
+ InGroup<RedundantDefer>, DefaultIgnore;
+
def err_va_start_captured_stmt : Error<
"'va_start' cannot be used in a captured statement">;
def err_va_start_outside_function : Error<
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 74fe253efa1374..8454f4f2bd8421 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -470,6 +470,23 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]);
}
+ // Find defer statements that immediately precede a break/continue statement.
+ std::optional<SourceLocation> DeferLoc = std::nullopt;
+ for (unsigned i = 0; i != NumElts; ++i) {
+ if (DeferLoc && isa<BreakStmt, ContinueStmt>(Elts[i]))
+ Diag(DeferLoc.value(), diag::warn_redundant_defer)
+ << Elts[i]->getSourceRange();
+ DeferLoc = isa<DeferStmt>(Elts[i])
+ ? std::optional<SourceLocation>(Elts[i]->getBeginLoc())
+ : std::nullopt;
+ }
+
+ // Check for defer as last statement.
+ Stmt *Back = NumElts > 0 ? Elts[NumElts - 1] : nullptr;
+ if (Back && isa<DeferStmt>(Back))
+ Diag(Back->getBeginLoc(), diag::warn_redundant_defer)
+ << Back->getSourceRange();
+
// Calculate difference between FP options in this compound statement and in
// the enclosing one. If this is a function body, take the difference against
// default options. In this case the difference will indicate options that are
@@ -997,6 +1014,14 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
if (!ConstevalOrNegatedConsteval && !elseStmt)
DiagnoseEmptyStmtBody(RParenLoc, thenStmt, diag::warn_empty_if_body);
+ if (isa<DeferStmt>(thenStmt))
+ Diag(thenStmt->getBeginLoc(), diag::warn_redundant_defer)
+ << thenStmt->getSourceRange();
+
+ if (elseStmt && isa<DeferStmt>(elseStmt))
+ Diag(elseStmt->getBeginLoc(), diag::warn_redundant_defer)
+ << elseStmt->getSourceRange();
+
if (ConstevalOrNegatedConsteval ||
StatementKind == IfStatementKind::Constexpr) {
auto DiagnoseLikelihood = [&](const Stmt *S) {
@@ -1837,6 +1862,9 @@ StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc,
if (isa<NullStmt>(Body))
getCurCompoundScope().setHasEmptyLoopBodies();
+ else if (isa<DeferStmt>(Body))
+ Diag(Body->getBeginLoc(), diag::warn_redundant_defer)
+ << Body->getSourceRange();
return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body,
WhileLoc, LParenLoc, RParenLoc);
@@ -2328,6 +2356,9 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
Expr *Third = third.release().getAs<Expr>();
if (isa<NullStmt>(Body))
getCurCompoundScope().setHasEmptyLoopBodies();
+ else if (isa<DeferStmt>(Body))
+ Diag(Body->getBeginLoc(), diag::warn_redundant_defer)
+ << Body->getSourceRange();
return new (Context)
ForStmt(Context, First, Second.get().second, Second.get().first, Third,
@@ -4033,11 +4064,17 @@ void Sema::ActOnDeferStmtError([[maybe_unused]] Scope *CurScope) {
CurrentDefer.pop_back();
}
-StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body,
- [[maybe_unused]] Scope *CurScope) {
+StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body, Scope *CurScope) {
assert(!CurrentDefer.empty() && CurrentDefer.back().first == CurScope);
+
SourceLocation DeferLoc = CurrentDefer.pop_back_val().second;
DiagnoseEmptyStmtBody(DeferLoc, Body, diag::warn_empty_defer_body);
+
+ // Check for superfluous nested defer.
+ if (isa<DeferStmt>(Body))
+ Diag(Body->getBeginLoc(), diag::warn_redundant_defer)
+ << Body->getSourceRange();
+
setFunctionHasBranchProtectedScope();
return DeferStmt::Create(Context, DeferLoc, Body);
}
diff --git a/clang/test/Parser/defer-ts.c b/clang/test/Parser/defer-ts.c
index 118fe9ee3cc8f5..1fe7eb2ec57163 100644
--- a/clang/test/Parser/defer-ts.c
+++ b/clang/test/Parser/defer-ts.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -verify %s
-// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -verify %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s
#define defer _Defer
diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c
new file mode 100644
index 00000000000000..2d9b92bbe9b63b
--- /dev/null
+++ b/clang/test/Sema/warn-redundant-defer.c
@@ -0,0 +1,151 @@
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify %s -Wredundant-defer
+
+#define defer _Defer
+
+void f1() {
+ defer {} // expected-warning {{redundant use of defer}}
+}
+
+void f2() {
+ defer {} // OK
+
+ defer defer {} // expected-warning {{redundant use of defer}}
+
+ defer defer defer {} // expected-warning 2 {{redundant use of defer}}
+
+ {
+ defer {} // OK
+ f1();
+ }
+
+ { defer {} } // expected-warning {{redundant use of defer}}
+
+ { defer defer {} } // expected-warning 2 {{redundant use of defer}}
+
+ {
+ defer { // OK
+ defer {} // OK
+ f1();
+ }
+ f1();
+ }
+
+ {
+ defer { // expected-warning {{redundant use of defer}}
+ defer defer {} // expected-warning 2 {{redundant use of defer}}
+ }
+ }
+
+ if (true) {
+ defer {} // OK
+ f1();
+ }
+
+ if (true)
+ defer {} // expected-warning {{redundant use of defer}}
+
+ if (true) {
+ defer {} // expected-warning {{redundant use of defer}}
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ }
+
+ for (;;)
+ defer {} // expected-warning {{redundant use of defer}}
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ break;
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ continue;
+ }
+
+ while (true) {
+ defer {} // OK
+ f1();
+ }
+
+ while (true)
+ defer {} // expected-warning {{redundant use of defer}}
+
+ while (true) {
+ defer {} // expected-warning {{redundant use of defer}}
+ }
+
+ while (true) {
+ defer {} // OK
+ f1();
+ break;
+ }
+
+ while (true) {
+ defer {} // expected-warning {{redundant use of defer}}
+ break;
+ }
+
+ while (true) {
+ defer {} // OK
+ f1();
+ continue;
+ }
+
+ while (true) {
+ defer {} // expected-warning {{redundant use of defer}}
+ continue;
+ }
+
+ do {
+ defer {} // OK
+ f1();
+ } while (true);
+
+ do {
+ defer {} // expected-warning {{redundant use of defer}}
+ } while (true);
+
+ do {
+ defer {} // OK
+ f1();
+ break;
+ } while (true);
+
+ do {
+ defer {} // expected-warning {{redundant use of defer}}
+ break;
+ } while (true);
+
+ do {
+ defer {} // OK
+ f1();
+ continue;
+ } while (true);
+
+ do {
+ defer {} // expected-warning {{redundant use of defer}}
+ continue;
+ } while (true);
+
+ defer {} // expected-warning {{redundant use of defer}}
+}
>From 43552bba54d55954537750ee28d0c88aaf7c5d8a Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 12:49:22 +0000
Subject: [PATCH 2/2] Remove accidental change to clang/test/Parser/defer-ts.c
---
clang/test/Parser/defer-ts.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/test/Parser/defer-ts.c b/clang/test/Parser/defer-ts.c
index 1fe7eb2ec57163..118fe9ee3cc8f5 100644
--- a/clang/test/Parser/defer-ts.c
+++ b/clang/test/Parser/defer-ts.c
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s
-// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s
+// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -verify %s
+// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -verify %s
#define defer _Defer
More information about the cfe-commits
mailing list