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 + PreferScopedEnumsCheck.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 "PreferScopedEnumsCheck.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"); CheckFactories.registerCheck( "cppcoreguidelines-pro-bounds-array-to-pointer-decay"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.h b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.h new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.h @@ -0,0 +1,56 @@ +//===--- PreferScopedEnumsCheck.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_PREFERSCOPEDENUMSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERSCOPEDENUMSCHECK_H + +#include "../ClangTidyCheck.h" +#include "llvm/ADT/DenseMap.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.html +class PreferScopedEnumsCheck : public ClangTidyCheck { +public: + PreferScopedEnumsCheck(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; + + void onEndOfTranslationUnit() override; + +private: + struct FoundEnumInfo { + bool DeclCanBeFixed{true}; + std::vector Usages{}; + std::vector ForwardDecls{}; + std::vector UsagesPreventFix{}; + std::vector ForwardDeclsPreventFix{}; + std::vector ImplicitCasts{}; + }; + + void handleUnscopedEnum(const EnumDecl *UnscopedEnumDecl); + void handleUnscopedEnumUsage(const EnumDecl *UnscopedEnumDecl, + const DeclRefExpr *EnumValueRef); + + llvm::DenseMap UnscopedEnumInfos; +}; + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERSCOPEDENUMSCHECK_H diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.cpp @@ -0,0 +1,210 @@ +//===--- PreferScopedEnumsCheck.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 "PreferScopedEnumsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace cppcoreguidelines { + +bool PreferScopedEnumsCheck::isLanguageVersionSupported( + const LangOptions &LangOpts) const { + return LangOpts.CPlusPlus11; +} + +void PreferScopedEnumsCheck::registerMatchers(MatchFinder *Finder) { + const auto UnscopedEnumDecl = []() { return enumDecl(unless(isScoped())); }; + + Finder->addMatcher(UnscopedEnumDecl().bind("enumDecl"), this); + + Finder->addMatcher( + declRefExpr(allOf(hasType(UnscopedEnumDecl().bind("declRefTypeDecl")), + hasDeclaration(enumConstantDecl()))) + .bind("declRef"), + this); + + Finder->addMatcher( + implicitCastExpr(allOf(hasSourceExpression(hasType( + UnscopedEnumDecl().bind("sourceEnumDecl"))), + anyOf(hasCastKind(CK_IntegralCast), + hasCastKind(CK_IntegralToBoolean), + hasCastKind(CK_IntegralToFloating)))) + .bind("implicitCast"), + this); +} + +void PreferScopedEnumsCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *MatchedEnumDecl = + Result.Nodes.getNodeAs("enumDecl")) { + handleUnscopedEnum(MatchedEnumDecl); + } else if (const auto *MatchedEnumUsage = + Result.Nodes.getNodeAs("declRef")) { + handleUnscopedEnumUsage(Result.Nodes.getNodeAs("declRefTypeDecl"), + MatchedEnumUsage); + } else if (const auto *MatchedImplicitCast = + Result.Nodes.getNodeAs("implicitCast")) { + const auto *SourceEnumDecl = + Result.Nodes.getNodeAs("sourceEnumDecl"); + assert(UnscopedEnumInfos.count(SourceEnumDecl) == 1 && + "The enum definition or a forward declaration must appear before an " + "implicit cast can happen'"); + UnscopedEnumInfos[SourceEnumDecl].ImplicitCasts.push_back( + MatchedImplicitCast); + } +} + +void PreferScopedEnumsCheck::onEndOfTranslationUnit() { + const auto DiagWarning = [this](SourceLocation Location, + const EnumDecl *UnscopedEnumDecl) { + return diag(Location, "enumeration %0 is not a scoped enumeration") + << UnscopedEnumDecl; + }; + + const auto DiagDeclWarning = + [&DiagWarning](const EnumDecl *UnscopedEnumDecl) { + return DiagWarning(UnscopedEnumDecl->getLocation(), UnscopedEnumDecl); + }; + + const auto DiagUsageWarning = + [&DiagWarning](const DeclRefExpr *EnumUsage, + const EnumDecl *UnscopedEnumDecl) { + return DiagWarning(EnumUsage->getLocation(), UnscopedEnumDecl); + }; + + const auto CanBeFixed = [](const FoundEnumInfo &EnumInfo) { + return EnumInfo.DeclCanBeFixed && EnumInfo.UsagesPreventFix.empty() && + EnumInfo.ForwardDeclsPreventFix.empty() && + EnumInfo.ImplicitCasts.empty(); + }; + + llvm::SmallString<128> Buffer; + const StringRef ClassInsertion{"class "}; + + for (const auto &EnumDeclAndInfo : UnscopedEnumInfos) { + const auto *UnscopedEnumDecl = EnumDeclAndInfo.first; + const auto &UnscopedEnumInfo = EnumDeclAndInfo.second; + + if (CanBeFixed(UnscopedEnumInfo)) { + DiagDeclWarning(UnscopedEnumDecl) << FixItHint::CreateInsertion( + UnscopedEnumDecl->getLocation(), ClassInsertion); + + for (const auto *ForwardDecl : UnscopedEnumInfo.ForwardDecls) + DiagDeclWarning(ForwardDecl) << FixItHint::CreateInsertion( + ForwardDecl->getLocation(), ClassInsertion); + + const auto FixInsertion = + (UnscopedEnumDecl->getName() + "::").toStringRef(Buffer); + for (const auto *EnumUsage : UnscopedEnumInfo.Usages) { + DiagUsageWarning(EnumUsage, UnscopedEnumDecl) + << FixItHint::CreateInsertion(EnumUsage->getLocation(), + FixInsertion); + } + Buffer.clear(); + } else { + DiagDeclWarning(UnscopedEnumDecl); + + if (!UnscopedEnumInfo.DeclCanBeFixed) { + diag(UnscopedEnumDecl->getLocation(), + "%select{forward declaration|definition}0 in macro prevents %1 " + "from being fixed", + DiagnosticIDs::Note) + << UnscopedEnumDecl->isCompleteDefinition() << UnscopedEnumDecl; + } + + for (const auto *const EnumUsage : UnscopedEnumInfo.Usages) + DiagUsageWarning(EnumUsage, UnscopedEnumDecl); + + for (const auto *const EnumUsage : UnscopedEnumInfo.UsagesPreventFix) { + DiagUsageWarning(EnumUsage, UnscopedEnumDecl); + diag(EnumUsage->getBeginLoc(), + "usage in macro prevents %0 from being fixed", DiagnosticIDs::Note) + << UnscopedEnumDecl; + } + + for (const auto *const ForwardDecl : UnscopedEnumInfo.ForwardDecls) + DiagDeclWarning(ForwardDecl); + + for (const auto *const ForwardDecl : + UnscopedEnumInfo.ForwardDeclsPreventFix) { + DiagDeclWarning(ForwardDecl); + diag(ForwardDecl->getBeginLoc(), + "forward declaration in macro prevents %0 from being fixed", + DiagnosticIDs::Note) + << UnscopedEnumDecl; + } + + for (const auto *const ImplicitCast : UnscopedEnumInfo.ImplicitCasts) { + diag(ImplicitCast->getBeginLoc(), + "implicit cast prevents %0 from being fixed", DiagnosticIDs::Note) + << UnscopedEnumDecl; + } + } + } +} + +void PreferScopedEnumsCheck::handleUnscopedEnum( + const EnumDecl *UnscopedEnumDecl) { + if (UnscopedEnumDecl->isCompleteDefinition()) { + assert( + (UnscopedEnumInfos.count(UnscopedEnumDecl) == 0 || + !UnscopedEnumInfos[UnscopedEnumDecl].ForwardDecls.empty() || + !UnscopedEnumInfos[UnscopedEnumDecl].ForwardDeclsPreventFix.empty()) && + "EnumDecl cannot appear twice without " + "forward declarations"); + UnscopedEnumInfos[UnscopedEnumDecl].DeclCanBeFixed = + UnscopedEnumDecl->getLocation().isFileID(); + } else if (const auto *Definition = UnscopedEnumDecl->getDefinition()) { + auto &EnumInfo = UnscopedEnumInfos[Definition]; + + if (UnscopedEnumDecl->getLocation().isFileID()) + EnumInfo.ForwardDecls.push_back(UnscopedEnumDecl); + else + EnumInfo.ForwardDeclsPreventFix.push_back(UnscopedEnumDecl); + } else { + // Unsatisfied forward declaration + UnscopedEnumInfos[UnscopedEnumDecl].DeclCanBeFixed = + UnscopedEnumDecl->getLocation().isFileID(); + } +} + +void PreferScopedEnumsCheck::handleUnscopedEnumUsage( + const EnumDecl *UnscopedEnumDecl, const DeclRefExpr *EnumValueRef) { + assert(UnscopedEnumInfos.find(UnscopedEnumDecl) != UnscopedEnumInfos.end() && + "The usage must appear after the EnumDecl is handled"); + + const bool NeedsToBeFixed = [UnscopedEnumDecl, EnumValueRef]() { + if (!EnumValueRef->hasQualifier()) + return true; + + auto *const Qualifier = EnumValueRef->getQualifier(); + if (Qualifier->getKind() != NestedNameSpecifier::SpecifierKind::TypeSpec) + return true; + + return UnscopedEnumDecl->getTypeForDecl()->getCanonicalTypeInternal() != + Qualifier->getAsType()->getCanonicalTypeInternal(); + }(); + + if (!NeedsToBeFixed) { + return; + } + + auto &EnumInfo = UnscopedEnumInfos[UnscopedEnumDecl]; + if (EnumValueRef->getLocation().isFileID()) + EnumInfo.Usages.push_back(EnumValueRef); + else + EnumInfo.UsagesPreventFix.push_back(EnumValueRef); +} + +} // namespace cppcoreguidelines +} // namespace tidy +} // namespace clang diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -76,6 +76,12 @@ Added an option `GetConfigPerFile` to support including files which use different naming styles. +- New :doc:`cppcoreguidelines-prefer-scoped-enums + ` check. + + Checks for unscoped enumerations. + + Improvements to include-fixer ----------------------------- diff --git a/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums.rst b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums.rst new file mode 100644 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums.rst @@ -0,0 +1,36 @@ +.. title:: clang-tidy - cppcoreguidelines-prefer-scoped-enums + +cppcoreguidelines-prefer-scoped-enums +=================================================== + +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 + }; + + Foo dummyValue = A; + +After the fix is applied, the ``enum`` will become: + +.. code-block:: c++ + + enum class Foo { + A, + B + }; + + Foo dummyValue = Foo::A; + +Note: Although ``enum struct`` and ``enum class`` are exactly equivalent, the +latter is used mainly. 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 `_, "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.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums.cpp new file mode 100644 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums.cpp @@ -0,0 +1,165 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-prefer-scoped-enums %t -- + +enum ForwardDeclaredUnscopedEnumUnsatisfied : int; +// CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'ForwardDeclaredUnscopedEnumUnsatisfied' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-FIXES: {{^}}enum class ForwardDeclaredUnscopedEnumUnsatisfied : int;{{$}} + +enum ForwardDeclaredUnscopedEnum : int; +// CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'ForwardDeclaredUnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-FIXES: {{^}}enum class ForwardDeclaredUnscopedEnum : int;{{$}} + +enum ForwardDeclaredUnscopedEnum : int { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'ForwardDeclaredUnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + // CHECK-FIXES: {{^}}enum class ForwardDeclaredUnscopedEnum : int {{{$}} + FWUE_FirstValue, + FWUE_SecondValue, +}; + +const auto unqualifiedFWUE = FWUE_FirstValue; +// CHECK-NOTES: :[[@LINE-1]]:30: warning: enumeration 'ForwardDeclaredUnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-FIXES: {{^}}const auto unqualifiedFWUE = ForwardDeclaredUnscopedEnum::FWUE_FirstValue;{{$}} + +const auto qualifiedFWUE = ForwardDeclaredUnscopedEnum::FWUE_SecondValue; + +using AliasedFDUE = ForwardDeclaredUnscopedEnum; +const auto qualifiedAFWUE = AliasedFDUE::FWUE_SecondValue; + +enum UnscopedEnum { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + // CHECK-FIXES: {{^}}enum class UnscopedEnum {{{$}} + UE_FirstValue, + UE_SecondValue, +}; + +auto qualifiedUE = UnscopedEnum::UE_FirstValue; + +auto unqualifiedUE = UE_FirstValue; +// CHECK-NOTES: :[[@LINE-1]]:22: warning: enumeration 'UnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-FIXES: {{^}}auto unqualifiedUE = UnscopedEnum::UE_FirstValue;{{$}} + +enum UnscopedEnumWithFixedUnderlyingType : char { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnscopedEnumWithFixedUnderlyingType' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + UEWFUT_FirstValue, + UEWFUT_SecondValue, +}; +// CHECK-FIXES: {{^}}enum class UnscopedEnumWithFixedUnderlyingType : char {{{$}} + +enum OpaqueUnscopedEnum : char; +// CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'OpaqueUnscopedEnum' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-FIXES: {{^}}enum class OpaqueUnscopedEnum : char;{{$}} + +enum UnfixableBecauseCast { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnfixableBecauseCast' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + UBC_FirstValue, + UBC_SecondValue, +}; + +const int castedUBC = UBC_FirstValue; +// CHECK-NOTES: :[[@LINE-1]]:23: warning: enumeration 'UnfixableBecauseCast' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + +const auto binaryOrUBC = UBC_FirstValue | UBC_SecondValue; +// CHECK-NOTES: :[[@LINE-1]]:26: warning: enumeration 'UnfixableBecauseCast' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-2]]:43: warning: enumeration 'UnfixableBecauseCast' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// notes about implicit casts are prompted after all of the warnings for the enums +// CHECK-NOTES: :[[@LINE-7]]:23: note: implicit cast prevents 'UnfixableBecauseCast' from being fixed +// CHECK-NOTES: :[[@LINE-5]]:26: note: implicit cast prevents 'UnfixableBecauseCast' from being fixed +// CHECK-NOTES: :[[@LINE-6]]:43: note: implicit cast prevents 'UnfixableBecauseCast' from being fixed + +enum UnfixableBecauseCastDefaultArgument { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnfixableBecauseCastDefaultArgument' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + UBCDA_FirstValue, + UBCDA_SecondValue, +}; + +void foo(int baz = UBCDA_FirstValue); +// CHECK-NOTES: :[[@LINE-1]]:20: warning: enumeration 'UnfixableBecauseCastDefaultArgument' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-2]]:20: note: implicit cast prevents 'UnfixableBecauseCastDefaultArgument' from being fixed + +enum UnfixableBecauseCastTemplateArgument { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnfixableBecauseCastTemplateArgument' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + UBCTA_FirstValue, + UBCTA_SecondValue, +}; + +template +struct TemplatedStruct {}; + +using FirstAliasType = TemplatedStruct; +// CHECK-NOTES: :[[@LINE-1]]:40: warning: enumeration 'UnfixableBecauseCastTemplateArgument' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-2]]:40: note: implicit cast prevents 'UnfixableBecauseCastTemplateArgument' from being fixed +using SecondAliasType = TemplatedStruct; +// CHECK-NOTES: :[[@LINE-1]]:41: note: implicit cast prevents 'UnfixableBecauseCastTemplateArgument' from being fixed + +enum UnfixableOpaqueBecauseCast : int; +// CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnfixableOpaqueBecauseCast' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + +void foo(UnfixableOpaqueBecauseCast lhs, UnfixableOpaqueBecauseCast rhs) { + const int baz = lhs | rhs; + // CHECK-NOTES: :[[@LINE-1]]:19: note: implicit cast prevents 'UnfixableOpaqueBecauseCast' from being fixed + // CHECK-NOTES: :[[@LINE-2]]:25: note: implicit cast prevents 'UnfixableOpaqueBecauseCast' from being fixed +} + +enum UnfixableBecauseCastWithQualified { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnfixableBecauseCastWithQualified' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + UBCWQ_FirstValue, + UBCWQ_SecondValue, +}; + +const int castedUBCWQ = UnfixableBecauseCastWithQualified::UBCWQ_FirstValue; +// CHECK-NOTES: :[[@LINE-1]]:25: note: implicit cast prevents 'UnfixableBecauseCastWithQualified' from being fixed + +#define FORWARD_DECLARE_UNSCOPED_DEFINED_IN_MACRO enum UnscopedDefinedInMacro : int + +FORWARD_DECLARE_UNSCOPED_DEFINED_IN_MACRO; +// CHECK-NOTES: :[[@LINE-1]]:1: warning: enumeration 'UnscopedDefinedInMacro' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-4]]:56: note: expanded from macro 'FORWARD_DECLARE_UNSCOPED_DEFINED_IN_MACRO' +// CHECK-NOTES: :[[@LINE-3]]:1: note: forward declaration in macro prevents 'UnscopedDefinedInMacro' from being fixed +// CHECK-NOTES: :[[@LINE-6]]:51: note: expanded from macro 'FORWARD_DECLARE_UNSCOPED_DEFINED_IN_MACRO' + +#define FORWARD_DECLARE(EnumName) enum EnumName : int + +FORWARD_DECLARE(UnscopedDefinedInMacro); +// CHECK-NOTES: :[[@LINE-1]]:17: warning: enumeration 'UnscopedDefinedInMacro' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-2]]:1: note: forward declaration in macro prevents 'UnscopedDefinedInMacro' from being fixed +// CHECK-NOTES: :[[@LINE-5]]:35: note: expanded from macro 'FORWARD_DECLARE' + +#define CREATE_ENUM enum UnscopedDefinedInMacro : int { \ + UDIM_FirstValue, \ + UDIM_SecondValue, \ +} + +CREATE_ENUM; +// CHECK-NOTES: :[[@LINE-1]]:1: warning: enumeration 'UnscopedDefinedInMacro' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-7]]:26: note: expanded from macro 'CREATE_ENUM' +// CHECK-NOTES: :[[@LINE-3]]:1: note: definition in macro prevents 'UnscopedDefinedInMacro' from being fixed +// CHECK-NOTES: :[[@LINE-9]]:26: note: expanded from macro 'CREATE_ENUM' + +enum UnscopedUsedInMacro { + // CHECK-NOTES: :[[@LINE-1]]:6: warning: enumeration 'UnscopedUsedInMacro' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] + UUIM_FirstValue, + UUIM_SecondValue, +}; + +#define CREATE_VARIABLE(Name, Value) const auto Name = Value; + +CREATE_VARIABLE(UsageInMacro, UUIM_FirstValue); +// CHECK-NOTES: :[[@LINE-1]]:31: warning: enumeration 'UnscopedUsedInMacro' is not a scoped enumeration [cppcoreguidelines-prefer-scoped-enums] +// CHECK-NOTES: :[[@LINE-2]]:31: note: usage in macro prevents 'UnscopedUsedInMacro' from being fixed + +enum class ScopedEnumWithClass { + SEWC_FirstValue, + SEWC_SecondValue, +}; + +auto qualifiedSEWC = ScopedEnumWithClass::SEWC_FirstValue; + +enum struct ScopedEnumWithStruct { + SEWS_FirstValue, + SEWS_SecondValue, +}; + +auto qualifiedSEWS = ScopedEnumWithStruct::SEWS_FirstValue; + +enum class OpaqueScopedEnum; + +enum class OpaqueScopedEnumWithFixedUnderlyingType : unsigned;