-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-tidy] add new check readability-enum-initial-value #86129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[clang-tidy] add new check readability-enum-initial-value #86129
Conversation
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang-tidy Author: Congcong Cai (HerrCai0907) ChangesFixes: #85243. Full diff: https://github.com/llvm/llvm-project/pull/86129.diff 9 Files Affected:
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,
+};
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Few things to consider.
Overall looks +- fine.
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst
Outdated
Show resolved
Hide resolved
clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.cpp
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just nits and a comment regarding the linear, all initialized enum. Also, all other checks use west-const from what I can tell.
clang-tools-extra/test/clang-tidy/checkers/readability/enum-initial-value.c
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks fine.
Some minor style issues.
Try getting rid of findNextTokenSkippingComments, and maybe just jump to begin() + token width
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/readability/enum-initial-value.rst
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
Do you think it is a good idea to use |
It doesn't work for macro. I think the most precise way is to fix it is using |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only have some minor comments, otherwise looks good to me
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/EnumInitialValueCheck.cpp
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For me looks fine (had to check how it behave when coma is in next line).
Some ideas for separate check or future options:
- enforce that enums are sorted by value
- enforce that enums are sorted by name
- enforce that there is no duplicated values (bugprone), there is warning for implicit duplicate, but not explicit.
Fixes: #85243.