Skip to content

Add check 'cppcoreguidelines-use-enum-class' #138282

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ add_clang_library(clangTidyCppCoreGuidelinesModule STATIC
RvalueReferenceParamNotMovedCheck.cpp
SlicingCheck.cpp
SpecialMemberFunctionsCheck.cpp
UseEnumClassCheck.cpp
VirtualClassDestructorCheck.cpp

LINK_LIBS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
#include "RvalueReferenceParamNotMovedCheck.h"
#include "SlicingCheck.h"
#include "SpecialMemberFunctionsCheck.h"
#include "UseEnumClassCheck.h"
#include "VirtualClassDestructorCheck.h"

namespace clang::tidy {
Expand Down Expand Up @@ -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>(
Expand Down
Original file line number Diff line number Diff line change
@@ -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
40 changes: 40 additions & 0 deletions clang-tools-extra/clang-tidy/cppcoreguidelines/UseEnumClassCheck.h
Original file line number Diff line number Diff line change
@@ -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 {

/// 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
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
6 changes: 6 additions & 0 deletions clang-tools-extra/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,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:`readability-ambiguous-smartptr-reset-call
<clang-tidy/checks/readability/ambiguous-smartptr-reset-call>` check.

Expand Down
Original file line number Diff line number Diff line change
@@ -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`.
1 change: 1 addition & 0 deletions clang-tools-extra/docs/clang-tidy/checks/list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>`,
: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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// RUN: %check_clang_tidy -std=c++11-or-later %s cppcoreguidelines-use-enum-class %t -- \
// RUN: -config="{CheckOptions: {cppcoreguidelines-use-enum-class.IgnoreUnscopedEnumsInClasses: true}}" --

enum E {};
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead

enum class EC {};

struct S {
enum E {};
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
// Ignore unscoped enums in recordDecl
enum class EC {};
};

enum ForwardE : int;
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use 'enum class' instead
enum class ForwardEC : int;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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

enum class EC {};

struct S {
enum E {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
enum class EC {};
};

class C {
enum E {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
enum class EC {};
};

template<class T>
class TC {
enum E {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
enum class EC {};
};

union U {
enum E {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: enum 'E' is unscoped, use 'enum class' instead
enum class EC {};
};

namespace {
enum E {};
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use 'enum class' instead
enum class EC {};
} // namespace

namespace N {
enum E {};
// CHECK-MESSAGES: :[[@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: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use 'enum class' instead
enum class ForwardEC : int;