[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