xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Edit/EditedSource.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===- EditedSource.cpp - Collection of source edits ----------------------===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg 
97330f729Sjoerg #include "clang/Edit/EditedSource.h"
107330f729Sjoerg #include "clang/Basic/CharInfo.h"
117330f729Sjoerg #include "clang/Basic/LLVM.h"
127330f729Sjoerg #include "clang/Basic/SourceLocation.h"
137330f729Sjoerg #include "clang/Basic/SourceManager.h"
147330f729Sjoerg #include "clang/Edit/Commit.h"
157330f729Sjoerg #include "clang/Edit/EditsReceiver.h"
167330f729Sjoerg #include "clang/Edit/FileOffset.h"
177330f729Sjoerg #include "clang/Lex/Lexer.h"
187330f729Sjoerg #include "llvm/ADT/STLExtras.h"
197330f729Sjoerg #include "llvm/ADT/SmallString.h"
207330f729Sjoerg #include "llvm/ADT/StringRef.h"
217330f729Sjoerg #include "llvm/ADT/Twine.h"
227330f729Sjoerg #include <algorithm>
237330f729Sjoerg #include <cassert>
247330f729Sjoerg #include <tuple>
257330f729Sjoerg #include <utility>
267330f729Sjoerg 
277330f729Sjoerg using namespace clang;
287330f729Sjoerg using namespace edit;
297330f729Sjoerg 
remove(CharSourceRange range)307330f729Sjoerg void EditsReceiver::remove(CharSourceRange range) {
317330f729Sjoerg   replace(range, StringRef());
327330f729Sjoerg }
337330f729Sjoerg 
deconstructMacroArgLoc(SourceLocation Loc,SourceLocation & ExpansionLoc,MacroArgUse & ArgUse)347330f729Sjoerg void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
357330f729Sjoerg                                           SourceLocation &ExpansionLoc,
367330f729Sjoerg                                           MacroArgUse &ArgUse) {
377330f729Sjoerg   assert(SourceMgr.isMacroArgExpansion(Loc));
387330f729Sjoerg   SourceLocation DefArgLoc =
397330f729Sjoerg       SourceMgr.getImmediateExpansionRange(Loc).getBegin();
407330f729Sjoerg   SourceLocation ImmediateExpansionLoc =
417330f729Sjoerg       SourceMgr.getImmediateExpansionRange(DefArgLoc).getBegin();
427330f729Sjoerg   ExpansionLoc = ImmediateExpansionLoc;
437330f729Sjoerg   while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
447330f729Sjoerg     ExpansionLoc =
457330f729Sjoerg         SourceMgr.getImmediateExpansionRange(ExpansionLoc).getBegin();
467330f729Sjoerg   SmallString<20> Buf;
477330f729Sjoerg   StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
487330f729Sjoerg                                          Buf, SourceMgr, LangOpts);
497330f729Sjoerg   ArgUse = MacroArgUse{nullptr, SourceLocation(), SourceLocation()};
507330f729Sjoerg   if (!ArgName.empty())
517330f729Sjoerg     ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
527330f729Sjoerg               SourceMgr.getSpellingLoc(DefArgLoc)};
537330f729Sjoerg }
547330f729Sjoerg 
startingCommit()557330f729Sjoerg void EditedSource::startingCommit() {}
567330f729Sjoerg 
finishedCommit()577330f729Sjoerg void EditedSource::finishedCommit() {
587330f729Sjoerg   for (auto &ExpArg : CurrCommitMacroArgExps) {
597330f729Sjoerg     SourceLocation ExpLoc;
607330f729Sjoerg     MacroArgUse ArgUse;
617330f729Sjoerg     std::tie(ExpLoc, ArgUse) = ExpArg;
62*e038c9c4Sjoerg     auto &ArgUses = ExpansionToArgMap[ExpLoc];
637330f729Sjoerg     if (llvm::find(ArgUses, ArgUse) == ArgUses.end())
647330f729Sjoerg       ArgUses.push_back(ArgUse);
657330f729Sjoerg   }
667330f729Sjoerg   CurrCommitMacroArgExps.clear();
677330f729Sjoerg }
687330f729Sjoerg 
copyString(const Twine & twine)697330f729Sjoerg StringRef EditedSource::copyString(const Twine &twine) {
707330f729Sjoerg   SmallString<128> Data;
717330f729Sjoerg   return copyString(twine.toStringRef(Data));
727330f729Sjoerg }
737330f729Sjoerg 
canInsertInOffset(SourceLocation OrigLoc,FileOffset Offs)747330f729Sjoerg bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
757330f729Sjoerg   FileEditsTy::iterator FA = getActionForOffset(Offs);
767330f729Sjoerg   if (FA != FileEdits.end()) {
777330f729Sjoerg     if (FA->first != Offs)
787330f729Sjoerg       return false; // position has been removed.
797330f729Sjoerg   }
807330f729Sjoerg 
817330f729Sjoerg   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
827330f729Sjoerg     SourceLocation ExpLoc;
837330f729Sjoerg     MacroArgUse ArgUse;
847330f729Sjoerg     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
85*e038c9c4Sjoerg     auto I = ExpansionToArgMap.find(ExpLoc);
867330f729Sjoerg     if (I != ExpansionToArgMap.end() &&
877330f729Sjoerg         find_if(I->second, [&](const MacroArgUse &U) {
887330f729Sjoerg           return ArgUse.Identifier == U.Identifier &&
897330f729Sjoerg                  std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
907330f729Sjoerg                      std::tie(U.ImmediateExpansionLoc, U.UseLoc);
917330f729Sjoerg         }) != I->second.end()) {
927330f729Sjoerg       // Trying to write in a macro argument input that has already been
937330f729Sjoerg       // written by a previous commit for another expansion of the same macro
947330f729Sjoerg       // argument name. For example:
957330f729Sjoerg       //
967330f729Sjoerg       // \code
977330f729Sjoerg       //   #define MAC(x) ((x)+(x))
987330f729Sjoerg       //   MAC(a)
997330f729Sjoerg       // \endcode
1007330f729Sjoerg       //
1017330f729Sjoerg       // A commit modified the macro argument 'a' due to the first '(x)'
1027330f729Sjoerg       // expansion inside the macro definition, and a subsequent commit tried
1037330f729Sjoerg       // to modify 'a' again for the second '(x)' expansion. The edits of the
1047330f729Sjoerg       // second commit will be rejected.
1057330f729Sjoerg       return false;
1067330f729Sjoerg     }
1077330f729Sjoerg   }
1087330f729Sjoerg   return true;
1097330f729Sjoerg }
1107330f729Sjoerg 
commitInsert(SourceLocation OrigLoc,FileOffset Offs,StringRef text,bool beforePreviousInsertions)1117330f729Sjoerg bool EditedSource::commitInsert(SourceLocation OrigLoc,
1127330f729Sjoerg                                 FileOffset Offs, StringRef text,
1137330f729Sjoerg                                 bool beforePreviousInsertions) {
1147330f729Sjoerg   if (!canInsertInOffset(OrigLoc, Offs))
1157330f729Sjoerg     return false;
1167330f729Sjoerg   if (text.empty())
1177330f729Sjoerg     return true;
1187330f729Sjoerg 
1197330f729Sjoerg   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
1207330f729Sjoerg     MacroArgUse ArgUse;
1217330f729Sjoerg     SourceLocation ExpLoc;
1227330f729Sjoerg     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
1237330f729Sjoerg     if (ArgUse.Identifier)
1247330f729Sjoerg       CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
1257330f729Sjoerg   }
1267330f729Sjoerg 
1277330f729Sjoerg   FileEdit &FA = FileEdits[Offs];
1287330f729Sjoerg   if (FA.Text.empty()) {
1297330f729Sjoerg     FA.Text = copyString(text);
1307330f729Sjoerg     return true;
1317330f729Sjoerg   }
1327330f729Sjoerg 
1337330f729Sjoerg   if (beforePreviousInsertions)
1347330f729Sjoerg     FA.Text = copyString(Twine(text) + FA.Text);
1357330f729Sjoerg   else
1367330f729Sjoerg     FA.Text = copyString(Twine(FA.Text) + text);
1377330f729Sjoerg 
1387330f729Sjoerg   return true;
1397330f729Sjoerg }
1407330f729Sjoerg 
commitInsertFromRange(SourceLocation OrigLoc,FileOffset Offs,FileOffset InsertFromRangeOffs,unsigned Len,bool beforePreviousInsertions)1417330f729Sjoerg bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
1427330f729Sjoerg                                    FileOffset Offs,
1437330f729Sjoerg                                    FileOffset InsertFromRangeOffs, unsigned Len,
1447330f729Sjoerg                                    bool beforePreviousInsertions) {
1457330f729Sjoerg   if (Len == 0)
1467330f729Sjoerg     return true;
1477330f729Sjoerg 
1487330f729Sjoerg   SmallString<128> StrVec;
1497330f729Sjoerg   FileOffset BeginOffs = InsertFromRangeOffs;
1507330f729Sjoerg   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
1517330f729Sjoerg   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
1527330f729Sjoerg   if (I != FileEdits.begin())
1537330f729Sjoerg     --I;
1547330f729Sjoerg 
1557330f729Sjoerg   for (; I != FileEdits.end(); ++I) {
1567330f729Sjoerg     FileEdit &FA = I->second;
1577330f729Sjoerg     FileOffset B = I->first;
1587330f729Sjoerg     FileOffset E = B.getWithOffset(FA.RemoveLen);
1597330f729Sjoerg 
1607330f729Sjoerg     if (BeginOffs == B)
1617330f729Sjoerg       break;
1627330f729Sjoerg 
1637330f729Sjoerg     if (BeginOffs < E) {
1647330f729Sjoerg       if (BeginOffs > B) {
1657330f729Sjoerg         BeginOffs = E;
1667330f729Sjoerg         ++I;
1677330f729Sjoerg       }
1687330f729Sjoerg       break;
1697330f729Sjoerg     }
1707330f729Sjoerg   }
1717330f729Sjoerg 
1727330f729Sjoerg   for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
1737330f729Sjoerg     FileEdit &FA = I->second;
1747330f729Sjoerg     FileOffset B = I->first;
1757330f729Sjoerg     FileOffset E = B.getWithOffset(FA.RemoveLen);
1767330f729Sjoerg 
1777330f729Sjoerg     if (BeginOffs < B) {
1787330f729Sjoerg       bool Invalid = false;
1797330f729Sjoerg       StringRef text = getSourceText(BeginOffs, B, Invalid);
1807330f729Sjoerg       if (Invalid)
1817330f729Sjoerg         return false;
1827330f729Sjoerg       StrVec += text;
1837330f729Sjoerg     }
1847330f729Sjoerg     StrVec += FA.Text;
1857330f729Sjoerg     BeginOffs = E;
1867330f729Sjoerg   }
1877330f729Sjoerg 
1887330f729Sjoerg   if (BeginOffs < EndOffs) {
1897330f729Sjoerg     bool Invalid = false;
1907330f729Sjoerg     StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
1917330f729Sjoerg     if (Invalid)
1927330f729Sjoerg       return false;
1937330f729Sjoerg     StrVec += text;
1947330f729Sjoerg   }
1957330f729Sjoerg 
1967330f729Sjoerg   return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
1977330f729Sjoerg }
1987330f729Sjoerg 
commitRemove(SourceLocation OrigLoc,FileOffset BeginOffs,unsigned Len)1997330f729Sjoerg void EditedSource::commitRemove(SourceLocation OrigLoc,
2007330f729Sjoerg                                 FileOffset BeginOffs, unsigned Len) {
2017330f729Sjoerg   if (Len == 0)
2027330f729Sjoerg     return;
2037330f729Sjoerg 
2047330f729Sjoerg   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
2057330f729Sjoerg   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
2067330f729Sjoerg   if (I != FileEdits.begin())
2077330f729Sjoerg     --I;
2087330f729Sjoerg 
2097330f729Sjoerg   for (; I != FileEdits.end(); ++I) {
2107330f729Sjoerg     FileEdit &FA = I->second;
2117330f729Sjoerg     FileOffset B = I->first;
2127330f729Sjoerg     FileOffset E = B.getWithOffset(FA.RemoveLen);
2137330f729Sjoerg 
2147330f729Sjoerg     if (BeginOffs < E)
2157330f729Sjoerg       break;
2167330f729Sjoerg   }
2177330f729Sjoerg 
2187330f729Sjoerg   FileOffset TopBegin, TopEnd;
2197330f729Sjoerg   FileEdit *TopFA = nullptr;
2207330f729Sjoerg 
2217330f729Sjoerg   if (I == FileEdits.end()) {
2227330f729Sjoerg     FileEditsTy::iterator
2237330f729Sjoerg       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
2247330f729Sjoerg     NewI->second.RemoveLen = Len;
2257330f729Sjoerg     return;
2267330f729Sjoerg   }
2277330f729Sjoerg 
2287330f729Sjoerg   FileEdit &FA = I->second;
2297330f729Sjoerg   FileOffset B = I->first;
2307330f729Sjoerg   FileOffset E = B.getWithOffset(FA.RemoveLen);
2317330f729Sjoerg   if (BeginOffs < B) {
2327330f729Sjoerg     FileEditsTy::iterator
2337330f729Sjoerg       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
2347330f729Sjoerg     TopBegin = BeginOffs;
2357330f729Sjoerg     TopEnd = EndOffs;
2367330f729Sjoerg     TopFA = &NewI->second;
2377330f729Sjoerg     TopFA->RemoveLen = Len;
2387330f729Sjoerg   } else {
2397330f729Sjoerg     TopBegin = B;
2407330f729Sjoerg     TopEnd = E;
2417330f729Sjoerg     TopFA = &I->second;
2427330f729Sjoerg     if (TopEnd >= EndOffs)
2437330f729Sjoerg       return;
2447330f729Sjoerg     unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
2457330f729Sjoerg     TopEnd = EndOffs;
2467330f729Sjoerg     TopFA->RemoveLen += diff;
2477330f729Sjoerg     if (B == BeginOffs)
2487330f729Sjoerg       TopFA->Text = StringRef();
2497330f729Sjoerg     ++I;
2507330f729Sjoerg   }
2517330f729Sjoerg 
2527330f729Sjoerg   while (I != FileEdits.end()) {
2537330f729Sjoerg     FileEdit &FA = I->second;
2547330f729Sjoerg     FileOffset B = I->first;
2557330f729Sjoerg     FileOffset E = B.getWithOffset(FA.RemoveLen);
2567330f729Sjoerg 
2577330f729Sjoerg     if (B >= TopEnd)
2587330f729Sjoerg       break;
2597330f729Sjoerg 
2607330f729Sjoerg     if (E <= TopEnd) {
2617330f729Sjoerg       FileEdits.erase(I++);
2627330f729Sjoerg       continue;
2637330f729Sjoerg     }
2647330f729Sjoerg 
2657330f729Sjoerg     if (B < TopEnd) {
2667330f729Sjoerg       unsigned diff = E.getOffset() - TopEnd.getOffset();
2677330f729Sjoerg       TopEnd = E;
2687330f729Sjoerg       TopFA->RemoveLen += diff;
2697330f729Sjoerg       FileEdits.erase(I);
2707330f729Sjoerg     }
2717330f729Sjoerg 
2727330f729Sjoerg     break;
2737330f729Sjoerg   }
2747330f729Sjoerg }
2757330f729Sjoerg 
commit(const Commit & commit)2767330f729Sjoerg bool EditedSource::commit(const Commit &commit) {
2777330f729Sjoerg   if (!commit.isCommitable())
2787330f729Sjoerg     return false;
2797330f729Sjoerg 
2807330f729Sjoerg   struct CommitRAII {
2817330f729Sjoerg     EditedSource &Editor;
2827330f729Sjoerg 
2837330f729Sjoerg     CommitRAII(EditedSource &Editor) : Editor(Editor) {
2847330f729Sjoerg       Editor.startingCommit();
2857330f729Sjoerg     }
2867330f729Sjoerg 
2877330f729Sjoerg     ~CommitRAII() {
2887330f729Sjoerg       Editor.finishedCommit();
2897330f729Sjoerg     }
2907330f729Sjoerg   } CommitRAII(*this);
2917330f729Sjoerg 
2927330f729Sjoerg   for (edit::Commit::edit_iterator
2937330f729Sjoerg          I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
2947330f729Sjoerg     const edit::Commit::Edit &edit = *I;
2957330f729Sjoerg     switch (edit.Kind) {
2967330f729Sjoerg     case edit::Commit::Act_Insert:
2977330f729Sjoerg       commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
2987330f729Sjoerg       break;
2997330f729Sjoerg     case edit::Commit::Act_InsertFromRange:
3007330f729Sjoerg       commitInsertFromRange(edit.OrigLoc, edit.Offset,
3017330f729Sjoerg                             edit.InsertFromRangeOffs, edit.Length,
3027330f729Sjoerg                             edit.BeforePrev);
3037330f729Sjoerg       break;
3047330f729Sjoerg     case edit::Commit::Act_Remove:
3057330f729Sjoerg       commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
3067330f729Sjoerg       break;
3077330f729Sjoerg     }
3087330f729Sjoerg   }
3097330f729Sjoerg 
3107330f729Sjoerg   return true;
3117330f729Sjoerg }
3127330f729Sjoerg 
3137330f729Sjoerg // Returns true if it is ok to make the two given characters adjacent.
canBeJoined(char left,char right,const LangOptions & LangOpts)3147330f729Sjoerg static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
3157330f729Sjoerg   // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
3167330f729Sjoerg   // making two '<' adjacent.
3177330f729Sjoerg   return !(Lexer::isIdentifierBodyChar(left, LangOpts) &&
3187330f729Sjoerg            Lexer::isIdentifierBodyChar(right, LangOpts));
3197330f729Sjoerg }
3207330f729Sjoerg 
3217330f729Sjoerg /// Returns true if it is ok to eliminate the trailing whitespace between
3227330f729Sjoerg /// the given characters.
canRemoveWhitespace(char left,char beforeWSpace,char right,const LangOptions & LangOpts)3237330f729Sjoerg static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
3247330f729Sjoerg                                 const LangOptions &LangOpts) {
3257330f729Sjoerg   if (!canBeJoined(left, right, LangOpts))
3267330f729Sjoerg     return false;
3277330f729Sjoerg   if (isWhitespace(left) || isWhitespace(right))
3287330f729Sjoerg     return true;
3297330f729Sjoerg   if (canBeJoined(beforeWSpace, right, LangOpts))
3307330f729Sjoerg     return false; // the whitespace was intentional, keep it.
3317330f729Sjoerg   return true;
3327330f729Sjoerg }
3337330f729Sjoerg 
3347330f729Sjoerg /// Check the range that we are going to remove and:
3357330f729Sjoerg /// -Remove any trailing whitespace if possible.
3367330f729Sjoerg /// -Insert a space if removing the range is going to mess up the source tokens.
adjustRemoval(const SourceManager & SM,const LangOptions & LangOpts,SourceLocation Loc,FileOffset offs,unsigned & len,StringRef & text)3377330f729Sjoerg static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
3387330f729Sjoerg                           SourceLocation Loc, FileOffset offs,
3397330f729Sjoerg                           unsigned &len, StringRef &text) {
3407330f729Sjoerg   assert(len && text.empty());
3417330f729Sjoerg   SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
3427330f729Sjoerg   if (BeginTokLoc != Loc)
3437330f729Sjoerg     return; // the range is not at the beginning of a token, keep the range.
3447330f729Sjoerg 
3457330f729Sjoerg   bool Invalid = false;
3467330f729Sjoerg   StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
3477330f729Sjoerg   if (Invalid)
3487330f729Sjoerg     return;
3497330f729Sjoerg 
3507330f729Sjoerg   unsigned begin = offs.getOffset();
3517330f729Sjoerg   unsigned end = begin + len;
3527330f729Sjoerg 
3537330f729Sjoerg   // Do not try to extend the removal if we're at the end of the buffer already.
3547330f729Sjoerg   if (end == buffer.size())
3557330f729Sjoerg     return;
3567330f729Sjoerg 
3577330f729Sjoerg   assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
3587330f729Sjoerg 
3597330f729Sjoerg   // FIXME: Remove newline.
3607330f729Sjoerg 
3617330f729Sjoerg   if (begin == 0) {
3627330f729Sjoerg     if (buffer[end] == ' ')
3637330f729Sjoerg       ++len;
3647330f729Sjoerg     return;
3657330f729Sjoerg   }
3667330f729Sjoerg 
3677330f729Sjoerg   if (buffer[end] == ' ') {
3687330f729Sjoerg     assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
3697330f729Sjoerg            "buffer not zero-terminated!");
3707330f729Sjoerg     if (canRemoveWhitespace(/*left=*/buffer[begin-1],
3717330f729Sjoerg                             /*beforeWSpace=*/buffer[end-1],
3727330f729Sjoerg                             /*right=*/buffer.data()[end + 1], // zero-terminated
3737330f729Sjoerg                             LangOpts))
3747330f729Sjoerg       ++len;
3757330f729Sjoerg     return;
3767330f729Sjoerg   }
3777330f729Sjoerg 
3787330f729Sjoerg   if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
3797330f729Sjoerg     text = " ";
3807330f729Sjoerg }
3817330f729Sjoerg 
applyRewrite(EditsReceiver & receiver,StringRef text,FileOffset offs,unsigned len,const SourceManager & SM,const LangOptions & LangOpts,bool shouldAdjustRemovals)3827330f729Sjoerg static void applyRewrite(EditsReceiver &receiver,
3837330f729Sjoerg                          StringRef text, FileOffset offs, unsigned len,
3847330f729Sjoerg                          const SourceManager &SM, const LangOptions &LangOpts,
3857330f729Sjoerg                          bool shouldAdjustRemovals) {
3867330f729Sjoerg   assert(offs.getFID().isValid());
3877330f729Sjoerg   SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
3887330f729Sjoerg   Loc = Loc.getLocWithOffset(offs.getOffset());
3897330f729Sjoerg   assert(Loc.isFileID());
3907330f729Sjoerg 
3917330f729Sjoerg   if (text.empty() && shouldAdjustRemovals)
3927330f729Sjoerg     adjustRemoval(SM, LangOpts, Loc, offs, len, text);
3937330f729Sjoerg 
3947330f729Sjoerg   CharSourceRange range = CharSourceRange::getCharRange(Loc,
3957330f729Sjoerg                                                      Loc.getLocWithOffset(len));
3967330f729Sjoerg 
3977330f729Sjoerg   if (text.empty()) {
3987330f729Sjoerg     assert(len);
3997330f729Sjoerg     receiver.remove(range);
4007330f729Sjoerg     return;
4017330f729Sjoerg   }
4027330f729Sjoerg 
4037330f729Sjoerg   if (len)
4047330f729Sjoerg     receiver.replace(range, text);
4057330f729Sjoerg   else
4067330f729Sjoerg     receiver.insert(Loc, text);
4077330f729Sjoerg }
4087330f729Sjoerg 
applyRewrites(EditsReceiver & receiver,bool shouldAdjustRemovals)4097330f729Sjoerg void EditedSource::applyRewrites(EditsReceiver &receiver,
4107330f729Sjoerg                                  bool shouldAdjustRemovals) {
4117330f729Sjoerg   SmallString<128> StrVec;
4127330f729Sjoerg   FileOffset CurOffs, CurEnd;
4137330f729Sjoerg   unsigned CurLen;
4147330f729Sjoerg 
4157330f729Sjoerg   if (FileEdits.empty())
4167330f729Sjoerg     return;
4177330f729Sjoerg 
4187330f729Sjoerg   FileEditsTy::iterator I = FileEdits.begin();
4197330f729Sjoerg   CurOffs = I->first;
4207330f729Sjoerg   StrVec = I->second.Text;
4217330f729Sjoerg   CurLen = I->second.RemoveLen;
4227330f729Sjoerg   CurEnd = CurOffs.getWithOffset(CurLen);
4237330f729Sjoerg   ++I;
4247330f729Sjoerg 
4257330f729Sjoerg   for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
4267330f729Sjoerg     FileOffset offs = I->first;
4277330f729Sjoerg     FileEdit act = I->second;
4287330f729Sjoerg     assert(offs >= CurEnd);
4297330f729Sjoerg 
4307330f729Sjoerg     if (offs == CurEnd) {
4317330f729Sjoerg       StrVec += act.Text;
4327330f729Sjoerg       CurLen += act.RemoveLen;
4337330f729Sjoerg       CurEnd.getWithOffset(act.RemoveLen);
4347330f729Sjoerg       continue;
4357330f729Sjoerg     }
4367330f729Sjoerg 
4377330f729Sjoerg     applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
4387330f729Sjoerg                  shouldAdjustRemovals);
4397330f729Sjoerg     CurOffs = offs;
4407330f729Sjoerg     StrVec = act.Text;
4417330f729Sjoerg     CurLen = act.RemoveLen;
4427330f729Sjoerg     CurEnd = CurOffs.getWithOffset(CurLen);
4437330f729Sjoerg   }
4447330f729Sjoerg 
4457330f729Sjoerg   applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
4467330f729Sjoerg                shouldAdjustRemovals);
4477330f729Sjoerg }
4487330f729Sjoerg 
clearRewrites()4497330f729Sjoerg void EditedSource::clearRewrites() {
4507330f729Sjoerg   FileEdits.clear();
4517330f729Sjoerg   StrAlloc.Reset();
4527330f729Sjoerg }
4537330f729Sjoerg 
getSourceText(FileOffset BeginOffs,FileOffset EndOffs,bool & Invalid)4547330f729Sjoerg StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
4557330f729Sjoerg                                       bool &Invalid) {
4567330f729Sjoerg   assert(BeginOffs.getFID() == EndOffs.getFID());
4577330f729Sjoerg   assert(BeginOffs <= EndOffs);
4587330f729Sjoerg   SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
4597330f729Sjoerg   BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
4607330f729Sjoerg   assert(BLoc.isFileID());
4617330f729Sjoerg   SourceLocation
4627330f729Sjoerg     ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
4637330f729Sjoerg   return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
4647330f729Sjoerg                               SourceMgr, LangOpts, &Invalid);
4657330f729Sjoerg }
4667330f729Sjoerg 
4677330f729Sjoerg EditedSource::FileEditsTy::iterator
getActionForOffset(FileOffset Offs)4687330f729Sjoerg EditedSource::getActionForOffset(FileOffset Offs) {
4697330f729Sjoerg   FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
4707330f729Sjoerg   if (I == FileEdits.begin())
4717330f729Sjoerg     return FileEdits.end();
4727330f729Sjoerg   --I;
4737330f729Sjoerg   FileEdit &FA = I->second;
4747330f729Sjoerg   FileOffset B = I->first;
4757330f729Sjoerg   FileOffset E = B.getWithOffset(FA.RemoveLen);
4767330f729Sjoerg   if (Offs >= B && Offs < E)
4777330f729Sjoerg     return I;
4787330f729Sjoerg 
4797330f729Sjoerg   return FileEdits.end();
4807330f729Sjoerg }
481