[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