https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/225748
>From 730360dc5a0f855d996f12b7f8ab2f8bdf5ebff1 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 08:22:44 +0000 Subject: [PATCH 01/25] [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 e5da258b9950a..a0126b0785d59 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 1da7698944b24..4b35e4247e2c4 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 36a18473f4d4c..17a15406d003a 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 74fe253efa137..8454f4f2bd842 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 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/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c new file mode 100644 index 0000000000000..2d9b92bbe9b63 --- /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 <[email protected]> Date: Wed, 23 Sep 2026 12:49:22 +0000 Subject: [PATCH 02/25] 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 1fe7eb2ec5716..118fe9ee3cc8f 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 <[email protected]> Date: Wed, 23 Sep 2026 14:52:28 +0000 Subject: [PATCH 03/25] 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 17a15406d003a..ee93b6c4c33c1 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 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 >From b8bd97ebc20fe865d3a91bc12185843964b56640 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 15:01:29 +0000 Subject: [PATCH 04/25] 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 8454f4f2bd842..00eccea0f5a0e 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 <[email protected]> Date: Wed, 23 Sep 2026 15:14:50 +0000 Subject: [PATCH 05/25] 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 00eccea0f5a0e..204d8a3270b58 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 <[email protected]> Date: Wed, 23 Sep 2026 15:44:58 +0000 Subject: [PATCH 06/25] 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 2d9b92bbe9b63..6e158d2c42566 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 <[email protected]> Date: Wed, 23 Sep 2026 15:48:49 +0000 Subject: [PATCH 07/25] 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 6e158d2c42566..61a5a75f2ee5b 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 <[email protected]> Date: Wed, 23 Sep 2026 16:18:29 +0000 Subject: [PATCH 08/25] 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 4b35e4247e2c4..1da7698944b24 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 ee93b6c4c33c1..72197af301c54 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 <[email protected]> Date: Wed, 23 Sep 2026 16:27:53 +0000 Subject: [PATCH 09/25] Update ReleaseNotes.md entry on -Wredundant-defer features Co-authored-by: Ambrose Leeb <[email protected]> --- 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 a0126b0785d59..3e9a67201c60e 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 <[email protected]> Date: Wed, 23 Sep 2026 16:33:35 +0000 Subject: [PATCH 10/25] Change wording in -Wredundant-defer from "defer" to "_Defer" Co-authored-by: Ambrose Leeb <[email protected]> --- 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 72197af301c54..cbf7d95c1cc7f 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 <[email protected]> Date: Fri, 25 Sep 2026 13:23:55 +0000 Subject: [PATCH 11/25] 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 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 >From 325c0193b6df1a72805c681662065c8e8e8bed11 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Fri, 25 Sep 2026 13:45:41 +0000 Subject: [PATCH 12/25] 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 61a5a75f2ee5b..f2b9f4272fd48 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 <[email protected]> Date: Fri, 25 Sep 2026 13:47:16 +0000 Subject: [PATCH 13/25] 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 204d8a3270b58..08bdb2d3908f4 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); >From 67452b6910bbabe743dd309254382ced082ddb4d Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Fri, 25 Sep 2026 16:07:35 +0000 Subject: [PATCH 14/25] Add nested label + nested defer test case --- clang/test/Sema/warn-redundant-defer.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index f2b9f4272fd48..c4f234b8512dc 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -17,6 +17,8 @@ void f2() { 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 @@ -25,7 +27,7 @@ void f2() { [[clang::likely]] defer defer {} // expected-warning {{redundant use of defer}} - l3: + l5: [[clang::likely]] defer defer {} // expected-warning {{redundant use of defer}} @@ -35,7 +37,7 @@ void f2() { __attribute__((suppress)) defer defer {} // expected-warning {{redundant use of defer}} - l4: + l6: __attribute__((unknown)) defer // expected-warning {{unknown attribute}} defer {} // expected-warning {{redundant use of defer}} @@ -107,17 +109,17 @@ void f2() { for (;;) { defer {} // OK f1(); - l5: break; + l7: break; } for (;;) { defer {} // expected-warning {{redundant use of defer}} - l6: break; + l8: break; } for (;;) { defer {} // expected-warning {{redundant use of defer}} - l7: l8: break; + l9: l10: break; } for (;;) { @@ -143,7 +145,7 @@ void f2() { for (;;) { defer {} // expected-warning {{redundant use of defer}} - l9: [[clang::likely]] break; + l11: [[clang::likely]] break; } for (;;) { @@ -160,17 +162,17 @@ void f2() { for (;;) { defer {} // OK f1(); - l10: continue; + l12: continue; } for (;;) { defer {} // expected-warning {{redundant use of defer}} - l11: continue; + l13: continue; } for (;;) { defer {} // expected-warning {{redundant use of defer}} - l12: l13: continue; + l14: l15: continue; } for (;;) { @@ -196,7 +198,7 @@ void f2() { for (;;) { defer {} // expected-warning {{redundant use of defer}} - l14: [[clang::likely]] continue; + l16: [[clang::likely]] continue; } while (true) { @@ -285,7 +287,7 @@ void f5() { void f6() { defer {} // expected-warning {{redundant use of defer}} - l15: return; + l17: return; } void f7() { @@ -295,5 +297,5 @@ void f7() { void f8() { defer {} // expected-warning {{redundant use of defer}} - l16: l17: [[clang::likely]] [[clang::suppress]] return; + l18: l19: [[clang::likely]] [[clang::suppress]] return; } >From 9bd6e9b7ab892a377e05abbdc231cdf78a91978d Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Tue, 29 Sep 2026 02:53:19 +0000 Subject: [PATCH 15/25] Support "defer" macro spelling as alternative to "_Defer" --- .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaStmt.cpp | 12 +- clang/test/Sema/warn-redundant-defer.c | 118 +++++++++--------- 3 files changed, 71 insertions(+), 61 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 68ac46a7b495e..3ab9207798b0a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11358,7 +11358,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 '%0'; the deferred statement is executed immediately">, InGroup<DiagGroup<"redundant-defer">>; def err_va_start_captured_stmt : Error< diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 08bdb2d3908f4..86469c4bad95b 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -446,9 +446,15 @@ static Stmt *GetInnermostStatement(Stmt *Outer) { 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(); + if (isa<DeferStmt>(Inner)) { + SourceLocation InnerLoc = Inner->getBeginLoc(); + StringRef DeferSpelling = + S.PP.getLastMacroWithSpelling(InnerLoc, {tok::kw__Defer}); + + S.Diag(InnerLoc, diag::warn_redundant_defer) + << Inner->getSourceRange() + << (DeferSpelling.empty() ? "_Defer" : DeferSpelling); + } } sema::CompoundScopeInfo &Sema::getCurCompoundScope() const { diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index c4f234b8512dc..8c91355dcb111 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -1,54 +1,58 @@ // 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}} + _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 {} // expected-warning {{redundant use of 'defer'}} - defer defer defer {} // expected-warning 2 {{redundant use of defer}} + defer defer defer {} // expected-warning 2 {{redundant use of 'defer'}} - l1: defer defer {} // expected-warning {{redundant use of defer}} + l1: defer defer {} // expected-warning {{redundant use of 'defer'}} - l2: defer defer defer {} // expected-warning 2 {{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}} + l3: l4: defer defer {} // expected-warning {{redundant use of 'defer'}} - defer [[clang::likely]] 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}} + defer {}; // expected-warning {{redundant use of 'defer'}} - [[clang::likely]] defer 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 {} // expected-warning {{redundant use of 'defer'}} defer __attribute__((suppress)) - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} __attribute__((suppress)) defer - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l6: __attribute__((unknown)) defer // expected-warning {{unknown attribute}} - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} { defer {} // OK f1(); } - { defer {} } // expected-warning {{redundant use of defer}} + { defer {} } // expected-warning {{redundant use of 'defer'}} - { defer defer {} } // expected-warning 2 {{redundant use of defer}} + { defer defer {} } // expected-warning 2 {{redundant use of 'defer'}} { defer { // OK @@ -59,15 +63,15 @@ void f2() { } { - defer { // expected-warning {{redundant use of defer}} - defer defer {} // expected-warning 2 {{redundant use of defer}} + 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 { // expected-warning {{redundant use of 'defer'}} [[clang::likely]] - defer defer {} // expected-warning 2 {{redundant use of defer}} + defer defer {} // expected-warning 2 {{redundant use of 'defer'}} } } @@ -77,10 +81,10 @@ void f2() { } if (true) - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} if (true) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} } for (;;) { @@ -89,10 +93,10 @@ void f2() { } for (;;) - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} } for (;;) { @@ -102,7 +106,7 @@ void f2() { } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} break; } @@ -113,12 +117,12 @@ void f2() { } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l8: break; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l9: l10: break; } @@ -129,22 +133,22 @@ void f2() { } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} [[clang::likely]] break; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} [[clang::likely]] [[clang::suppress]] break; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} __attribute__((suppress)) continue; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l11: [[clang::likely]] break; } @@ -155,7 +159,7 @@ void f2() { } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} continue; } @@ -166,12 +170,12 @@ void f2() { } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l13: continue; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l14: l15: continue; } @@ -182,22 +186,22 @@ void f2() { } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} [[clang::likely]] continue; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} [[clang::likely]] [[clang::suppress]] continue; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} __attribute__((suppress)) continue; } for (;;) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} l16: [[clang::likely]] continue; } @@ -207,10 +211,10 @@ void f2() { } while (true) - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} while (true) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} } while (true) { @@ -220,7 +224,7 @@ void f2() { } while (true) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} break; } @@ -231,7 +235,7 @@ void f2() { } while (true) { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} continue; } @@ -241,7 +245,7 @@ void f2() { } while (true); do { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} } while (true); do { @@ -251,7 +255,7 @@ void f2() { } while (true); do { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} break; } while (true); @@ -262,40 +266,40 @@ void f2() { } while (true); do { - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} continue; } while (true); - defer {} // expected-warning {{redundant use of defer}} + defer {} // expected-warning {{redundant use of 'defer'}} } -int f3() { +int f4() { defer {} // OK return 0; } -void f4() { +void f5() { defer {} // OK f1(); return; } -void f5() { - defer {} // expected-warning {{redundant use of defer}} +void f6() { + defer {} // expected-warning {{redundant use of 'defer'}} return; } -void f6() { - defer {} // expected-warning {{redundant use of defer}} +void f7() { + defer {} // expected-warning {{redundant use of 'defer'}} l17: return; } -void f7() { - defer {} // expected-warning {{redundant use of defer}} +void f8() { + defer {} // expected-warning {{redundant use of 'defer'}} [[clang::likely]] return; } -void f8() { - defer {} // expected-warning {{redundant use of defer}} +void f9() { + defer {} // expected-warning {{redundant use of 'defer'}} l18: l19: [[clang::likely]] [[clang::suppress]] return; } >From 01dd444fe2b94643450fabf10332fe624936ea81 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Thu, 1 Oct 2026 10:35:39 +0000 Subject: [PATCH 16/25] Introduce fix-it hints for redundant _Defer under if statements --- clang/include/clang/Basic/DiagnosticGroups.td | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 4 +- clang/lib/Sema/SemaStmt.cpp | 51 ++++++++++++++++--- clang/test/Sema/warn-redundant-defer.c | 28 ++++++++-- 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 1da7698944b24..fd7f3a4dc9d62 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -356,6 +356,8 @@ def ExtraSemiStmt : DiagGroup<"extra-semi-stmt", [EmptyInitStatement]>; def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi, CXX11ExtraSemi]>; +def RedundantDefer : DiagGroup<"redundant-defer">; + def GNUFlexibleArrayInitializer : DiagGroup<"gnu-flexible-array-initializer">; def GNUFlexibleArrayUnionMember : DiagGroup<"gnu-flexible-array-union-member">; def GNUFoldingConstant : DiagGroup<"gnu-folding-constant">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 3ab9207798b0a..2ea1f00f6bb4b 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11359,7 +11359,9 @@ def note_empty_body_on_separate_line : Note< def warn_redundant_defer: Warning< "redundant use of '%0'; the deferred statement is executed immediately">, - InGroup<DiagGroup<"redundant-defer">>; + InGroup<RedundantDefer>; +def note_redundant_defer_if : Note< + "did you mean to use '%0 if'?">, InGroup<RedundantDefer>; def err_va_start_captured_stmt : Error< "'va_start' cannot be used in a captured statement">; diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 86469c4bad95b..9a78e6f5caba0 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -442,18 +442,24 @@ static Stmt *GetInnermostStatement(Stmt *Outer) { return Outer; } +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 void CheckRedundantDeferStmt(Sema &S, Stmt *Body) { Stmt *Inner = GetInnermostStatement(Body); if (isa<DeferStmt>(Inner)) { - SourceLocation InnerLoc = Inner->getBeginLoc(); - StringRef DeferSpelling = - S.PP.getLastMacroWithSpelling(InnerLoc, {tok::kw__Defer}); + SourceLocation DeferLoc = Inner->getBeginLoc(); - S.Diag(InnerLoc, diag::warn_redundant_defer) - << Inner->getSourceRange() - << (DeferSpelling.empty() ? "_Defer" : DeferSpelling); + S.Diag(DeferLoc, diag::warn_redundant_defer) + << Inner->getSourceRange() << GetDeferKeywordSpelling(S, DeferLoc); } } @@ -1015,6 +1021,37 @@ class CommaVisitor : public EvaluatedExprVisitor<CommaVisitor> { }; } +static void DiagnoseIfStmtRedundantDeferBody(Sema &S, SourceLocation IfLoc, + Stmt *thenStmt) { + DeferStmt *Defer = dyn_cast_or_null<DeferStmt>(thenStmt); + if (Defer) + CheckRedundantDeferStmt(S, Defer); + else { + // If the body is a CompoundStmt, CheckRedundantDeferStmt() has + // already been called by Sema::ActOnCompoundStmt; the only + // thing left to do here is to issue the fix-it hint below. + CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(thenStmt); + if (Body && Body->size() == 1) + Defer = dyn_cast_or_null<DeferStmt>(Body->body_back()); + + if (!Defer) + // All good; no redundant defer statement to check. + return; + } + + SourceLocation DeferLoc = Defer->getBeginLoc(); + StringRef DeferSpelling = GetDeferKeywordSpelling(S, DeferLoc); + + // Offer a fix-it hint to replace `if (X) _Defer Y;` with `_Defer if (X) Y;`. + // This must preserve the `{}` if the `if` body is a CompoundStmt. + S.Diag(DeferLoc, diag::note_redundant_defer_if) + << DeferSpelling + << FixItHint::CreateInsertion(IfLoc, std::string(DeferSpelling) + " ") + << FixItHint::CreateRemoval(SourceRange( + DeferLoc, + SourceLocation(DeferLoc.getLocWithOffset(DeferSpelling.size())))); +} + StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, IfStatementKind StatementKind, SourceLocation LParenLoc, Stmt *InitStmt, @@ -1039,7 +1076,7 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, if (!ConstevalOrNegatedConsteval && !elseStmt) DiagnoseEmptyStmtBody(RParenLoc, thenStmt, diag::warn_empty_if_body); - CheckRedundantDeferStmt(*this, thenStmt); + DiagnoseIfStmtRedundantDeferBody(*this, IfLoc, thenStmt); if (elseStmt) CheckRedundantDeferStmt(*this, elseStmt); diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index 8c91355dcb111..62aad570eba5d 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -1,6 +1,12 @@ -// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify %s -Wredundant-defer +// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify -Wredundant-defer %s +// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -fdiagnostics-parseable-fixits -Wredundant-defer %s 2>&1 | FileCheck %s void f1() { + if (true) _Defer {} // expected-warning {{redundant use of '_Defer'}} \ + // expected-note {{did you mean to use '_Defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"_Defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:20}:"" + _Defer {} // expected-warning {{redundant use of '_Defer'}} } @@ -80,12 +86,28 @@ void f3() { f1(); } + if (true) defer {} // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:19}:"" + if (true) - defer {} // expected-warning {{redundant use of 'defer'}} + defer {} // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" + + if (true) { defer {} } // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:15-[[@LINE-3]]:21}:"" if (true) { - defer {} // expected-warning {{redundant use of 'defer'}} + defer {} // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} } + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" for (;;) { defer {} // OK >From 4db9969e823cca7f1828ebb1f2c59bbbf66efe1e Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Thu, 1 Oct 2026 15:17:51 +0000 Subject: [PATCH 17/25] Support smarter brace handling for redundant-defer fixit hints --- clang/lib/Sema/SemaStmt.cpp | 27 +++++++++++-- clang/test/Sema/warn-redundant-defer.c | 53 ++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 9a78e6f5caba0..77ea9fda5b334 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -1039,17 +1039,38 @@ static void DiagnoseIfStmtRedundantDeferBody(Sema &S, SourceLocation IfLoc, return; } + // The following is responsible for preparing a fix-it hint to replace + // occurrances of `if (X) _Defer Y` with `_Defer if (X) Y` or similar. + + SmallVector<FixItHint, 2> DeferBraceRemoval; + CompoundStmt *DeferBody = dyn_cast_or_null<CompoundStmt>(Defer->getBody()); + + // Remove surrounding `{}` from the defer statement body if it is not needed, + // for example: `if (X) _Defer { foo(); }` -> `_Defer if (X) foo();` + // This can also apply when the if statement body is already a CompoundStmt, + // meaning that the original `{}` around the defer substatement would be + // redundant after applying the fix-it. + if (DeferBody && (DeferBody->size() == 1 || + (isa<CompoundStmt>(thenStmt) && DeferBody->size() != 0))) { + DeferBraceRemoval.push_back(FixItHint::CreateRemoval(SourceRange( + DeferBody->getLBracLoc(), + DeferBody->body_front()->getBeginLoc().getLocWithOffset(-1)))); + + DeferBraceRemoval.push_back(FixItHint::CreateRemoval( + SourceRange(DeferBody->body_back()->getEndLoc().getLocWithOffset(2), + DeferBody->getRBracLoc()))); + } + SourceLocation DeferLoc = Defer->getBeginLoc(); StringRef DeferSpelling = GetDeferKeywordSpelling(S, DeferLoc); - // Offer a fix-it hint to replace `if (X) _Defer Y;` with `_Defer if (X) Y;`. - // This must preserve the `{}` if the `if` body is a CompoundStmt. S.Diag(DeferLoc, diag::note_redundant_defer_if) << DeferSpelling << FixItHint::CreateInsertion(IfLoc, std::string(DeferSpelling) + " ") << FixItHint::CreateRemoval(SourceRange( DeferLoc, - SourceLocation(DeferLoc.getLocWithOffset(DeferSpelling.size())))); + SourceLocation(DeferLoc.getLocWithOffset(DeferSpelling.size())))) + << DeferBraceRemoval; } StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index 62aad570eba5d..284acbf980f41 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -97,6 +97,30 @@ void f3() { // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" + if (true) + defer f1(); // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" + + if (true) + defer { f1(); } // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:11-[[@LINE-4]]:13}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:18-[[@LINE-5]]:20}:"" + + if (true) + defer { + f1(); + f2(); + } + // expected-warning@-4 {{redundant use of 'defer'}} + // expected-note@-5 {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:5-[[@LINE-7]]:11}:"" + if (true) { defer {} } // expected-warning {{redundant use of 'defer'}} \ // expected-note {{did you mean to use 'defer if'}} // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"defer " @@ -109,6 +133,35 @@ void f3() { // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" + if (true) { + defer { f1(); } // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + } + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:11-[[@LINE-5]]:13}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:18-[[@LINE-6]]:20}:"" + + if (true) { + defer f1(); // expected-warning {{redundant use of 'defer'}} \ + // expected-note {{did you mean to use 'defer if'}} + } + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" + + if (true) { + defer { + f1(); + f2(); + } + } + // expected-warning@-5 {{redundant use of 'defer'}} + // expected-note@-6 {{did you mean to use 'defer if'}} + // CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:3-[[@LINE-8]]:3}:"defer " + // CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:5-[[@LINE-8]]:11}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-9]]:11-[[@LINE-8]]:7}:"" + // CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:12-[[@LINE-7]]:6}:"" + for (;;) { defer {} // OK f1(); >From 7954f36b4fd556e8c155379f4a0e3b9b5655940b Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Thu, 1 Oct 2026 16:08:20 +0000 Subject: [PATCH 18/25] Remove note_redundant_defer_if Note from DiagGroup --- clang/include/clang/Basic/DiagnosticGroups.td | 2 -- clang/include/clang/Basic/DiagnosticSemaKinds.td | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index fd7f3a4dc9d62..1da7698944b24 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -356,8 +356,6 @@ def ExtraSemiStmt : DiagGroup<"extra-semi-stmt", [EmptyInitStatement]>; def ExtraSemi : DiagGroup<"extra-semi", [CXX98CompatExtraSemi, CXX11ExtraSemi]>; -def RedundantDefer : DiagGroup<"redundant-defer">; - def GNUFlexibleArrayInitializer : DiagGroup<"gnu-flexible-array-initializer">; def GNUFlexibleArrayUnionMember : DiagGroup<"gnu-flexible-array-union-member">; def GNUFoldingConstant : DiagGroup<"gnu-folding-constant">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2ea1f00f6bb4b..56b5e9309806e 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11357,11 +11357,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< +def warn_redundant_defer : Warning< "redundant use of '%0'; the deferred statement is executed immediately">, - InGroup<RedundantDefer>; -def note_redundant_defer_if : Note< - "did you mean to use '%0 if'?">, InGroup<RedundantDefer>; + 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">; >From f469056a44e382e002065b22eb196195397506b8 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Sat, 3 Oct 2026 04:15:36 +0000 Subject: [PATCH 19/25] Reduce usage of empty lines in helper functions --- clang/lib/Sema/SemaStmt.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 77ea9fda5b334..2eb75897e2f8e 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -435,7 +435,6 @@ void Sema::ActOnFinishOfCompoundStmt() { static Stmt *GetInnermostStatement(Stmt *Outer) { if (isa<LabelStmt>(Outer)) Outer = cast<LabelStmt>(Outer)->getInnermostLabeledStmt(); - if (isa<AttributedStmt>(Outer)) Outer = cast<AttributedStmt>(Outer)->getSubStmt(); @@ -454,10 +453,8 @@ static StringRef GetDeferKeywordSpelling(Sema &S, SourceLocation DeferLoc) { // 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)) { SourceLocation DeferLoc = Inner->getBeginLoc(); - S.Diag(DeferLoc, diag::warn_redundant_defer) << Inner->getSourceRange() << GetDeferKeywordSpelling(S, DeferLoc); } >From f96df42835e6dc2f6fd495da9ccd5392719fcdfd Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Sat, 3 Oct 2026 04:27:35 +0000 Subject: [PATCH 20/25] Replace GetInnermostStatement() with Stmt::stripLabelLikeStatements() --- clang/lib/Sema/SemaStmt.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 2eb75897e2f8e..da5139bf5df3e 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -430,17 +430,6 @@ 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; -} - static StringRef GetDeferKeywordSpelling(Sema &S, SourceLocation DeferLoc) { StringRef DeferSpelling = S.PP.getLastMacroWithSpelling(DeferLoc, {tok::kw__Defer}); @@ -452,7 +441,7 @@ static StringRef GetDeferKeywordSpelling(Sema &S, SourceLocation DeferLoc) { // Diagnose if the given statement is a redundant _Defer statement. static void CheckRedundantDeferStmt(Sema &S, Stmt *Body) { - Stmt *Inner = GetInnermostStatement(Body); + Stmt *Inner = Body->stripLabelLikeStatements(); if (isa<DeferStmt>(Inner)) { SourceLocation DeferLoc = Inner->getBeginLoc(); S.Diag(DeferLoc, diag::warn_redundant_defer) @@ -504,7 +493,7 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, // or a plain `return` statement. if (NumElts > 1) { for (unsigned i = 0; i != NumElts - 1; ++i) { - Stmt *Inner = GetInnermostStatement(Elts[i + 1]); + Stmt *Inner = Elts[i + 1]->stripLabelLikeStatements(); if (isa<BreakStmt, ContinueStmt>(Inner) || (isa<ReturnStmt>(Inner) && !cast<ReturnStmt>(Inner)->getRetValue())) CheckRedundantDeferStmt(*this, Elts[i]); >From 4ca5c1eb50ffb05f356572f0d268f20160c6e7c4 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Sat, 3 Oct 2026 06:48:20 +0000 Subject: [PATCH 21/25] Remove fix-it hint for redundant defer under if statements --- clang/lib/Sema/SemaStmt.cpp | 60 +++------------------ clang/test/Sema/warn-redundant-defer.c | 73 +------------------------- 2 files changed, 7 insertions(+), 126 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index da5139bf5df3e..3bce6165606ba 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -440,13 +440,15 @@ static StringRef GetDeferKeywordSpelling(Sema &S, SourceLocation DeferLoc) { } // Diagnose if the given statement is a redundant _Defer statement. -static void CheckRedundantDeferStmt(Sema &S, Stmt *Body) { +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 { @@ -1007,58 +1009,6 @@ class CommaVisitor : public EvaluatedExprVisitor<CommaVisitor> { }; } -static void DiagnoseIfStmtRedundantDeferBody(Sema &S, SourceLocation IfLoc, - Stmt *thenStmt) { - DeferStmt *Defer = dyn_cast_or_null<DeferStmt>(thenStmt); - if (Defer) - CheckRedundantDeferStmt(S, Defer); - else { - // If the body is a CompoundStmt, CheckRedundantDeferStmt() has - // already been called by Sema::ActOnCompoundStmt; the only - // thing left to do here is to issue the fix-it hint below. - CompoundStmt *Body = dyn_cast_or_null<CompoundStmt>(thenStmt); - if (Body && Body->size() == 1) - Defer = dyn_cast_or_null<DeferStmt>(Body->body_back()); - - if (!Defer) - // All good; no redundant defer statement to check. - return; - } - - // The following is responsible for preparing a fix-it hint to replace - // occurrances of `if (X) _Defer Y` with `_Defer if (X) Y` or similar. - - SmallVector<FixItHint, 2> DeferBraceRemoval; - CompoundStmt *DeferBody = dyn_cast_or_null<CompoundStmt>(Defer->getBody()); - - // Remove surrounding `{}` from the defer statement body if it is not needed, - // for example: `if (X) _Defer { foo(); }` -> `_Defer if (X) foo();` - // This can also apply when the if statement body is already a CompoundStmt, - // meaning that the original `{}` around the defer substatement would be - // redundant after applying the fix-it. - if (DeferBody && (DeferBody->size() == 1 || - (isa<CompoundStmt>(thenStmt) && DeferBody->size() != 0))) { - DeferBraceRemoval.push_back(FixItHint::CreateRemoval(SourceRange( - DeferBody->getLBracLoc(), - DeferBody->body_front()->getBeginLoc().getLocWithOffset(-1)))); - - DeferBraceRemoval.push_back(FixItHint::CreateRemoval( - SourceRange(DeferBody->body_back()->getEndLoc().getLocWithOffset(2), - DeferBody->getRBracLoc()))); - } - - SourceLocation DeferLoc = Defer->getBeginLoc(); - StringRef DeferSpelling = GetDeferKeywordSpelling(S, DeferLoc); - - S.Diag(DeferLoc, diag::note_redundant_defer_if) - << DeferSpelling - << FixItHint::CreateInsertion(IfLoc, std::string(DeferSpelling) + " ") - << FixItHint::CreateRemoval(SourceRange( - DeferLoc, - SourceLocation(DeferLoc.getLocWithOffset(DeferSpelling.size())))) - << DeferBraceRemoval; -} - StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, IfStatementKind StatementKind, SourceLocation LParenLoc, Stmt *InitStmt, @@ -1083,7 +1033,9 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, if (!ConstevalOrNegatedConsteval && !elseStmt) DiagnoseEmptyStmtBody(RParenLoc, thenStmt, diag::warn_empty_if_body); - DiagnoseIfStmtRedundantDeferBody(*this, IfLoc, thenStmt); + if (CheckRedundantDeferStmt(*this, thenStmt)) + Diag(thenStmt->getBeginLoc(), diag::note_redundant_defer_if) + << GetDeferKeywordSpelling(*this, thenStmt->getBeginLoc()); if (elseStmt) CheckRedundantDeferStmt(*this, elseStmt); diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index 284acbf980f41..9ca45c5d3a012 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -1,11 +1,8 @@ // RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify -Wredundant-defer %s -// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -fdiagnostics-parseable-fixits -Wredundant-defer %s 2>&1 | FileCheck %s void f1() { if (true) _Defer {} // expected-warning {{redundant use of '_Defer'}} \ // expected-note {{did you mean to use '_Defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"_Defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:20}:"" _Defer {} // expected-warning {{redundant use of '_Defer'}} } @@ -86,81 +83,13 @@ void f3() { f1(); } - if (true) defer {} // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:13-[[@LINE-3]]:19}:"" - - if (true) - defer {} // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" - - if (true) - defer f1(); // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" - - if (true) - defer { f1(); } // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:3-[[@LINE-3]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:5-[[@LINE-3]]:11}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:11-[[@LINE-4]]:13}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:18-[[@LINE-5]]:20}:"" - if (true) - defer { - f1(); - f2(); - } - // expected-warning@-4 {{redundant use of 'defer'}} - // expected-note@-5 {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:3-[[@LINE-7]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-7]]:5-[[@LINE-7]]:11}:"" - - if (true) { defer {} } // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-2]]:3-[[@LINE-2]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-3]]:15-[[@LINE-3]]:21}:"" - - if (true) { defer {} // expected-warning {{redundant use of 'defer'}} \ // expected-note {{did you mean to use 'defer if'}} - } - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" - - if (true) { - defer { f1(); } // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - } - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-5]]:11-[[@LINE-5]]:13}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-6]]:18-[[@LINE-6]]:20}:"" if (true) { - defer f1(); // expected-warning {{redundant use of 'defer'}} \ - // expected-note {{did you mean to use 'defer if'}} - } - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:3-[[@LINE-4]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-4]]:5-[[@LINE-4]]:11}:"" - - if (true) { - defer { - f1(); - f2(); - } + defer {} // expected-warning {{redundant use of 'defer'}} } - // expected-warning@-5 {{redundant use of 'defer'}} - // expected-note@-6 {{did you mean to use 'defer if'}} - // CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:3-[[@LINE-8]]:3}:"defer " - // CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:5-[[@LINE-8]]:11}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-9]]:11-[[@LINE-8]]:7}:"" - // CHECK: fix-it:"{{.*}}":{[[@LINE-8]]:12-[[@LINE-7]]:6}:"" for (;;) { defer {} // OK >From b87e60a7e7a4860af0b341e444f362421ca81b7d Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Sat, 3 Oct 2026 07:08:51 +0000 Subject: [PATCH 22/25] Remove unneeded empty line in GetDeferKeywordSpelling --- clang/lib/Sema/SemaStmt.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 3bce6165606ba..9b31c55f1a1f8 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -435,7 +435,6 @@ static StringRef GetDeferKeywordSpelling(Sema &S, SourceLocation DeferLoc) { S.PP.getLastMacroWithSpelling(DeferLoc, {tok::kw__Defer}); if (DeferSpelling.empty()) DeferSpelling = "_Defer"; - return DeferSpelling; } >From 92555d593a6edbe5607e7f4f3a8eb0f1cd1fa1ca Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Mon, 5 Oct 2026 20:30:55 +0000 Subject: [PATCH 23/25] Cherry-pick changes from PR #226101 --- clang/lib/Sema/SemaStmt.cpp | 56 ++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 20 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 9b31c55f1a1f8..a2801dbf22e7a 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -482,7 +482,12 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, } // Check for suspicious empty body (null statement) in `for' and `while' - // statements. Don't do anything for template instantiations, this just adds + // statements, for example: + // + // for (;;); <- warning: for loop has empty body + // foo(); + // + // Don't do anything for template instantiations, this just adds // noise. if (NumElts != 0 && !CurrentInstantiationScope && getCurCompoundScope().HasEmptyLoopBodies) { @@ -1855,18 +1860,37 @@ Sema::DiagnoseAssignmentEnum(QualType DstType, QualType SrcType, << DstType.getUnqualifiedType(); } +// Checks for issues that are common to `for`/`while` statements. +static void CheckLoopBody(Sema &S, Expr *CondExpr, Stmt *Body) { + // Check for comma operator misuse. + if (CondExpr && + !S.Diags.isIgnored(diag::warn_comma_operator, CondExpr->getExprLoc())) + CommaVisitor(S).Visit(CondExpr); + + if (isa<NullStmt>(Body)) { + // Tell Sema::ActOnCompoundStmt to perform a check on + // this suspicious empty `for`/`while` loop when + // processing the compound statement that contains this loop. + // + // The actual check cannot be done here directly as it may + // depend on other statements following the `for`/`while` + // loop, in the outer enclosing CompoundStmt; see the + // comment in Sema::ActOnCompoundStmt for an example + // of when this happens. + // + // 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, SourceLocation LParenLoc, ConditionResult Cond, SourceLocation RParenLoc, Stmt *Body) { if (Cond.isInvalid()) return StmtError(); - auto CondVal = Cond.get(); - - if (CondVal.second && - !Diags.isIgnored(diag::warn_comma_operator, CondVal.second->getExprLoc())) - CommaVisitor(*this).Visit(CondVal.second); - // OpenACC3.3 2.14.4: // The update directive is executable. It must not appear in place of the // statement following an 'if', 'while', 'do', 'switch', or 'label' in C or @@ -1876,10 +1900,9 @@ StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, Body = new (Context) NullStmt(Body->getBeginLoc()); } - if (isa<NullStmt>(Body)) - getCurCompoundScope().setHasEmptyLoopBodies(); - else - CheckRedundantDeferStmt(*this, Body); + auto CondVal = Cond.get(); + + CheckLoopBody(*this, CondVal.second, Body); return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body, WhileLoc, LParenLoc, RParenLoc); @@ -2363,16 +2386,9 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Body); CheckForRedundantIteration(*this, third.get(), Body); - if (Second.get().second && - !Diags.isIgnored(diag::warn_comma_operator, - Second.get().second->getExprLoc())) - CommaVisitor(*this).Visit(Second.get().second); + CheckLoopBody(*this, Second.get().second, Body); - Expr *Third = third.release().getAs<Expr>(); - if (isa<NullStmt>(Body)) - getCurCompoundScope().setHasEmptyLoopBodies(); - else - CheckRedundantDeferStmt(*this, Body); + Expr *Third = third.release().getAs<Expr>(); return new (Context) ForStmt(Context, First, Second.get().second, Second.get().first, Third, >From 649eaefb67223af1bf5e91743a95e3d461d2ce1d Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Tue, 6 Oct 2026 22:33:03 +0000 Subject: [PATCH 24/25] Apply suggestion from @Sirraide Co-authored-by: Ambrose Leeb <[email protected]> --- clang/lib/Sema/SemaStmt.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index b83fa95c895d9..59d85774f01ec 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -4096,13 +4096,9 @@ void Sema::ActOnDeferStmtError([[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. CheckRedundantDeferStmt(*this, Body); - setFunctionHasBranchProtectedScope(); return DeferStmt::Create(Context, DeferLoc, Body); } >From d60c431c93c1d05e37563d74ab02c3796d6ff3d9 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Tue, 6 Oct 2026 22:28:32 +0000 Subject: [PATCH 25/25] Re-add [[maybe_unused]] to CurScope parameter on ActOnEndOfDeferStmt --- clang/lib/Sema/SemaStmt.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 59d85774f01ec..a3e3e403188c7 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -4094,7 +4094,8 @@ void Sema::ActOnDeferStmtError([[maybe_unused]] Scope *CurScope) { CurrentDefer.pop_back(); } -StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body, Scope *CurScope) { +StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body, + [[maybe_unused]] Scope *CurScope) { assert(!CurrentDefer.empty() && CurrentDefer.back().first == CurScope); SourceLocation DeferLoc = CurrentDefer.pop_back_val().second; DiagnoseEmptyStmtBody(DeferLoc, Body, diag::warn_empty_defer_body); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
