summaryrefslogtreecommitdiffstats
path: root/lib/Tooling
diff options
context:
space:
mode:
authorEric Liu <ioeric@google.com>2017-03-01 13:14:01 +0000
committerEric Liu <ioeric@google.com>2017-03-01 13:14:01 +0000
commit912f198bafdc43d6a4229461eb4ff031a5d940c5 (patch)
treea7aa71be6b99b8cb18e9793590d422345ff4da04 /lib/Tooling
parent59d31eb1ffc704b402cf2dfa390461308c4bca08 (diff)
Introducing clang::tooling::AtomicChange for refactoring tools.
Summary: An AtomicChange is used to create and group a set of source edits, e.g. replacements or header insertions. Edits in an AtomicChange should be related, e.g. replacements for the same type reference and the corresponding header insertion/deletion. An AtomicChange is uniquely identified by a key position and will either be fully applied or not applied at all. The key position should be the location of the key syntactical element that is being changed, e.g. the call to a refactored method. Next step: add a tool that applies AtomicChange. Reviewers: klimek, djasper Reviewed By: klimek Subscribers: alexshap, cfe-commits, djasper, mgorny Differential Revision: https://reviews.llvm.org/D27054 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@296616 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Tooling')
-rw-r--r--lib/Tooling/CMakeLists.txt1
-rw-r--r--lib/Tooling/Refactoring/AtomicChange.cpp167
-rw-r--r--lib/Tooling/Refactoring/CMakeLists.txt12
3 files changed, 180 insertions, 0 deletions
diff --git a/lib/Tooling/CMakeLists.txt b/lib/Tooling/CMakeLists.txt
index 2eec1dba2f..7b0c58e794 100644
--- a/lib/Tooling/CMakeLists.txt
+++ b/lib/Tooling/CMakeLists.txt
@@ -4,6 +4,7 @@ set(LLVM_LINK_COMPONENTS
)
add_subdirectory(Core)
+add_subdirectory(Refactoring)
add_clang_library(clangTooling
ArgumentsAdjusters.cpp
diff --git a/lib/Tooling/Refactoring/AtomicChange.cpp b/lib/Tooling/Refactoring/AtomicChange.cpp
new file mode 100644
index 0000000000..3572d2a1be
--- /dev/null
+++ b/lib/Tooling/Refactoring/AtomicChange.cpp
@@ -0,0 +1,167 @@
+//===--- AtomicChange.cpp - AtomicChange implementation -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/Refactoring/AtomicChange.h"
+#include "clang/Tooling/ReplacementsYaml.h"
+#include "llvm/Support/YAMLTraits.h"
+#include <string>
+
+LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string)
+LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::AtomicChange)
+
+namespace {
+/// \brief Helper to (de)serialize an AtomicChange since we don't have direct
+/// access to its data members.
+/// Data members of a normalized AtomicChange can be directly mapped from/to
+/// YAML string.
+struct NormalizedAtomicChange {
+ NormalizedAtomicChange() = default;
+
+ NormalizedAtomicChange(const llvm::yaml::IO &) {}
+
+ // This converts AtomicChange's internal implementation of the replacements
+ // set to a vector of replacements.
+ NormalizedAtomicChange(const llvm::yaml::IO &,
+ const clang::tooling::AtomicChange &E)
+ : Key(E.getKey()), FilePath(E.getFilePath()), Error(E.getError()),
+ InsertedHeaders(E.getInsertedHeaders()),
+ RemovedHeaders(E.getRemovedHeaders()),
+ Replaces(E.getReplacements().begin(), E.getReplacements().end()) {}
+
+ // This is not expected to be called but needed for template instantiation.
+ clang::tooling::AtomicChange denormalize(const llvm::yaml::IO &) {
+ llvm_unreachable("Do not convert YAML to AtomicChange directly with '>>'. "
+ "Use AtomicChange::convertFromYAML instead.");
+ }
+ std::string Key;
+ std::string FilePath;
+ std::string Error;
+ std::vector<std::string> InsertedHeaders;
+ std::vector<std::string> RemovedHeaders;
+ std::vector<clang::tooling::Replacement> Replaces;
+};
+} // anonymous namespace
+
+namespace llvm {
+namespace yaml {
+
+/// \brief Specialized MappingTraits to describe how an AtomicChange is
+/// (de)serialized.
+template <> struct MappingTraits<NormalizedAtomicChange> {
+ static void mapping(IO &Io, NormalizedAtomicChange &Doc) {
+ Io.mapRequired("Key", Doc.Key);
+ Io.mapRequired("FilePath", Doc.FilePath);
+ Io.mapRequired("Error", Doc.Error);
+ Io.mapRequired("InsertedHeaders", Doc.InsertedHeaders);
+ Io.mapRequired("RemovedHeaders", Doc.RemovedHeaders);
+ Io.mapRequired("Replacements", Doc.Replaces);
+ }
+};
+
+/// \brief Specialized MappingTraits to describe how an AtomicChange is
+/// (de)serialized.
+template <> struct MappingTraits<clang::tooling::AtomicChange> {
+ static void mapping(IO &Io, clang::tooling::AtomicChange &Doc) {
+ MappingNormalization<NormalizedAtomicChange, clang::tooling::AtomicChange>
+ Keys(Io, Doc);
+ Io.mapRequired("Key", Keys->Key);
+ Io.mapRequired("FilePath", Keys->FilePath);
+ Io.mapRequired("Error", Keys->Error);
+ Io.mapRequired("InsertedHeaders", Keys->InsertedHeaders);
+ Io.mapRequired("RemovedHeaders", Keys->RemovedHeaders);
+ Io.mapRequired("Replacements", Keys->Replaces);
+ }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+namespace clang {
+namespace tooling {
+
+AtomicChange::AtomicChange(const SourceManager &SM,
+ SourceLocation KeyPosition) {
+ const FullSourceLoc FullKeyPosition(KeyPosition, SM);
+ std::pair<FileID, unsigned> FileIDAndOffset =
+ FullKeyPosition.getSpellingLoc().getDecomposedLoc();
+ const FileEntry *FE = SM.getFileEntryForID(FileIDAndOffset.first);
+ assert(FE && "Cannot create AtomicChange with invalid location.");
+ FilePath = FE->getName();
+ Key = FilePath + ":" + std::to_string(FileIDAndOffset.second);
+}
+
+AtomicChange::AtomicChange(std::string Key, std::string FilePath,
+ std::string Error,
+ std::vector<std::string> InsertedHeaders,
+ std::vector<std::string> RemovedHeaders,
+ clang::tooling::Replacements Replaces)
+ : Key(std::move(Key)), FilePath(std::move(FilePath)),
+ Error(std::move(Error)), InsertedHeaders(std::move(InsertedHeaders)),
+ RemovedHeaders(std::move(RemovedHeaders)), Replaces(std::move(Replaces)) {
+}
+
+std::string AtomicChange::toYAMLString() {
+ std::string YamlContent;
+ llvm::raw_string_ostream YamlContentStream(YamlContent);
+
+ llvm::yaml::Output YAML(YamlContentStream);
+ YAML << *this;
+ YamlContentStream.flush();
+ return YamlContent;
+}
+
+AtomicChange AtomicChange::convertFromYAML(llvm::StringRef YAMLContent) {
+ NormalizedAtomicChange NE;
+ llvm::yaml::Input YAML(YAMLContent);
+ YAML >> NE;
+ AtomicChange E(NE.Key, NE.FilePath, NE.Error, NE.InsertedHeaders,
+ NE.RemovedHeaders, tooling::Replacements());
+ for (const auto &R : NE.Replaces) {
+ llvm::Error Err = E.Replaces.add(R);
+ if (Err)
+ llvm_unreachable(
+ "Failed to add replacement when Converting YAML to AtomicChange.");
+ llvm::consumeError(std::move(Err));
+ }
+ return E;
+}
+
+llvm::Error AtomicChange::insert(const SourceManager &SM, SourceLocation Loc,
+ llvm::StringRef Text, bool InsertAfter) {
+ if (Text.empty())
+ return llvm::Error::success();
+ Replacement R(SM, Loc, 0, Text);
+ llvm::Error Err = Replaces.add(R);
+ if (Err) {
+ return llvm::handleErrors(
+ std::move(Err), [&](const ReplacementError &RE) -> llvm::Error {
+ if (RE.get() != replacement_error::insert_conflict)
+ return llvm::make_error<ReplacementError>(RE);
+ unsigned NewOffset = Replaces.getShiftedCodePosition(R.getOffset());
+ if (!InsertAfter)
+ NewOffset -=
+ RE.getExistingReplacement()->getReplacementText().size();
+ Replacement NewR(R.getFilePath(), NewOffset, 0, Text);
+ Replaces = Replaces.merge(Replacements(NewR));
+ return llvm::Error::success();
+ });
+ }
+ return llvm::Error::success();
+}
+
+void AtomicChange::addHeader(llvm::StringRef Header) {
+ InsertedHeaders.push_back(Header);
+}
+
+void AtomicChange::removeHeader(llvm::StringRef Header) {
+ RemovedHeaders.push_back(Header);
+}
+
+} // end namespace tooling
+} // end namespace clang
diff --git a/lib/Tooling/Refactoring/CMakeLists.txt b/lib/Tooling/Refactoring/CMakeLists.txt
new file mode 100644
index 0000000000..b2f9b4f4c0
--- /dev/null
+++ b/lib/Tooling/Refactoring/CMakeLists.txt
@@ -0,0 +1,12 @@
+set(LLVM_LINK_COMPONENTS
+ Option
+ Support
+ )
+
+add_clang_library(clangToolingRefactor
+ AtomicChange.cpp
+
+ LINK_LIBS
+ clangBasic
+ clangToolingCore
+ )