https://github.com/davidmenggx updated https://github.com/llvm/llvm-project/pull/209367
>From 141f008aa375fdfe02314a1b5b806f8c5b79d077 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 `readability-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/readability/CMakeLists.txt | 1 + .../readability/ReadabilityTidyModule.cpp | 3 + .../RedundantZeroInitializerCheck.cpp | 65 ++++++++++ .../RedundantZeroInitializerCheck.h | 38 ++++++ clang-tools-extra/docs/ReleaseNotes.rst | 108 +++++++++++++++++ .../docs/clang-tidy/checks/list.rst | 1 + .../readability/redundant-zero-initializer.md | 36 ++++++ .../readability/redundant-zero-initializer.c | 16 +++ .../redundant-zero-initializer.cpp | 114 ++++++++++++++++++ 9 files changed, 382 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/redundant-zero-initializer.md create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/redundant-zero-initializer.c create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/redundant-zero-initializer.cpp diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 8a4b5753de890..11cadf9f39879 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -54,6 +54,7 @@ add_clang_library(clangTidyReadabilityModule STATIC RedundantStringCStrCheck.cpp RedundantStringInitCheck.cpp RedundantTypenameCheck.cpp + RedundantZeroInitializerCheck.cpp ReferenceToConstructedTemporaryCheck.cpp SimplifyBooleanExprCheck.cpp SimplifySubscriptExprCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index 69b31d6711bcd..0592ae5f1bf60 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -56,6 +56,7 @@ #include "RedundantStringCStrCheck.h" #include "RedundantStringInitCheck.h" #include "RedundantTypenameCheck.h" +#include "RedundantZeroInitializerCheck.h" #include "ReferenceToConstructedTemporaryCheck.h" #include "SimplifyBooleanExprCheck.h" #include "SimplifySubscriptExprCheck.h" @@ -160,6 +161,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-redundant-qualified-alias"); CheckFactories.registerCheck<RedundantTypenameCheck>( "readability-redundant-typename"); + CheckFactories.registerCheck<RedundantZeroInitializerCheck>( + "readability-redundant-zero-initializer"); CheckFactories.registerCheck<ReferenceToConstructedTemporaryCheck>( "readability-reference-to-constructed-temporary"); CheckFactories.registerCheck<SimplifySubscriptExprCheck>( diff --git a/clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.cpp b/clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.cpp new file mode 100644 index 0000000000000..7f1700edd2666 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.cpp @@ -0,0 +1,65 @@ +//===----------------------------------------------------------------------===// +// +// 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/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/AST/TypeLoc.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +namespace { +AST_MATCHER(InitListExpr, isSingleElementBracedList) { + return Node.isExplicit() && Node.getNumInits() == 1; +} + +AST_MATCHER(InitListExpr, isInMacro) { + return Node.getBeginLoc().isMacroID() || Node.getEndLoc().isMacroID(); +} + +AST_MATCHER(InitListExpr, hasScalarArrayType) { + const ConstantArrayType *CAT = + Finder->getASTContext().getAsConstantArrayType(Node.getType()); + return CAT && CAT->getElementType()->isScalarType(); +} + +// Matches an array declarator whose bound is deduced from the initializer +// (``T a[]``); replacing ``{0}`` with ``{}`` there would change the size. +AST_MATCHER(TypeLoc, hasDeducedArrayBound) { + const auto ATL = Node.getAs<ArrayTypeLoc>(); + return ATL && ATL.getSizeExpr() == nullptr; +} +} // namespace + +void RedundantZeroInitializerCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher( + initListExpr( + hasScalarArrayType(), isSingleElementBracedList(), + hasInit(0, ignoringParenImpCasts(integerLiteral(equals(0)))), + unless(isInMacro()), + unless(hasParent(varDecl(hasTypeLoc(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::readability diff --git a/clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.h b/clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.h new file mode 100644 index 0000000000000..54906c4e07977 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/RedundantZeroInitializerCheck.h @@ -0,0 +1,38 @@ +//===----------------------------------------------------------------------===// +// +// 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_READABILITY_REDUNDANTZEROINITIALIZERCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTZEROINITIALIZERCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// 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/readability/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; + } + std::optional<TraversalKind> getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTZEROINITIALIZERCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 69c3bcf67b8db..157edac9127fd 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-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-redundant-zero-initializer + <clang-tidy/checks/readability/redundant-zero-initializer>` check. + + Finds explicit zero initializers of arrays that can be replaced with empty + braces. + +- 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..f5bfc586aec0e 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -426,6 +426,7 @@ Clang-Tidy Checks :doc:`readability-redundant-string-cstr <readability/redundant-string-cstr>`, "Yes" :doc:`readability-redundant-string-init <readability/redundant-string-init>`, "Yes" :doc:`readability-redundant-typename <readability/redundant-typename>`, "Yes" + :doc:`readability-redundant-zero-initializer <readability/redundant-zero-initializer>`, "Yes" :doc:`readability-reference-to-constructed-temporary <readability/reference-to-constructed-temporary>`, :doc:`readability-simplify-boolean-expr <readability/simplify-boolean-expr>`, "Yes" :doc:`readability-simplify-subscript-expr <readability/simplify-subscript-expr>`, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-zero-initializer.md b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-zero-initializer.md new file mode 100644 index 0000000000000..88d7b074d27aa --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/redundant-zero-initializer.md @@ -0,0 +1,36 @@ +```{title} clang-tidy - readability-redundant-zero-initializer +``` + +# readability-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/readability/redundant-zero-initializer.c b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-zero-initializer.c new file mode 100644 index 0000000000000..18a72bd4db507 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-zero-initializer.c @@ -0,0 +1,16 @@ +// RUN: %check_clang_tidy -std=c17 -check-suffixes=C17 %s readability-redundant-zero-initializer %t +// RUN: %check_clang_tidy -std=c23-or-later -check-suffixes=C23 %s readability-redundant-zero-initializer %t + +char a[12] = {0}; +// CHECK-MESSAGES-C23: :[[@LINE-1]]:14: warning: redundant zero initializer; replace with empty braces [readability-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] = {}; + +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/readability/redundant-zero-initializer.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-zero-initializer.cpp new file mode 100644 index 0000000000000..b2f6ff9de6682 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/redundant-zero-initializer.cpp @@ -0,0 +1,114 @@ +// RUN: %check_clang_tidy -std=c++14-or-later %s readability-redundant-zero-initializer %t + +char a[12] = {0}; +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: redundant zero initializer; replace with empty braces [readability-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] = {}; +} + +using Arr = int[2]; +void use(const int (&)[2]); +void arrayPrvalue() { + use(Arr{0}); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: redundant zero initializer; replace with empty braces + // CHECK-FIXES: use(Arr{}); +} + +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}; + +char nullChar[4] = {'\0'}; +double zeroDouble[3] = {0.0}; +void *nullPtr[4] = {nullptr}; + +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
