janosbenjaminantal updated this revision to Diff 287478.
janosbenjaminantal added a comment.

Fix clang-tidy issues.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D85697/new/

https://reviews.llvm.org/D85697

Files:
  clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
  clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
  clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.cpp
  clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.h
  clang-tools-extra/docs/ReleaseNotes.rst
  
clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines-prefer-scoped-enums.cpp
===================================================================
--- /dev/null
+++ 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 <int Foo>
+struct TemplatedStruct {};
+
+using FirstAliasType = TemplatedStruct<UBCTA_FirstValue>;
+// 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<UnfixableBecauseCastTemplateArgument::UBCTA_SecondValue>;
+// 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;
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -141,6 +141,7 @@
    `cppcoreguidelines-narrowing-conversions <cppcoreguidelines-narrowing-conversions.html>`_,
    `cppcoreguidelines-no-malloc <cppcoreguidelines-no-malloc.html>`_,
    `cppcoreguidelines-owning-memory <cppcoreguidelines-owning-memory.html>`_,
+   `cppcoreguidelines-prefer-scoped-enums <cppcoreguidelines-prefer-scoped-enums.html>`_, "Yes"
    `cppcoreguidelines-pro-bounds-array-to-pointer-decay <cppcoreguidelines-pro-bounds-array-to-pointer-decay.html>`_,
    `cppcoreguidelines-pro-bounds-constant-array-index <cppcoreguidelines-pro-bounds-constant-array-index.html>`_, "Yes"
    `cppcoreguidelines-pro-bounds-pointer-arithmetic <cppcoreguidelines-pro-bounds-pointer-arithmetic.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums.rst
===================================================================
--- /dev/null
+++ 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 <https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Renum-class>`_ 
+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.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ 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
+  <clang-tidy/checks/cppcoreguidelines-prefer-scoped-enums>` check.
+
+  Checks for unscoped enumerations.
+
+
 Improvements to include-fixer
 -----------------------------
 
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.h
===================================================================
--- /dev/null
+++ 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<const DeclRefExpr *> Usages{};
+    std::vector<const EnumDecl *> ForwardDecls{};
+    std::vector<const DeclRefExpr *> UsagesPreventFix{};
+    std::vector<const EnumDecl *> ForwardDeclsPreventFix{};
+    std::vector<const ImplicitCastExpr *> ImplicitCasts{};
+  };
+
+  void handleUnscopedEnum(const EnumDecl *UnscopedEnumDecl);
+  void handleUnscopedEnumUsage(const EnumDecl *UnscopedEnumDecl,
+                               const DeclRefExpr *EnumValueRef);
+
+  llvm::DenseMap<const EnumDecl *, FoundEnumInfo> UnscopedEnumInfos;
+};
+
+} // namespace cppcoreguidelines
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_CPPCOREGUIDELINES_PREFERSCOPEDENUMSCHECK_H
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/PreferScopedEnumsCheck.cpp
===================================================================
--- /dev/null
+++ 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>("enumDecl")) {
+    handleUnscopedEnum(MatchedEnumDecl);
+  } else if (const auto *MatchedEnumUsage =
+                 Result.Nodes.getNodeAs<DeclRefExpr>("declRef")) {
+    handleUnscopedEnumUsage(Result.Nodes.getNodeAs<EnumDecl>("declRefTypeDecl"),
+                            MatchedEnumUsage);
+  } else if (const auto *MatchedImplicitCast =
+                 Result.Nodes.getNodeAs<ImplicitCastExpr>("implicitCast")) {
+    const auto *SourceEnumDecl =
+        Result.Nodes.getNodeAs<EnumDecl>("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
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CppCoreGuidelinesTidyModule.cpp
+++ 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<OwningMemoryCheck>(
         "cppcoreguidelines-owning-memory");
+    CheckFactories.registerCheck<PreferScopedEnumsCheck>(
+        "cppcoreguidelines-prefer-scoped-enums");
     CheckFactories.registerCheck<ProBoundsArrayToPointerDecayCheck>(
         "cppcoreguidelines-pro-bounds-array-to-pointer-decay");
     CheckFactories.registerCheck<ProBoundsConstantArrayIndexCheck>(
Index: clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cppcoreguidelines/CMakeLists.txt
+++ 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D85697: [cla... János Benjamin Antal via Phabricator via cfe-commits
    • [PATCH] D85697:... János Benjamin Antal via Phabricator via cfe-commits
    • [PATCH] D85697:... János Benjamin Antal via Phabricator via cfe-commits

Reply via email to