Skip to content

Commit 9e2d442

Browse files
committed
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
1 parent ccca3c6 commit 9e2d442

File tree

8 files changed

+161
-0
lines changed

8 files changed

+161
-0
lines changed

clang-tools-extra/clang-tidy/modernize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ add_clang_library(clangTidyModernizeModule
3434
UseDefaultMemberInitCheck.cpp
3535
UseDesignatedInitializersCheck.cpp
3636
UseEmplaceCheck.cpp
37+
UseEnumClassCheck.cpp
3738
UseEqualsDefaultCheck.cpp
3839
UseEqualsDeleteCheck.cpp
3940
UseNodiscardCheck.cpp

clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "UseDefaultMemberInitCheck.h"
3636
#include "UseDesignatedInitializersCheck.h"
3737
#include "UseEmplaceCheck.h"
38+
#include "UseEnumClassCheck.h"
3839
#include "UseEqualsDefaultCheck.h"
3940
#include "UseEqualsDeleteCheck.h"
4041
#include "UseNodiscardCheck.h"
@@ -107,6 +108,7 @@ class ModernizeModule : public ClangTidyModule {
107108
CheckFactories.registerCheck<UseDefaultMemberInitCheck>(
108109
"modernize-use-default-member-init");
109110
CheckFactories.registerCheck<UseEmplaceCheck>("modernize-use-emplace");
111+
CheckFactories.registerCheck<UseEnumClassCheck>("modernize-use-enum-class");
110112
CheckFactories.registerCheck<UseEqualsDefaultCheck>("modernize-use-equals-default");
111113
CheckFactories.registerCheck<UseEqualsDeleteCheck>(
112114
"modernize-use-equals-delete");
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- UseEnumClassCheck.cpp - clang-tidy -------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "UseEnumClassCheck.h"
10+
#include "clang/ASTMatchers/ASTMatchFinder.h"
11+
12+
using namespace clang::ast_matchers;
13+
14+
namespace clang::tidy::modernize {
15+
16+
void UseEnumClassCheck::registerMatchers(MatchFinder *Finder) {
17+
Finder->addMatcher(
18+
traverse(TK_AsIs,
19+
enumDecl(unless(isScoped()), unless(hasParent(recordDecl()))))
20+
.bind("unscoped_enum"),
21+
this);
22+
}
23+
24+
void UseEnumClassCheck::check(const MatchFinder::MatchResult &Result) {
25+
const auto *UnscopedEnum = Result.Nodes.getNodeAs<EnumDecl>("unscoped_enum");
26+
27+
diag(UnscopedEnum->getLocation(),
28+
"enum %0 is unscoped, use enum class instead")
29+
<< UnscopedEnum;
30+
diag(UnscopedEnum->getLocation(), "insert 'class'", DiagnosticIDs::Note)
31+
<< FixItHint::CreateInsertion(UnscopedEnum->getLocation(), "class ");
32+
}
33+
34+
} // namespace clang::tidy::modernize
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//===--- UseEnumClassCheck.h - clang-tidy -----------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEENUMCLASSCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USEENUMCLASSCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::modernize {
15+
16+
/// Check for unscoped enums that are not contained in classes/structs.
17+
/// Suggest to use scoped enums (enum class) instead.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/use-enum-class.html
21+
class UseEnumClassCheck : public ClangTidyCheck {
22+
public:
23+
UseEnumClassCheck(StringRef Name, ClangTidyContext *Context)
24+
: ClangTidyCheck(Name, Context) {}
25+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
26+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
27+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
28+
return LangOpts.CPlusPlus;
29+
}
30+
};
31+
32+
} // namespace clang::tidy::modernize
33+
34+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USE_ENUM_CLASS_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,11 @@ New checks
115115
Gives warnings for tagged unions, where the number of tags is
116116
different from the number of data members inside the union.
117117

118+
- New :doc:`modernize-use-enum-class
119+
<clang-tidy/checks/modernize/use-enum-class>` check.
120+
121+
Finds plain non-class enum definitions that could use ``enum class``.
122+
118123
New check aliases
119124
^^^^^^^^^^^^^^^^^
120125

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ Clang-Tidy Checks
297297
:doc:`modernize-use-default-member-init <modernize/use-default-member-init>`, "Yes"
298298
:doc:`modernize-use-designated-initializers <modernize/use-designated-initializers>`, "Yes"
299299
:doc:`modernize-use-emplace <modernize/use-emplace>`, "Yes"
300+
:doc:`modernize-use-enum-class <modernize/use-enum-class>`, "Yes"
300301
:doc:`modernize-use-equals-default <modernize/use-equals-default>`, "Yes"
301302
:doc:`modernize-use-equals-delete <modernize/use-equals-delete>`, "Yes"
302303
:doc:`modernize-use-nodiscard <modernize/use-nodiscard>`, "Yes"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.. title:: clang-tidy - modernize-use-enum-class
2+
3+
modernize-use-enum-class
4+
=============================
5+
6+
Scoped enums (enum class) should be preferred over unscoped enums:
7+
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Renum-class
8+
9+
Unscoped enums in classes are not reported since it is a well
10+
established pattern to limit the scope of plain enums.
11+
12+
Example:
13+
14+
.. code-block:: c++
15+
16+
enum E {}; // use "enum class E {};" instead
17+
enum class E {}; // OK
18+
19+
struct S {
20+
enum E {}; // OK, scope already limited
21+
};
22+
23+
namespace N {
24+
enum E {}; // use "enum class E {};" instead
25+
// report since it is hard to detect how large the surrounding namespace is
26+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
// RUN: %check_clang_tidy -std=c++17-or-later %s modernize-use-enum-class %t
2+
3+
enum E {};
4+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
5+
6+
enum class EC {};
7+
8+
struct S {
9+
enum E {};
10+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
11+
// Ignore unscoped enums in recordDecl
12+
enum class EC {};
13+
};
14+
15+
class C {
16+
enum E {};
17+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
18+
// Ignore unscoped enums in recordDecl
19+
enum class EC {};
20+
};
21+
22+
template<class T>
23+
class TC {
24+
enum E {};
25+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
26+
// Ignore unscoped enums in recordDecl
27+
enum class EC {};
28+
};
29+
30+
union U {
31+
enum E {};
32+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:12: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
33+
// Ignore unscoped enums in recordDecl
34+
enum class EC {};
35+
};
36+
37+
namespace {
38+
enum E {};
39+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
40+
enum class EC {};
41+
} // namespace
42+
43+
namespace N {
44+
enum E {};
45+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'E' is unscoped, use enum class instead [modernize-use-enum-class]
46+
enum class EC {};
47+
} // namespace N
48+
49+
template<enum ::EC>
50+
static void foo();
51+
52+
using enum S::E;
53+
using enum S::EC;
54+
55+
enum ForwardE : int;
56+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enum 'ForwardE' is unscoped, use enum class instead [modernize-use-enum-class]
57+
58+
enum class ForwardEC : int;

0 commit comments

Comments
 (0)