[clang-tools-extra] Add check 'modernize-use-enum-class' (PR #138282)
Philipp Jung via cfe-commits
cfe-commits at lists.llvm.org
Fri May 9 08:48:08 PDT 2025
https://github.com/JungPhilipp updated https://github.com/llvm/llvm-project/pull/138282
>From 0567bc8e1168bb409ee759dd5505861a644a8ead Mon Sep 17 00:00:00 2001
From: Philipp Jung <philipp.jung01 at sap.com>
Date: Fri, 2 May 2025 15:22:40 +0200
Subject: [PATCH 1/5] Add check 'modernize-use-enum-class'
Warn on non-class enum definitions as suggested by the Core Guidelines:
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 2 +
.../modernize/UseEnumClassCheck.cpp | 34 +++++++++++
.../clang-tidy/modernize/UseEnumClassCheck.h | 34 +++++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 5 ++
.../docs/clang-tidy/checks/list.rst | 1 +
.../checks/modernize/use-enum-class.rst | 26 +++++++++
.../checkers/modernize/use-enum-class.cpp | 58 +++++++++++++++++++
8 files changed, 161 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index bab1167fb15ff..ea19586b1f08c 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -34,6 +34,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseDefaultMemberInitCheck.cpp
UseDesignatedInitializersCheck.cpp
UseEmplaceCheck.cpp
+ UseEnumClassCheck.cpp
UseEqualsDefaultCheck.cpp
UseEqualsDeleteCheck.cpp
UseIntegerSignComparisonCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index fc46c72982fdc..1f77c9a94d25a 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -35,6 +35,7 @@
#include "UseDefaultMemberInitCheck.h"
#include "UseDesignatedInitializersCheck.h"
#include "UseEmplaceCheck.h"
+#include "UseEnumClassCheck.h"
#include "UseEqualsDefaultCheck.h"
#include "UseEqualsDeleteCheck.h"
#include "UseIntegerSignComparisonCheck.h"
@@ -110,6 +111,7 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
"modernize-use-default-member-init");
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
+ CheckFactories.registerCheck<UseEnumClassCheck>("modernize-use-enum-class");
CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
"modernize-use-equals-delete");
diff --git a/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp
new file mode 100644
index 0000000000000..9fc3614aaf498
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp
@@ -0,0 +1,34 @@
+//===--- 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::modernize {
+
+void UseEnumClassCheck::registerMatchers(MatchFinder *Finder) {
+ Finder->addMatcher(
+ traverse(TK_AsIs,
+ enumDecl(unless(isScoped()), unless(hasParent(recordDecl()))))
+ .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;
+ diag(UnscopedEnum->getLocation(), "insert 'class'", DiagnosticIDs::Note)
+ << FixItHint::CreateInsertion(UnscopedEnum->getLocation(), "class ");
+}
+
+} // namespace clang::tidy::modernize
diff --git a/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h b/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h
new file mode 100644
index 0000000000000..9cfb2024b9cfd
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h
@@ -0,0 +1,34 @@
+//===--- 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_MODERNIZE_USEENUMCLASSCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEENUMCLASSCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Check for unscoped enums that are not contained in classes/structs.
+/// Suggest to use scoped enums (enum class) instead.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-enum-class.html
+class UseEnumClassCheck : public ClangTidyCheck {
+public:
+ UseEnumClassCheck(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 {
+ return LangOpts.CPlusPlus;
+ }
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_ENUM_CLASS_H
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 579fca54924d5..b34a4d3d31b9c 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -136,6 +136,11 @@ New checks
Finds potentially erroneous calls to ``reset`` method on smart pointers when
the pointee type also has a ``reset`` method.
+- New :doc:`modernize-use-enum-class
+ <clang-tidy/checks/modernize/use-enum-class>` check.
+
+ Finds plain non-class enum definitions that could use ``enum class``.
+
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 18f1467285fab..c358419efd7b9 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -302,6 +302,7 @@ Clang-Tidy Checks
:doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
:doc:`modernize-use-emplace <modernize/use-emplace>`, "Yes"
+ :doc:`modernize-use-enum-class <modernize/use-enum-class>`, "Yes"
:doc:`modernize-use-equals-default <modernize/use-equals-default>`, "Yes"
:doc:`modernize-use-equals-delete <modernize/use-equals-delete>`, "Yes"
:doc:`modernize-use-integer-sign-comparison <modernize/use-integer-sign-comparison>`, "Yes"
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst
new file mode 100644
index 0000000000000..3adb6e204ad92
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst
@@ -0,0 +1,26 @@
+.. title:: clang-tidy - modernize-use-enum-class
+
+modernize-use-enum-class
+=============================
+
+Scoped enums (enum class) should be preferred over unscoped enums:
+https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class
+
+Unscoped enums in classes are not reported since it is a well
+established pattern to limit the scope of plain enums.
+
+Example:
+
+.. code-block:: c++
+
+ enum E {}; // use "enum class E {};" instead
+ enum class E {}; // OK
+
+ struct S {
+ enum E {}; // OK, scope already limited
+ };
+
+ namespace N {
+ enum E {}; // use "enum class E {};" instead
+ // report since it is hard to detect how large the surrounding namespace is
+ }
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp
new file mode 100644
index 0000000000000..9712fbc0aac4a
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp
@@ -0,0 +1,58 @@
+// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-enum-class %t
+
+enum E {};
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+
+enum class EC {};
+
+struct S {
+ enum E {};
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // Ignore unscoped enums in recordDecl
+ enum class EC {};
+};
+
+class C {
+ enum E {};
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // Ignore unscoped enums in recordDecl
+ enum class EC {};
+};
+
+template<class T>
+class TC {
+ enum E {};
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // Ignore unscoped enums in recordDecl
+ enum class EC {};
+};
+
+union U {
+ enum E {};
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // Ignore unscoped enums in recordDecl
+ enum class EC {};
+};
+
+namespace {
+enum E {};
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+enum class EC {};
+} // namespace
+
+namespace N {
+enum E {};
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+enum class EC {};
+} // namespace N
+
+template<enum ::EC>
+static void foo();
+
+using enum S::E;
+using enum S::EC;
+
+enum ForwardE : int;
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use enum class instead [modernize-use-enum-class]
+
+enum class ForwardEC : int;
>From a4afe4aed98c7a38e4e7b0b0592bb2ff6b703f00 Mon Sep 17 00:00:00 2001
From: Philipp Jung <philipp.jung01 at sap.com>
Date: Fri, 9 May 2025 09:56:01 +0200
Subject: [PATCH 2/5] Move check to cppcoreguidelines- section
---
.../cppcoreguidelines/CMakeLists.txt | 1 +
.../CppCoreGuidelinesTidyModule.cpp | 3 +++
.../UseEnumClassCheck.cpp | 4 ++--
.../UseEnumClassCheck.h | 12 ++++++------
.../clang-tidy/modernize/CMakeLists.txt | 1 -
.../modernize/ModernizeTidyModule.cpp | 2 --
clang-tools-extra/docs/ReleaseNotes.rst | 4 ++--
.../use-enum-class.rst | 4 ++--
.../docs/clang-tidy/checks/list.rst | 2 +-
.../use-enum-class.cpp | 18 +++++++++---------
10 files changed, 26 insertions(+), 25 deletions(-)
rename clang-tools-extra/clang-tidy/{modernize => cppcoreguidelines}/UseEnumClassCheck.cpp (92%)
rename clang-tools-extra/clang-tidy/{modernize => cppcoreguidelines}/UseEnumClassCheck.h (70%)
rename clang-tools-extra/docs/clang-tidy/checks/{modernize => cppcoreguidelines}/use-enum-class.rst (87%)
rename clang-tools-extra/test/clang-tidy/checkers/{modernize => cppcoreguidelines}/use-enum-class.cpp (66%)
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 6adef04264347..409ebbf19349e 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 {
@@ -59,6 +60,8 @@ class CppCoreGuidelinesModule : public ClangTidyModule {
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
CheckFactories.registerCheck<AvoidCapturingLambdaCoroutinesCheck>(
"cppcoreguidelines-avoid-capturing-lambda-coroutines");
+ CheckFactories.registerCheck<UseEnumClassCheck>(
+ "cppcoreguidelines-use-enum-class");
CheckFactories.registerCheck<modernize::AvoidCArraysCheck>(
"cppcoreguidelines-avoid-c-arrays");
CheckFactories.registerCheck<AvoidConstOrRefDataMembersCheck>(
diff --git a/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
similarity index 92%
rename from clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp
rename to clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
index 9fc3614aaf498..c64d0d5046a18 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
@@ -11,7 +11,7 @@
using namespace clang::ast_matchers;
-namespace clang::tidy::modernize {
+namespace clang::tidy::cppcoreguidelines {
void UseEnumClassCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
@@ -31,4 +31,4 @@ void UseEnumClassCheck::check(const MatchFinder::MatchResult &Result) {
<< FixItHint::CreateInsertion(UnscopedEnum->getLocation(), "class ");
}
-} // namespace clang::tidy::modernize
+} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
similarity index 70%
rename from clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h
rename to clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
index 9cfb2024b9cfd..136ac7f3f253b 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseEnumClassCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
@@ -6,18 +6,18 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEENUMCLASSCHECK_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEENUMCLASSCHECK_H
+#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::modernize {
+namespace clang::tidy::cppcoreguidelines {
/// Check for unscoped enums that are not contained in classes/structs.
/// Suggest to use scoped enums (enum class) instead.
///
/// For the user-facing documentation see:
-/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-enum-class.html
+/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/use-enum-class.html
class UseEnumClassCheck : public ClangTidyCheck {
public:
UseEnumClassCheck(StringRef Name, ClangTidyContext *Context)
@@ -29,6 +29,6 @@ class UseEnumClassCheck : public ClangTidyCheck {
}
};
-} // namespace clang::tidy::modernize
+} // namespace clang::tidy::cppcoreguidelines
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_ENUM_CLASS_H
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_USEENUMCLASSCHECK_H
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index ea19586b1f08c..bab1167fb15ff 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -34,7 +34,6 @@ add_clang_library(clangTidyModernizeModule STATIC
UseDefaultMemberInitCheck.cpp
UseDesignatedInitializersCheck.cpp
UseEmplaceCheck.cpp
- UseEnumClassCheck.cpp
UseEqualsDefaultCheck.cpp
UseEqualsDeleteCheck.cpp
UseIntegerSignComparisonCheck.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 1f77c9a94d25a..fc46c72982fdc 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -35,7 +35,6 @@
#include "UseDefaultMemberInitCheck.h"
#include "UseDesignatedInitializersCheck.h"
#include "UseEmplaceCheck.h"
-#include "UseEnumClassCheck.h"
#include "UseEqualsDefaultCheck.h"
#include "UseEqualsDeleteCheck.h"
#include "UseIntegerSignComparisonCheck.h"
@@ -111,7 +110,6 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
"modernize-use-default-member-init");
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
- CheckFactories.registerCheck<UseEnumClassCheck>("modernize-use-enum-class");
CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
"modernize-use-equals-delete");
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index b34a4d3d31b9c..603368f9aaf07 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -136,8 +136,8 @@ New checks
Finds potentially erroneous calls to ``reset`` method on smart pointers when
the pointee type also has a ``reset`` method.
-- New :doc:`modernize-use-enum-class
- <clang-tidy/checks/modernize/use-enum-class>` check.
+- New :doc:`cppcoreguidelines-use-enum-class
+ <clang-tidy/checks/cppcoreguidelines/use-enum-class>` check.
Finds plain non-class enum definitions that could use ``enum class``.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst
similarity index 87%
rename from clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst
rename to clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst
index 3adb6e204ad92..01d620a5a795f 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-enum-class.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines/use-enum-class.rst
@@ -1,6 +1,6 @@
-.. title:: clang-tidy - modernize-use-enum-class
+.. title:: clang-tidy - cppcoreguidelines-use-enum-class
-modernize-use-enum-class
+cppcoreguidelines-use-enum-class
=============================
Scoped enums (enum class) should be preferred over unscoped enums:
diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst
index c358419efd7b9..6cb79c1f58c3c 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -211,6 +211,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>`, "Yes"
: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"
@@ -302,7 +303,6 @@ Clang-Tidy Checks
:doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
:doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
:doc:`modernize-use-emplace <modernize/use-emplace>`, "Yes"
- :doc:`modernize-use-enum-class <modernize/use-enum-class>`, "Yes"
:doc:`modernize-use-equals-default <modernize/use-equals-default>`, "Yes"
:doc:`modernize-use-equals-delete <modernize/use-equals-delete>`, "Yes"
:doc:`modernize-use-integer-sign-comparison <modernize/use-integer-sign-comparison>`, "Yes"
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp
similarity index 66%
rename from clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp
rename to clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp
index 9712fbc0aac4a..924ef3d3140ac 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/use-enum-class.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class.cpp
@@ -1,20 +1,20 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-enum-class %t
+// RUN: %check_clang_tidy -std=c++17-or-later %s cppcoreguidelines-use-enum-class %t
enum E {};
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
enum class EC {};
struct S {
enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
// Ignore unscoped enums in recordDecl
enum class EC {};
};
class C {
enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
// Ignore unscoped enums in recordDecl
enum class EC {};
};
@@ -22,27 +22,27 @@ class C {
template<class T>
class TC {
enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
// Ignore unscoped enums in recordDecl
enum class EC {};
};
union U {
enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
// Ignore unscoped enums in recordDecl
enum class EC {};
};
namespace {
enum E {};
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
enum class EC {};
} // namespace
namespace N {
enum E {};
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
enum class EC {};
} // namespace N
@@ -53,6 +53,6 @@ using enum S::E;
using enum S::EC;
enum ForwardE : int;
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use enum class instead [modernize-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
enum class ForwardEC : int;
>From 129b95dd41593dc73cb5fcc69f1af24968c134f0 Mon Sep 17 00:00:00 2001
From: Philipp Jung <philipp.jung01 at sap.com>
Date: Fri, 9 May 2025 14:28:37 +0200
Subject: [PATCH 3/5] Remove traverse call
---
.../clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp | 3 +--
.../clang-tidy/cppcoreguidelines/UseEnumClassCheck.h | 3 +++
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
index c64d0d5046a18..759ff3f437bd9 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
@@ -15,8 +15,7 @@ namespace clang::tidy::cppcoreguidelines {
void UseEnumClassCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
- traverse(TK_AsIs,
- enumDecl(unless(isScoped()), unless(hasParent(recordDecl()))))
+ enumDecl(unless(isScoped()), unless(hasParent(recordDecl())))
.bind("unscoped_enum"),
this);
}
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
index 136ac7f3f253b..445ffaa675f82 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
@@ -27,6 +27,9 @@ class UseEnumClassCheck : public ClangTidyCheck {
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
return LangOpts.CPlusPlus;
}
+ std::optional<TraversalKind> getCheckTraversalKind() const override {
+ return TraversalKind::TK_IgnoreUnlessSpelledInSource;
+ }
};
} // namespace clang::tidy::cppcoreguidelines
>From 723c91371c27ce32a9f0a8717837ea06a6a13988 Mon Sep 17 00:00:00 2001
From: Philipp Jung <philipp.jung01 at sap.com>
Date: Fri, 9 May 2025 16:04:38 +0200
Subject: [PATCH 4/5] Add option to ignore unscoped enums in classes
---
.../cppcoreguidelines/UseEnumClassCheck.cpp | 23 ++++++----
.../cppcoreguidelines/UseEnumClassCheck.h | 9 ++--
...class-ignore-unscoped-enums-in-classes.cpp | 13 ++++++
.../cppcoreguidelines/use-enum-class.cpp | 42 ++++++++-----------
4 files changed, 52 insertions(+), 35 deletions(-)
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class-ignore-unscoped-enums-in-classes.cpp
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
index 759ff3f437bd9..ec7d9237afa3c 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.cpp
@@ -13,21 +13,30 @@ 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) {
- Finder->addMatcher(
- enumDecl(unless(isScoped()), unless(hasParent(recordDecl())))
- .bind("unscoped_enum"),
- this);
+ 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")
+ "enum %0 is unscoped, use 'enum class' instead")
<< UnscopedEnum;
- diag(UnscopedEnum->getLocation(), "insert 'class'", DiagnosticIDs::Note)
- << FixItHint::CreateInsertion(UnscopedEnum->getLocation(), "class ");
}
} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
index 445ffaa675f82..d259171031932 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
@@ -20,16 +20,19 @@ namespace clang::tidy::cppcoreguidelines {
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/use-enum-class.html
class UseEnumClassCheck : public ClangTidyCheck {
public:
- UseEnumClassCheck(StringRef Name, ClangTidyContext *Context)
- : ClangTidyCheck(Name, Context) {}
+ 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.CPlusPlus;
+ return LangOpts.CPlusPlus11;
}
std::optional<TraversalKind> getCheckTraversalKind() const override {
return TraversalKind::TK_IgnoreUnlessSpelledInSource;
}
+
+private:
+ const bool IgnoreUnscopedEnumsInClasses;
};
} // namespace clang::tidy::cppcoreguidelines
diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class-ignore-unscoped-enums-in-classes.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class-ignore-unscoped-enums-in-classes.cpp
new file mode 100644
index 0000000000000..b467e84a07082
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/use-enum-class-ignore-unscoped-enums-in-classes.cpp
@@ -0,0 +1,13 @@
+// RUN: %check_clang_tidy -std=c++11-or-later %s cppcoreguidelines-use-enum-class %t -- -config="{CheckOptions: {cppcoreguidelines-use-enum-class.IgnoreUnscopedEnumsInClasses: true}}" --
+
+enum E {};
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
+
+enum class EC {};
+
+struct S {
+ enum E {};
+ // CHECK-MESSAGES-NOT: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
+ // Ignore unscoped enums in recordDecl
+ enum class EC {};
+};
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
index 924ef3d3140ac..9620bfa28ab40 100644
--- 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
@@ -1,58 +1,50 @@
-// RUN: %check_clang_tidy -std=c++17-or-later %s cppcoreguidelines-use-enum-class %t
+// RUN: %check_clang_tidy -std=c++11-or-later %s cppcoreguidelines-use-enum-class %t
enum E {};
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
enum class EC {};
struct S {
- enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
- // Ignore unscoped enums in recordDecl
- enum class EC {};
+ enum E {};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
+ enum class EC {};
};
class C {
- enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
- // Ignore unscoped enums in recordDecl
- enum class EC {};
+ enum E {};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
+ enum class EC {};
};
template<class T>
class TC {
- enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
- // Ignore unscoped enums in recordDecl
- enum class EC {};
+ enum E {};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
+ enum class EC {};
};
union U {
- enum E {};
- // CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
- // Ignore unscoped enums in recordDecl
- enum class EC {};
+ enum E {};
+ // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
+ enum class EC {};
};
namespace {
enum E {};
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
enum class EC {};
} // namespace
namespace N {
enum E {};
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
enum class EC {};
} // namespace N
template<enum ::EC>
static void foo();
-using enum S::E;
-using enum S::EC;
-
enum ForwardE : int;
-// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use enum class instead [cppcoreguidelines-use-enum-class]
-
+// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use 'enum class' instead [cppcoreguidelines-use-enum-class]
enum class ForwardEC : int;
>From f7463f6a661d9a4721f1776e066ae83a5b5d4d35 Mon Sep 17 00:00:00 2001
From: Philipp Jung <philipp.jung01 at sap.com>
Date: Fri, 9 May 2025 16:07:15 +0200
Subject: [PATCH 5/5] Improve documentation
---
.../cppcoreguidelines/UseEnumClassCheck.h | 4 ++--
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../checks/cppcoreguidelines/use-enum-class.rst | 17 +++++++++++------
3 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
index d259171031932..bfcdebe705627 100644
--- a/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
+++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
@@ -13,8 +13,8 @@
namespace clang::tidy::cppcoreguidelines {
-/// Check for unscoped enums that are not contained in classes/structs.
-/// Suggest to use scoped enums (enum class) instead.
+/// Check for unscoped enums and suggest to use scoped enums (enum class).
+/// Optionally, ignore unscoped enums in classes via IgnoreUnscopedEnumsInClasses
///
/// For the user-facing documentation see:
/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines/use-enum-class.html
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 603368f9aaf07..f5136f3e5e171 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -139,7 +139,7 @@ New checks
- New :doc:`cppcoreguidelines-use-enum-class
<clang-tidy/checks/cppcoreguidelines/use-enum-class>` check.
- Finds plain non-class enum definitions that could use ``enum class``.
+ Finds plain non-class ``enum`` definitions that could use ``enum class``.
New check aliases
^^^^^^^^^^^^^^^^^
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
index 01d620a5a795f..3e36f45e0d4cd 100644
--- 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
@@ -3,11 +3,11 @@
cppcoreguidelines-use-enum-class
=============================
-Scoped enums (enum class) should be preferred over unscoped enums:
-https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class
+Finds plain non-class ``enum`` definitions that could use ``enum class``.
-Unscoped enums in classes are not reported since it is a well
-established pattern to limit the scope of plain enums.
+This check implements `Enum.3
+<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class>`_
+from the C++ Core Guidelines."
Example:
@@ -17,10 +17,15 @@ Example:
enum class E {}; // OK
struct S {
- enum E {}; // OK, scope already limited
+ enum E {}; // use "enum class E {};" instead
+ // OK with option IgnoreUnscopedEnumsInClasses
};
namespace N {
enum E {}; // use "enum class E {};" instead
- // report since it is hard to detect how large the surrounding namespace is
}
+
+
+.. option:: IgnoreUnscopedEnumsInClasses
+
+ When `true` (default is `false`), ignores unscoped ``enum`` declarations in classes.
More information about the cfe-commits
mailing list