diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt @@ -13,6 +13,7 @@ NarrowingConversionsCheck.cpp NoMallocCheck.cpp OwningMemoryCheck.cpp + PreferScopedEnumsOverUnscopedCheck.cpp ProBoundsArrayToPointerDecayCheck.cpp ProBoundsConstantArrayIndexCheck.cpp ProBoundsPointerArithmeticCheck.cpp diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp @@ -22,6 +22,7 @@ #include "NarrowingConversionsCheck.h" #include "NoMallocCheck.h" #include "OwningMemoryCheck.h" +#include "PreferScopedEnumsOverUnscopedCheck.h" #include "ProBoundsArrayToPointerDecayCheck.h" #include "ProBoundsConstantArrayIndexCheck.h" #include "ProBoundsPointerArithmeticCheck.h" @@ -66,6 +67,8 @@ "cppcoreguidelines-non-private-member-variables-in-classes"); CheckFactories.registerCheck( "cppcoreguidelines-owning-memory"); + CheckFactories.registerCheck( + "cppcoreguidelines-prefer-scoped-enums-over-unscoped"); CheckFactories.registerCheck( "cppcoreguidelines-pro-bounds-array-to-pointer-decay"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsOverUnscopedCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsOverUnscopedCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsOverUnscopedCheck.h @@ -0,0 +1,37 @@ +//===--- PreferScopedEnumsOverUnscopedCheck.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_PREFERSCOPEDENUMSOVERUNSCOPEDCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERSCOPEDENUMSOVERUNSCOPEDCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +/// Checks Enum.3: +/// Flags every unscoped enumeration declaration. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums-over-unscoped.html +class PreferScopedEnumsOverUnscopedCheck : public ClangTidyCheck { +public: + PreferScopedEnumsOverUnscopedCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override; + + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERSCOPEDENUMSOVERUNSCOPEDCHECK_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsOverUnscopedCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsOverUnscopedCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsOverUnscopedCheck.cpp @@ -0,0 +1,42 @@ +//===--- PreferScopedEnumsOverUnscopedCheck.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 "PreferScopedEnumsOverUnscopedCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +bool PreferScopedEnumsOverUnscopedCheck::isLanguageVersionSupported( + const LangOptions &LangOpts) const { + return LangOpts.CPlusPlus11; +} + +void PreferScopedEnumsOverUnscopedCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(enumDecl().bind("enumDecls"), this); +} + +void PreferScopedEnumsOverUnscopedCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs("enumDecls"); + if (MatchedDecl->isScoped()) + return; + diag(MatchedDecl->getLocation(), "enumeration %0 is not a scoped enumeration") + << MatchedDecl; + diag(MatchedDecl->getLocation(), "use scoped enumeration instead", + DiagnosticIDs::Note) + << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "class "); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums-over-unscoped.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums-over-unscoped.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums-over-unscoped.rst @@ -0,0 +1,64 @@ +.. title:: clang-tidy - cppcoreguidelines-prefer-scoped-enums-over-unscoped + +cppcoreguidelines-prefer-scoped-enums-over-unscoped +=================================================== + +Values of unscoped enumerations are implicitly-convertible to integral types. +To avoid such unwanted conversions, use scoped enumerations. + +This check implements +`Enum.3 `_ +from the CppCoreGuidelines. + +Example: + +.. code-block:: c++ + + enum Foo { + A, + B + } + +After the fix is applied, the enum will become: + +.. code-block:: c++ + + enum class Foo { + A, + B + } + +Note: Although ``enum struct`` and ``enum class`` are exactly equivalent, the +latter is used mainly. + +Limitations +=========== + +If the enumeration is generated via a macro and the enumeration name is +received as a parameter, then the suggested fix would append the ``class`` +word before the name of enumeration at the macro invocation instead of the +macro definition. + +Example: + +.. code-block:: c++ + + #define CREATE_ENUM(Name) \ + enum Name { \ + A, \ + B \ + } + + CREATE_ENUM(Foo); + +After the fix is applied, it will become: + +.. code-block:: c++ + + #define CREATE_ENUM(Name) \ + enum Name { \ + A, \ + B \ + } + + CREATE_ENUM(class Foo); \ No newline at end of file diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -141,6 +141,7 @@ `cppcoreguidelines-narrowing-conversions `_, `cppcoreguidelines-no-malloc `_, `cppcoreguidelines-owning-memory `_, + `cppcoreguidelines-prefer-scoped-enums-over-unscoped `_, "Yes" `cppcoreguidelines-pro-bounds-array-to-pointer-decay `_, `cppcoreguidelines-pro-bounds-constant-array-index `_, "Yes" `cppcoreguidelines-pro-bounds-pointer-arithmetic `_, diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums-over-unscoped.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums-over-unscoped.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums-over-unscoped.cpp @@ -0,0 +1,33 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-prefer-scoped-enums-over-unscoped %t -- + +enum UnscopedEnum { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enumeration 'UnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums-over-unscoped] + UE_FirstValue, + UE_SecondValue, +}; +// CHECK-FIXES: {{^}}enum class UnscopedEnum {{{$}} + +enum UnscopedEnumWithFixedUnderlyingType : char { + // CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enumeration 'UnscopedEnumWithFixedUnderlyingType' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums-over-unscoped] + UEWFUT_FirstValue, + UEWFUT_SecondValue, +}; +// CHECK-FIXES: {{^}}enum class UnscopedEnumWithFixedUnderlyingType : char {{{$}} + +enum OpaqueUnscopedEnum : char; +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: enumeration 'OpaqueUnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums-over-unscoped] +// CHECK-FIXES: {{^}}enum class OpaqueUnscopedEnum : char;{{$}} + +enum class ScopedEnumWithClass { + SEWC_FirstValue, + SEWC_SecondValue, +}; + +enum struct ScopedEnumWithStruct { + SEWC_FirstValue, + SEWC_SecondValue, +}; + +enum class OpaqueScopedEnum; + +enum class OpaqueScopedEnumWithFixedUnderlyingType : unsigned;