[clang-tools-extra] 018066d - [clangd] Add a tweak for filling in enumerators of a switch statement.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 28 04:37:44 PDT 2020


Author: Tadeo Kondrak
Date: 2020-09-28T13:37:18+02:00
New Revision: 018066d9475dac8d4b7a91bf967ea9231ff4b3f1

URL: https://github.com/llvm/llvm-project/commit/018066d9475dac8d4b7a91bf967ea9231ff4b3f1
DIFF: https://github.com/llvm/llvm-project/commit/018066d9475dac8d4b7a91bf967ea9231ff4b3f1.diff

LOG: [clangd] Add a tweak for filling in enumerators of a switch statement.

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;
  }
}
```

Reviewed By: sammccall

Differential Revision: https://reviews.llvm.org/D88383

Added: 
    clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp

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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
index 8f708cacfdf8..caa69947c47f 100644
--- a/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
+++ b/clang-tools-extra/clangd/refactor/tweaks/CMakeLists.txt
@@ -22,6 +22,7 @@ add_clang_library(clangDaemonTweaks OBJECT
   ExtractFunction.cpp
   ExtractVariable.cpp
   ObjCLocalizeStringLiteral.cpp
+  PopulateSwitch.cpp
   RawStringLiteral.cpp
   RemoveUsingNamespace.cpp
   SwapIfBranches.cpp

diff  --git a/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp b/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp
new file mode 100644
index 000000000000..e84a420f6218
--- /dev/null
+++ b/clang-tools-extra/clangd/refactor/tweaks/PopulateSwitch.cpp
@@ -0,0 +1,146 @@
+//===--- 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 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;
+
+  Body = dyn_cast<CompoundStmt>(Switch->getBody());
+  if (!Body)
+    return false;
+
+  // Since we currently always insert all enumerators, don't suggest this tweak
+  // if the body is not empty.
+  if (!Body->body_empty())
+    return false;
+
+  const Expr *Cond = Switch->getCond();
+  if (!Cond)
+    return false;
+
+  // Ignore implicit casts, since enums implicitly cast to integer types.
+  Cond = Cond->IgnoreParenImpCasts();
+
+  const EnumType *EnumT = Cond->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->getRBracLoc();
+
+  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

diff  --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp
index 53d574146ab5..e6fa01a52b3d 100644
--- a/clang-tools-extra/clangd/unittests/TweakTests.cpp
+++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp
@@ -2813,6 +2813,96 @@ class cc {
   }
 }
 
+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


        


More information about the cfe-commits mailing list