https://github.com/Expertcoderz 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 1/7] [Clang][Sema] Add -Wredundant-defer to diagnose redundant `_Defer`s This PR adds a new `-Wredundant-defer` diagnostic to have Clang emit warnings for superfluous uses of `_Defer`, where the substatement is always executed at the point of defer. With `-Wredundant-defer`, `_Defer` statements that occur in these positions will trigger the warning: - as the last statement of a compound statement or function - as the body of an `if`/`else` statement - as the body of a `for`/`while` loop - immediately preceding a `break` or `continue` statement - as the substatement of another `_Defer` This helps to detect mistakes and some potential misuses of `_Defer`. --- clang/docs/ReleaseNotes.md | 5 + clang/include/clang/Basic/DiagnosticGroups.td | 2 + .../clang/Basic/DiagnosticSemaKinds.td | 4 + clang/lib/Sema/SemaStmt.cpp | 41 ++++- clang/test/Parser/defer-ts.c | 4 +- clang/test/Sema/warn-redundant-defer.c | 151 ++++++++++++++++++ 6 files changed, 203 insertions(+), 4 deletions(-) create mode 100644 clang/test/Sema/warn-redundant-defer.c diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index e5da258b9950a3..a0126b0785d59d 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -518,6 +518,11 @@ features cannot lower the translation-unit ABI level; - Improve Clang diagnoses when unary `__imag` operator with non-complex type operand is used as lvalue. (GH222383) +- Added `-Wredundant-defer` to diagnose redundant uses of the `_Defer` + keyword, such as when deferring the last statement of a block; when + used as the body of a conditional; or when it immediately precedes + a `break`/`continue` statement. + ### Improvements to Clang's time-trace ### Improvements to Coverage Mapping diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td index 1da7698944b24b..4b35e4247e2c4c 100644 --- a/clang/include/clang/Basic/DiagnosticGroups.td +++ b/clang/include/clang/Basic/DiagnosticGroups.td @@ -345,6 +345,8 @@ def EmptyBody : DiagGroup<"empty-body">; def Exceptions : DiagGroup<"exceptions">; def DeclarationAfterStatement : DiagGroup<"declaration-after-statement">; +def RedundantDefer : DiagGroup<"redundant-defer">; + def GNUEmptyStruct : DiagGroup<"gnu-empty-struct">; def ExtraTokens : DiagGroup<"extra-tokens">; def CXX98CompatExtraSemi : DiagGroup<"c++98-compat-extra-semi">; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 36a18473f4d4cc..17a15406d003aa 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11342,6 +11342,10 @@ def warn_empty_defer_body : Warning< def note_empty_body_on_separate_line : Note< "put the semicolon on a separate line to silence this warning">; +def warn_redundant_defer: Warning< + "redundant use of defer; the deferred statement is executed immediately">, + InGroup<RedundantDefer>, DefaultIgnore; + def err_va_start_captured_stmt : Error< "'va_start' cannot be used in a captured statement">; def err_va_start_outside_function : Error< diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 74fe253efa1374..8454f4f2bd8421 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -470,6 +470,23 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]); } + // Find defer statements that immediately precede a break/continue statement. + std::optional<SourceLocation> DeferLoc = std::nullopt; + for (unsigned i = 0; i != NumElts; ++i) { + if (DeferLoc && isa<BreakStmt, ContinueStmt>(Elts[i])) + Diag(DeferLoc.value(), diag::warn_redundant_defer) + << Elts[i]->getSourceRange(); + DeferLoc = isa<DeferStmt>(Elts[i]) + ? std::optional<SourceLocation>(Elts[i]->getBeginLoc()) + : std::nullopt; + } + + // Check for defer as last statement. + Stmt *Back = NumElts > 0 ? Elts[NumElts - 1] : nullptr; + if (Back && isa<DeferStmt>(Back)) + Diag(Back->getBeginLoc(), diag::warn_redundant_defer) + << Back->getSourceRange(); + // Calculate difference between FP options in this compound statement and in // the enclosing one. If this is a function body, take the difference against // default options. In this case the difference will indicate options that are @@ -997,6 +1014,14 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, if (!ConstevalOrNegatedConsteval && !elseStmt) DiagnoseEmptyStmtBody(RParenLoc, thenStmt, diag::warn_empty_if_body); + if (isa<DeferStmt>(thenStmt)) + Diag(thenStmt->getBeginLoc(), diag::warn_redundant_defer) + << thenStmt->getSourceRange(); + + if (elseStmt && isa<DeferStmt>(elseStmt)) + Diag(elseStmt->getBeginLoc(), diag::warn_redundant_defer) + << elseStmt->getSourceRange(); + if (ConstevalOrNegatedConsteval || StatementKind == IfStatementKind::Constexpr) { auto DiagnoseLikelihood = [&](const Stmt *S) { @@ -1837,6 +1862,9 @@ StmtResult Sema::ActOnWhileStmt(SourceLocation WhileLoc, if (isa<NullStmt>(Body)) getCurCompoundScope().setHasEmptyLoopBodies(); + else if (isa<DeferStmt>(Body)) + Diag(Body->getBeginLoc(), diag::warn_redundant_defer) + << Body->getSourceRange(); return WhileStmt::Create(Context, CondVal.first, CondVal.second, Body, WhileLoc, LParenLoc, RParenLoc); @@ -2328,6 +2356,9 @@ StmtResult Sema::ActOnForStmt(SourceLocation ForLoc, SourceLocation LParenLoc, Expr *Third = third.release().getAs<Expr>(); if (isa<NullStmt>(Body)) getCurCompoundScope().setHasEmptyLoopBodies(); + else if (isa<DeferStmt>(Body)) + Diag(Body->getBeginLoc(), diag::warn_redundant_defer) + << Body->getSourceRange(); return new (Context) ForStmt(Context, First, Second.get().second, Second.get().first, Third, @@ -4033,11 +4064,17 @@ void Sema::ActOnDeferStmtError([[maybe_unused]] Scope *CurScope) { CurrentDefer.pop_back(); } -StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body, - [[maybe_unused]] Scope *CurScope) { +StmtResult Sema::ActOnEndOfDeferStmt(Stmt *Body, Scope *CurScope) { assert(!CurrentDefer.empty() && CurrentDefer.back().first == CurScope); + SourceLocation DeferLoc = CurrentDefer.pop_back_val().second; DiagnoseEmptyStmtBody(DeferLoc, Body, diag::warn_empty_defer_body); + + // Check for superfluous nested defer. + if (isa<DeferStmt>(Body)) + Diag(Body->getBeginLoc(), diag::warn_redundant_defer) + << Body->getSourceRange(); + setFunctionHasBranchProtectedScope(); return DeferStmt::Create(Context, DeferLoc, Body); } diff --git a/clang/test/Parser/defer-ts.c b/clang/test/Parser/defer-ts.c index 118fe9ee3cc8f5..1fe7eb2ec57163 100644 --- a/clang/test/Parser/defer-ts.c +++ b/clang/test/Parser/defer-ts.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -verify %s -// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -verify %s +// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s +// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s #define defer _Defer diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c new file mode 100644 index 00000000000000..2d9b92bbe9b63b --- /dev/null +++ b/clang/test/Sema/warn-redundant-defer.c @@ -0,0 +1,151 @@ +// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify %s -Wredundant-defer + +#define defer _Defer + +void f1() { + defer {} // expected-warning {{redundant use of defer}} +} + +void f2() { + defer {} // OK + + defer defer {} // expected-warning {{redundant use of defer}} + + defer defer defer {} // expected-warning 2 {{redundant use of defer}} + + { + defer {} // OK + f1(); + } + + { defer {} } // expected-warning {{redundant use of defer}} + + { defer defer {} } // expected-warning 2 {{redundant use of defer}} + + { + defer { // OK + defer {} // OK + f1(); + } + f1(); + } + + { + defer { // expected-warning {{redundant use of defer}} + defer defer {} // expected-warning 2 {{redundant use of defer}} + } + } + + if (true) { + defer {} // OK + f1(); + } + + if (true) + defer {} // expected-warning {{redundant use of defer}} + + if (true) { + defer {} // expected-warning {{redundant use of defer}} + } + + for (;;) { + defer {} // OK + f1(); + } + + for (;;) + defer {} // expected-warning {{redundant use of defer}} + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + } + + for (;;) { + defer {} // OK + f1(); + break; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + break; + } + + for (;;) { + defer {} // OK + f1(); + continue; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + continue; + } + + while (true) { + defer {} // OK + f1(); + } + + while (true) + defer {} // expected-warning {{redundant use of defer}} + + while (true) { + defer {} // expected-warning {{redundant use of defer}} + } + + while (true) { + defer {} // OK + f1(); + break; + } + + while (true) { + defer {} // expected-warning {{redundant use of defer}} + break; + } + + while (true) { + defer {} // OK + f1(); + continue; + } + + while (true) { + defer {} // expected-warning {{redundant use of defer}} + continue; + } + + do { + defer {} // OK + f1(); + } while (true); + + do { + defer {} // expected-warning {{redundant use of defer}} + } while (true); + + do { + defer {} // OK + f1(); + break; + } while (true); + + do { + defer {} // expected-warning {{redundant use of defer}} + break; + } while (true); + + do { + defer {} // OK + f1(); + continue; + } while (true); + + do { + defer {} // expected-warning {{redundant use of defer}} + continue; + } while (true); + + defer {} // expected-warning {{redundant use of defer}} +} >From 43552bba54d55954537750ee28d0c88aaf7c5d8a Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 12:49:22 +0000 Subject: [PATCH 2/7] Remove accidental change to clang/test/Parser/defer-ts.c --- clang/test/Parser/defer-ts.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/Parser/defer-ts.c b/clang/test/Parser/defer-ts.c index 1fe7eb2ec57163..118fe9ee3cc8f5 100644 --- a/clang/test/Parser/defer-ts.c +++ b/clang/test/Parser/defer-ts.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s -// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -Wno-redundant-defer -verify %s +// RUN: %clang_cc1 -std=c11 -fsyntax-only -fdefer-ts -verify %s +// RUN: %clang_cc1 -std=c23 -fsyntax-only -fdefer-ts -verify %s #define defer _Defer >From 995108a364b039bef658384a02cfe380e0bdf4a2 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 14:52:28 +0000 Subject: [PATCH 3/7] Remove DefaultIgnore on -Wredundant-defer --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/test/Sema/defer-ts-seh.c | 2 +- clang/test/Sema/defer-ts-sjlj.c | 2 +- clang/test/Sema/defer-ts.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 17a15406d003aa..ee93b6c4c33c17 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11344,7 +11344,7 @@ def note_empty_body_on_separate_line : Note< def warn_redundant_defer: Warning< "redundant use of defer; the deferred statement is executed immediately">, - InGroup<RedundantDefer>, DefaultIgnore; + InGroup<RedundantDefer>; def err_va_start_captured_stmt : Error< "'va_start' cannot be used in a captured statement">; diff --git a/clang/test/Sema/defer-ts-seh.c b/clang/test/Sema/defer-ts-seh.c index 4b773ed3f09a0c..de515f38aae876 100644 --- a/clang/test/Sema/defer-ts-seh.c +++ b/clang/test/Sema/defer-ts-seh.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c23 -fdefer-ts -fms-compatibility -triple x86_64-windows-msvc -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c23 -fdefer-ts -Wno-redundant-defer -fms-compatibility -triple x86_64-windows-msvc -fsyntax-only -verify %s void f() { __try { diff --git a/clang/test/Sema/defer-ts-sjlj.c b/clang/test/Sema/defer-ts-sjlj.c index 49230fa721e0f7..dc23f1f6969d41 100644 --- a/clang/test/Sema/defer-ts-sjlj.c +++ b/clang/test/Sema/defer-ts-sjlj.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=gnu23 -fdefer-ts -fsyntax-only -fblocks -verify %s +// RUN: %clang_cc1 -triple x86_64-windows-msvc -std=gnu23 -fdefer-ts -Wno-redundant-defer -fsyntax-only -fblocks -verify %s typedef void** jmp_buf; typedef void** sigjmp_buf; diff --git a/clang/test/Sema/defer-ts.c b/clang/test/Sema/defer-ts.c index 95c68fa213eaae..d1fba4128b088c 100644 --- a/clang/test/Sema/defer-ts.c +++ b/clang/test/Sema/defer-ts.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c23 -fdefer-ts -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c23 -fdefer-ts -Wno-redundant-defer -fsyntax-only -verify %s #define defer _Defer >From b8bd97ebc20fe865d3a91bc12185843964b56640 Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 15:01:29 +0000 Subject: [PATCH 4/7] Simplify check for _Defer before break/continue statements --- clang/lib/Sema/SemaStmt.cpp | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 8454f4f2bd8421..00eccea0f5a0e2 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -471,14 +471,10 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, } // Find defer statements that immediately precede a break/continue statement. - std::optional<SourceLocation> DeferLoc = std::nullopt; - for (unsigned i = 0; i != NumElts; ++i) { - if (DeferLoc && isa<BreakStmt, ContinueStmt>(Elts[i])) - Diag(DeferLoc.value(), diag::warn_redundant_defer) + for (unsigned i = 0; i != NumElts - 1; ++i) { + if (isa<DeferStmt>(Elts[i]) && isa<BreakStmt, ContinueStmt>(Elts[i+1])) + Diag(Elts[i]->getBeginLoc(), diag::warn_redundant_defer) << Elts[i]->getSourceRange(); - DeferLoc = isa<DeferStmt>(Elts[i]) - ? std::optional<SourceLocation>(Elts[i]->getBeginLoc()) - : std::nullopt; } // Check for defer as last statement. >From bca3b93e62a75a5969b576ce8c7b8fa5bd2c8dff Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 15:14:50 +0000 Subject: [PATCH 5/7] Add redundant _Defer check for void return statements --- clang/lib/Sema/SemaStmt.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp index 00eccea0f5a0e2..204d8a3270b58a 100644 --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -470,11 +470,19 @@ StmtResult Sema::ActOnCompoundStmt(SourceLocation L, SourceLocation R, DiagnoseEmptyLoopBody(Elts[i], Elts[i + 1]); } - // Find defer statements that immediately precede a break/continue statement. - for (unsigned i = 0; i != NumElts - 1; ++i) { - if (isa<DeferStmt>(Elts[i]) && isa<BreakStmt, ContinueStmt>(Elts[i+1])) - Diag(Elts[i]->getBeginLoc(), diag::warn_redundant_defer) - << Elts[i]->getSourceRange(); + // Find defer statements that immediately precede a `break`/`continue` + // or a plain `return` statement. + if (NumElts > 1) { + for (unsigned i = 0; i != NumElts - 1; ++i) { + if (!isa<DeferStmt>(Elts[i])) + continue; + + if (isa<BreakStmt, ContinueStmt>(Elts[i + 1]) || + (isa<ReturnStmt>(Elts[i + 1]) && + !cast<ReturnStmt>(Elts[i + 1])->getRetValue())) + Diag(Elts[i]->getBeginLoc(), diag::warn_redundant_defer) + << Elts[i]->getSourceRange(); + } } // Check for defer as last statement. >From 2058a8ed833e5dfc61a34dbe231b5d9dd12c16dd Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 15:44:58 +0000 Subject: [PATCH 6/7] Add -Wredundant-defer tests for break/continue with labels/attributes --- clang/test/Sema/warn-redundant-defer.c | 64 ++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index 2d9b92bbe9b63b..6e158d2c425667 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -71,6 +71,38 @@ void f2() { break; } + for (;;) { + defer {} // OK + f1(); + l1: break; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + l2: break; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + l3: l4: break; + } + + for (;;) { + defer {} // OK + f1(); + [[clang::likely]] break; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + [[clang::likely]] break; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + [[clang::likely]] [[clang::suppress]] break; + } + for (;;) { defer {} // OK f1(); @@ -82,6 +114,38 @@ void f2() { continue; } + for (;;) { + defer {} // OK + f1(); + l5: continue; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + l6: continue; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + l7: l8: continue; + } + + for (;;) { + defer {} // OK + f1(); + [[clang::likely]] continue; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + [[clang::likely]] continue; + } + + for (;;) { + defer {} // expected-warning {{redundant use of defer}} + [[clang::likely]] [[clang::suppress]] continue; + } + while (true) { defer {} // OK f1(); >From 17819b914de3d1a8ffa7a923d5ef27989e305ada Mon Sep 17 00:00:00 2001 From: Expertcoderz <[email protected]> Date: Wed, 23 Sep 2026 15:48:49 +0000 Subject: [PATCH 7/7] Add -Wredundant-defer tests for _Defer before return --- clang/test/Sema/warn-redundant-defer.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/clang/test/Sema/warn-redundant-defer.c b/clang/test/Sema/warn-redundant-defer.c index 6e158d2c425667..61a5a75f2ee5bc 100644 --- a/clang/test/Sema/warn-redundant-defer.c +++ b/clang/test/Sema/warn-redundant-defer.c @@ -213,3 +213,19 @@ void f2() { defer {} // expected-warning {{redundant use of defer}} } + +int f3() { + defer {} // OK + return 0; +} + +void f4() { + defer {} // OK + f1(); + return; +} + +void f5() { + defer {} // expected-warning {{redundant use of defer}} + return; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
