diff options
Diffstat (limited to 'lib/Tooling/Refactoring/AtomicChange.cpp')
-rw-r--r-- | lib/Tooling/Refactoring/AtomicChange.cpp | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/lib/Tooling/Refactoring/AtomicChange.cpp b/lib/Tooling/Refactoring/AtomicChange.cpp new file mode 100644 index 0000000000000..321bbfa2866ae --- /dev/null +++ b/lib/Tooling/Refactoring/AtomicChange.cpp @@ -0,0 +1,178 @@ +//===--- 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::replace(const SourceManager &SM, + const CharSourceRange &Range, + llvm::StringRef ReplacementText) { + return Replaces.add(Replacement(SM, Range, ReplacementText)); +} + +llvm::Error AtomicChange::replace(const SourceManager &SM, SourceLocation Loc, + unsigned Length, llvm::StringRef Text) { + return Replaces.add(Replacement(SM, Loc, Length, Text)); +} + +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 |