[clang-tools-extra] 669627d - Add check 'cppcoreguidelines-use-enum-class' (#138282)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 18 02:02:57 PDT 2025
Author: Philipp Jung
Date: 2025-06-18T11:02:53+02:00
New Revision: 669627d0c77ed8408358bc8c5973255fe28a36ea
URL: https://github.com/llvm/llvm-project/commit/669627d0c77ed8408358bc8c5973255fe28a36ea
DIFF: https://github.com/llvm/llvm-project/commit/669627d0c77ed8408358bc8c5973255fe28a36ea.diff
LOG: Add check 'cppcoreguidelines-use-enum-class' (#138282)
Warn on non-class enum definitions as suggested by the Core Guidelines:
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class
Added:
clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp
Modified:
clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
Removed:
################################################################################
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
index b023f76a25432..2fb4d7f1d7349 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
@@ -33,6 +33,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule STATIC
RvalueReferenceParamNotMovedCheck.cpp
SlicingCheck.cpp
SpecialMemberFunctionsCheck.cpp
+ UseEnumClassCheck.cpp
VirtualClassDestructorCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
index 4dd9b0904f075..4b3b7bf963fdc 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
@@ -48,6 +48,7 @@
#include "RvalueReferenceParamNotMovedCheck.h"
#include "SlicingCheck.h"
#include "SpecialMemberFunctionsCheck.h"
+#include "UseEnumClassCheck.h"
#include "VirtualClassDestructorCheck.h"
namespace clang::tidy {
@@ -131,6 +132,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
CheckFactories.registerCheck<SlicingCheck>("cppcoreguidelines-slicing");
CheckFactories.registerCheck<modernize::UseDefaultMemberInitCheck>(
"cppcoreguidelines-use-default-member-init");
+ CheckFactories.registerCheck<UseEnumClassCheck>(
+ "cppcoreguidelines-use-enum-class");
CheckFactories.registerCheck<misc::UnconventionalAssignOperatorCheck>(
"cppcoreguidelines-c-copy-assignment-signature");
CheckFactories.registerCheck<VirtualClassDestructorCheck>(
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
new file mode 100644
index 0000000000000..ec7d9237afa3c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
@@ -0,0 +1,42 @@
+//===--- UseEnumClassCheck.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 "UseEnumClassCheck.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::cppcoreguidelines {
+
+UseEnumClassCheck::UseEnumClassCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context),
+ IgnoreUnscopedEnumsInClasses(
+ Options.get("IgnoreUnscopedEnumsInClasses", false)) {}
+
+void UseEnumClassCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
+ Options.store(Opts, "IgnoreUnscopedEnumsInClasses",
+ IgnoreUnscopedEnumsInClasses);
+}
+
+void UseEnumClassCheck::registerMatchers(MatchFinder *Finder) {
+ auto EnumDecl =
+ IgnoreUnscopedEnumsInClasses
+ ? enumDecl(unless(isScoped()), unless(hasParent(recordDecl())))
+ : enumDecl(unless(isScoped()));
+ Finder->addMatcher(EnumDecl.bind("unscoped_enum"), this);
+}
+
+void UseEnumClassCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *UnscopedEnum = Result.Nodes.getNodeAs<EnumDecl>("unscoped_enum");
+
+ diag(UnscopedEnum->getLocation(),
+ "enum %0 is unscoped, use 'enum class' instead")
+ << UnscopedEnum;
+}
+
+} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
new file mode 100644
index 0000000000000..dfa4b7e3fda62
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
@@ -0,0 +1,40 @@
+//===--- UseEnumClassCheck.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_CPPCOREGUIDELINES_USEENUMCLASSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_USEENUMCLASSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::cppcoreguidelines {
+
+/// Finds unscoped (non-class) enum declarations and suggests using enum class
+/// instead.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/use-enum-class.html
+class UseEnumClassCheck : public ClangTidyCheck {
+public:
+ UseEnumClassCheck(StringRef Name, ClangTidyContext *Context);
+ void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+ bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
+ return LangOpts.CPlusPlus11;
+ }
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TraversalKind::TK_IgnoreUnlessSpelledInSource;
+ }
+
+private:
+ const bool IgnoreUnscopedEnumsInClasses;
+};
+
+} // namespace clang::tidy::cppcoreguidelines
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_USEENUMCLASSCHECK_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 3c1ca2f929044..7c0c534dbc738 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -136,6 +136,12 @@ New checks
Finds unintended character output from ``unsigned char`` and ``signed char``
to an ``ostream``.
+- New :doc:`cppcoreguidelines-use-enum-class
+ <clang-tidy/checks/cppcoreguidelines/use-enum-class>` check.
+
+ Finds unscoped (non-class) ``enum`` declarations and suggests using
+ ``enum class`` instead.
+
- New :doc:`portability-avoid-pragma-once
<clang-tidy/checks/portability/avoid-pragma-once>` check.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst
new file mode 100644
index 0000000000000..9e9f4c99dc240
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst
@@ -0,0 +1,35 @@
+.. title:: clang-tidy - cppcoreguidelines-use-enum-class
+
+cppcoreguidelines-use-enum-class
+================================
+
+Finds unscoped (non-class) ``enum`` declarations and suggests using
+``enum class`` instead.
+
+This check implements `Enum.3
+<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class>`_
+from the C++ Core Guidelines."
+
+Example:
+
+.. code-block:: c++
+
+ enum E {}; // use "enum class E {};" instead
+ enum class E {}; // OK
+
+ struct S {
+ enum E {}; // use "enum class E {};" instead
+ // OK with option IgnoreUnscopedEnumsInClasses
+ };
+
+ namespace N {
+ enum E {}; // use "enum class E {};" instead
+ }
+
+Options
+-------
+
+.. option:: IgnoreUnscopedEnumsInClasses
+
+ When `true`, ignores unscoped ``enum`` declarations in classes.
+ Default is `false`.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index 5a79d61b1fd7e..ccb78ee45e9c4 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -212,6 +212,7 @@ Clang-Tidy Checks
:doc:`cppcoreguidelines-rvalue-reference-param-not-moved <cppcoreguidelines/rvalue-reference-param-not-moved>`,
:doc:`cppcoreguidelines-slicing <cppcoreguidelines/slicing>`,
:doc:`cppcoreguidelines-special-member-functions <cppcoreguidelines/special-member-functions>`,
+ :doc:`cppcoreguidelines-use-enum-class <cppcoreguidelines/use-enum-class>`,
:doc:`cppcoreguidelines-virtual-class-destructor <cppcoreguidelines/virtual-class-destructor>`, "Yes"
:doc:`darwin-avoid-spinlock <darwin/avoid-spinlock>`,
:doc:`darwin-dispatch-once-nonstatic <darwin/dispatch-once-nonstatic>`, "Yes"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp
new file mode 100644
index 0000000000000..f53d787f80efa
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp
@@ -0,0 +1,62 @@
+// RUN: %check_clang_tidy -std=c++11-or-later -check-suffix=ALL,DEFAULT %s \
+// RUN: cppcoreguidelines-use-enum-class %t --
+
+// RUN: %check_clang_tidy -std=c++11-or-later -check-suffix=ALL %s \
+// RUN: cppcoreguidelines-use-enum-class %t -- \
+// RUN: -config="{CheckOptions: { \
+// RUN: cppcoreguidelines-use-enum-class.IgnoreUnscopedEnumsInClasses: true \
+// RUN: }}" --
+
+enum E {};
+// CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead
+
+enum class EC {};
+
+enum struct ES {};
+
+struct S {
+ enum E {};
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
+ enum class EC {};
+};
+
+class C {
+ enum E {};
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
+ enum class EC {};
+};
+
+template<class T>
+class TC {
+ enum E {};
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
+ enum class EC {};
+};
+
+union U {
+ enum E {};
+ // CHECK-MESSAGES-DEFAULT: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
+ enum class EC {};
+};
+
+namespace {
+enum E {};
+// CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead
+enum class EC {};
+} // namespace
+
+namespace N {
+enum E {};
+// CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead
+enum class EC {};
+} // namespace N
+
+template<enum ::EC>
+static void foo();
+
+enum ForwardE : int;
+// CHECK-MESSAGES-ALL: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use 'enum class' instead
+
+enum class ForwardEC : int;
+
+enum struct ForwardES : int;
More information about the cfe-commits
mailing list