1 //===--- AtomicChange.cpp - AtomicChange implementation -----------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "clang/Tooling/Refactoring/AtomicChange.h" 11 #include "clang/Tooling/ReplacementsYaml.h" 12 #include "llvm/Support/YAMLTraits.h" 13 #include <string> 14 15 LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::AtomicChange) 16 17 namespace { 18 /// \brief Helper to (de)serialize an AtomicChange since we don't have direct 19 /// access to its data members. 20 /// Data members of a normalized AtomicChange can be directly mapped from/to 21 /// YAML string. 22 struct NormalizedAtomicChange { 23 NormalizedAtomicChange() = default; 24 25 NormalizedAtomicChange(const llvm::yaml::IO &) {} 26 27 // This converts AtomicChange's internal implementation of the replacements 28 // set to a vector of replacements. 29 NormalizedAtomicChange(const llvm::yaml::IO &, 30 const clang::tooling::AtomicChange &E) 31 : Key(E.getKey()), FilePath(E.getFilePath()), Error(E.getError()), 32 InsertedHeaders(E.getInsertedHeaders()), 33 RemovedHeaders(E.getRemovedHeaders()), 34 Replaces(E.getReplacements().begin(), E.getReplacements().end()) {} 35 36 // This is not expected to be called but needed for template instantiation. 37 clang::tooling::AtomicChange denormalize(const llvm::yaml::IO &) { 38 llvm_unreachable("Do not convert YAML to AtomicChange directly with '>>'. " 39 "Use AtomicChange::convertFromYAML instead."); 40 } 41 std::string Key; 42 std::string FilePath; 43 std::string Error; 44 std::vector<std::string> InsertedHeaders; 45 std::vector<std::string> RemovedHeaders; 46 std::vector<clang::tooling::Replacement> Replaces; 47 }; 48 } // anonymous namespace 49 50 namespace llvm { 51 namespace yaml { 52 53 /// \brief Specialized MappingTraits to describe how an AtomicChange is 54 /// (de)serialized. 55 template <> struct MappingTraits<NormalizedAtomicChange> { 56 static void mapping(IO &Io, NormalizedAtomicChange &Doc) { 57 Io.mapRequired("Key", Doc.Key); 58 Io.mapRequired("FilePath", Doc.FilePath); 59 Io.mapRequired("Error", Doc.Error); 60 Io.mapRequired("InsertedHeaders", Doc.InsertedHeaders); 61 Io.mapRequired("RemovedHeaders", Doc.RemovedHeaders); 62 Io.mapRequired("Replacements", Doc.Replaces); 63 } 64 }; 65 66 /// \brief Specialized MappingTraits to describe how an AtomicChange is 67 /// (de)serialized. 68 template <> struct MappingTraits<clang::tooling::AtomicChange> { 69 static void mapping(IO &Io, clang::tooling::AtomicChange &Doc) { 70 MappingNormalization<NormalizedAtomicChange, clang::tooling::AtomicChange> 71 Keys(Io, Doc); 72 Io.mapRequired("Key", Keys->Key); 73 Io.mapRequired("FilePath", Keys->FilePath); 74 Io.mapRequired("Error", Keys->Error); 75 Io.mapRequired("InsertedHeaders", Keys->InsertedHeaders); 76 Io.mapRequired("RemovedHeaders", Keys->RemovedHeaders); 77 Io.mapRequired("Replacements", Keys->Replaces); 78 } 79 }; 80 81 } // end namespace yaml 82 } // end namespace llvm 83 84 namespace clang { 85 namespace tooling { 86 87 AtomicChange::AtomicChange(const SourceManager &SM, 88 SourceLocation KeyPosition) { 89 const FullSourceLoc FullKeyPosition(KeyPosition, SM); 90 std::pair<FileID, unsigned> FileIDAndOffset = 91 FullKeyPosition.getSpellingLoc().getDecomposedLoc(); 92 const FileEntry *FE = SM.getFileEntryForID(FileIDAndOffset.first); 93 assert(FE && "Cannot create AtomicChange with invalid location."); 94 FilePath = FE->getName(); 95 Key = FilePath + ":" + std::to_string(FileIDAndOffset.second); 96 } 97 98 AtomicChange::AtomicChange(std::string Key, std::string FilePath, 99 std::string Error, 100 std::vector<std::string> InsertedHeaders, 101 std::vector<std::string> RemovedHeaders, 102 clang::tooling::Replacements Replaces) 103 : Key(std::move(Key)), FilePath(std::move(FilePath)), 104 Error(std::move(Error)), InsertedHeaders(std::move(InsertedHeaders)), 105 RemovedHeaders(std::move(RemovedHeaders)), Replaces(std::move(Replaces)) { 106 } 107 108 std::string AtomicChange::toYAMLString() { 109 std::string YamlContent; 110 llvm::raw_string_ostream YamlContentStream(YamlContent); 111 112 llvm::yaml::Output YAML(YamlContentStream); 113 YAML << *this; 114 YamlContentStream.flush(); 115 return YamlContent; 116 } 117 118 AtomicChange AtomicChange::convertFromYAML(llvm::StringRef YAMLContent) { 119 NormalizedAtomicChange NE; 120 llvm::yaml::Input YAML(YAMLContent); 121 YAML >> NE; 122 AtomicChange E(NE.Key, NE.FilePath, NE.Error, NE.InsertedHeaders, 123 NE.RemovedHeaders, tooling::Replacements()); 124 for (const auto &R : NE.Replaces) { 125 llvm::Error Err = E.Replaces.add(R); 126 if (Err) 127 llvm_unreachable( 128 "Failed to add replacement when Converting YAML to AtomicChange."); 129 llvm::consumeError(std::move(Err)); 130 } 131 return E; 132 } 133 134 llvm::Error AtomicChange::replace(const SourceManager &SM, 135 const CharSourceRange &Range, 136 llvm::StringRef ReplacementText) { 137 return Replaces.add(Replacement(SM, Range, ReplacementText)); 138 } 139 140 llvm::Error AtomicChange::replace(const SourceManager &SM, SourceLocation Loc, 141 unsigned Length, llvm::StringRef Text) { 142 return Replaces.add(Replacement(SM, Loc, Length, Text)); 143 } 144 145 llvm::Error AtomicChange::insert(const SourceManager &SM, SourceLocation Loc, 146 llvm::StringRef Text, bool InsertAfter) { 147 if (Text.empty()) 148 return llvm::Error::success(); 149 Replacement R(SM, Loc, 0, Text); 150 llvm::Error Err = Replaces.add(R); 151 if (Err) { 152 return llvm::handleErrors( 153 std::move(Err), [&](const ReplacementError &RE) -> llvm::Error { 154 if (RE.get() != replacement_error::insert_conflict) 155 return llvm::make_error<ReplacementError>(RE); 156 unsigned NewOffset = Replaces.getShiftedCodePosition(R.getOffset()); 157 if (!InsertAfter) 158 NewOffset -= 159 RE.getExistingReplacement()->getReplacementText().size(); 160 Replacement NewR(R.getFilePath(), NewOffset, 0, Text); 161 Replaces = Replaces.merge(Replacements(NewR)); 162 return llvm::Error::success(); 163 }); 164 } 165 return llvm::Error::success(); 166 } 167 168 void AtomicChange::addHeader(llvm::StringRef Header) { 169 InsertedHeaders.push_back(Header); 170 } 171 172 void AtomicChange::removeHeader(llvm::StringRef Header) { 173 RemovedHeaders.push_back(Header); 174 } 175 176 } // end namespace tooling 177 } // end namespace clang 178