1234287Sdim//===----- EditedSource.cpp - Collection of source edits ------------------===// 2234287Sdim// 3234287Sdim// The LLVM Compiler Infrastructure 4234287Sdim// 5234287Sdim// This file is distributed under the University of Illinois Open Source 6234287Sdim// License. See LICENSE.TXT for details. 7234287Sdim// 8234287Sdim//===----------------------------------------------------------------------===// 9234287Sdim 10234287Sdim#include "clang/Edit/EditedSource.h" 11252723Sdim#include "clang/Basic/CharInfo.h" 12252723Sdim#include "clang/Basic/SourceManager.h" 13234287Sdim#include "clang/Edit/Commit.h" 14234287Sdim#include "clang/Edit/EditsReceiver.h" 15234287Sdim#include "clang/Lex/Lexer.h" 16234287Sdim#include "llvm/ADT/SmallString.h" 17234287Sdim#include "llvm/ADT/Twine.h" 18234287Sdim 19234287Sdimusing namespace clang; 20234287Sdimusing namespace edit; 21234287Sdim 22234287Sdimvoid EditsReceiver::remove(CharSourceRange range) { 23234287Sdim replace(range, StringRef()); 24234287Sdim} 25234287Sdim 26234287SdimStringRef EditedSource::copyString(const Twine &twine) { 27252723Sdim SmallString<128> Data; 28234287Sdim return copyString(twine.toStringRef(Data)); 29234287Sdim} 30234287Sdim 31234287Sdimbool EditedSource::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { 32234287Sdim FileEditsTy::iterator FA = getActionForOffset(Offs); 33234287Sdim if (FA != FileEdits.end()) { 34234287Sdim if (FA->first != Offs) 35234287Sdim return false; // position has been removed. 36234287Sdim } 37234287Sdim 38234287Sdim if (SourceMgr.isMacroArgExpansion(OrigLoc)) { 39234287Sdim SourceLocation 40234287Sdim DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; 41234287Sdim SourceLocation 42234287Sdim ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; 43234287Sdim llvm::DenseMap<unsigned, SourceLocation>::iterator 44234287Sdim I = ExpansionToArgMap.find(ExpLoc.getRawEncoding()); 45234287Sdim if (I != ExpansionToArgMap.end() && I->second != DefArgLoc) 46234287Sdim return false; // Trying to write in a macro argument input that has 47234287Sdim // already been written for another argument of the same macro. 48234287Sdim } 49234287Sdim 50234287Sdim return true; 51234287Sdim} 52234287Sdim 53234287Sdimbool EditedSource::commitInsert(SourceLocation OrigLoc, 54234287Sdim FileOffset Offs, StringRef text, 55234287Sdim bool beforePreviousInsertions) { 56234287Sdim if (!canInsertInOffset(OrigLoc, Offs)) 57234287Sdim return false; 58234287Sdim if (text.empty()) 59234287Sdim return true; 60234287Sdim 61234287Sdim if (SourceMgr.isMacroArgExpansion(OrigLoc)) { 62234287Sdim SourceLocation 63234287Sdim DefArgLoc = SourceMgr.getImmediateExpansionRange(OrigLoc).first; 64234287Sdim SourceLocation 65234287Sdim ExpLoc = SourceMgr.getImmediateExpansionRange(DefArgLoc).first; 66234287Sdim ExpansionToArgMap[ExpLoc.getRawEncoding()] = DefArgLoc; 67234287Sdim } 68234287Sdim 69234287Sdim FileEdit &FA = FileEdits[Offs]; 70234287Sdim if (FA.Text.empty()) { 71234287Sdim FA.Text = copyString(text); 72234287Sdim return true; 73234287Sdim } 74234287Sdim 75234287Sdim Twine concat; 76234287Sdim if (beforePreviousInsertions) 77234287Sdim concat = Twine(text) + FA.Text; 78234287Sdim else 79234287Sdim concat = Twine(FA.Text) + text; 80234287Sdim 81234287Sdim FA.Text = copyString(concat); 82234287Sdim return true; 83234287Sdim} 84234287Sdim 85234287Sdimbool EditedSource::commitInsertFromRange(SourceLocation OrigLoc, 86234287Sdim FileOffset Offs, 87234287Sdim FileOffset InsertFromRangeOffs, unsigned Len, 88234287Sdim bool beforePreviousInsertions) { 89234287Sdim if (Len == 0) 90234287Sdim return true; 91234287Sdim 92252723Sdim SmallString<128> StrVec; 93234287Sdim FileOffset BeginOffs = InsertFromRangeOffs; 94234287Sdim FileOffset EndOffs = BeginOffs.getWithOffset(Len); 95234287Sdim FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 96234287Sdim if (I != FileEdits.begin()) 97234287Sdim --I; 98234287Sdim 99234287Sdim for (; I != FileEdits.end(); ++I) { 100234287Sdim FileEdit &FA = I->second; 101234287Sdim FileOffset B = I->first; 102234287Sdim FileOffset E = B.getWithOffset(FA.RemoveLen); 103234287Sdim 104245431Sdim if (BeginOffs == B) 105245431Sdim break; 106245431Sdim 107234287Sdim if (BeginOffs < E) { 108245431Sdim if (BeginOffs > B) { 109234287Sdim BeginOffs = E; 110234287Sdim ++I; 111234287Sdim } 112234287Sdim break; 113234287Sdim } 114234287Sdim } 115234287Sdim 116234287Sdim for (; I != FileEdits.end() && EndOffs > I->first; ++I) { 117234287Sdim FileEdit &FA = I->second; 118234287Sdim FileOffset B = I->first; 119234287Sdim FileOffset E = B.getWithOffset(FA.RemoveLen); 120234287Sdim 121234287Sdim if (BeginOffs < B) { 122234287Sdim bool Invalid = false; 123234287Sdim StringRef text = getSourceText(BeginOffs, B, Invalid); 124234287Sdim if (Invalid) 125234287Sdim return false; 126234287Sdim StrVec += text; 127234287Sdim } 128234287Sdim StrVec += FA.Text; 129234287Sdim BeginOffs = E; 130234287Sdim } 131234287Sdim 132234287Sdim if (BeginOffs < EndOffs) { 133234287Sdim bool Invalid = false; 134234287Sdim StringRef text = getSourceText(BeginOffs, EndOffs, Invalid); 135234287Sdim if (Invalid) 136234287Sdim return false; 137234287Sdim StrVec += text; 138234287Sdim } 139234287Sdim 140234287Sdim return commitInsert(OrigLoc, Offs, StrVec.str(), beforePreviousInsertions); 141234287Sdim} 142234287Sdim 143234287Sdimvoid EditedSource::commitRemove(SourceLocation OrigLoc, 144234287Sdim FileOffset BeginOffs, unsigned Len) { 145234287Sdim if (Len == 0) 146234287Sdim return; 147234287Sdim 148234287Sdim FileOffset EndOffs = BeginOffs.getWithOffset(Len); 149234287Sdim FileEditsTy::iterator I = FileEdits.upper_bound(BeginOffs); 150234287Sdim if (I != FileEdits.begin()) 151234287Sdim --I; 152234287Sdim 153234287Sdim for (; I != FileEdits.end(); ++I) { 154234287Sdim FileEdit &FA = I->second; 155234287Sdim FileOffset B = I->first; 156234287Sdim FileOffset E = B.getWithOffset(FA.RemoveLen); 157234287Sdim 158234287Sdim if (BeginOffs < E) 159234287Sdim break; 160234287Sdim } 161234287Sdim 162234287Sdim FileOffset TopBegin, TopEnd; 163234287Sdim FileEdit *TopFA = 0; 164234287Sdim 165234287Sdim if (I == FileEdits.end()) { 166234287Sdim FileEditsTy::iterator 167234287Sdim NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 168234287Sdim NewI->second.RemoveLen = Len; 169234287Sdim return; 170234287Sdim } 171234287Sdim 172234287Sdim FileEdit &FA = I->second; 173234287Sdim FileOffset B = I->first; 174234287Sdim FileOffset E = B.getWithOffset(FA.RemoveLen); 175234287Sdim if (BeginOffs < B) { 176234287Sdim FileEditsTy::iterator 177234287Sdim NewI = FileEdits.insert(I, std::make_pair(BeginOffs, FileEdit())); 178234287Sdim TopBegin = BeginOffs; 179234287Sdim TopEnd = EndOffs; 180234287Sdim TopFA = &NewI->second; 181234287Sdim TopFA->RemoveLen = Len; 182234287Sdim } else { 183234287Sdim TopBegin = B; 184234287Sdim TopEnd = E; 185234287Sdim TopFA = &I->second; 186234287Sdim if (TopEnd >= EndOffs) 187234287Sdim return; 188234287Sdim unsigned diff = EndOffs.getOffset() - TopEnd.getOffset(); 189234287Sdim TopEnd = EndOffs; 190234287Sdim TopFA->RemoveLen += diff; 191252723Sdim if (B == BeginOffs) 192252723Sdim TopFA->Text = StringRef(); 193234287Sdim ++I; 194234287Sdim } 195234287Sdim 196234287Sdim while (I != FileEdits.end()) { 197234287Sdim FileEdit &FA = I->second; 198234287Sdim FileOffset B = I->first; 199234287Sdim FileOffset E = B.getWithOffset(FA.RemoveLen); 200234287Sdim 201234287Sdim if (B >= TopEnd) 202234287Sdim break; 203234287Sdim 204234287Sdim if (E <= TopEnd) { 205234287Sdim FileEdits.erase(I++); 206234287Sdim continue; 207234287Sdim } 208234287Sdim 209234287Sdim if (B < TopEnd) { 210234287Sdim unsigned diff = E.getOffset() - TopEnd.getOffset(); 211234287Sdim TopEnd = E; 212234287Sdim TopFA->RemoveLen += diff; 213234287Sdim FileEdits.erase(I); 214234287Sdim } 215234287Sdim 216234287Sdim break; 217234287Sdim } 218234287Sdim} 219234287Sdim 220234287Sdimbool EditedSource::commit(const Commit &commit) { 221234287Sdim if (!commit.isCommitable()) 222234287Sdim return false; 223234287Sdim 224234287Sdim for (edit::Commit::edit_iterator 225234287Sdim I = commit.edit_begin(), E = commit.edit_end(); I != E; ++I) { 226234287Sdim const edit::Commit::Edit &edit = *I; 227234287Sdim switch (edit.Kind) { 228234287Sdim case edit::Commit::Act_Insert: 229234287Sdim commitInsert(edit.OrigLoc, edit.Offset, edit.Text, edit.BeforePrev); 230234287Sdim break; 231234287Sdim case edit::Commit::Act_InsertFromRange: 232234287Sdim commitInsertFromRange(edit.OrigLoc, edit.Offset, 233234287Sdim edit.InsertFromRangeOffs, edit.Length, 234234287Sdim edit.BeforePrev); 235234287Sdim break; 236234287Sdim case edit::Commit::Act_Remove: 237234287Sdim commitRemove(edit.OrigLoc, edit.Offset, edit.Length); 238234287Sdim break; 239234287Sdim } 240234287Sdim } 241234287Sdim 242234287Sdim return true; 243234287Sdim} 244234287Sdim 245252723Sdim// \brief Returns true if it is ok to make the two given characters adjacent. 246252723Sdimstatic bool canBeJoined(char left, char right, const LangOptions &LangOpts) { 247252723Sdim // FIXME: Should use TokenConcatenation to make sure we don't allow stuff like 248252723Sdim // making two '<' adjacent. 249252723Sdim return !(Lexer::isIdentifierBodyChar(left, LangOpts) && 250252723Sdim Lexer::isIdentifierBodyChar(right, LangOpts)); 251252723Sdim} 252252723Sdim 253252723Sdim/// \brief Returns true if it is ok to eliminate the trailing whitespace between 254252723Sdim/// the given characters. 255252723Sdimstatic bool canRemoveWhitespace(char left, char beforeWSpace, char right, 256252723Sdim const LangOptions &LangOpts) { 257252723Sdim if (!canBeJoined(left, right, LangOpts)) 258252723Sdim return false; 259252723Sdim if (isWhitespace(left) || isWhitespace(right)) 260252723Sdim return true; 261252723Sdim if (canBeJoined(beforeWSpace, right, LangOpts)) 262252723Sdim return false; // the whitespace was intentional, keep it. 263252723Sdim return true; 264252723Sdim} 265252723Sdim 266252723Sdim/// \brief Check the range that we are going to remove and: 267252723Sdim/// -Remove any trailing whitespace if possible. 268252723Sdim/// -Insert a space if removing the range is going to mess up the source tokens. 269252723Sdimstatic void adjustRemoval(const SourceManager &SM, const LangOptions &LangOpts, 270252723Sdim SourceLocation Loc, FileOffset offs, 271252723Sdim unsigned &len, StringRef &text) { 272252723Sdim assert(len && text.empty()); 273252723Sdim SourceLocation BeginTokLoc = Lexer::GetBeginningOfToken(Loc, SM, LangOpts); 274252723Sdim if (BeginTokLoc != Loc) 275252723Sdim return; // the range is not at the beginning of a token, keep the range. 276252723Sdim 277252723Sdim bool Invalid = false; 278252723Sdim StringRef buffer = SM.getBufferData(offs.getFID(), &Invalid); 279252723Sdim if (Invalid) 280252723Sdim return; 281252723Sdim 282252723Sdim unsigned begin = offs.getOffset(); 283252723Sdim unsigned end = begin + len; 284252723Sdim 285252723Sdim // FIXME: Remove newline. 286252723Sdim 287252723Sdim if (begin == 0) { 288252723Sdim if (buffer[end] == ' ') 289252723Sdim ++len; 290252723Sdim return; 291252723Sdim } 292252723Sdim 293252723Sdim if (buffer[end] == ' ') { 294252723Sdim if (canRemoveWhitespace(/*left=*/buffer[begin-1], 295252723Sdim /*beforeWSpace=*/buffer[end-1], 296252723Sdim /*right=*/buffer[end+1], 297252723Sdim LangOpts)) 298252723Sdim ++len; 299252723Sdim return; 300252723Sdim } 301252723Sdim 302252723Sdim if (!canBeJoined(buffer[begin-1], buffer[end], LangOpts)) 303252723Sdim text = " "; 304252723Sdim} 305252723Sdim 306234287Sdimstatic void applyRewrite(EditsReceiver &receiver, 307234287Sdim StringRef text, FileOffset offs, unsigned len, 308252723Sdim const SourceManager &SM, const LangOptions &LangOpts) { 309234287Sdim assert(!offs.getFID().isInvalid()); 310234287Sdim SourceLocation Loc = SM.getLocForStartOfFile(offs.getFID()); 311234287Sdim Loc = Loc.getLocWithOffset(offs.getOffset()); 312234287Sdim assert(Loc.isFileID()); 313252723Sdim 314252723Sdim if (text.empty()) 315252723Sdim adjustRemoval(SM, LangOpts, Loc, offs, len, text); 316252723Sdim 317234287Sdim CharSourceRange range = CharSourceRange::getCharRange(Loc, 318234287Sdim Loc.getLocWithOffset(len)); 319234287Sdim 320234287Sdim if (text.empty()) { 321234287Sdim assert(len); 322234287Sdim receiver.remove(range); 323234287Sdim return; 324234287Sdim } 325234287Sdim 326234287Sdim if (len) 327234287Sdim receiver.replace(range, text); 328234287Sdim else 329234287Sdim receiver.insert(Loc, text); 330234287Sdim} 331234287Sdim 332234287Sdimvoid EditedSource::applyRewrites(EditsReceiver &receiver) { 333252723Sdim SmallString<128> StrVec; 334234287Sdim FileOffset CurOffs, CurEnd; 335234287Sdim unsigned CurLen; 336234287Sdim 337234287Sdim if (FileEdits.empty()) 338234287Sdim return; 339234287Sdim 340234287Sdim FileEditsTy::iterator I = FileEdits.begin(); 341234287Sdim CurOffs = I->first; 342234287Sdim StrVec = I->second.Text; 343234287Sdim CurLen = I->second.RemoveLen; 344234287Sdim CurEnd = CurOffs.getWithOffset(CurLen); 345234287Sdim ++I; 346234287Sdim 347234287Sdim for (FileEditsTy::iterator E = FileEdits.end(); I != E; ++I) { 348234287Sdim FileOffset offs = I->first; 349234287Sdim FileEdit act = I->second; 350234287Sdim assert(offs >= CurEnd); 351234287Sdim 352234287Sdim if (offs == CurEnd) { 353234287Sdim StrVec += act.Text; 354234287Sdim CurLen += act.RemoveLen; 355234287Sdim CurEnd.getWithOffset(act.RemoveLen); 356234287Sdim continue; 357234287Sdim } 358234287Sdim 359252723Sdim applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr, LangOpts); 360234287Sdim CurOffs = offs; 361234287Sdim StrVec = act.Text; 362234287Sdim CurLen = act.RemoveLen; 363234287Sdim CurEnd = CurOffs.getWithOffset(CurLen); 364234287Sdim } 365234287Sdim 366252723Sdim applyRewrite(receiver, StrVec.str(), CurOffs, CurLen, SourceMgr, LangOpts); 367234287Sdim} 368234287Sdim 369234287Sdimvoid EditedSource::clearRewrites() { 370234287Sdim FileEdits.clear(); 371234287Sdim StrAlloc.Reset(); 372234287Sdim} 373234287Sdim 374234287SdimStringRef EditedSource::getSourceText(FileOffset BeginOffs, FileOffset EndOffs, 375234287Sdim bool &Invalid) { 376234287Sdim assert(BeginOffs.getFID() == EndOffs.getFID()); 377234287Sdim assert(BeginOffs <= EndOffs); 378234287Sdim SourceLocation BLoc = SourceMgr.getLocForStartOfFile(BeginOffs.getFID()); 379234287Sdim BLoc = BLoc.getLocWithOffset(BeginOffs.getOffset()); 380234287Sdim assert(BLoc.isFileID()); 381234287Sdim SourceLocation 382234287Sdim ELoc = BLoc.getLocWithOffset(EndOffs.getOffset() - BeginOffs.getOffset()); 383234287Sdim return Lexer::getSourceText(CharSourceRange::getCharRange(BLoc, ELoc), 384234287Sdim SourceMgr, LangOpts, &Invalid); 385234287Sdim} 386234287Sdim 387234287SdimEditedSource::FileEditsTy::iterator 388234287SdimEditedSource::getActionForOffset(FileOffset Offs) { 389234287Sdim FileEditsTy::iterator I = FileEdits.upper_bound(Offs); 390234287Sdim if (I == FileEdits.begin()) 391234287Sdim return FileEdits.end(); 392234287Sdim --I; 393234287Sdim FileEdit &FA = I->second; 394234287Sdim FileOffset B = I->first; 395234287Sdim FileOffset E = B.getWithOffset(FA.RemoveLen); 396234287Sdim if (Offs >= B && Offs < E) 397234287Sdim return I; 398234287Sdim 399234287Sdim return FileEdits.end(); 400234287Sdim} 401