https://github.com/JungPhilipp created https://github.com/llvm/llvm-project/pull/138282
Warn on non-class enum definitions as suggested by the Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class >From 9e2d442e38d2e4ec28c4bbd432bab25aaa6fee51 Mon Sep 17 00:00:00 2001 From: Philipp Jung <philipp.jun...@sap.com> Date: Fri, 2 May 2025 15:22:40 +0200 Subject: [PATCH] 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 4f68c487cac9d..92e6866c5f17b 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 UseDefaultMemberInitCheck.cpp UseDesignatedInitializersCheck.cpp UseEmplaceCheck.cpp + UseEnumClassCheck.cpp UseEqualsDefaultCheck.cpp UseEqualsDeleteCheck.cpp UseNodiscardCheck.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 1860759332063..30e6938dbd7db 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 "UseNodiscardCheck.h" @@ -107,6 +108,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 b4792d749a86c..f39dcecae63f2 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -115,6 +115,11 @@ New checks Gives warnings for tagged unions, where the number of tags is different from the number of data members inside the union. +- 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 e3dfabba8fad1..6533828a96fdf 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -297,6 +297,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-nodiscard <modernize/use-nodiscard>`, "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; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits