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;


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to