[clang] 5163510 - [Clang][Sema] Add -Wredundant-defer to diagnose redundant `_Defer`s (#225748)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 7 08:56:41 PDT 2026
Author: Expertcoderz
Date: 2026-10-07T17:56:34+02:00
New Revision: 51635105dc9c95fb14224a26640c8cf91fb6daa7
URL: https://github.com/llvm/llvm-project/commit/51635105dc9c95fb14224a26640c8cf91fb6daa7
DIFF: https://github.com/llvm/llvm-project/commit/51635105dc9c95fb14224a26640c8cf91fb6daa7.diff
LOG: [Clang][Sema] Add -Wredundant-defer to diagnose redundant `_Defer`s (#225748)
(Support for the C2y
[`_Defer`/`defer`](https://thephd.dev/_vendor/future_cxx/technical%20specification/C%20-%20defer/C%20-%20defer%20Technical%20Specification.pdf)
statement was introduced in #162848.)
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 in a loop
- as the substatement of another `_Defer`
This helps to detect mistakes and some potential misuses of `_Defer`:
```c
#include <stddefer.h>
struct Resource *resource;
bool needs_cleanup(struct Resource *);
void do_cleanup(struct Resource *);
void do_work(struct Resource *);
void f(void) {
if (needs_cleanup(resource))
defer do_cleanup(resource); // Warning: redundant use of defer
// cleanup actually runs now.
do_work(resource);
// resource will *not* be cleaned up.
}
```
The defer-ts tests have been updated to account for this new diagnostic.
Added:
clang/test/Sema/warn-redundant-defer.c
Modified:
clang/docs/ReleaseNotes.md
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaStmt.cpp
clang/test/Parser/defer-ts.c
clang/test/Sema/defer-ts-seh.c
clang/test/Sema/defer-ts-sjlj.c
clang/test/Sema/defer-ts.c
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index efbcc198a28e3..85561125a9530 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -578,6 +578,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 or a `return` with no argument.
+
### Improvements to Clang's time-trace
### Improvements to Coverage Mapping
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9208aba1445d7..66155d9780e64 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -11366,6 +11366,11 @@ 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 '%0'; the deferred statement is executed immediately">,
+ InGroup<DiagGroup<"redundant-defer">>;
+def note_redundant_defer_if : Note<"did you mean to use '%0 if'?">;
+
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 0106e63806e37..a3e3e403188c7 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -430,6 +430,26 @@ void Sema::ActOnFinishOfCompoundStmt() {
PopCompoundScope();
}
+static StringRef GetDeferKeywordSpelling(Sema &S, SourceLocation DeferLoc) {
+ StringRef DeferSpelling =
+ S.PP.getLastMacroWithSpelling(DeferLoc, {tok::kw__Defer});
+ if (DeferSpelling.empty())
+ DeferSpelling = "_Defer";
+ return DeferSpelling;
+}
+
+// Diagnose if the given statement is a redundant _Defer statement.
+static bool CheckRedundantDeferStmt(Sema &S, Stmt *Body) {
+ Stmt *Inner = Body->stripLabelLikeStatements();
+ if (isa<DeferStmt>(Inner)) {
+ SourceLocation DeferLoc = Inner->getBeginLoc();
+ S.Diag(DeferLoc, diag::warn_redundant_defer)
+ << Inner->getSourceRange() << GetDeferKeywordSpelling(S, DeferLoc);
+ return true;
+ }
+ return false;
+}
+
sema::CompoundScopeInfo &Sema::getCurCompoundScope() const {
return getCurFunction()->CompoundScopes.back();
}
@@ -475,6 +495,21 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R,
DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]);
}
+ // 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) {
+ Stmt *Inner = Elts[i + 1]->stripLabelLikeStatements();
+ if (isa<BreakStmt, ContinueStmt>(Inner) ||
+ (isa<ReturnStmt>(Inner) && !cast<ReturnStmt>(Inner)->getRetValue()))
+ CheckRedundantDeferStmt(*this, Elts[i]);
+ }
+ }
+
+ // Check for defer as last statement.
+ if (NumElts > 0)
+ CheckRedundantDeferStmt(*this, Elts[NumElts - 1]);
+
// Calculate
diff erence between FP options in this compound statement and in
// the enclosing one. If this is a function body, take the
diff erence against
// default options. In this case the
diff erence will indicate options that are
@@ -1002,6 +1037,12 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
if (!ConstevalOrNegatedConsteval && !elseStmt)
DiagnoseEmptyStmtBody(RParenLoc, thenStmt, diag::warn_empty_if_body);
+ if (CheckRedundantDeferStmt(*this, thenStmt))
+ Diag(thenStmt->getBeginLoc(), diag::note_redundant_defer_if)
+ << GetDeferKeywordSpelling(*this, thenStmt->getBeginLoc());
+ if (elseStmt)
+ CheckRedundantDeferStmt(*this, elseStmt);
+
if (ConstevalOrNegatedConsteval ||
StatementKind == IfStatementKind::Constexpr) {
auto DiagnoseLikelihood = [&](const Stmt *S) {
@@ -1840,7 +1881,8 @@ static void CheckLoopBody(Sema &S, Expr *CondExpr, Stmt *Body) {
// This does not apply for `if` statements and range-`for`
// loops which call DiagnoseEmptyStmtBody() directly.
S.getCurCompoundScope().setHasEmptyLoopBodies();
- }
+ } else
+ CheckRedundantDeferStmt(S, Body);
}
StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc,
@@ -4057,6 +4099,7 @@ StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body,
assert(!CurrentDefer.empty() && CurrentDefer.back().first == CurScope);
SourceLocation DeferLoc = CurrentDefer.pop_back_val().second;
DiagnoseEmptyStmtBody(DeferLoc, Body, diag::warn_empty_defer_body);
+ CheckRedundantDeferStmt(*this, Body);
setFunctionHasBranchProtectedScope();
return DeferStmt::Create(Context, DeferLoc, Body);
}
diff --git a/clang/test/Parser/defer-ts.c b/clang/test/Parser/defer-ts.c
index 118fe9ee3cc8f..1fe7eb2ec5716 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/defer-ts-seh.c b/clang/test/Sema/defer-ts-seh.c
index 4b773ed3f09a0..de515f38aae87 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 49230fa721e0f..dc23f1f6969d4 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 95c68fa213eaa..d1fba4128b088 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
diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c
new file mode 100644
index 0000000000000..9ca45c5d3a012
--- /dev/null
+++ b/clang/test/Sema/warn-redundant-defer.c
@@ -0,0 +1,309 @@
+// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify -Wredundant-defer %s
+
+void f1() {
+ if (true) _Defer {} // expected-warning {{redundant use of '_Defer'}} \
+ // expected-note {{did you mean to use '_Defer if'}}
+
+ _Defer {} // expected-warning {{redundant use of '_Defer'}}
+}
+
+#define defer _Defer
+
+void f2() {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+}
+
+void f3() {
+ defer {} // OK
+
+ defer defer {} // expected-warning {{redundant use of 'defer'}}
+
+ 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'}}
+
+ l3: l4: defer defer {} // expected-warning {{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'}}
+
+ l5:
+ [[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'}}
+
+ l6:
+ __attribute__((unknown)) defer // expected-warning {{unknown attribute}}
+ defer {} // expected-warning {{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'}}
+ }
+ }
+
+ {
+ [[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();
+ }
+
+ if (true)
+ defer {} // expected-warning {{redundant use of 'defer'}} \
+ // expected-note {{did you mean to use 'defer if'}}
+
+ 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();
+ l7: break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l8: break;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l9: l10: 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 {} // expected-warning {{redundant use of 'defer'}}
+ __attribute__((suppress)) continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l11: [[clang::likely]] break;
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ continue;
+ }
+
+ for (;;) {
+ defer {} // OK
+ f1();
+ l12: continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l13: continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l14: l15: 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;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ __attribute__((suppress)) continue;
+ }
+
+ for (;;) {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l16: [[clang::likely]] 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'}}
+}
+
+int f4() {
+ defer {} // OK
+ return 0;
+}
+
+void f5() {
+ defer {} // OK
+ f1();
+ return;
+}
+
+void f6() {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ return;
+}
+
+void f7() {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l17: return;
+}
+
+void f8() {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ [[clang::likely]] return;
+}
+
+void f9() {
+ defer {} // expected-warning {{redundant use of 'defer'}}
+ l18: l19: [[clang::likely]] [[clang::suppress]] return;
+}
More information about the cfe-commits
mailing list