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