//===--- 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 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 InsertedHeaders; std::vector RemovedHeaders; std::vector Replaces; }; } // anonymous namespace namespace llvm { namespace yaml { /// \brief Specialized MappingTraits to describe how an AtomicChange is /// (de)serialized. template <> struct MappingTraits { 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 { static void mapping(IO &Io, clang::tooling::AtomicChange &Doc) { MappingNormalization 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 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 InsertedHeaders, std::vector 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(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