[clang] [Clang][Sema] Add -Wredundant-defer to diagnose redundant `_Defer`s (PR #225748)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 25 06:53:05 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 01/13] [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 02/13] 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
>From 995108a364b039bef658384a02cfe380e0bdf4a2 Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 14:52:28 +0000
Subject: [PATCH 03/13] Remove DefaultIgnore on -Wredundant-defer
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
clang/test/Sema/defer-ts-seh.c | 2 +-
clang/test/Sema/defer-ts-sjlj.c | 2 +-
clang/test/Sema/defer-ts.c | 2 +-
4 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 17a15406d003aa..ee93b6c4c33c17 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11344,7 +11344,7 @@ def note_empty_body_on_separate_line : Note<
def warn_redundant_defer: Warning<
"redundant use of defer; the deferred statement is executed immediately">,
- InGroup<RedundantDefer>, DefaultIgnore;
+ InGroup<RedundantDefer>;
def err_va_start_captured_stmt : Error<
"'va_start' cannot be used in a captured statement">;
diff --git a/clang/test/Sema/defer-ts-seh.c b/clang/test/Sema/defer-ts-seh.c
index 4b773ed3f09a0c..de515f38aae876 100644
--- a/clang/test/Sema/defer-ts-seh.c
+++ b/clang/test/Sema/defer-ts-seh.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c23 -fdefer-ts -fms-compatibility -triple x86_64-windows-msvc -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -Wno-redundant-defer -fms-compatibility -triple x86_64-windows-msvc -fsyntax-only -verify %s
void f() {
__try {
diff --git a/clang/test/Sema/defer-ts-sjlj.c b/clang/test/Sema/defer-ts-sjlj.c
index 49230fa721e0f7..dc23f1f6969d41 100644
--- a/clang/test/Sema/defer-ts-sjlj.c
+++ b/clang/test/Sema/defer-ts-sjlj.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=gnu23 -fdefer-ts -fsyntax-only -fblocks -verify %s
+// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=gnu23 -fdefer-ts -Wno-redundant-defer -fsyntax-only -fblocks -verify %s
typedef void** jmp_buf;
typedef void** sigjmp_buf;
diff --git a/clang/test/Sema/defer-ts.c b/clang/test/Sema/defer-ts.c
index 95c68fa213eaae..d1fba4128b088c 100644
--- a/clang/test/Sema/defer-ts.c
+++ b/clang/test/Sema/defer-ts.c
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -Wno-redundant-defer -fsyntax-only -verify %s
#define defer _Defer
>From b8bd97ebc20fe865d3a91bc12185843964b56640 Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 15:01:29 +0000
Subject: [PATCH 04/13] Simplify check for _Defer before break/continue
statements
---
clang/lib/Sema/SemaStmt.cpp | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 8454f4f2bd8421..00eccea0f5a0e2 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -471,14 +471,10 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
}
// 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)
+ for (unsigned i = 0; i != NumElts - 1; ++i) {
+ if (isa<DeferStmt>(Elts[i]) && isa<BreakStmt, ContinueStmt>(Elts[i+1]))
+ Diag(Elts[i]->getBeginLoc(), 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.
>From bca3b93e62a75a5969b576ce8c7b8fa5bd2c8dff Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 15:14:50 +0000
Subject: [PATCH 05/13] Add redundant _Defer check for void return statements
---
clang/lib/Sema/SemaStmt.cpp | 18 +++++++++++++-----
1 file changed, 13 insertions(+), 5 deletions(-)
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 00eccea0f5a0e2..204d8a3270b58a 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -470,11 +470,19 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]);
}
- // Find defer statements that immediately precede a break/continue statement.
- for (unsigned i = 0; i != NumElts - 1; ++i) {
- if (isa<DeferStmt>(Elts[i]) && isa<BreakStmt, ContinueStmt>(Elts[i+1]))
- Diag(Elts[i]->getBeginLoc(), diag::warn_redundant_defer)
- << Elts[i]->getSourceRange();
+ // Find defer statements that immediately precede a `break`/`continue`
+ // or a plain `return` statement.
+ if (NumElts > 1) {
+ for (unsigned i = 0; i != NumElts - 1; ++i) {
+ if (!isa<DeferStmt>(Elts[i]))
+ continue;
+
+ if (isa<BreakStmt, ContinueStmt>(Elts[i + 1]) ||
+ (isa<ReturnStmt>(Elts[i + 1]) &&
+ !cast<ReturnStmt>(Elts[i + 1])->getRetValue()))
+ Diag(Elts[i]->getBeginLoc(), diag::warn_redundant_defer)
+ << Elts[i]->getSourceRange();
+ }
}
// Check for defer as last statement.
>From 2058a8ed833e5dfc61a34dbe231b5d9dd12c16dd Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 15:44:58 +0000
Subject: [PATCH 06/13] Add -Wredundant-defer tests for break/continue with
labels/attributes
---
clang/test/Sema/warn-redundant-defer.c | 64 ++++++++++++++++++++++++++
1 file changed, 64 insertions(+)
diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c
index 2d9b92bbe9b63b..6e158d2c425667 100644
--- a/clang/test/Sema/warn-redundant-defer.c
+++ b/clang/test/Sema/warn-redundant-defer.c
@@ -71,6 +71,38 @@ void f2() {
break;
}
+ for (;;) {
+ defer {} // OK
+ f1();
+ l1: break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ l2: break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ l3: l4: break;
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ [[clang::likely]] break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ [[clang::likely]] break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ [[clang::likely]] [[clang::suppress]] break;
+ }
+
for (;;) {
defer {} // OK
f1();
@@ -82,6 +114,38 @@ void f2() {
continue;
}
+ for (;;) {
+ defer {} // OK
+ f1();
+ l5: continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ l6: continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ l7: l8: continue;
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ [[clang::likely]] continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ [[clang::likely]] continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ [[clang::likely]] [[clang::suppress]] continue;
+ }
+
while (true) {
defer {} // OK
f1();
>From 17819b914de3d1a8ffa7a923d5ef27989e305ada Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 15:48:49 +0000
Subject: [PATCH 07/13] Add -Wredundant-defer tests for _Defer before return
---
clang/test/Sema/warn-redundant-defer.c | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c
index 6e158d2c425667..61a5a75f2ee5bc 100644
--- a/clang/test/Sema/warn-redundant-defer.c
+++ b/clang/test/Sema/warn-redundant-defer.c
@@ -213,3 +213,19 @@ void f2() {
defer {} // expected-warning {{redundant use of defer}}
}
+
+int f3() {
+ defer {} // OK
+ return 0;
+}
+
+void f4() {
+ defer {} // OK
+ f1();
+ return;
+}
+
+void f5() {
+ defer {} // expected-warning {{redundant use of defer}}
+ return;
+}
>From 699f9d5af02a4db715bb5f2316f4f0497bf6004d Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 16:18:29 +0000
Subject: [PATCH 08/13] Inline the DiagGroup of warn_redundant_defer
---
clang/include/clang/Basic/DiagnosticGroups.td | 2 --
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
2 files changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index 4b35e4247e2c4c..1da7698944b24b 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -345,8 +345,6 @@ 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 ee93b6c4c33c17..72197af301c54a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11344,7 +11344,7 @@ def note_empty_body_on_separate_line : Note<
def warn_redundant_defer: Warning<
"redundant use of defer; the deferred statement is executed immediately">,
- InGroup<RedundantDefer>;
+ InGroup<DiagGroup<"redundant-defer">>;
def err_va_start_captured_stmt : Error<
"'va_start' cannot be used in a captured statement">;
>From 98013854a670123adaf4bb85ffa80c10f584bd0c Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 16:27:53 +0000
Subject: [PATCH 09/13] Update ReleaseNotes.md entry on -Wredundant-defer
features
Co-authored-by: Ambrose Leeb <aeternalmail at gmail.com>
---
clang/docs/ReleaseNotes.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index a0126b0785d59d..3e9a67201c60ef 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -521,7 +521,7 @@ features cannot lower the translation-unit ABI level;
- 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.
+ a `break`/`continue` statement or a `return` with no argument.
### Improvements to Clang's time-trace
>From 9f5ff658fca2f0d4c7795de9a2f99acdb92a1116 Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Wed, 23 Sep 2026 16:33:35 +0000
Subject: [PATCH 10/13] Change wording in -Wredundant-defer from "defer" to
"_Defer"
Co-authored-by: Ambrose Leeb <aeternalmail at gmail.com>
---
clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 72197af301c54a..cbf7d95c1cc7ff 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11343,7 +11343,7 @@ 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">,
+ "redundant use of '_Defer'; the deferred statement is executed immediately">,
InGroup<DiagGroup<"redundant-defer">>;
def err_va_start_captured_stmt : Error<
>From 7e12e83b3a3a97978c14f53696944e930bce66d4 Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Fri, 25 Sep 2026 13:23:55 +0000
Subject: [PATCH 11/13] Ignore unexpected -Wredundant-defer warnings in
defer-ts Parser test
---
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 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
>From 325c0193b6df1a72805c681662065c8e8e8bed11 Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Fri, 25 Sep 2026 13:45:41 +0000
Subject: [PATCH 12/13] Add stronger testcases with labels/attributes for
redundant-defer
---
clang/test/Sema/warn-redundant-defer.c | 80 ++++++++++++++++++++++++--
1 file changed, 74 insertions(+), 6 deletions(-)
diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c
index 61a5a75f2ee5bc..f2b9f4272fd48f 100644
--- a/clang/test/Sema/warn-redundant-defer.c
+++ b/clang/test/Sema/warn-redundant-defer.c
@@ -13,6 +13,32 @@ void f2() {
defer defer defer {} // expected-warning 2 {{redundant use of defer}}
+ l1: defer defer {} // expected-warning {{redundant use of defer}}
+
+ l2: defer defer defer {} // expected-warning 2 {{redundant use of defer}}
+
+ defer [[clang::likely]] defer {} // expected-warning {{redundant use of defer}}
+
+ defer
+ [[clang::likely]] [[clang::suppress]]
+ defer {}; // expected-warning {{redundant use of defer}}
+
+ [[clang::likely]] defer defer {} // expected-warning {{redundant use of defer}}
+
+ l3:
+ [[clang::likely]] defer
+ defer {} // expected-warning {{redundant use of defer}}
+
+ defer __attribute__((suppress))
+ defer {} // expected-warning {{redundant use of defer}}
+
+ __attribute__((suppress)) defer
+ defer {} // expected-warning {{redundant use of defer}}
+
+ l4:
+ __attribute__((unknown)) defer // expected-warning {{unknown attribute}}
+ defer {} // expected-warning {{redundant use of defer}}
+
{
defer {} // OK
f1();
@@ -36,6 +62,13 @@ void f2() {
}
}
+ {
+ [[clang::likely]] defer { // expected-warning {{redundant use of defer}}
+ [[clang::likely]]
+ defer defer {} // expected-warning 2 {{redundant use of defer}}
+ }
+ }
+
if (true) {
defer {} // OK
f1();
@@ -74,17 +107,17 @@ void f2() {
for (;;) {
defer {} // OK
f1();
- l1: break;
+ l5: break;
}
for (;;) {
defer {} // expected-warning {{redundant use of defer}}
- l2: break;
+ l6: break;
}
for (;;) {
defer {} // expected-warning {{redundant use of defer}}
- l3: l4: break;
+ l7: l8: break;
}
for (;;) {
@@ -103,6 +136,16 @@ void f2() {
[[clang::likely]] [[clang::suppress]] break;
}
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ __attribute__((suppress)) continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ l9: [[clang::likely]] break;
+ }
+
for (;;) {
defer {} // OK
f1();
@@ -117,17 +160,17 @@ void f2() {
for (;;) {
defer {} // OK
f1();
- l5: continue;
+ l10: continue;
}
for (;;) {
defer {} // expected-warning {{redundant use of defer}}
- l6: continue;
+ l11: continue;
}
for (;;) {
defer {} // expected-warning {{redundant use of defer}}
- l7: l8: continue;
+ l12: l13: continue;
}
for (;;) {
@@ -146,6 +189,16 @@ void f2() {
[[clang::likely]] [[clang::suppress]] continue;
}
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ __attribute__((suppress)) continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of defer}}
+ l14: [[clang::likely]] continue;
+ }
+
while (true) {
defer {} // OK
f1();
@@ -229,3 +282,18 @@ void f5() {
defer {} // expected-warning {{redundant use of defer}}
return;
}
+
+void f6() {
+ defer {} // expected-warning {{redundant use of defer}}
+ l15: return;
+}
+
+void f7() {
+ defer {} // expected-warning {{redundant use of defer}}
+ [[clang::likely]] return;
+}
+
+void f8() {
+ defer {} // expected-warning {{redundant use of defer}}
+ l16: l17: [[clang::likely]] [[clang::suppress]] return;
+}
>From d0a7f75e71afd8462ef7a25535650a469ade53cf Mon Sep 17 00:00:00 2001
From: Expertcoderz <expertcoderzx at gmail.com>
Date: Fri, 25 Sep 2026 13:47:16 +0000
Subject: [PATCH 13/13] Handle AttributedStmt and LabelStmt for redundant-defer
checks
---
clang/lib/Sema/SemaStmt.cpp | 63 ++++++++++++++++++++-----------------
1 file changed, 35 insertions(+), 28 deletions(-)
diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 204d8a3270b58a..08bdb2d3908f41 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -430,6 +430,27 @@ void Sema::ActOnFinishOfCompoundStmt() {
PopCompoundScope();
}
+// Returns the given statement as if its labels and attributes were
+// stripped, if any.
+static Stmt *GetInnermostStatement(Stmt *Outer) {
+ if (isa<LabelStmt>(Outer))
+ Outer = cast<LabelStmt>(Outer)->getInnermostLabeledStmt();
+
+ if (isa<AttributedStmt>(Outer))
+ Outer = cast<AttributedStmt>(Outer)->getSubStmt();
+
+ return Outer;
+}
+
+// Diagnose if the given statement is a redundant _Defer statement.
+static void CheckRedundantDeferStmt(Sema &S, Stmt *Body) {
+ Stmt *Inner = GetInnermostStatement(Body);
+
+ if (isa<DeferStmt>(Inner))
+ S.Diag(Inner->getBeginLoc(), diag::warn_redundant_defer)
+ << Inner->getSourceRange();
+}
+
sema::CompoundScopeInfo &Sema::getCurCompoundScope() const {
return getCurFunction()->CompoundScopes.back();
}
@@ -474,22 +495,16 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
// or a plain `return` statement.
if (NumElts > 1) {
for (unsigned i = 0; i != NumElts - 1; ++i) {
- if (!isa<DeferStmt>(Elts[i]))
- continue;
-
- if (isa<BreakStmt, ContinueStmt>(Elts[i + 1]) ||
- (isa<ReturnStmt>(Elts[i + 1]) &&
- !cast<ReturnStmt>(Elts[i + 1])->getRetValue()))
- Diag(Elts[i]->getBeginLoc(), diag::warn_redundant_defer)
- << Elts[i]->getSourceRange();
+ Stmt *Inner = GetInnermostStatement(Elts[i + 1]);
+ if (isa<BreakStmt, ContinueStmt>(Inner) ||
+ (isa<ReturnStmt>(Inner) && !cast<ReturnStmt>(Inner)->getRetValue()))
+ CheckRedundantDeferStmt(*this, Elts[i]);
}
}
// 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();
+ if (NumElts > 0)
+ CheckRedundantDeferStmt(*this, Elts[NumElts - 1]);
// Calculate difference between FP options in this compound statement and in
// the enclosing one. If this is a function body, take the difference against
@@ -1018,13 +1033,9 @@ 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();
+ CheckRedundantDeferStmt(*this, thenStmt);
+ if (elseStmt)
+ CheckRedundantDeferStmt(*this, elseStmt);
if (ConstevalOrNegatedConsteval ||
StatementKind == IfStatementKind::Constexpr) {
@@ -1866,9 +1877,8 @@ 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();
+ else
+ CheckRedundantDeferStmt(*this, Body);
return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body,
WhileLoc, LParenLoc, RParenLoc);
@@ -2360,9 +2370,8 @@ 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();
+ else
+ CheckRedundantDeferStmt(*this, Body);
return new (Context)
ForStmt(Context, First, Second.get().second, Second.get().first, Third,
@@ -4075,9 +4084,7 @@ StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body, Scope *CurScope) {
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();
+ CheckRedundantDeferStmt(*this, Body);
setFunctionHasBranchProtectedScope();
return DeferStmt::Create(Context, DeferLoc, Body);
More information about the cfe-commits
mailing list