https://github.com/davidmenggx updated https://github.com/llvm/llvm-project/pull/209367
>From 72ec4751ef9ffa8e8537adf6d73221b488aaf60c Mon Sep 17 00:00:00 2001 From: David Meng <[email protected]> Date: Mon, 13 Jul 2026 21:09:06 -0700 Subject: [PATCH] [clang-tidy] Add `modernize-redundant-zero-initializer` Add a check that finds explicit single-element zero initializers of arrays and rewrites them to empty braces, e.g. `char a[12] = {0};` becomes `char a[12] = {};`. Empty-brace initialization zero-initializes every element, so the explicit `{0}` is redundant. The check is only enabled in C++ and in C23 or later. The check is conservative and only rewrites a single-element `{0}` list whose sole element is the integer literal `0` and whose array has an explicit bound. It leaves alone, among others: - arrays whose bound is deduced from the initializer (`char a[] = {0};`) - multi-dimensional arrays (`int m[2][3] = {0};`) - initializers with more than one element (`int a[3] = {0, 0};`); - scalars and class/struct types - zero written in another form such as `'\0'`, `0.0` or `nullptr` Closes #209139 --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ModernizeTidyModule.cpp | 3 + .../RedundantZeroInitializerCheck.cpp | 70 +++++++++++ .../modernize/RedundantZeroInitializerCheck.h | 35 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 108 +++++++++++++++++ .../docs/clang-tidy/checks/list.rst | 1 + .../modernize/redundant-zero-initializer.md | 36 ++++++ .../modernize/redundant-zero-initializer.c | 17 +++ .../modernize/redundant-zero-initializer.cpp | 110 ++++++++++++++++++ 9 files changed, 381 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/redundant-zero-initializer.md create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.c create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index 2c5c44db587fe..d348b4db9fb7a 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -24,6 +24,7 @@ add_clang_library(clangTidyModernizeModule STATIC PassByValueCheck.cpp RawStringLiteralCheck.cpp RedundantVoidArgCheck.cpp + RedundantZeroInitializerCheck.cpp ReplaceAutoPtrCheck.cpp ReplaceDisallowCopyAndAssignMacroCheck.cpp ReplaceRandomShuffleCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index cc13da7535bcb..7c9e509f48712 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -24,6 +24,7 @@ #include "PassByValueCheck.h" #include "RawStringLiteralCheck.h" #include "RedundantVoidArgCheck.h" +#include "RedundantZeroInitializerCheck.h" #include "ReplaceAutoPtrCheck.h" #include "ReplaceDisallowCopyAndAssignMacroCheck.h" #include "ReplaceRandomShuffleCheck.h" @@ -106,6 +107,8 @@ class ModernizeModule : public ClangTidyModule { "modernize-raw-string-literal"); CheckFactories.registerCheck<RedundantVoidArgCheck>( "modernize-redundant-void-arg"); + CheckFactories.registerCheck<RedundantZeroInitializerCheck>( + "modernize-redundant-zero-initializer"); CheckFactories.registerCheck<ReplaceAutoPtrCheck>( "modernize-replace-auto-ptr"); CheckFactories.registerCheck<ReplaceDisallowCopyAndAssignMacroCheck>( diff --git a/clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.cpp b/clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.cpp new file mode 100644 index 0000000000000..6850d72b4066c --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.cpp @@ -0,0 +1,70 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RedundantZeroInitializerCheck.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/TypeLoc.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::modernize { + +namespace { +// Matches an explicitly written ``{<single element>}`` list. ``isExplicit()`` +// excludes compiler-synthesized subobject lists (e.g. from brace elision in a +// multi-dimensional array), which are not written with braces in the source. +AST_MATCHER(InitListExpr, isSingleElementBracedList) { + return Node.isExplicit() && Node.getNumInits() == 1; +} + +// Matches an initializer list inside a macro expansion. +AST_MATCHER(InitListExpr, isInMacro) { + return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID(); +} + +// Matches a variable whose array bound is deduced from the initializer +// (``T a[] = ...``); replacing ``{0}`` with ``{}`` there would change the size. +AST_MATCHER(VarDecl, hasDeducedArrayBound) { + const TypeSourceInfo *TSI = Node.getTypeSourceInfo(); + if (!TSI) + return false; + const auto ATL = TSI->getTypeLoc().getAs<ArrayTypeLoc>(); + return ATL && !ATL.getSizeExpr(); +} +} // namespace + +void RedundantZeroInitializerCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + initListExpr( + hasType(constantArrayType()), isSingleElementBracedList(), + hasInit(0, ignoringParenImpCasts(integerLiteral(equals(0)))), + unless(isInMacro()), + // In a dependent pattern the element type is unknown, so `{}` may not + // be equivalent to `{0}`. An instantiation shares the pattern's + // written braces, so rewriting one could break a sibling. + unless(isInstantiationDependent()), + unless(isInTemplateInstantiation()), + // A deduced array bound (`T a[] = {0}`) would change size under `{}`. + unless(hasParent(varDecl(hasDeducedArrayBound())))) + .bind("init"), + this); +} + +void RedundantZeroInitializerCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *ILE = Result.Nodes.getNodeAs<InitListExpr>("init"); + const SourceRange Range = ILE->getSourceRange(); + diag(Range.getBegin(), + "redundant zero initializer; replace with empty braces") + << FixItHint::CreateReplacement(Range, "{}"); +} + +} // namespace clang::tidy::modernize diff --git a/clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.h b/clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.h new file mode 100644 index 0000000000000..2a363dc1141ff --- /dev/null +++ b/clang-tools-extra/clang-tidy/modernize/RedundantZeroInitializerCheck.h @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANTZEROINITIALIZERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANTZEROINITIALIZERCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::modernize { + +/// Finds explicit zero initializers of arrays that can be replaced with empty +/// braces, e.g. ``char a[12] = {0};`` becomes ``char a[12] = {};``. +/// +/// For the user-facing documentation see: +/// https://clang.llvm.org/extra/clang-tidy/checks/modernize/redundant-zero-initializer.html +class RedundantZeroInitializerCheck : public ClangTidyCheck { +public: + RedundantZeroInitializerCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + // Empty braces are only valid in C++ and, for C, since C23. + return LangOpts.CPlusPlus || LangOpts.C23; + } +}; + +} // namespace clang::tidy::modernize + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_REDUNDANTZEROINITIALIZERCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 69c3bcf67b8db..2753f44b37c87 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -97,6 +97,114 @@ Improvements to clang-tidy New checks ^^^^^^^^^^ +- New :doc:`bugprone-assignment-in-selection-statement + <clang-tidy/checks/bugprone/assignment-in-selection-statement>` check. + + Finds assignments within selection statements. + +- New :doc:`bugprone-missing-end-comparison + <clang-tidy/checks/bugprone/missing-end-comparison>` check. + + Finds instances where the result of a standard algorithm is used in a Boolean + context without being compared to the end iterator. + +- New :doc:`bugprone-unsafe-to-allow-exceptions + <clang-tidy/checks/bugprone/unsafe-to-allow-exceptions>` check. + + Finds functions where throwing exceptions is unsafe but the function is still + marked as potentially throwing. + +- New :doc:`llvm-formatv-string + <clang-tidy/checks/llvm/formatv-string>` check. + + Validates ``llvm::formatv`` format strings against the provided arguments, + diagnosing mismatched argument counts, unused arguments, and mixed index styles. + +- New :doc:`llvm-redundant-casting + <clang-tidy/checks/llvm/redundant-casting>` check. + + Points out uses of ``cast<>``, ``dyn_cast<>`` and their ``or_null`` variants + that are unnecessary because the argument already is of the target type, or a + derived type thereof. Also does similar analysis for calls to ``isa<>`` that + always return ``true``. + +- New :doc:`llvm-type-switch-case-types + <clang-tidy/checks/llvm/type-switch-case-types>` check. + + Finds ``llvm::TypeSwitch::Case`` calls with redundant explicit template + arguments that can be inferred from the lambda parameter type. + +- New :doc:`llvm-use-vector-utils + <clang-tidy/checks/llvm/use-vector-utils>` check. + + Finds calls to ``llvm::to_vector(llvm::map_range(...))`` and + ``llvm::to_vector(llvm::make_filter_range(...))`` that can be replaced with + ``llvm::map_to_vector`` and ``llvm::filter_to_vector``. + +- New :doc:`misc-static-initialization-cycle + <clang-tidy/checks/misc/static-initialization-cycle>` check. + + Finds cyclical initialization of static variables. + +- New :doc:`modernize-redundant-zero-initializer + <clang-tidy/checks/modernize/redundant-zero-initializer>` check. + + Finds explicit zero initializers of arrays that can be replaced with empty + braces. + +- New :doc:`modernize-use-std-bit + <clang-tidy/checks/modernize/use-std-bit>` check. + + Finds common idioms which can be replaced by standard functions from the + ``<bit>`` C++20 header. + +- New :doc:`modernize-use-string-view + <clang-tidy/checks/modernize/use-string-view>` check. + + Looks for functions returning ``std::[w|u8|u16|u32]string`` and suggests to + change it to ``std::[...]string_view`` for performance reasons if possible. + +- New :doc:`modernize-use-structured-binding + <clang-tidy/checks/modernize/use-structured-binding>` check. + + Finds places where structured bindings could be used to decompose pairs and + suggests replacing them. + +- New :doc:`performance-string-view-conversions + <clang-tidy/checks/performance/string-view-conversions>` check. + + Finds and removes redundant conversions from ``std::[w|u8|u16|u32]string_view`` to + ``std::[...]string`` in call expressions expecting ``std::[...]string_view``. + +- New :doc:`performance-use-std-move + <clang-tidy/checks/performance/use-std-move>` check. + + Suggests insertion of ``std::move(...)`` to turn copy assignment operator + calls into move assignment ones, when deemed valid and profitable. + +- New :doc:`readability-redundant-lambda-parameter-list + <clang-tidy/checks/readability/redundant-lambda-parameter-list>` check. + + Finds lambda expressions with a redundant empty parameter list and removes it. + +- New :doc:`readability-redundant-nested-if + <clang-tidy/checks/readability/redundant-nested-if>` check. + + Finds nested ``if`` statements that can be merged into a single ``if`` by + combining conditions with ``&&``. + +- New :doc:`readability-redundant-qualified-alias + <clang-tidy/checks/readability/redundant-qualified-alias>` check. + + Finds redundant identity type aliases that re-expose a qualified name and can + be replaced with a ``using`` declaration. + +- New :doc:`readability-trailing-comma + <clang-tidy/checks/readability/trailing-comma>` check. + + Checks for presence or absence of trailing commas in enum definitions and + initializer lists. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 2a44dc78fbc89..477d68fa4684c 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -305,6 +305,7 @@ Clang-Tidy Checks :doc:`modernize-pass-by-value <modernize/pass-by-value>`, "Yes" :doc:`modernize-raw-string-literal <modernize/raw-string-literal>`, "Yes" :doc:`modernize-redundant-void-arg <modernize/redundant-void-arg>`, "Yes" + :doc:`modernize-redundant-zero-initializer <modernize/redundant-zero-initializer>`, "Yes" :doc:`modernize-replace-auto-ptr <modernize/replace-auto-ptr>`, "Yes" :doc:`modernize-replace-disallow-copy-and-assign-macro <modernize/replace-disallow-copy-and-assign-macro>`, "Yes" :doc:`modernize-replace-random-shuffle <modernize/replace-random-shuffle>`, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/redundant-zero-initializer.md b/clang-tools-extra/docs/clang-tidy/checks/modernize/redundant-zero-initializer.md new file mode 100644 index 0000000000000..908da8fd06ee0 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/redundant-zero-initializer.md @@ -0,0 +1,36 @@ +```{title} clang-tidy - modernize-redundant-zero-initializer +``` + +# modernize-redundant-zero-initializer + +Finds explicit zero initializers of arrays that can be replaced with empty +braces. + +In C++ and since C23, an empty braced initializer zero-initializes every element +of an array, so an explicit `{0}` is redundant. + +```cpp +char a[12] = {0}; +int b[5] = {0}; + +// becomes + +char a[12] = {}; +int b[5] = {}; +``` + +The check is only enabled in C++ and in C23 or later. + +## Limitations + +To keep the fix always safe, the check is intentionally conservative and only +handles single-element `{0}` initializers of arrays with an explicit bound. +It does not flag, among others: + +- scalars (`int x = {0};`) and class or struct types (`S s = {0};`); +- arrays whose bound is deduced from the initializer (`char a[] = {0};`), + where `{}` would change the deduced size; +- multi-dimensional arrays (`int m[2][3] = {0};`); +- initializers with more than one element (`int a[3] = {0, 0};`); +- zero written as something other than the integer literal `0`, for example + `'\0'`, `0.0` or `nullptr`. diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.c b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.c new file mode 100644 index 0000000000000..de8908fe5b5bc --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.c @@ -0,0 +1,17 @@ +// RUN: %check_clang_tidy -std=c17 -check-suffixes=C17 %s modernize-redundant-zero-initializer %t +// RUN: %check_clang_tidy -std=c23-or-later -check-suffixes=C23 %s modernize-redundant-zero-initializer %t + +char a[12] = {0}; +// CHECK-MESSAGES-C23: :[[@LINE-1]]:14: warning: redundant zero initializer; replace with empty braces [modernize-redundant-zero-initializer] +// CHECK-FIXES-C23: char a[12] = {}; + +int b[5] = {0}; +// CHECK-MESSAGES-C23: :[[@LINE-1]]:12: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES-C23: int b[5] = {}; + +// Negatives. +char deduced[] = {0}; +int multiZero[3] = {0, 0}; +int mixed[4] = {0, 5}; +int oneByOne[1][1] = {0}; +char nullChar[4] = {'\0'}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.cpp new file mode 100644 index 0000000000000..7e3b241bc1ceb --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/redundant-zero-initializer.cpp @@ -0,0 +1,110 @@ +// RUN: %check_clang_tidy -std=c++14-or-later %s modernize-redundant-zero-initializer %t + +char a[12] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant zero initializer; replace with empty braces [modernize-redundant-zero-initializer] +// CHECK-FIXES: char a[12] = {}; + +int b[5] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: int b[5] = {}; + +double d[3] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: double d[3] = {}; + +void *p[4] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: void *p[4] = {}; + +char one[1] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: char one[1] = {}; + +const char cq[8] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: const char cq[8] = {}; + +int spaced[2] = { 0 }; +// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: int spaced[2] = {}; + +int trailingComma[3] = {0,}; +// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: int trailingComma[3] = {}; + +int paren[2] = {(0)}; +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: int paren[2] = {}; + +double nestedParen[2] = {((0))}; +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: redundant zero initializer; replace with empty braces +// CHECK-FIXES: double nestedParen[2] = {}; + +struct S { + char buf[4] = {0}; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: redundant zero initializer; replace with empty braces + // CHECK-FIXES: char buf[4] = {}; +}; + +void localVariables() { + int local[4] = {0}; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: redundant zero initializer; replace with empty braces + // CHECK-FIXES: int local[4] = {}; + static int staticLocal[2] = {0}; + // CHECK-MESSAGES: :[[@LINE-1]]:31: warning: redundant zero initializer; replace with empty braces + // CHECK-FIXES: static int staticLocal[2] = {}; +} + +// Negatives. + +char emptyBraces[12] = {}; +int nonZero[3] = {1}; +int multiZero[3] = {0, 0}; +int mixed[4] = {0, 5}; +int noInit[3]; + +// Multi-dimensional arrays without inner braces: the single `0` initializes a +// subobject through brace elision, so there is no written single-element `{0}` +// list to rewrite. +int twoD[2][3] = {0}; +int oneByOne[1][1] = {0}; +int rowVector[1][3] = {0}; +int columnVector[3][1] = {0}; + +// The array bound is deduced from the initializer; `{}` would change the size. +char deduced[] = {0}; + +// Zero written in a form other than the integer literal `0`. +char nullChar[4] = {'\0'}; +double zeroDouble[3] = {0.0}; +void *nullPtr[4] = {nullptr}; + +// Not an array. +int scalar = {0}; + +struct P { int x; int y; }; +P pod = {0}; + +// Array of a class type with a default member initializer: `{0}` and `{}` are +// not equivalent, so it must not be rewritten. +struct WithDefault { int v = 7; }; +WithDefault wd[2] = {0}; + +// Template instantiations share the pattern's written braces. Rewriting `{0}` +// in the pattern would break the `templateFn<X>` instantiation, whose element +// type is not default-constructible, so neither the pattern nor any of its +// instantiations may be flagged. +struct X { + X(int); + X() = delete; +}; + +template <class T> +void templateFn() { + T arr[1] = {0}; +} + +void instantiate() { + templateFn<int>(); + templateFn<X>(); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
