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