tdeo created this revision.
tdeo added a reviewer: sammccall.
tdeo added a project: clang-tools-extra.
Herald added subscribers: cfe-commits, usaxena95, kadircet, arphaman, mgorny.
Herald added a project: clang.
tdeo requested review of this revision.
Herald added subscribers: MaskRay, ilya-biryukov.

Add a tweak that populates an empty switch statement of an enumeration type 
with all of the enumerators of that type.

Before:

  enum Color { RED, GREEN, BLUE };
  void f(Color color) {
    switch (color) {}
  }

After:

  enum Color { RED, GREEN, BLUE };
  void f(Color color) {
    switch (color) {
    case RED:
    case GREEN:
    case BLUE:
      break;
    }
  }


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D88383

Files:
  clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
  clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp
  clang-tools-extra/clangd/unittests/TweakTests.cpp

Index: clang-tools-extra/clangd/unittests/TweakTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TweakTests.cpp
+++ clang-tools-extra/clangd/unittests/TweakTests.cpp
@@ -2802,6 +2802,96 @@
   }
 }
 
+TWEAK_TEST(PopulateSwitch);
+TEST_F(PopulateSwitchTest, Test) {
+  struct Case {
+    CodeContext Context;
+    llvm::StringRef TestSource;
+    llvm::StringRef ExpectedSource;
+  };
+
+  Case Cases[]{
+      {
+          // No enumerators
+          Function,
+          R""(enum Enum {}; ^switch ((Enum)0) {})"",
+          "unavailable",
+      },
+      {
+          // Existing enumerators in switch
+          Function,
+          R""(enum Enum {A}; ^switch ((Enum)0) {case A:break;})"",
+          "unavailable",
+      },
+      {
+          // Body not CompoundStmt
+          Function,
+          R""(enum Enum {A}; ^switch (A);)"",
+          "unavailable",
+      },
+      {
+          // Selection on switch token
+          Function,
+          R""(enum Enum {A}; ^switch (A) {})"",
+          R""(enum Enum {A}; switch (A) {case A:break;})"",
+      },
+      {
+          // Selection on switch condition
+          Function,
+          R""(enum Enum {A}; switch (^A) {})"",
+          R""(enum Enum {A}; switch (A) {case A:break;})"",
+      },
+      {
+          // Selection in switch body
+          Function,
+          R""(enum Enum {A}; switch (A) {^})"",
+          R""(enum Enum {A}; switch (A) {case A:break;})"",
+      },
+      {
+          // Scoped enumeration
+          Function,
+          R""(enum class Enum {A}; ^switch (Enum::A) {})"",
+          R""(enum class Enum {A}; switch (Enum::A) {case Enum::A:break;})"",
+      },
+      {
+          // Scoped enumeration with multiple enumerators
+          Function,
+          R""(enum class Enum {A,B}; ^switch (Enum::A) {})"",
+          R""(enum class Enum {A,B}; )""
+          R""(switch (Enum::A) {case Enum::A:case Enum::B:break;})"",
+      },
+      {
+          // Scoped enumerations in namespace
+          File,
+          R""(
+            namespace ns { enum class Enum {A}; }
+            void function() { ^switch (ns::Enum::A) {} }
+          )"",
+          R""(
+            namespace ns { enum class Enum {A}; }
+            void function() { switch (ns::Enum::A) {case ns::Enum::A:break;} }
+          )"",
+      },
+      {
+          // Unscoped enumerations in namespace
+          File,
+          R""(
+            namespace ns { enum Enum {A}; }
+            void function() { ^switch (ns::A) {} }
+          )"",
+          R""(
+            namespace ns { enum Enum {A}; }
+            void function() { switch (ns::A) {case ns::A:break;} }
+          )"",
+      },
+  };
+
+  for (const auto &Case : Cases) {
+    Context = Case.Context;
+    EXPECT_EQ(apply(Case.TestSource), Case.ExpectedSource);
+  }
+}
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp
@@ -0,0 +1,145 @@
+//===--- PopulateSwitch.cpp --------------------------------------*- 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Tweak that populates an empty switch statement of an enumeration type with
+// all of the enumerators of that type.
+//
+// Before:
+//   enum Color { RED, GREEN, BLUE };
+//
+//   void f(Color color) {
+//     switch (color) {}
+//   }
+//
+// After:
+//   enum Color { RED, GREEN, BLUE };
+//
+//   void f(Color color) {
+//     switch (color) {
+//     case RED:
+//     case GREEN:
+//     case BLUE:
+//       break;
+//     }
+//   }
+//
+//===----------------------------------------------------------------------===//
+
+#include "AST.h"
+#include "Selection.h"
+#include "refactor/Tweak.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Tooling/Core/Replacement.h"
+#include <string>
+
+namespace clang {
+namespace clangd {
+namespace {
+class PopulateSwitch : public Tweak {
+  const char *id() const override;
+  bool prepare(const Selection &Sel) override;
+  Expected<Effect> apply(const Selection &Sel) override;
+  std::string title() const override { return "Populate switch"; }
+  Intent intent() const override { return Refactor; }
+
+private:
+  ASTContext *ASTCtx = nullptr;
+  const DeclContext *DeclCtx = nullptr;
+  const SwitchStmt *Switch = nullptr;
+  const CompoundStmt *Body = nullptr;
+  const EnumType *EnumT = nullptr;
+  const EnumDecl *EnumD = nullptr;
+};
+
+REGISTER_TWEAK(PopulateSwitch)
+
+bool PopulateSwitch::prepare(const Selection &Sel) {
+  ASTCtx = &Sel.AST->getASTContext();
+
+  const SelectionTree::Node *CA = Sel.ASTSelection.commonAncestor();
+  if (!CA)
+    return false;
+
+  const Stmt *CAStmt = CA->ASTNode.get<Stmt>();
+  if (!CAStmt)
+    return false;
+
+  // Go up a level if we see a compound statement.
+  // switch (value) {}
+  //                ^^
+  if (isa<CompoundStmt>(CAStmt)) {
+    CA = CA->Parent;
+    if (!CA)
+      return false;
+
+    CAStmt = CA->ASTNode.get<Stmt>();
+    if (!CAStmt)
+      return false;
+  }
+
+  DeclCtx = &CA->getDeclContext();
+  Switch = dyn_cast<SwitchStmt>(CAStmt);
+  if (!Switch)
+    return false;
+
+  // Since we currently always insert all enumerators,
+  // don't suggest this tweak if there are already cases in the switch.
+  if (Switch->getSwitchCaseList())
+    return false;
+
+  Body = dyn_cast<CompoundStmt>(Switch->getBody());
+  if (!Body)
+    return false;
+
+  // Ignore implicit casts, since enums implicitly cast to integer types.
+  EnumT = Switch->getCond()
+              ->IgnoreParenImpCasts()
+              ->getType()
+              ->getAsAdjusted<EnumType>();
+  if (!EnumT)
+    return false;
+
+  EnumD = EnumT->getDecl();
+  if (!EnumD)
+    return false;
+
+  // If there aren't any enumerators, there's nothing to insert.
+  if (EnumD->enumerator_begin() == EnumD->enumerator_end())
+    return false;
+
+  return true;
+}
+
+Expected<Tweak::Effect> PopulateSwitch::apply(const Selection &Sel) {
+  const SourceManager &SM = ASTCtx->getSourceManager();
+  SourceLocation Loc = Body->getLBracLoc().getLocWithOffset(1);
+
+  std::string Text;
+  for (EnumConstantDecl *Enumerator : EnumD->enumerators()) {
+    Text += "case ";
+    Text += getQualification(*ASTCtx, DeclCtx, Loc, EnumD);
+    if (EnumD->isScoped()) {
+      Text += EnumD->getName();
+      Text += "::";
+    }
+    Text += Enumerator->getName();
+    Text += ":";
+  }
+  Text += "break;";
+
+  return Effect::mainFileEdit(
+      SM, tooling::Replacements(tooling::Replacement(SM, Loc, 0, Text)));
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -22,6 +22,7 @@
   ExtractFunction.cpp
   ExtractVariable.cpp
   ObjCLocalizeStringLiteral.cpp
+  PopulateSwitch.cpp
   RawStringLiteral.cpp
   RemoveUsingNamespace.cpp
   SwapIfBranches.cpp
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to