1*e5dd7070Spatrick //===--- AtomicChange.cpp - AtomicChange implementation -----------------*- C++ -*-===// 2*e5dd7070Spatrick // 3*e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*e5dd7070Spatrick // 7*e5dd7070Spatrick //===----------------------------------------------------------------------===// 8*e5dd7070Spatrick 9*e5dd7070Spatrick #include "clang/Tooling/Refactoring/AtomicChange.h" 10*e5dd7070Spatrick #include "clang/Tooling/ReplacementsYaml.h" 11*e5dd7070Spatrick #include "llvm/Support/YAMLTraits.h" 12*e5dd7070Spatrick #include <string> 13*e5dd7070Spatrick 14*e5dd7070Spatrick LLVM_YAML_IS_SEQUENCE_VECTOR(clang::tooling::AtomicChange) 15*e5dd7070Spatrick 16*e5dd7070Spatrick namespace { 17*e5dd7070Spatrick /// Helper to (de)serialize an AtomicChange since we don't have direct 18*e5dd7070Spatrick /// access to its data members. 19*e5dd7070Spatrick /// Data members of a normalized AtomicChange can be directly mapped from/to 20*e5dd7070Spatrick /// YAML string. 21*e5dd7070Spatrick struct NormalizedAtomicChange { 22*e5dd7070Spatrick NormalizedAtomicChange() = default; 23*e5dd7070Spatrick 24*e5dd7070Spatrick NormalizedAtomicChange(const llvm::yaml::IO &) {} 25*e5dd7070Spatrick 26*e5dd7070Spatrick // This converts AtomicChange's internal implementation of the replacements 27*e5dd7070Spatrick // set to a vector of replacements. 28*e5dd7070Spatrick NormalizedAtomicChange(const llvm::yaml::IO &, 29*e5dd7070Spatrick const clang::tooling::AtomicChange &E) 30*e5dd7070Spatrick : Key(E.getKey()), FilePath(E.getFilePath()), Error(E.getError()), 31*e5dd7070Spatrick InsertedHeaders(E.getInsertedHeaders()), 32*e5dd7070Spatrick RemovedHeaders(E.getRemovedHeaders()), 33*e5dd7070Spatrick Replaces(E.getReplacements().begin(), E.getReplacements().end()) {} 34*e5dd7070Spatrick 35*e5dd7070Spatrick // This is not expected to be called but needed for template instantiation. 36*e5dd7070Spatrick clang::tooling::AtomicChange denormalize(const llvm::yaml::IO &) { 37*e5dd7070Spatrick llvm_unreachable("Do not convert YAML to AtomicChange directly with '>>'. " 38*e5dd7070Spatrick "Use AtomicChange::convertFromYAML instead."); 39*e5dd7070Spatrick } 40*e5dd7070Spatrick std::string Key; 41*e5dd7070Spatrick std::string FilePath; 42*e5dd7070Spatrick std::string Error; 43*e5dd7070Spatrick std::vector<std::string> InsertedHeaders; 44*e5dd7070Spatrick std::vector<std::string> RemovedHeaders; 45*e5dd7070Spatrick std::vector<clang::tooling::Replacement> Replaces; 46*e5dd7070Spatrick }; 47*e5dd7070Spatrick } // anonymous namespace 48*e5dd7070Spatrick 49*e5dd7070Spatrick namespace llvm { 50*e5dd7070Spatrick namespace yaml { 51*e5dd7070Spatrick 52*e5dd7070Spatrick /// Specialized MappingTraits to describe how an AtomicChange is 53*e5dd7070Spatrick /// (de)serialized. 54*e5dd7070Spatrick template <> struct MappingTraits<NormalizedAtomicChange> { 55*e5dd7070Spatrick static void mapping(IO &Io, NormalizedAtomicChange &Doc) { 56*e5dd7070Spatrick Io.mapRequired("Key", Doc.Key); 57*e5dd7070Spatrick Io.mapRequired("FilePath", Doc.FilePath); 58*e5dd7070Spatrick Io.mapRequired("Error", Doc.Error); 59*e5dd7070Spatrick Io.mapRequired("InsertedHeaders", Doc.InsertedHeaders); 60*e5dd7070Spatrick Io.mapRequired("RemovedHeaders", Doc.RemovedHeaders); 61*e5dd7070Spatrick Io.mapRequired("Replacements", Doc.Replaces); 62*e5dd7070Spatrick } 63*e5dd7070Spatrick }; 64*e5dd7070Spatrick 65*e5dd7070Spatrick /// Specialized MappingTraits to describe how an AtomicChange is 66*e5dd7070Spatrick /// (de)serialized. 67*e5dd7070Spatrick template <> struct MappingTraits<clang::tooling::AtomicChange> { 68*e5dd7070Spatrick static void mapping(IO &Io, clang::tooling::AtomicChange &Doc) { 69*e5dd7070Spatrick MappingNormalization<NormalizedAtomicChange, clang::tooling::AtomicChange> 70*e5dd7070Spatrick Keys(Io, Doc); 71*e5dd7070Spatrick Io.mapRequired("Key", Keys->Key); 72*e5dd7070Spatrick Io.mapRequired("FilePath", Keys->FilePath); 73*e5dd7070Spatrick Io.mapRequired("Error", Keys->Error); 74*e5dd7070Spatrick Io.mapRequired("InsertedHeaders", Keys->InsertedHeaders); 75*e5dd7070Spatrick Io.mapRequired("RemovedHeaders", Keys->RemovedHeaders); 76*e5dd7070Spatrick Io.mapRequired("Replacements", Keys->Replaces); 77*e5dd7070Spatrick } 78*e5dd7070Spatrick }; 79*e5dd7070Spatrick 80*e5dd7070Spatrick } // end namespace yaml 81*e5dd7070Spatrick } // end namespace llvm 82*e5dd7070Spatrick 83*e5dd7070Spatrick namespace clang { 84*e5dd7070Spatrick namespace tooling { 85*e5dd7070Spatrick namespace { 86*e5dd7070Spatrick 87*e5dd7070Spatrick // Returns true if there is any line that violates \p ColumnLimit in range 88*e5dd7070Spatrick // [Start, End]. 89*e5dd7070Spatrick bool violatesColumnLimit(llvm::StringRef Code, unsigned ColumnLimit, 90*e5dd7070Spatrick unsigned Start, unsigned End) { 91*e5dd7070Spatrick auto StartPos = Code.rfind('\n', Start); 92*e5dd7070Spatrick StartPos = (StartPos == llvm::StringRef::npos) ? 0 : StartPos + 1; 93*e5dd7070Spatrick 94*e5dd7070Spatrick auto EndPos = Code.find("\n", End); 95*e5dd7070Spatrick if (EndPos == llvm::StringRef::npos) 96*e5dd7070Spatrick EndPos = Code.size(); 97*e5dd7070Spatrick 98*e5dd7070Spatrick llvm::SmallVector<llvm::StringRef, 8> Lines; 99*e5dd7070Spatrick Code.substr(StartPos, EndPos - StartPos).split(Lines, '\n'); 100*e5dd7070Spatrick for (llvm::StringRef Line : Lines) 101*e5dd7070Spatrick if (Line.size() > ColumnLimit) 102*e5dd7070Spatrick return true; 103*e5dd7070Spatrick return false; 104*e5dd7070Spatrick } 105*e5dd7070Spatrick 106*e5dd7070Spatrick std::vector<Range> 107*e5dd7070Spatrick getRangesForFormating(llvm::StringRef Code, unsigned ColumnLimit, 108*e5dd7070Spatrick ApplyChangesSpec::FormatOption Format, 109*e5dd7070Spatrick const clang::tooling::Replacements &Replaces) { 110*e5dd7070Spatrick // kNone suppresses formatting entirely. 111*e5dd7070Spatrick if (Format == ApplyChangesSpec::kNone) 112*e5dd7070Spatrick return {}; 113*e5dd7070Spatrick std::vector<clang::tooling::Range> Ranges; 114*e5dd7070Spatrick // This works assuming that replacements are ordered by offset. 115*e5dd7070Spatrick // FIXME: use `getAffectedRanges()` to calculate when it does not include '\n' 116*e5dd7070Spatrick // at the end of an insertion in affected ranges. 117*e5dd7070Spatrick int Offset = 0; 118*e5dd7070Spatrick for (const clang::tooling::Replacement &R : Replaces) { 119*e5dd7070Spatrick int Start = R.getOffset() + Offset; 120*e5dd7070Spatrick int End = Start + R.getReplacementText().size(); 121*e5dd7070Spatrick if (!R.getReplacementText().empty() && 122*e5dd7070Spatrick R.getReplacementText().back() == '\n' && R.getLength() == 0 && 123*e5dd7070Spatrick R.getOffset() > 0 && R.getOffset() <= Code.size() && 124*e5dd7070Spatrick Code[R.getOffset() - 1] == '\n') 125*e5dd7070Spatrick // If we are inserting at the start of a line and the replacement ends in 126*e5dd7070Spatrick // a newline, we don't need to format the subsequent line. 127*e5dd7070Spatrick --End; 128*e5dd7070Spatrick Offset += R.getReplacementText().size() - R.getLength(); 129*e5dd7070Spatrick 130*e5dd7070Spatrick if (Format == ApplyChangesSpec::kAll || 131*e5dd7070Spatrick violatesColumnLimit(Code, ColumnLimit, Start, End)) 132*e5dd7070Spatrick Ranges.emplace_back(Start, End - Start); 133*e5dd7070Spatrick } 134*e5dd7070Spatrick return Ranges; 135*e5dd7070Spatrick } 136*e5dd7070Spatrick 137*e5dd7070Spatrick inline llvm::Error make_string_error(const llvm::Twine &Message) { 138*e5dd7070Spatrick return llvm::make_error<llvm::StringError>(Message, 139*e5dd7070Spatrick llvm::inconvertibleErrorCode()); 140*e5dd7070Spatrick } 141*e5dd7070Spatrick 142*e5dd7070Spatrick // Creates replacements for inserting/deleting #include headers. 143*e5dd7070Spatrick llvm::Expected<Replacements> 144*e5dd7070Spatrick createReplacementsForHeaders(llvm::StringRef FilePath, llvm::StringRef Code, 145*e5dd7070Spatrick llvm::ArrayRef<AtomicChange> Changes, 146*e5dd7070Spatrick const format::FormatStyle &Style) { 147*e5dd7070Spatrick // Create header insertion/deletion replacements to be cleaned up 148*e5dd7070Spatrick // (i.e. converted to real insertion/deletion replacements). 149*e5dd7070Spatrick Replacements HeaderReplacements; 150*e5dd7070Spatrick for (const auto &Change : Changes) { 151*e5dd7070Spatrick for (llvm::StringRef Header : Change.getInsertedHeaders()) { 152*e5dd7070Spatrick std::string EscapedHeader = 153*e5dd7070Spatrick Header.startswith("<") || Header.startswith("\"") 154*e5dd7070Spatrick ? Header.str() 155*e5dd7070Spatrick : ("\"" + Header + "\"").str(); 156*e5dd7070Spatrick std::string ReplacementText = "#include " + EscapedHeader; 157*e5dd7070Spatrick // Offset UINT_MAX and length 0 indicate that the replacement is a header 158*e5dd7070Spatrick // insertion. 159*e5dd7070Spatrick llvm::Error Err = HeaderReplacements.add( 160*e5dd7070Spatrick tooling::Replacement(FilePath, UINT_MAX, 0, ReplacementText)); 161*e5dd7070Spatrick if (Err) 162*e5dd7070Spatrick return std::move(Err); 163*e5dd7070Spatrick } 164*e5dd7070Spatrick for (const std::string &Header : Change.getRemovedHeaders()) { 165*e5dd7070Spatrick // Offset UINT_MAX and length 1 indicate that the replacement is a header 166*e5dd7070Spatrick // deletion. 167*e5dd7070Spatrick llvm::Error Err = 168*e5dd7070Spatrick HeaderReplacements.add(Replacement(FilePath, UINT_MAX, 1, Header)); 169*e5dd7070Spatrick if (Err) 170*e5dd7070Spatrick return std::move(Err); 171*e5dd7070Spatrick } 172*e5dd7070Spatrick } 173*e5dd7070Spatrick 174*e5dd7070Spatrick // cleanupAroundReplacements() converts header insertions/deletions into 175*e5dd7070Spatrick // actual replacements that add/remove headers at the right location. 176*e5dd7070Spatrick return clang::format::cleanupAroundReplacements(Code, HeaderReplacements, 177*e5dd7070Spatrick Style); 178*e5dd7070Spatrick } 179*e5dd7070Spatrick 180*e5dd7070Spatrick // Combine replacements in all Changes as a `Replacements`. This ignores the 181*e5dd7070Spatrick // file path in all replacements and replaces them with \p FilePath. 182*e5dd7070Spatrick llvm::Expected<Replacements> 183*e5dd7070Spatrick combineReplacementsInChanges(llvm::StringRef FilePath, 184*e5dd7070Spatrick llvm::ArrayRef<AtomicChange> Changes) { 185*e5dd7070Spatrick Replacements Replaces; 186*e5dd7070Spatrick for (const auto &Change : Changes) 187*e5dd7070Spatrick for (const auto &R : Change.getReplacements()) 188*e5dd7070Spatrick if (auto Err = Replaces.add(Replacement( 189*e5dd7070Spatrick FilePath, R.getOffset(), R.getLength(), R.getReplacementText()))) 190*e5dd7070Spatrick return std::move(Err); 191*e5dd7070Spatrick return Replaces; 192*e5dd7070Spatrick } 193*e5dd7070Spatrick 194*e5dd7070Spatrick } // end namespace 195*e5dd7070Spatrick 196*e5dd7070Spatrick AtomicChange::AtomicChange(const SourceManager &SM, 197*e5dd7070Spatrick SourceLocation KeyPosition) { 198*e5dd7070Spatrick const FullSourceLoc FullKeyPosition(KeyPosition, SM); 199*e5dd7070Spatrick std::pair<FileID, unsigned> FileIDAndOffset = 200*e5dd7070Spatrick FullKeyPosition.getSpellingLoc().getDecomposedLoc(); 201*e5dd7070Spatrick const FileEntry *FE = SM.getFileEntryForID(FileIDAndOffset.first); 202*e5dd7070Spatrick assert(FE && "Cannot create AtomicChange with invalid location."); 203*e5dd7070Spatrick FilePath = FE->getName(); 204*e5dd7070Spatrick Key = FilePath + ":" + std::to_string(FileIDAndOffset.second); 205*e5dd7070Spatrick } 206*e5dd7070Spatrick 207*e5dd7070Spatrick AtomicChange::AtomicChange(std::string Key, std::string FilePath, 208*e5dd7070Spatrick std::string Error, 209*e5dd7070Spatrick std::vector<std::string> InsertedHeaders, 210*e5dd7070Spatrick std::vector<std::string> RemovedHeaders, 211*e5dd7070Spatrick clang::tooling::Replacements Replaces) 212*e5dd7070Spatrick : Key(std::move(Key)), FilePath(std::move(FilePath)), 213*e5dd7070Spatrick Error(std::move(Error)), InsertedHeaders(std::move(InsertedHeaders)), 214*e5dd7070Spatrick RemovedHeaders(std::move(RemovedHeaders)), Replaces(std::move(Replaces)) { 215*e5dd7070Spatrick } 216*e5dd7070Spatrick 217*e5dd7070Spatrick bool AtomicChange::operator==(const AtomicChange &Other) const { 218*e5dd7070Spatrick if (Key != Other.Key || FilePath != Other.FilePath || Error != Other.Error) 219*e5dd7070Spatrick return false; 220*e5dd7070Spatrick if (!(Replaces == Other.Replaces)) 221*e5dd7070Spatrick return false; 222*e5dd7070Spatrick // FXIME: Compare header insertions/removals. 223*e5dd7070Spatrick return true; 224*e5dd7070Spatrick } 225*e5dd7070Spatrick 226*e5dd7070Spatrick std::string AtomicChange::toYAMLString() { 227*e5dd7070Spatrick std::string YamlContent; 228*e5dd7070Spatrick llvm::raw_string_ostream YamlContentStream(YamlContent); 229*e5dd7070Spatrick 230*e5dd7070Spatrick llvm::yaml::Output YAML(YamlContentStream); 231*e5dd7070Spatrick YAML << *this; 232*e5dd7070Spatrick YamlContentStream.flush(); 233*e5dd7070Spatrick return YamlContent; 234*e5dd7070Spatrick } 235*e5dd7070Spatrick 236*e5dd7070Spatrick AtomicChange AtomicChange::convertFromYAML(llvm::StringRef YAMLContent) { 237*e5dd7070Spatrick NormalizedAtomicChange NE; 238*e5dd7070Spatrick llvm::yaml::Input YAML(YAMLContent); 239*e5dd7070Spatrick YAML >> NE; 240*e5dd7070Spatrick AtomicChange E(NE.Key, NE.FilePath, NE.Error, NE.InsertedHeaders, 241*e5dd7070Spatrick NE.RemovedHeaders, tooling::Replacements()); 242*e5dd7070Spatrick for (const auto &R : NE.Replaces) { 243*e5dd7070Spatrick llvm::Error Err = E.Replaces.add(R); 244*e5dd7070Spatrick if (Err) 245*e5dd7070Spatrick llvm_unreachable( 246*e5dd7070Spatrick "Failed to add replacement when Converting YAML to AtomicChange."); 247*e5dd7070Spatrick llvm::consumeError(std::move(Err)); 248*e5dd7070Spatrick } 249*e5dd7070Spatrick return E; 250*e5dd7070Spatrick } 251*e5dd7070Spatrick 252*e5dd7070Spatrick llvm::Error AtomicChange::replace(const SourceManager &SM, 253*e5dd7070Spatrick const CharSourceRange &Range, 254*e5dd7070Spatrick llvm::StringRef ReplacementText) { 255*e5dd7070Spatrick return Replaces.add(Replacement(SM, Range, ReplacementText)); 256*e5dd7070Spatrick } 257*e5dd7070Spatrick 258*e5dd7070Spatrick llvm::Error AtomicChange::replace(const SourceManager &SM, SourceLocation Loc, 259*e5dd7070Spatrick unsigned Length, llvm::StringRef Text) { 260*e5dd7070Spatrick return Replaces.add(Replacement(SM, Loc, Length, Text)); 261*e5dd7070Spatrick } 262*e5dd7070Spatrick 263*e5dd7070Spatrick llvm::Error AtomicChange::insert(const SourceManager &SM, SourceLocation Loc, 264*e5dd7070Spatrick llvm::StringRef Text, bool InsertAfter) { 265*e5dd7070Spatrick if (Text.empty()) 266*e5dd7070Spatrick return llvm::Error::success(); 267*e5dd7070Spatrick Replacement R(SM, Loc, 0, Text); 268*e5dd7070Spatrick llvm::Error Err = Replaces.add(R); 269*e5dd7070Spatrick if (Err) { 270*e5dd7070Spatrick return llvm::handleErrors( 271*e5dd7070Spatrick std::move(Err), [&](const ReplacementError &RE) -> llvm::Error { 272*e5dd7070Spatrick if (RE.get() != replacement_error::insert_conflict) 273*e5dd7070Spatrick return llvm::make_error<ReplacementError>(RE); 274*e5dd7070Spatrick unsigned NewOffset = Replaces.getShiftedCodePosition(R.getOffset()); 275*e5dd7070Spatrick if (!InsertAfter) 276*e5dd7070Spatrick NewOffset -= 277*e5dd7070Spatrick RE.getExistingReplacement()->getReplacementText().size(); 278*e5dd7070Spatrick Replacement NewR(R.getFilePath(), NewOffset, 0, Text); 279*e5dd7070Spatrick Replaces = Replaces.merge(Replacements(NewR)); 280*e5dd7070Spatrick return llvm::Error::success(); 281*e5dd7070Spatrick }); 282*e5dd7070Spatrick } 283*e5dd7070Spatrick return llvm::Error::success(); 284*e5dd7070Spatrick } 285*e5dd7070Spatrick 286*e5dd7070Spatrick void AtomicChange::addHeader(llvm::StringRef Header) { 287*e5dd7070Spatrick InsertedHeaders.push_back(Header); 288*e5dd7070Spatrick } 289*e5dd7070Spatrick 290*e5dd7070Spatrick void AtomicChange::removeHeader(llvm::StringRef Header) { 291*e5dd7070Spatrick RemovedHeaders.push_back(Header); 292*e5dd7070Spatrick } 293*e5dd7070Spatrick 294*e5dd7070Spatrick llvm::Expected<std::string> 295*e5dd7070Spatrick applyAtomicChanges(llvm::StringRef FilePath, llvm::StringRef Code, 296*e5dd7070Spatrick llvm::ArrayRef<AtomicChange> Changes, 297*e5dd7070Spatrick const ApplyChangesSpec &Spec) { 298*e5dd7070Spatrick llvm::Expected<Replacements> HeaderReplacements = 299*e5dd7070Spatrick createReplacementsForHeaders(FilePath, Code, Changes, Spec.Style); 300*e5dd7070Spatrick if (!HeaderReplacements) 301*e5dd7070Spatrick return make_string_error( 302*e5dd7070Spatrick "Failed to create replacements for header changes: " + 303*e5dd7070Spatrick llvm::toString(HeaderReplacements.takeError())); 304*e5dd7070Spatrick 305*e5dd7070Spatrick llvm::Expected<Replacements> Replaces = 306*e5dd7070Spatrick combineReplacementsInChanges(FilePath, Changes); 307*e5dd7070Spatrick if (!Replaces) 308*e5dd7070Spatrick return make_string_error("Failed to combine replacements in all changes: " + 309*e5dd7070Spatrick llvm::toString(Replaces.takeError())); 310*e5dd7070Spatrick 311*e5dd7070Spatrick Replacements AllReplaces = std::move(*Replaces); 312*e5dd7070Spatrick for (const auto &R : *HeaderReplacements) { 313*e5dd7070Spatrick llvm::Error Err = AllReplaces.add(R); 314*e5dd7070Spatrick if (Err) 315*e5dd7070Spatrick return make_string_error( 316*e5dd7070Spatrick "Failed to combine existing replacements with header replacements: " + 317*e5dd7070Spatrick llvm::toString(std::move(Err))); 318*e5dd7070Spatrick } 319*e5dd7070Spatrick 320*e5dd7070Spatrick if (Spec.Cleanup) { 321*e5dd7070Spatrick llvm::Expected<Replacements> CleanReplaces = 322*e5dd7070Spatrick format::cleanupAroundReplacements(Code, AllReplaces, Spec.Style); 323*e5dd7070Spatrick if (!CleanReplaces) 324*e5dd7070Spatrick return make_string_error("Failed to cleanup around replacements: " + 325*e5dd7070Spatrick llvm::toString(CleanReplaces.takeError())); 326*e5dd7070Spatrick AllReplaces = std::move(*CleanReplaces); 327*e5dd7070Spatrick } 328*e5dd7070Spatrick 329*e5dd7070Spatrick // Apply all replacements. 330*e5dd7070Spatrick llvm::Expected<std::string> ChangedCode = 331*e5dd7070Spatrick applyAllReplacements(Code, AllReplaces); 332*e5dd7070Spatrick if (!ChangedCode) 333*e5dd7070Spatrick return make_string_error("Failed to apply all replacements: " + 334*e5dd7070Spatrick llvm::toString(ChangedCode.takeError())); 335*e5dd7070Spatrick 336*e5dd7070Spatrick // Sort inserted headers. This is done even if other formatting is turned off 337*e5dd7070Spatrick // as incorrectly sorted headers are always just wrong, it's not a matter of 338*e5dd7070Spatrick // taste. 339*e5dd7070Spatrick Replacements HeaderSortingReplacements = format::sortIncludes( 340*e5dd7070Spatrick Spec.Style, *ChangedCode, AllReplaces.getAffectedRanges(), FilePath); 341*e5dd7070Spatrick ChangedCode = applyAllReplacements(*ChangedCode, HeaderSortingReplacements); 342*e5dd7070Spatrick if (!ChangedCode) 343*e5dd7070Spatrick return make_string_error( 344*e5dd7070Spatrick "Failed to apply replacements for sorting includes: " + 345*e5dd7070Spatrick llvm::toString(ChangedCode.takeError())); 346*e5dd7070Spatrick 347*e5dd7070Spatrick AllReplaces = AllReplaces.merge(HeaderSortingReplacements); 348*e5dd7070Spatrick 349*e5dd7070Spatrick std::vector<Range> FormatRanges = getRangesForFormating( 350*e5dd7070Spatrick *ChangedCode, Spec.Style.ColumnLimit, Spec.Format, AllReplaces); 351*e5dd7070Spatrick if (!FormatRanges.empty()) { 352*e5dd7070Spatrick Replacements FormatReplacements = 353*e5dd7070Spatrick format::reformat(Spec.Style, *ChangedCode, FormatRanges, FilePath); 354*e5dd7070Spatrick ChangedCode = applyAllReplacements(*ChangedCode, FormatReplacements); 355*e5dd7070Spatrick if (!ChangedCode) 356*e5dd7070Spatrick return make_string_error( 357*e5dd7070Spatrick "Failed to apply replacements for formatting changed code: " + 358*e5dd7070Spatrick llvm::toString(ChangedCode.takeError())); 359*e5dd7070Spatrick } 360*e5dd7070Spatrick return ChangedCode; 361*e5dd7070Spatrick } 362*e5dd7070Spatrick 363*e5dd7070Spatrick } // end namespace tooling 364*e5dd7070Spatrick } // end namespace clang 365