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