xref: /freebsd-src/contrib/llvm-project/clang/lib/Edit/EditedSource.cpp (revision 972a253a57b6f144b0e4a3e2080a2a0076ec55a0)
10b57cec5SDimitry Andric //===- EditedSource.cpp - Collection of source edits ----------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "clang/Edit/EditedSource.h"
100b57cec5SDimitry Andric #include "clang/Basic/CharInfo.h"
110b57cec5SDimitry Andric #include "clang/Basic/LLVM.h"
120b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h"
130b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h"
140b57cec5SDimitry Andric #include "clang/Edit/Commit.h"
150b57cec5SDimitry Andric #include "clang/Edit/EditsReceiver.h"
160b57cec5SDimitry Andric #include "clang/Edit/FileOffset.h"
170b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
180b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
190b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
200b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
210b57cec5SDimitry Andric #include "llvm/ADT/Twine.h"
220b57cec5SDimitry Andric #include <algorithm>
230b57cec5SDimitry Andric #include <cassert>
240b57cec5SDimitry Andric #include <tuple>
250b57cec5SDimitry Andric #include <utility>
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric using namespace clang;
280b57cec5SDimitry Andric using namespace edit;
290b57cec5SDimitry Andric 
remove(CharSourceRange range)300b57cec5SDimitry Andric void EditsReceiver::remove(CharSourceRange range) {
310b57cec5SDimitry Andric   replace(range, StringRef());
320b57cec5SDimitry Andric }
330b57cec5SDimitry Andric 
deconstructMacroArgLoc(SourceLocation Loc,SourceLocation & ExpansionLoc,MacroArgUse & ArgUse)340b57cec5SDimitry Andric void EditedSource::deconstructMacroArgLoc(SourceLocation Loc,
350b57cec5SDimitry Andric                                           SourceLocation &ExpansionLoc,
360b57cec5SDimitry Andric                                           MacroArgUse &ArgUse) {
370b57cec5SDimitry Andric   assert(SourceMgr.isMacroArgExpansion(Loc));
380b57cec5SDimitry Andric   SourceLocation DefArgLoc =
390b57cec5SDimitry Andric       SourceMgr.getImmediateExpansionRange(Loc).getBegin();
400b57cec5SDimitry Andric   SourceLocation ImmediateExpansionLoc =
410b57cec5SDimitry Andric       SourceMgr.getImmediateExpansionRange(DefArgLoc).getBegin();
420b57cec5SDimitry Andric   ExpansionLoc = ImmediateExpansionLoc;
430b57cec5SDimitry Andric   while (SourceMgr.isMacroBodyExpansion(ExpansionLoc))
440b57cec5SDimitry Andric     ExpansionLoc =
450b57cec5SDimitry Andric         SourceMgr.getImmediateExpansionRange(ExpansionLoc).getBegin();
460b57cec5SDimitry Andric   SmallString<20> Buf;
470b57cec5SDimitry Andric   StringRef ArgName = Lexer::getSpelling(SourceMgr.getSpellingLoc(DefArgLoc),
480b57cec5SDimitry Andric                                          Buf, SourceMgr, LangOpts);
490b57cec5SDimitry Andric   ArgUse = MacroArgUse{nullptr, SourceLocation(), SourceLocation()};
500b57cec5SDimitry Andric   if (!ArgName.empty())
510b57cec5SDimitry Andric     ArgUse = {&IdentTable.get(ArgName), ImmediateExpansionLoc,
520b57cec5SDimitry Andric               SourceMgr.getSpellingLoc(DefArgLoc)};
530b57cec5SDimitry Andric }
540b57cec5SDimitry Andric 
startingCommit()550b57cec5SDimitry Andric void EditedSource::startingCommit() {}
560b57cec5SDimitry Andric 
finishedCommit()570b57cec5SDimitry Andric void EditedSource::finishedCommit() {
580b57cec5SDimitry Andric   for (auto &ExpArg : CurrCommitMacroArgExps) {
590b57cec5SDimitry Andric     SourceLocation ExpLoc;
600b57cec5SDimitry Andric     MacroArgUse ArgUse;
610b57cec5SDimitry Andric     std::tie(ExpLoc, ArgUse) = ExpArg;
62e8d8bef9SDimitry Andric     auto &ArgUses = ExpansionToArgMap[ExpLoc];
63349cc55cSDimitry Andric     if (!llvm::is_contained(ArgUses, ArgUse))
640b57cec5SDimitry Andric       ArgUses.push_back(ArgUse);
650b57cec5SDimitry Andric   }
660b57cec5SDimitry Andric   CurrCommitMacroArgExps.clear();
670b57cec5SDimitry Andric }
680b57cec5SDimitry Andric 
copyString(const Twine & twine)690b57cec5SDimitry Andric StringRef EditedSource::copyString(const Twine &twine) {
700b57cec5SDimitry Andric   SmallString<128> Data;
710b57cec5SDimitry Andric   return copyString(twine.toStringRef(Data));
720b57cec5SDimitry Andric }
730b57cec5SDimitry Andric 
canInsertInOffset(SourceLocation OrigLoc,FileOffset Offs)740b57cec5SDimitry Andric bool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) {
750b57cec5SDimitry Andric   FileEditsTy::iterator FA = getActionForOffset(Offs);
760b57cec5SDimitry Andric   if (FA != FileEdits.end()) {
770b57cec5SDimitry Andric     if (FA->first != Offs)
780b57cec5SDimitry Andric       return false; // position has been removed.
790b57cec5SDimitry Andric   }
800b57cec5SDimitry Andric 
810b57cec5SDimitry Andric   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
820b57cec5SDimitry Andric     SourceLocation ExpLoc;
830b57cec5SDimitry Andric     MacroArgUse ArgUse;
840b57cec5SDimitry Andric     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
85e8d8bef9SDimitry Andric     auto I = ExpansionToArgMap.find(ExpLoc);
860b57cec5SDimitry Andric     if (I != ExpansionToArgMap.end() &&
87*972a253aSDimitry Andric         llvm::any_of(I->second, [&](const MacroArgUse &U) {
880b57cec5SDimitry Andric           return ArgUse.Identifier == U.Identifier &&
890b57cec5SDimitry Andric                  std::tie(ArgUse.ImmediateExpansionLoc, ArgUse.UseLoc) !=
900b57cec5SDimitry Andric                      std::tie(U.ImmediateExpansionLoc, U.UseLoc);
91*972a253aSDimitry Andric         })) {
920b57cec5SDimitry Andric       // Trying to write in a macro argument input that has already been
930b57cec5SDimitry Andric       // written by a previous commit for another expansion of the same macro
940b57cec5SDimitry Andric       // argument name. For example:
950b57cec5SDimitry Andric       //
960b57cec5SDimitry Andric       // \code
970b57cec5SDimitry Andric       //   #define MAC(x) ((x)+(x))
980b57cec5SDimitry Andric       //   MAC(a)
990b57cec5SDimitry Andric       // \endcode
1000b57cec5SDimitry Andric       //
1010b57cec5SDimitry Andric       // A commit modified the macro argument 'a' due to the first '(x)'
1020b57cec5SDimitry Andric       // expansion inside the macro definition, and a subsequent commit tried
1030b57cec5SDimitry Andric       // to modify 'a' again for the second '(x)' expansion. The edits of the
1040b57cec5SDimitry Andric       // second commit will be rejected.
1050b57cec5SDimitry Andric       return false;
1060b57cec5SDimitry Andric     }
1070b57cec5SDimitry Andric   }
1080b57cec5SDimitry Andric   return true;
1090b57cec5SDimitry Andric }
1100b57cec5SDimitry Andric 
commitInsert(SourceLocation OrigLoc,FileOffset Offs,StringRef text,bool beforePreviousInsertions)1110b57cec5SDimitry Andric bool EditedSource::commitInsert(SourceLocation OrigLoc,
1120b57cec5SDimitry Andric                                 FileOffset Offs, StringRef text,
1130b57cec5SDimitry Andric                                 bool beforePreviousInsertions) {
1140b57cec5SDimitry Andric   if (!canInsertInOffset(OrigLoc, Offs))
1150b57cec5SDimitry Andric     return false;
1160b57cec5SDimitry Andric   if (text.empty())
1170b57cec5SDimitry Andric     return true;
1180b57cec5SDimitry Andric 
1190b57cec5SDimitry Andric   if (SourceMgr.isMacroArgExpansion(OrigLoc)) {
1200b57cec5SDimitry Andric     MacroArgUse ArgUse;
1210b57cec5SDimitry Andric     SourceLocation ExpLoc;
1220b57cec5SDimitry Andric     deconstructMacroArgLoc(OrigLoc, ExpLoc, ArgUse);
1230b57cec5SDimitry Andric     if (ArgUse.Identifier)
1240b57cec5SDimitry Andric       CurrCommitMacroArgExps.emplace_back(ExpLoc, ArgUse);
1250b57cec5SDimitry Andric   }
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric   FileEdit &FA = FileEdits[Offs];
1280b57cec5SDimitry Andric   if (FA.Text.empty()) {
1290b57cec5SDimitry Andric     FA.Text = copyString(text);
1300b57cec5SDimitry Andric     return true;
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric 
1330b57cec5SDimitry Andric   if (beforePreviousInsertions)
1340b57cec5SDimitry Andric     FA.Text = copyString(Twine(text) + FA.Text);
1350b57cec5SDimitry Andric   else
1360b57cec5SDimitry Andric     FA.Text = copyString(Twine(FA.Text) + text);
1370b57cec5SDimitry Andric 
1380b57cec5SDimitry Andric   return true;
1390b57cec5SDimitry Andric }
1400b57cec5SDimitry Andric 
commitInsertFromRange(SourceLocation OrigLoc,FileOffset Offs,FileOffset InsertFromRangeOffs,unsigned Len,bool beforePreviousInsertions)1410b57cec5SDimitry Andric bool EditedSource::commitInsertFromRange(SourceLocation OrigLoc,
1420b57cec5SDimitry Andric                                    FileOffset Offs,
1430b57cec5SDimitry Andric                                    FileOffset InsertFromRangeOffs, unsigned Len,
1440b57cec5SDimitry Andric                                    bool beforePreviousInsertions) {
1450b57cec5SDimitry Andric   if (Len == 0)
1460b57cec5SDimitry Andric     return true;
1470b57cec5SDimitry Andric 
1480b57cec5SDimitry Andric   SmallString<128> StrVec;
1490b57cec5SDimitry Andric   FileOffset BeginOffs = InsertFromRangeOffs;
1500b57cec5SDimitry Andric   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
1510b57cec5SDimitry Andric   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
1520b57cec5SDimitry Andric   if (I != FileEdits.begin())
1530b57cec5SDimitry Andric     --I;
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric   for (; I != FileEdits.end(); ++I) {
1560b57cec5SDimitry Andric     FileEdit &FA = I->second;
1570b57cec5SDimitry Andric     FileOffset B = I->first;
1580b57cec5SDimitry Andric     FileOffset E = B.getWithOffset(FA.RemoveLen);
1590b57cec5SDimitry Andric 
1600b57cec5SDimitry Andric     if (BeginOffs == B)
1610b57cec5SDimitry Andric       break;
1620b57cec5SDimitry Andric 
1630b57cec5SDimitry Andric     if (BeginOffs < E) {
1640b57cec5SDimitry Andric       if (BeginOffs > B) {
1650b57cec5SDimitry Andric         BeginOffs = E;
1660b57cec5SDimitry Andric         ++I;
1670b57cec5SDimitry Andric       }
1680b57cec5SDimitry Andric       break;
1690b57cec5SDimitry Andric     }
1700b57cec5SDimitry Andric   }
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   for (; I != FileEdits.end() && EndOffs > I->first; ++I) {
1730b57cec5SDimitry Andric     FileEdit &FA = I->second;
1740b57cec5SDimitry Andric     FileOffset B = I->first;
1750b57cec5SDimitry Andric     FileOffset E = B.getWithOffset(FA.RemoveLen);
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric     if (BeginOffs < B) {
1780b57cec5SDimitry Andric       bool Invalid = false;
1790b57cec5SDimitry Andric       StringRef text = getSourceText(BeginOffs, B, Invalid);
1800b57cec5SDimitry Andric       if (Invalid)
1810b57cec5SDimitry Andric         return false;
1820b57cec5SDimitry Andric       StrVec += text;
1830b57cec5SDimitry Andric     }
1840b57cec5SDimitry Andric     StrVec += FA.Text;
1850b57cec5SDimitry Andric     BeginOffs = E;
1860b57cec5SDimitry Andric   }
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   if (BeginOffs < EndOffs) {
1890b57cec5SDimitry Andric     bool Invalid = false;
1900b57cec5SDimitry Andric     StringRef text = getSourceText(BeginOffs, EndOffs, Invalid);
1910b57cec5SDimitry Andric     if (Invalid)
1920b57cec5SDimitry Andric       return false;
1930b57cec5SDimitry Andric     StrVec += text;
1940b57cec5SDimitry Andric   }
1950b57cec5SDimitry Andric 
1960b57cec5SDimitry Andric   return commitInsert(OrigLoc, Offs, StrVec, beforePreviousInsertions);
1970b57cec5SDimitry Andric }
1980b57cec5SDimitry Andric 
commitRemove(SourceLocation OrigLoc,FileOffset BeginOffs,unsigned Len)1990b57cec5SDimitry Andric void EditedSource::commitRemove(SourceLocation OrigLoc,
2000b57cec5SDimitry Andric                                 FileOffset BeginOffs, unsigned Len) {
2010b57cec5SDimitry Andric   if (Len == 0)
2020b57cec5SDimitry Andric     return;
2030b57cec5SDimitry Andric 
2040b57cec5SDimitry Andric   FileOffset EndOffs = BeginOffs.getWithOffset(Len);
2050b57cec5SDimitry Andric   FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs);
2060b57cec5SDimitry Andric   if (I != FileEdits.begin())
2070b57cec5SDimitry Andric     --I;
2080b57cec5SDimitry Andric 
2090b57cec5SDimitry Andric   for (; I != FileEdits.end(); ++I) {
2100b57cec5SDimitry Andric     FileEdit &FA = I->second;
2110b57cec5SDimitry Andric     FileOffset B = I->first;
2120b57cec5SDimitry Andric     FileOffset E = B.getWithOffset(FA.RemoveLen);
2130b57cec5SDimitry Andric 
2140b57cec5SDimitry Andric     if (BeginOffs < E)
2150b57cec5SDimitry Andric       break;
2160b57cec5SDimitry Andric   }
2170b57cec5SDimitry Andric 
2180b57cec5SDimitry Andric   FileOffset TopBegin, TopEnd;
2190b57cec5SDimitry Andric   FileEdit *TopFA = nullptr;
2200b57cec5SDimitry Andric 
2210b57cec5SDimitry Andric   if (I == FileEdits.end()) {
2220b57cec5SDimitry Andric     FileEditsTy::iterator
2230b57cec5SDimitry Andric       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
2240b57cec5SDimitry Andric     NewI->second.RemoveLen = Len;
2250b57cec5SDimitry Andric     return;
2260b57cec5SDimitry Andric   }
2270b57cec5SDimitry Andric 
2280b57cec5SDimitry Andric   FileEdit &FA = I->second;
2290b57cec5SDimitry Andric   FileOffset B = I->first;
2300b57cec5SDimitry Andric   FileOffset E = B.getWithOffset(FA.RemoveLen);
2310b57cec5SDimitry Andric   if (BeginOffs < B) {
2320b57cec5SDimitry Andric     FileEditsTy::iterator
2330b57cec5SDimitry Andric       NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit()));
2340b57cec5SDimitry Andric     TopBegin = BeginOffs;
2350b57cec5SDimitry Andric     TopEnd = EndOffs;
2360b57cec5SDimitry Andric     TopFA = &NewI->second;
2370b57cec5SDimitry Andric     TopFA->RemoveLen = Len;
2380b57cec5SDimitry Andric   } else {
2390b57cec5SDimitry Andric     TopBegin = B;
2400b57cec5SDimitry Andric     TopEnd = E;
2410b57cec5SDimitry Andric     TopFA = &I->second;
2420b57cec5SDimitry Andric     if (TopEnd >= EndOffs)
2430b57cec5SDimitry Andric       return;
2440b57cec5SDimitry Andric     unsigned diff = EndOffs.getOffset() - TopEnd.getOffset();
2450b57cec5SDimitry Andric     TopEnd = EndOffs;
2460b57cec5SDimitry Andric     TopFA->RemoveLen += diff;
2470b57cec5SDimitry Andric     if (B == BeginOffs)
2480b57cec5SDimitry Andric       TopFA->Text = StringRef();
2490b57cec5SDimitry Andric     ++I;
2500b57cec5SDimitry Andric   }
2510b57cec5SDimitry Andric 
2520b57cec5SDimitry Andric   while (I != FileEdits.end()) {
2530b57cec5SDimitry Andric     FileEdit &FA = I->second;
2540b57cec5SDimitry Andric     FileOffset B = I->first;
2550b57cec5SDimitry Andric     FileOffset E = B.getWithOffset(FA.RemoveLen);
2560b57cec5SDimitry Andric 
2570b57cec5SDimitry Andric     if (B >= TopEnd)
2580b57cec5SDimitry Andric       break;
2590b57cec5SDimitry Andric 
2600b57cec5SDimitry Andric     if (E <= TopEnd) {
2610b57cec5SDimitry Andric       FileEdits.erase(I++);
2620b57cec5SDimitry Andric       continue;
2630b57cec5SDimitry Andric     }
2640b57cec5SDimitry Andric 
2650b57cec5SDimitry Andric     if (B < TopEnd) {
2660b57cec5SDimitry Andric       unsigned diff = E.getOffset() - TopEnd.getOffset();
2670b57cec5SDimitry Andric       TopEnd = E;
2680b57cec5SDimitry Andric       TopFA->RemoveLen += diff;
2690b57cec5SDimitry Andric       FileEdits.erase(I);
2700b57cec5SDimitry Andric     }
2710b57cec5SDimitry Andric 
2720b57cec5SDimitry Andric     break;
2730b57cec5SDimitry Andric   }
2740b57cec5SDimitry Andric }
2750b57cec5SDimitry Andric 
commit(const Commit & commit)2760b57cec5SDimitry Andric bool EditedSource::commit(const Commit &commit) {
2770b57cec5SDimitry Andric   if (!commit.isCommitable())
2780b57cec5SDimitry Andric     return false;
2790b57cec5SDimitry Andric 
2800b57cec5SDimitry Andric   struct CommitRAII {
2810b57cec5SDimitry Andric     EditedSource &Editor;
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric     CommitRAII(EditedSource &Editor) : Editor(Editor) {
2840b57cec5SDimitry Andric       Editor.startingCommit();
2850b57cec5SDimitry Andric     }
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric     ~CommitRAII() {
2880b57cec5SDimitry Andric       Editor.finishedCommit();
2890b57cec5SDimitry Andric     }
2900b57cec5SDimitry Andric   } CommitRAII(*this);
2910b57cec5SDimitry Andric 
2920b57cec5SDimitry Andric   for (edit::Commit::edit_iterator
2930b57cec5SDimitry Andric          I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) {
2940b57cec5SDimitry Andric     const edit::Commit::Edit &edit = *I;
2950b57cec5SDimitry Andric     switch (edit.Kind) {
2960b57cec5SDimitry Andric     case edit::Commit::Act_Insert:
2970b57cec5SDimitry Andric       commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev);
2980b57cec5SDimitry Andric       break;
2990b57cec5SDimitry Andric     case edit::Commit::Act_InsertFromRange:
3000b57cec5SDimitry Andric       commitInsertFromRange(edit.OrigLoc, edit.Offset,
3010b57cec5SDimitry Andric                             edit.InsertFromRangeOffs, edit.Length,
3020b57cec5SDimitry Andric                             edit.BeforePrev);
3030b57cec5SDimitry Andric       break;
3040b57cec5SDimitry Andric     case edit::Commit::Act_Remove:
3050b57cec5SDimitry Andric       commitRemove(edit.OrigLoc, edit.Offset, edit.Length);
3060b57cec5SDimitry Andric       break;
3070b57cec5SDimitry Andric     }
3080b57cec5SDimitry Andric   }
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric   return true;
3110b57cec5SDimitry Andric }
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric // Returns true if it is ok to make the two given characters adjacent.
canBeJoined(char left,char right,const LangOptions & LangOpts)3140b57cec5SDimitry Andric static bool canBeJoined(char left, char right, const LangOptions &LangOpts) {
3150b57cec5SDimitry Andric   // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like
3160b57cec5SDimitry Andric   // making two '<' adjacent.
317349cc55cSDimitry Andric   return !(Lexer::isAsciiIdentifierContinueChar(left, LangOpts) &&
318349cc55cSDimitry Andric            Lexer::isAsciiIdentifierContinueChar(right, LangOpts));
3190b57cec5SDimitry Andric }
3200b57cec5SDimitry Andric 
3210b57cec5SDimitry Andric /// Returns true if it is ok to eliminate the trailing whitespace between
3220b57cec5SDimitry Andric /// the given characters.
canRemoveWhitespace(char left,char beforeWSpace,char right,const LangOptions & LangOpts)3230b57cec5SDimitry Andric static bool canRemoveWhitespace(char left, char beforeWSpace, char right,
3240b57cec5SDimitry Andric                                 const LangOptions &LangOpts) {
3250b57cec5SDimitry Andric   if (!canBeJoined(left, right, LangOpts))
3260b57cec5SDimitry Andric     return false;
3270b57cec5SDimitry Andric   if (isWhitespace(left) || isWhitespace(right))
3280b57cec5SDimitry Andric     return true;
3290b57cec5SDimitry Andric   if (canBeJoined(beforeWSpace, right, LangOpts))
3300b57cec5SDimitry Andric     return false; // the whitespace was intentional, keep it.
3310b57cec5SDimitry Andric   return true;
3320b57cec5SDimitry Andric }
3330b57cec5SDimitry Andric 
3340b57cec5SDimitry Andric /// Check the range that we are going to remove and:
3350b57cec5SDimitry Andric /// -Remove any trailing whitespace if possible.
3360b57cec5SDimitry Andric /// -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)3370b57cec5SDimitry Andric static void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts,
3380b57cec5SDimitry Andric                           SourceLocation Loc, FileOffset offs,
3390b57cec5SDimitry Andric                           unsigned &len, StringRef &text) {
3400b57cec5SDimitry Andric   assert(len && text.empty());
3410b57cec5SDimitry Andric   SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts);
3420b57cec5SDimitry Andric   if (BeginTokLoc != Loc)
3430b57cec5SDimitry Andric     return; // the range is not at the beginning of a token, keep the range.
3440b57cec5SDimitry Andric 
3450b57cec5SDimitry Andric   bool Invalid = false;
3460b57cec5SDimitry Andric   StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid);
3470b57cec5SDimitry Andric   if (Invalid)
3480b57cec5SDimitry Andric     return;
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric   unsigned begin = offs.getOffset();
3510b57cec5SDimitry Andric   unsigned end = begin + len;
3520b57cec5SDimitry Andric 
3530b57cec5SDimitry Andric   // Do not try to extend the removal if we're at the end of the buffer already.
3540b57cec5SDimitry Andric   if (end == buffer.size())
3550b57cec5SDimitry Andric     return;
3560b57cec5SDimitry Andric 
3570b57cec5SDimitry Andric   assert(begin < buffer.size() && end < buffer.size() && "Invalid range!");
3580b57cec5SDimitry Andric 
3590b57cec5SDimitry Andric   // FIXME: Remove newline.
3600b57cec5SDimitry Andric 
3610b57cec5SDimitry Andric   if (begin == 0) {
3620b57cec5SDimitry Andric     if (buffer[end] == ' ')
3630b57cec5SDimitry Andric       ++len;
3640b57cec5SDimitry Andric     return;
3650b57cec5SDimitry Andric   }
3660b57cec5SDimitry Andric 
3670b57cec5SDimitry Andric   if (buffer[end] == ' ') {
3680b57cec5SDimitry Andric     assert((end + 1 != buffer.size() || buffer.data()[end + 1] == 0) &&
3690b57cec5SDimitry Andric            "buffer not zero-terminated!");
3700b57cec5SDimitry Andric     if (canRemoveWhitespace(/*left=*/buffer[begin-1],
3710b57cec5SDimitry Andric                             /*beforeWSpace=*/buffer[end-1],
3720b57cec5SDimitry Andric                             /*right=*/buffer.data()[end + 1], // zero-terminated
3730b57cec5SDimitry Andric                             LangOpts))
3740b57cec5SDimitry Andric       ++len;
3750b57cec5SDimitry Andric     return;
3760b57cec5SDimitry Andric   }
3770b57cec5SDimitry Andric 
3780b57cec5SDimitry Andric   if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts))
3790b57cec5SDimitry Andric     text = " ";
3800b57cec5SDimitry Andric }
3810b57cec5SDimitry Andric 
applyRewrite(EditsReceiver & receiver,StringRef text,FileOffset offs,unsigned len,const SourceManager & SM,const LangOptions & LangOpts,bool shouldAdjustRemovals)3820b57cec5SDimitry Andric static void applyRewrite(EditsReceiver &receiver,
3830b57cec5SDimitry Andric                          StringRef text, FileOffset offs, unsigned len,
3840b57cec5SDimitry Andric                          const SourceManager &SM, const LangOptions &LangOpts,
3850b57cec5SDimitry Andric                          bool shouldAdjustRemovals) {
3860b57cec5SDimitry Andric   assert(offs.getFID().isValid());
3870b57cec5SDimitry Andric   SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID());
3880b57cec5SDimitry Andric   Loc = Loc.getLocWithOffset(offs.getOffset());
3890b57cec5SDimitry Andric   assert(Loc.isFileID());
3900b57cec5SDimitry Andric 
3910b57cec5SDimitry Andric   if (text.empty() && shouldAdjustRemovals)
3920b57cec5SDimitry Andric     adjustRemoval(SM, LangOpts, Loc, offs, len, text);
3930b57cec5SDimitry Andric 
3940b57cec5SDimitry Andric   CharSourceRange range = CharSourceRange::getCharRange(Loc,
3950b57cec5SDimitry Andric                                                      Loc.getLocWithOffset(len));
3960b57cec5SDimitry Andric 
3970b57cec5SDimitry Andric   if (text.empty()) {
3980b57cec5SDimitry Andric     assert(len);
3990b57cec5SDimitry Andric     receiver.remove(range);
4000b57cec5SDimitry Andric     return;
4010b57cec5SDimitry Andric   }
4020b57cec5SDimitry Andric 
4030b57cec5SDimitry Andric   if (len)
4040b57cec5SDimitry Andric     receiver.replace(range, text);
4050b57cec5SDimitry Andric   else
4060b57cec5SDimitry Andric     receiver.insert(Loc, text);
4070b57cec5SDimitry Andric }
4080b57cec5SDimitry Andric 
applyRewrites(EditsReceiver & receiver,bool shouldAdjustRemovals)4090b57cec5SDimitry Andric void EditedSource::applyRewrites(EditsReceiver &receiver,
4100b57cec5SDimitry Andric                                  bool shouldAdjustRemovals) {
4110b57cec5SDimitry Andric   SmallString<128> StrVec;
4120b57cec5SDimitry Andric   FileOffset CurOffs, CurEnd;
4130b57cec5SDimitry Andric   unsigned CurLen;
4140b57cec5SDimitry Andric 
4150b57cec5SDimitry Andric   if (FileEdits.empty())
4160b57cec5SDimitry Andric     return;
4170b57cec5SDimitry Andric 
4180b57cec5SDimitry Andric   FileEditsTy::iterator I = FileEdits.begin();
4190b57cec5SDimitry Andric   CurOffs = I->first;
4200b57cec5SDimitry Andric   StrVec = I->second.Text;
4210b57cec5SDimitry Andric   CurLen = I->second.RemoveLen;
4220b57cec5SDimitry Andric   CurEnd = CurOffs.getWithOffset(CurLen);
4230b57cec5SDimitry Andric   ++I;
4240b57cec5SDimitry Andric 
4250b57cec5SDimitry Andric   for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) {
4260b57cec5SDimitry Andric     FileOffset offs = I->first;
4270b57cec5SDimitry Andric     FileEdit act = I->second;
4280b57cec5SDimitry Andric     assert(offs >= CurEnd);
4290b57cec5SDimitry Andric 
4300b57cec5SDimitry Andric     if (offs == CurEnd) {
4310b57cec5SDimitry Andric       StrVec += act.Text;
4320b57cec5SDimitry Andric       CurLen += act.RemoveLen;
4330b57cec5SDimitry Andric       CurEnd.getWithOffset(act.RemoveLen);
4340b57cec5SDimitry Andric       continue;
4350b57cec5SDimitry Andric     }
4360b57cec5SDimitry Andric 
4370b57cec5SDimitry Andric     applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
4380b57cec5SDimitry Andric                  shouldAdjustRemovals);
4390b57cec5SDimitry Andric     CurOffs = offs;
4400b57cec5SDimitry Andric     StrVec = act.Text;
4410b57cec5SDimitry Andric     CurLen = act.RemoveLen;
4420b57cec5SDimitry Andric     CurEnd = CurOffs.getWithOffset(CurLen);
4430b57cec5SDimitry Andric   }
4440b57cec5SDimitry Andric 
4450b57cec5SDimitry Andric   applyRewrite(receiver, StrVec, CurOffs, CurLen, SourceMgr, LangOpts,
4460b57cec5SDimitry Andric                shouldAdjustRemovals);
4470b57cec5SDimitry Andric }
4480b57cec5SDimitry Andric 
clearRewrites()4490b57cec5SDimitry Andric void EditedSource::clearRewrites() {
4500b57cec5SDimitry Andric   FileEdits.clear();
4510b57cec5SDimitry Andric   StrAlloc.Reset();
4520b57cec5SDimitry Andric }
4530b57cec5SDimitry Andric 
getSourceText(FileOffset BeginOffs,FileOffset EndOffs,bool & Invalid)4540b57cec5SDimitry Andric StringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs,
4550b57cec5SDimitry Andric                                       bool &Invalid) {
4560b57cec5SDimitry Andric   assert(BeginOffs.getFID() == EndOffs.getFID());
4570b57cec5SDimitry Andric   assert(BeginOffs <= EndOffs);
4580b57cec5SDimitry Andric   SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID());
4590b57cec5SDimitry Andric   BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset());
4600b57cec5SDimitry Andric   assert(BLoc.isFileID());
4610b57cec5SDimitry Andric   SourceLocation
4620b57cec5SDimitry Andric     ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset());
4630b57cec5SDimitry Andric   return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc),
4640b57cec5SDimitry Andric                               SourceMgr, LangOpts, &Invalid);
4650b57cec5SDimitry Andric }
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric EditedSource::FileEditsTy::iterator
getActionForOffset(FileOffset Offs)4680b57cec5SDimitry Andric EditedSource::getActionForOffset(FileOffset Offs) {
4690b57cec5SDimitry Andric   FileEditsTy::iterator I = FileEdits.upper_bound(Offs);
4700b57cec5SDimitry Andric   if (I == FileEdits.begin())
4710b57cec5SDimitry Andric     return FileEdits.end();
4720b57cec5SDimitry Andric   --I;
4730b57cec5SDimitry Andric   FileEdit &FA = I->second;
4740b57cec5SDimitry Andric   FileOffset B = I->first;
4750b57cec5SDimitry Andric   FileOffset E = B.getWithOffset(FA.RemoveLen);
4760b57cec5SDimitry Andric   if (Offs >= B && Offs < E)
4770b57cec5SDimitry Andric     return I;
4780b57cec5SDimitry Andric 
4790b57cec5SDimitry Andric   return FileEdits.end();
4800b57cec5SDimitry Andric }
481