https://github.com/HerrCai0907 created https://github.com/llvm/llvm-project/pull/86129
Fixes: #85243. >From 4e0845a143a820d4a68ffbdced206654c7593359 Mon Sep 17 00:00:00 2001 From: Congcong Cai <congcongcai0...@163.com> Date: Fri, 15 Mar 2024 08:07:47 +0800 Subject: [PATCH] [clang-tidy] add new check readability-enum-initial-value Fixes: #85243. --- .../clang-tidy/readability/CMakeLists.txt | 1 + .../readability/EnumInitialValueCheck.cpp | 82 +++++++++++++++++++ .../readability/EnumInitialValueCheck.h | 31 +++++++ .../readability/ReadabilityTidyModule.cpp | 3 + clang-tools-extra/docs/ReleaseNotes.rst | 6 ++ .../docs/clang-tidy/checks/list.rst | 1 + .../checks/readability/enum-initial-value.rst | 45 ++++++++++ .../checkers/readability/enum-initial-value.c | 27 ++++++ .../readability/enum-initial-value.cpp | 27 ++++++ 9 files changed, 223 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.c create mode 100644 clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.cpp diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt index 5728c9970fb65d..dd772d69202548 100644 --- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt @@ -17,6 +17,7 @@ add_clang_library(clangTidyReadabilityModule DeleteNullPointerCheck.cpp DuplicateIncludeCheck.cpp ElseAfterReturnCheck.cpp + EnumInitialValueCheck.cpp FunctionCognitiveComplexityCheck.cpp FunctionSizeCheck.cpp IdentifierLengthCheck.cpp diff --git a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp new file mode 100644 index 00000000000000..78d5101d439dde --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp @@ -0,0 +1,82 @@ +//===--- EnumInitialValueCheck.cpp - clang-tidy ---------------------------===// +// +// 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 "EnumInitialValueCheck.h" +#include "../utils/LexerUtils.h" +#include "clang/AST/Decl.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang::ast_matchers; + +namespace clang::tidy::readability { + +namespace { + +AST_MATCHER(EnumDecl, isNoneEnumeratorsInitialized) { + return llvm::all_of(Node.enumerators(), [](EnumConstantDecl const *ECD) { + return ECD->getInitExpr() == nullptr; + }); +} + +AST_MATCHER(EnumDecl, isOnlyFirstEnumeratorsInitialized) { + for (EnumConstantDecl const *ECD : Node.enumerators()) + if (ECD == *Node.enumerator_begin()) { + if (ECD->getInitExpr() == nullptr) + return false; + } else { + if (ECD->getInitExpr() != nullptr) + return false; + } + return true; +} + +AST_MATCHER(EnumDecl, isAllEnumeratorsInitialized) { + return llvm::all_of(Node.enumerators(), [](EnumConstantDecl const *ECD) { + return ECD->getInitExpr() != nullptr; + }); +} + +} // namespace + +void EnumInitialValueCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(enumDecl(unless(anyOf(isNoneEnumeratorsInitialized(), + isOnlyFirstEnumeratorsInitialized(), + isAllEnumeratorsInitialized()))) + .bind("enum"), + this); +} + +void EnumInitialValueCheck::check(const MatchFinder::MatchResult &Result) { + const auto *Enum = Result.Nodes.getNodeAs<EnumDecl>("enum"); + assert(Enum != nullptr); + SourceLocation Loc = Enum->getBeginLoc(); + if (Loc.isInvalid() || Loc.isMacroID()) + return; + DiagnosticBuilder Diag = + diag(Loc, "inital value in enum %0 has readability issue, " + "explicit initialization of all of enumerators") + << Enum->getName(); + for (EnumConstantDecl const *ECD : Enum->enumerators()) + if (ECD->getInitExpr() == nullptr) { + SourceLocation ECDLoc = ECD->getEndLoc(); + if (ECDLoc.isInvalid() || ECDLoc.isMacroID()) + continue; + std::optional<Token> Next = utils::lexer::findNextTokenSkippingComments( + ECDLoc, *Result.SourceManager, getLangOpts()); + if (!Next.has_value() || Next->getLocation().isMacroID()) + continue; + llvm::SmallString<8> Str{" = "}; + ECD->getInitVal().toString(Str); + Diag << FixItHint::CreateInsertion(Next->getLocation(), Str); + } +} + +} // namespace clang::tidy::readability diff --git a/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.h b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.h new file mode 100644 index 00000000000000..6b4e0e28e35be0 --- /dev/null +++ b/clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.h @@ -0,0 +1,31 @@ +//===--- EnumInitialValueCheck.h - clang-tidy -------------------*- C++ -*-===// +// +// 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_ENUMINITIALVALUECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ENUMINITIALVALUECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::readability { + +/// Detects explicit initialization of a part of enumerators in an enumeration, and +/// relying on compiler to initialize the others. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability/enum-initial-value.html +class EnumInitialValueCheck : public ClangTidyCheck { +public: + EnumInitialValueCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::readability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_ENUMINITIALVALUECHECK_H diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp index bca2c425111f6c..376b84683df74e 100644 --- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp @@ -22,6 +22,7 @@ #include "DeleteNullPointerCheck.h" #include "DuplicateIncludeCheck.h" #include "ElseAfterReturnCheck.h" +#include "EnumInitialValueCheck.h" #include "FunctionCognitiveComplexityCheck.h" #include "FunctionSizeCheck.h" #include "IdentifierLengthCheck.h" @@ -92,6 +93,8 @@ class ReadabilityModule : public ClangTidyModule { "readability-duplicate-include"); CheckFactories.registerCheck<ElseAfterReturnCheck>( "readability-else-after-return"); + CheckFactories.registerCheck<EnumInitialValueCheck>( + "readability-enum-initial-value"); CheckFactories.registerCheck<FunctionCognitiveComplexityCheck>( "readability-function-cognitive-complexity"); CheckFactories.registerCheck<FunctionSizeCheck>( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 44680f79de6f54..53ce4acad90d52 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -116,6 +116,12 @@ New checks Finds initializer lists for aggregate types that could be written as designated initializers instead. +- New :doc:`readability-enum-initial-value + <clang-tidy/checks/readability/enum-initial-value>` check. + + Detects explicit initialization of a part of enumerators in an enumeration, and + relying on compiler to initialize the others. + - New :doc:`readability-use-std-min-max <clang-tidy/checks/readability/use-std-min-max>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index d03e7af688f007..727489c8de65f4 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -351,6 +351,7 @@ Clang-Tidy Checks :doc:`readability-delete-null-pointer <readability/delete-null-pointer>`, "Yes" :doc:`readability-duplicate-include <readability/duplicate-include>`, "Yes" :doc:`readability-else-after-return <readability/else-after-return>`, "Yes" + :doc:`readability-enum-initial-value <readability/enum-initial-value>`, "Yes" :doc:`readability-function-cognitive-complexity <readability/function-cognitive-complexity>`, :doc:`readability-function-size <readability/function-size>`, :doc:`readability-identifier-length <readability/identifier-length>`, diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst new file mode 100644 index 00000000000000..f6292a0933aa60 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst @@ -0,0 +1,45 @@ +.. title:: clang-tidy - readability-enum-initial-value + +readability-enum-initial-value +============================== + +Detects explicit initialization of a part of enumerators in an enumeration, and +relying on compiler to initialize the others. + +It causes readability issues and reduces the maintainability. When adding new +enumerations, the developers need to be careful for potiential enumeration value +conflicts. + +In an enumeration, the following three cases are accepted. +1. none of enumerators are explicit initialized. +2. the first enumerator is explicit initialized. +3. all of enumerators are explicit initialized. + +.. code-block:: c++ + // valid, none of enumerators are initialized. + enum A { + e0, + e1, + e2, + }; + + // valid, the first enumerator is initialized. + enum A { + e0 = 0, + e1, + e2, + }; + + // valid, all of enumerators are initialized. + enum A { + e0 = 0, + e1 = 1, + e2 = 2, + }; + + // invalid, e1 is not explicit initialized. + enum A { + e0 = 0, + e1, + e2 = 2, + }; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.c b/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.c new file mode 100644 index 00000000000000..bb8bdf9e709f2a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.c @@ -0,0 +1,27 @@ +// RUN: %check_clang_tidy %s readability-enum-initial-value %t + +enum EError { + // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital value in enum EError has readability issue, explicit initialization of all of enumerators + EError_a = 1, + EError_b, + // CHECK-FIXES: EError_b = 2, + EError_c = 3, +}; + +enum ENone { + ENone_a, + ENone_b, + eENone_c, +}; + +enum EFirst { + EFirst_a = 1, + EFirst_b, + EFirst_c, +}; + +enum EAll { + EAll_a = 1, + EAll_b = 2, + EAll_c = 3, +}; diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.cpp new file mode 100644 index 00000000000000..e95b3a6d8d675a --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.cpp @@ -0,0 +1,27 @@ +// RUN: %check_clang_tidy %s readability-enum-initial-value %t + +enum class EError { + // CHECK-MESSAGES: :[[@LINE-1]]:1: warning: inital value in enum EError has readability issue, explicit initialization of all of enumerators + EError_a = 1, + EError_b, + // CHECK-FIXES: EError_b = 2, + EError_c = 3, +}; + +enum class ENone { + ENone_a, + ENone_b, + eENone_c, +}; + +enum class EFirst { + EFirst_a = 1, + EFirst_b, + EFirst_c, +}; + +enum class EAll { + EAll_a = 1, + EAll_b = 2, + EAll_c = 3, +}; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits