1 //===--- Replacement.cpp - Framework for clang refactoring tools ----------===//
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 //  Implements classes to support/store refactorings.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Tooling/Core/Replacement.h"
15 
16 #include "clang/Basic/Diagnostic.h"
17 #include "clang/Basic/DiagnosticIDs.h"
18 #include "clang/Basic/DiagnosticOptions.h"
19 #include "clang/Basic/FileManager.h"
20 #include "clang/Basic/SourceManager.h"
21 #include "clang/Lex/Lexer.h"
22 #include "clang/Rewrite/Core/Rewriter.h"
23 #include "llvm/ADT/SmallPtrSet.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/Path.h"
26 #include "llvm/Support/raw_os_ostream.h"
27 
28 namespace clang {
29 namespace tooling {
30 
31 static const char * const InvalidLocation = "";
32 
33 Replacement::Replacement()
34   : FilePath(InvalidLocation) {}
35 
36 Replacement::Replacement(StringRef FilePath, unsigned Offset, unsigned Length,
37                          StringRef ReplacementText)
38     : FilePath(FilePath), ReplacementRange(Offset, Length),
39       ReplacementText(ReplacementText) {}
40 
41 Replacement::Replacement(const SourceManager &Sources, SourceLocation Start,
42                          unsigned Length, StringRef ReplacementText) {
43   setFromSourceLocation(Sources, Start, Length, ReplacementText);
44 }
45 
46 Replacement::Replacement(const SourceManager &Sources,
47                          const CharSourceRange &Range,
48                          StringRef ReplacementText,
49                          const LangOptions &LangOpts) {
50   setFromSourceRange(Sources, Range, ReplacementText, LangOpts);
51 }
52 
53 bool Replacement::isApplicable() const {
54   return FilePath != InvalidLocation;
55 }
56 
57 bool Replacement::apply(Rewriter &Rewrite) const {
58   SourceManager &SM = Rewrite.getSourceMgr();
59   const FileEntry *Entry = SM.getFileManager().getFile(FilePath);
60   if (!Entry)
61     return false;
62 
63   FileID ID = SM.getOrCreateFileID(Entry, SrcMgr::C_User);
64   const SourceLocation Start =
65     SM.getLocForStartOfFile(ID).
66     getLocWithOffset(ReplacementRange.getOffset());
67   // ReplaceText returns false on success.
68   // ReplaceText only fails if the source location is not a file location, in
69   // which case we already returned false earlier.
70   bool RewriteSucceeded = !Rewrite.ReplaceText(
71       Start, ReplacementRange.getLength(), ReplacementText);
72   assert(RewriteSucceeded);
73   return RewriteSucceeded;
74 }
75 
76 std::string Replacement::toString() const {
77   std::string Result;
78   llvm::raw_string_ostream Stream(Result);
79   Stream << FilePath << ": " << ReplacementRange.getOffset() << ":+"
80          << ReplacementRange.getLength() << ":\"" << ReplacementText << "\"";
81   return Stream.str();
82 }
83 
84 bool operator<(const Replacement &LHS, const Replacement &RHS) {
85   if (LHS.getOffset() != RHS.getOffset())
86     return LHS.getOffset() < RHS.getOffset();
87 
88   if (LHS.getLength() != RHS.getLength())
89     return LHS.getLength() < RHS.getLength();
90 
91   if (LHS.getFilePath() != RHS.getFilePath())
92     return LHS.getFilePath() < RHS.getFilePath();
93   return LHS.getReplacementText() < RHS.getReplacementText();
94 }
95 
96 bool operator==(const Replacement &LHS, const Replacement &RHS) {
97   return LHS.getOffset() == RHS.getOffset() &&
98          LHS.getLength() == RHS.getLength() &&
99          LHS.getFilePath() == RHS.getFilePath() &&
100          LHS.getReplacementText() == RHS.getReplacementText();
101 }
102 
103 void Replacement::setFromSourceLocation(const SourceManager &Sources,
104                                         SourceLocation Start, unsigned Length,
105                                         StringRef ReplacementText) {
106   const std::pair<FileID, unsigned> DecomposedLocation =
107       Sources.getDecomposedLoc(Start);
108   const FileEntry *Entry = Sources.getFileEntryForID(DecomposedLocation.first);
109   this->FilePath = Entry ? Entry->getName() : InvalidLocation;
110   this->ReplacementRange = Range(DecomposedLocation.second, Length);
111   this->ReplacementText = ReplacementText;
112 }
113 
114 // FIXME: This should go into the Lexer, but we need to figure out how
115 // to handle ranges for refactoring in general first - there is no obvious
116 // good way how to integrate this into the Lexer yet.
117 static int getRangeSize(const SourceManager &Sources,
118                         const CharSourceRange &Range,
119                         const LangOptions &LangOpts) {
120   SourceLocation SpellingBegin = Sources.getSpellingLoc(Range.getBegin());
121   SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd());
122   std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin);
123   std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd);
124   if (Start.first != End.first) return -1;
125   if (Range.isTokenRange())
126     End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts);
127   return End.second - Start.second;
128 }
129 
130 void Replacement::setFromSourceRange(const SourceManager &Sources,
131                                      const CharSourceRange &Range,
132                                      StringRef ReplacementText,
133                                      const LangOptions &LangOpts) {
134   setFromSourceLocation(Sources, Sources.getSpellingLoc(Range.getBegin()),
135                         getRangeSize(Sources, Range, LangOpts),
136                         ReplacementText);
137 }
138 
139 Replacement
140 Replacements::getReplacementInChangedCode(const Replacement &R) const {
141   unsigned NewStart = getShiftedCodePosition(R.getOffset());
142   unsigned NewEnd = getShiftedCodePosition(R.getOffset() + R.getLength());
143   return Replacement(R.getFilePath(), NewStart, NewEnd - NewStart,
144                      R.getReplacementText());
145 }
146 
147 static llvm::Error makeConflictReplacementsError(const Replacement &New,
148                                                  const Replacement &Existing) {
149   return llvm::make_error<llvm::StringError>(
150       "New replacement:\n" + New.toString() +
151           "\nconflicts with existing replacement:\n" + Existing.toString(),
152       llvm::inconvertibleErrorCode());
153 }
154 
155 Replacements Replacements::getCanonicalReplacements() const {
156   std::vector<Replacement> NewReplaces;
157   // Merge adjacent replacements.
158   for (const auto &R : Replaces) {
159     if (NewReplaces.empty()) {
160       NewReplaces.push_back(R);
161       continue;
162     }
163     auto &Prev = NewReplaces.back();
164     unsigned PrevEnd = Prev.getOffset() + Prev.getLength();
165     if (PrevEnd < R.getOffset()) {
166       NewReplaces.push_back(R);
167     } else {
168       assert(PrevEnd == R.getOffset() &&
169              "Existing replacements must not overlap.");
170       Replacement NewR(
171           R.getFilePath(), Prev.getOffset(), Prev.getLength() + R.getLength(),
172           (Prev.getReplacementText() + R.getReplacementText()).str());
173       Prev = NewR;
174     }
175   }
176   ReplacementsImpl NewReplacesImpl(NewReplaces.begin(), NewReplaces.end());
177   return Replacements(NewReplacesImpl.begin(), NewReplacesImpl.end());
178 }
179 
180 // `R` and `Replaces` are order-independent if applying them in either order
181 // has the same effect, so we need to compare replacements associated to
182 // applying them in either order.
183 llvm::Expected<Replacements>
184 Replacements::mergeIfOrderIndependent(const Replacement &R) const {
185   Replacements Rs(R);
186   // A Replacements set containg a single replacement that is `R` referring to
187   // the code after the existing replacements `Replaces` are applied.
188   Replacements RsShiftedByReplaces(getReplacementInChangedCode(R));
189   // A Replacements set that is `Replaces` referring to the code after `R` is
190   // applied.
191   Replacements ReplacesShiftedByRs;
192   for (const auto &Replace : Replaces)
193     ReplacesShiftedByRs.Replaces.insert(
194         Rs.getReplacementInChangedCode(Replace));
195   // This is equivalent to applying `Replaces` first and then `R`.
196   auto MergeShiftedRs = merge(RsShiftedByReplaces);
197   // This is equivalent to applying `R` first and then `Replaces`.
198   auto MergeShiftedReplaces = Rs.merge(ReplacesShiftedByRs);
199 
200   // Since empty or segmented replacements around existing replacements might be
201   // produced above, we need to compare replacements in canonical forms.
202   if (MergeShiftedRs.getCanonicalReplacements() ==
203       MergeShiftedReplaces.getCanonicalReplacements())
204     return MergeShiftedRs;
205   return makeConflictReplacementsError(R, *Replaces.begin());
206 }
207 
208 llvm::Error Replacements::add(const Replacement &R) {
209   // Check the file path.
210   if (!Replaces.empty() && R.getFilePath() != Replaces.begin()->getFilePath())
211     return llvm::make_error<llvm::StringError>(
212         "All replacements must have the same file path. New replacement: " +
213             R.getFilePath() + ", existing replacements: " +
214             Replaces.begin()->getFilePath() + "\n",
215         llvm::inconvertibleErrorCode());
216 
217   // Special-case header insertions.
218   if (R.getOffset() == UINT_MAX) {
219     Replaces.insert(R);
220     return llvm::Error::success();
221   }
222 
223   // This replacement cannot conflict with replacements that end before
224   // this replacement starts or start after this replacement ends.
225   // We also know that there currently are no overlapping replacements.
226   // Thus, we know that all replacements that start after the end of the current
227   // replacement cannot overlap.
228   Replacement AtEnd(R.getFilePath(), R.getOffset() + R.getLength(), 0, "");
229 
230   // Find the first entry that starts after or at the end of R. Note that
231   // entries that start at the end can still be conflicting if R is an
232   // insertion.
233   auto I = Replaces.lower_bound(AtEnd);
234   // If `I` starts at the same offset as `R`, `R` must be an insertion.
235   if (I != Replaces.end() && R.getOffset() == I->getOffset()) {
236     assert(R.getLength() == 0);
237     // `I` is also an insertion, `R` and `I` conflict.
238     if (I->getLength() == 0) {
239       // Check if two insertions are order-indepedent: if inserting them in
240       // either order produces the same text, they are order-independent.
241       if ((R.getReplacementText() + I->getReplacementText()).str() !=
242           (I->getReplacementText() + R.getReplacementText()).str()) {
243         return makeConflictReplacementsError(R, *I);
244       }
245       // If insertions are order-independent, we can merge them.
246       Replacement NewR(
247           R.getFilePath(), R.getOffset(), 0,
248           (R.getReplacementText() + I->getReplacementText()).str());
249       Replaces.erase(I);
250       Replaces.insert(std::move(NewR));
251       return llvm::Error::success();
252     }
253     // Insertion `R` is adjacent to a non-insertion replacement `I`, so they
254     // are order-independent. It is safe to assume that `R` will not conflict
255     // with any replacement before `I` since all replacements before `I` must
256     // either end before `R` or end at `R` but has length > 0 (if the
257     // replacement before `I` is an insertion at `R`, it would have been `I`
258     // since it is a lower bound of `AtEnd` and ordered before the current `I`
259     // in the set).
260     Replaces.insert(R);
261     return llvm::Error::success();
262   }
263 
264   // `I` is the smallest iterator (after `R`) whose entry cannot overlap.
265   // If that is begin(), there are no overlaps.
266   if (I == Replaces.begin()) {
267     Replaces.insert(R);
268     return llvm::Error::success();
269   }
270   --I;
271   auto Overlap = [](const Replacement &R1, const Replacement &R2) -> bool {
272     return Range(R1.getOffset(), R1.getLength())
273         .overlapsWith(Range(R2.getOffset(), R2.getLength()));
274   };
275   // If the previous entry does not overlap, we know that entries before it
276   // can also not overlap.
277   if (!Overlap(R, *I)) {
278     // If `R` and `I` do not have the same offset, it is safe to add `R` since
279     // it must come after `I`. Otherwise:
280     //   - If `R` is an insertion, `I` must not be an insertion since it would
281     //   have come after `AtEnd`.
282     //   - If `R` is not an insertion, `I` must be an insertion; otherwise, `R`
283     //   and `I` would have overlapped.
284     // In either case, we can safely insert `R`.
285     Replaces.insert(R);
286   } else {
287     // `I` overlaps with `R`. We need to check `R` against all overlapping
288     // replacements to see if they are order-indepedent. If they are, merge `R`
289     // with them and replace them with the merged replacements.
290     auto MergeBegin = I;
291     auto MergeEnd = std::next(I);
292     while (I != Replaces.begin()) {
293       --I;
294       // If `I` doesn't overlap with `R`, don't merge it.
295       if (!Overlap(R, *I))
296         break;
297       MergeBegin = I;
298     }
299     Replacements OverlapReplaces(MergeBegin, MergeEnd);
300     llvm::Expected<Replacements> Merged =
301         OverlapReplaces.mergeIfOrderIndependent(R);
302     if (!Merged)
303       return Merged.takeError();
304     Replaces.erase(MergeBegin, MergeEnd);
305     Replaces.insert(Merged->begin(), Merged->end());
306   }
307   return llvm::Error::success();
308 }
309 
310 namespace {
311 
312 // Represents a merged replacement, i.e. a replacement consisting of multiple
313 // overlapping replacements from 'First' and 'Second' in mergeReplacements.
314 //
315 // Position projection:
316 // Offsets and lengths of the replacements can generally refer to two different
317 // coordinate spaces. Replacements from 'First' refer to the original text
318 // whereas replacements from 'Second' refer to the text after applying 'First'.
319 //
320 // MergedReplacement always operates in the coordinate space of the original
321 // text, i.e. transforms elements from 'Second' to take into account what was
322 // changed based on the elements from 'First'.
323 //
324 // We can correctly calculate this projection as we look at the replacements in
325 // order of strictly increasing offsets.
326 //
327 // Invariants:
328 // * We always merge elements from 'First' into elements from 'Second' and vice
329 //   versa. Within each set, the replacements are non-overlapping.
330 // * We only extend to the right, i.e. merge elements with strictly increasing
331 //   offsets.
332 class MergedReplacement {
333 public:
334   MergedReplacement(const Replacement &R, bool MergeSecond, int D)
335       : MergeSecond(MergeSecond), Delta(D), FilePath(R.getFilePath()),
336         Offset(R.getOffset() + (MergeSecond ? 0 : Delta)), Length(R.getLength()),
337         Text(R.getReplacementText()) {
338     Delta += MergeSecond ? 0 : Text.size() - Length;
339     DeltaFirst = MergeSecond ? Text.size() - Length : 0;
340   }
341 
342   // Merges the next element 'R' into this merged element. As we always merge
343   // from 'First' into 'Second' or vice versa, the MergedReplacement knows what
344   // set the next element is coming from.
345   void merge(const Replacement &R) {
346     if (MergeSecond) {
347       unsigned REnd = R.getOffset() + Delta + R.getLength();
348       unsigned End = Offset + Text.size();
349       if (REnd > End) {
350         Length += REnd - End;
351         MergeSecond = false;
352       }
353       StringRef TextRef = Text;
354       StringRef Head = TextRef.substr(0, R.getOffset() + Delta - Offset);
355       StringRef Tail = TextRef.substr(REnd - Offset);
356       Text = (Head + R.getReplacementText() + Tail).str();
357       Delta += R.getReplacementText().size() - R.getLength();
358     } else {
359       unsigned End = Offset + Length;
360       StringRef RText = R.getReplacementText();
361       StringRef Tail = RText.substr(End - R.getOffset());
362       Text = (Text + Tail).str();
363       if (R.getOffset() + RText.size() > End) {
364         Length = R.getOffset() + R.getLength() - Offset;
365         MergeSecond = true;
366       } else {
367         Length += R.getLength() - RText.size();
368       }
369       DeltaFirst += RText.size() - R.getLength();
370     }
371   }
372 
373   // Returns 'true' if 'R' starts strictly after the MergedReplacement and thus
374   // doesn't need to be merged.
375   bool endsBefore(const Replacement &R) const {
376     if (MergeSecond)
377       return Offset + Text.size() < R.getOffset() + Delta;
378     return Offset + Length < R.getOffset();
379   }
380 
381   // Returns 'true' if an element from the second set should be merged next.
382   bool mergeSecond() const { return MergeSecond; }
383   int deltaFirst() const { return DeltaFirst; }
384   Replacement asReplacement() const { return {FilePath, Offset, Length, Text}; }
385 
386 private:
387   bool MergeSecond;
388 
389   // Amount of characters that elements from 'Second' need to be shifted by in
390   // order to refer to the original text.
391   int Delta;
392 
393   // Sum of all deltas (text-length - length) of elements from 'First' merged
394   // into this element. This is used to update 'Delta' once the
395   // MergedReplacement is completed.
396   int DeltaFirst;
397 
398   // Data of the actually merged replacement. FilePath and Offset aren't changed
399   // as the element is only extended to the right.
400   const StringRef FilePath;
401   const unsigned Offset;
402   unsigned Length;
403   std::string Text;
404 };
405 
406 } // namespace
407 
408 Replacements Replacements::merge(const Replacements &ReplacesToMerge) const {
409   if (empty() || ReplacesToMerge.empty())
410     return empty() ? ReplacesToMerge : *this;
411 
412   auto &First = Replaces;
413   auto &Second = ReplacesToMerge.Replaces;
414   // Delta is the amount of characters that replacements from 'Second' need to
415   // be shifted so that their offsets refer to the original text.
416   int Delta = 0;
417   ReplacementsImpl Result;
418 
419   // Iterate over both sets and always add the next element (smallest total
420   // Offset) from either 'First' or 'Second'. Merge that element with
421   // subsequent replacements as long as they overlap. See more details in the
422   // comment on MergedReplacement.
423   for (auto FirstI = First.begin(), SecondI = Second.begin();
424        FirstI != First.end() || SecondI != Second.end();) {
425     bool NextIsFirst = SecondI == Second.end() ||
426                        (FirstI != First.end() &&
427                         FirstI->getOffset() < SecondI->getOffset() + Delta);
428     MergedReplacement Merged(NextIsFirst ? *FirstI : *SecondI, NextIsFirst,
429                              Delta);
430     ++(NextIsFirst ? FirstI : SecondI);
431 
432     while ((Merged.mergeSecond() && SecondI != Second.end()) ||
433            (!Merged.mergeSecond() && FirstI != First.end())) {
434       auto &I = Merged.mergeSecond() ? SecondI : FirstI;
435       if (Merged.endsBefore(*I))
436         break;
437       Merged.merge(*I);
438       ++I;
439     }
440     Delta -= Merged.deltaFirst();
441     Result.insert(Merged.asReplacement());
442   }
443   return Replacements(Result.begin(), Result.end());
444 }
445 
446 // Combines overlapping ranges in \p Ranges and sorts the combined ranges.
447 // Returns a set of non-overlapping and sorted ranges that is equivalent to
448 // \p Ranges.
449 static std::vector<Range> combineAndSortRanges(std::vector<Range> Ranges) {
450   std::sort(Ranges.begin(), Ranges.end(),
451             [](const Range &LHS, const Range &RHS) {
452               if (LHS.getOffset() != RHS.getOffset())
453                 return LHS.getOffset() < RHS.getOffset();
454               return LHS.getLength() < RHS.getLength();
455             });
456   std::vector<Range> Result;
457   for (const auto &R : Ranges) {
458     if (Result.empty() ||
459         Result.back().getOffset() + Result.back().getLength() < R.getOffset()) {
460       Result.push_back(R);
461     } else {
462       unsigned NewEnd =
463           std::max(Result.back().getOffset() + Result.back().getLength(),
464                    R.getOffset() + R.getLength());
465       Result[Result.size() - 1] =
466           Range(Result.back().getOffset(), NewEnd - Result.back().getOffset());
467     }
468   }
469   return Result;
470 }
471 
472 std::vector<Range>
473 calculateRangesAfterReplacements(const Replacements &Replaces,
474                                  const std::vector<Range> &Ranges) {
475   // To calculate the new ranges,
476   //   - Turn \p Ranges into Replacements at (offset, length) with an empty
477   //     (unimportant) replacement text of length "length".
478   //   - Merge with \p Replaces.
479   //   - The new ranges will be the affected ranges of the merged replacements.
480   auto MergedRanges = combineAndSortRanges(Ranges);
481   if (Replaces.empty())
482     return MergedRanges;
483   tooling::Replacements FakeReplaces;
484   for (const auto &R : MergedRanges) {
485     auto Err = FakeReplaces.add(Replacement(Replaces.begin()->getFilePath(),
486                                             R.getOffset(), R.getLength(),
487                                             std::string(R.getLength(), ' ')));
488     assert(!Err &&
489            "Replacements must not conflict since ranges have been merged.");
490     (void)Err;
491   }
492   return FakeReplaces.merge(Replaces).getAffectedRanges();
493 }
494 
495 std::vector<Range> Replacements::getAffectedRanges() const {
496   std::vector<Range> ChangedRanges;
497   int Shift = 0;
498   for (const Replacement &R : Replaces) {
499     unsigned Offset = R.getOffset() + Shift;
500     unsigned Length = R.getReplacementText().size();
501     Shift += Length - R.getLength();
502     ChangedRanges.push_back(Range(Offset, Length));
503   }
504   return combineAndSortRanges(ChangedRanges);
505 }
506 
507 unsigned Replacements::getShiftedCodePosition(unsigned Position) const {
508   unsigned Offset = 0;
509   for (const auto& R : Replaces) {
510     if (R.getOffset() + R.getLength() <= Position) {
511       Offset += R.getReplacementText().size() - R.getLength();
512       continue;
513     }
514     if (R.getOffset() < Position &&
515         R.getOffset() + R.getReplacementText().size() <= Position) {
516       Position = R.getOffset() + R.getReplacementText().size();
517       if (R.getReplacementText().size() > 0)
518         Position--;
519     }
520     break;
521   }
522   return Position + Offset;
523 }
524 
525 bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) {
526   bool Result = true;
527   for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
528     if (I->isApplicable()) {
529       Result = I->apply(Rewrite) && Result;
530     } else {
531       Result = false;
532     }
533   }
534   return Result;
535 }
536 
537 llvm::Expected<std::string> applyAllReplacements(StringRef Code,
538                                                 const Replacements &Replaces) {
539   if (Replaces.empty())
540     return Code.str();
541 
542   IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
543       new vfs::InMemoryFileSystem);
544   FileManager Files(FileSystemOptions(), InMemoryFileSystem);
545   DiagnosticsEngine Diagnostics(
546       IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
547       new DiagnosticOptions);
548   SourceManager SourceMgr(Diagnostics, Files);
549   Rewriter Rewrite(SourceMgr, LangOptions());
550   InMemoryFileSystem->addFile(
551       "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>"));
552   FileID ID = SourceMgr.createFileID(Files.getFile("<stdin>"), SourceLocation(),
553                                      clang::SrcMgr::C_User);
554   for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) {
555     Replacement Replace("<stdin>", I->getOffset(), I->getLength(),
556                         I->getReplacementText());
557     if (!Replace.apply(Rewrite))
558       return llvm::make_error<llvm::StringError>(
559           "Failed to apply replacement: " + Replace.toString(),
560           llvm::inconvertibleErrorCode());
561   }
562   std::string Result;
563   llvm::raw_string_ostream OS(Result);
564   Rewrite.getEditBuffer(ID).write(OS);
565   OS.flush();
566   return Result;
567 }
568 
569 std::map<std::string, Replacements> groupReplacementsByFile(
570     FileManager &FileMgr,
571     const std::map<std::string, Replacements> &FileToReplaces) {
572   std::map<std::string, Replacements> Result;
573   llvm::SmallPtrSet<const FileEntry *, 16> ProcessedFileEntries;
574   for (const auto &Entry : FileToReplaces) {
575     const FileEntry *FE = FileMgr.getFile(Entry.first);
576     if (!FE)
577       llvm::errs() << "File path " << Entry.first << " is invalid.\n";
578     else if (ProcessedFileEntries.insert(FE).second)
579       Result[Entry.first] = std::move(Entry.second);
580   }
581   return Result;
582 }
583 
584 } // end namespace tooling
585 } // end namespace clang
586