1234287Sdim//===----- Commit.cpp - A unit of 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/Commit.h" 11249423Sdim#include "clang/Basic/SourceManager.h" 12234287Sdim#include "clang/Edit/EditedSource.h" 13234287Sdim#include "clang/Lex/Lexer.h" 14249423Sdim#include "clang/Lex/PPConditionalDirectiveRecord.h" 15234287Sdim 16234287Sdimusing namespace clang; 17234287Sdimusing namespace edit; 18234287Sdim 19234287SdimSourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const { 20234287Sdim SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID()); 21234287Sdim Loc = Loc.getLocWithOffset(Offset.getOffset()); 22234287Sdim assert(Loc.isFileID()); 23234287Sdim return Loc; 24234287Sdim} 25234287Sdim 26234287SdimCharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const { 27234287Sdim SourceLocation Loc = getFileLocation(SM); 28234287Sdim return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); 29234287Sdim} 30234287Sdim 31234287SdimCharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const { 32234287Sdim SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID()); 33234287Sdim Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset()); 34234287Sdim assert(Loc.isFileID()); 35234287Sdim return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); 36234287Sdim} 37234287Sdim 38234287SdimCommit::Commit(EditedSource &Editor) 39234287Sdim : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()), 40249423Sdim PPRec(Editor.getPPCondDirectiveRecord()), 41263508Sdim Editor(&Editor), 42263508Sdim ForceCommitInSystemHeader(Editor.getForceCommitInSystemHeader()), 43263508Sdim IsCommitable(true) { } 44234287Sdim 45234287Sdimbool Commit::insert(SourceLocation loc, StringRef text, 46234287Sdim bool afterToken, bool beforePreviousInsertions) { 47234287Sdim if (text.empty()) 48234287Sdim return true; 49234287Sdim 50234287Sdim FileOffset Offs; 51234287Sdim if ((!afterToken && !canInsert(loc, Offs)) || 52234287Sdim ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { 53234287Sdim IsCommitable = false; 54234287Sdim return false; 55234287Sdim } 56234287Sdim 57234287Sdim addInsert(loc, Offs, text, beforePreviousInsertions); 58234287Sdim return true; 59234287Sdim} 60234287Sdim 61234287Sdimbool Commit::insertFromRange(SourceLocation loc, 62234287Sdim CharSourceRange range, 63234287Sdim bool afterToken, bool beforePreviousInsertions) { 64234287Sdim FileOffset RangeOffs; 65234287Sdim unsigned RangeLen; 66234287Sdim if (!canRemoveRange(range, RangeOffs, RangeLen)) { 67234287Sdim IsCommitable = false; 68234287Sdim return false; 69234287Sdim } 70234287Sdim 71234287Sdim FileOffset Offs; 72234287Sdim if ((!afterToken && !canInsert(loc, Offs)) || 73234287Sdim ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { 74234287Sdim IsCommitable = false; 75234287Sdim return false; 76234287Sdim } 77234287Sdim 78234287Sdim if (PPRec && 79234287Sdim PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) { 80234287Sdim IsCommitable = false; 81234287Sdim return false; 82234287Sdim } 83234287Sdim 84234287Sdim addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions); 85234287Sdim return true; 86234287Sdim} 87234287Sdim 88234287Sdimbool Commit::remove(CharSourceRange range) { 89234287Sdim FileOffset Offs; 90234287Sdim unsigned Len; 91234287Sdim if (!canRemoveRange(range, Offs, Len)) { 92234287Sdim IsCommitable = false; 93234287Sdim return false; 94234287Sdim } 95234287Sdim 96234287Sdim addRemove(range.getBegin(), Offs, Len); 97234287Sdim return true; 98234287Sdim} 99234287Sdim 100234287Sdimbool Commit::insertWrap(StringRef before, CharSourceRange range, 101234287Sdim StringRef after) { 102234287Sdim bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false, 103234287Sdim /*beforePreviousInsertions=*/true); 104234287Sdim bool commitableAfter; 105234287Sdim if (range.isTokenRange()) 106234287Sdim commitableAfter = insertAfterToken(range.getEnd(), after); 107234287Sdim else 108234287Sdim commitableAfter = insert(range.getEnd(), after); 109234287Sdim 110234287Sdim return commitableBefore && commitableAfter; 111234287Sdim} 112234287Sdim 113234287Sdimbool Commit::replace(CharSourceRange range, StringRef text) { 114234287Sdim if (text.empty()) 115234287Sdim return remove(range); 116234287Sdim 117234287Sdim FileOffset Offs; 118234287Sdim unsigned Len; 119234287Sdim if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) { 120234287Sdim IsCommitable = false; 121234287Sdim return false; 122234287Sdim } 123234287Sdim 124234287Sdim addRemove(range.getBegin(), Offs, Len); 125234287Sdim addInsert(range.getBegin(), Offs, text, false); 126234287Sdim return true; 127234287Sdim} 128234287Sdim 129234287Sdimbool Commit::replaceWithInner(CharSourceRange range, 130234287Sdim CharSourceRange replacementRange) { 131234287Sdim FileOffset OuterBegin; 132234287Sdim unsigned OuterLen; 133234287Sdim if (!canRemoveRange(range, OuterBegin, OuterLen)) { 134234287Sdim IsCommitable = false; 135234287Sdim return false; 136234287Sdim } 137234287Sdim 138234287Sdim FileOffset InnerBegin; 139234287Sdim unsigned InnerLen; 140234287Sdim if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) { 141234287Sdim IsCommitable = false; 142234287Sdim return false; 143234287Sdim } 144234287Sdim 145234287Sdim FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen); 146234287Sdim FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen); 147234287Sdim if (OuterBegin.getFID() != InnerBegin.getFID() || 148234287Sdim InnerBegin < OuterBegin || 149234287Sdim InnerBegin > OuterEnd || 150234287Sdim InnerEnd > OuterEnd) { 151234287Sdim IsCommitable = false; 152234287Sdim return false; 153234287Sdim } 154234287Sdim 155234287Sdim addRemove(range.getBegin(), 156234287Sdim OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset()); 157234287Sdim addRemove(replacementRange.getEnd(), 158234287Sdim InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset()); 159234287Sdim return true; 160234287Sdim} 161234287Sdim 162234287Sdimbool Commit::replaceText(SourceLocation loc, StringRef text, 163234287Sdim StringRef replacementText) { 164234287Sdim if (text.empty() || replacementText.empty()) 165234287Sdim return true; 166234287Sdim 167234287Sdim FileOffset Offs; 168234287Sdim unsigned Len; 169234287Sdim if (!canReplaceText(loc, replacementText, Offs, Len)) { 170234287Sdim IsCommitable = false; 171234287Sdim return false; 172234287Sdim } 173234287Sdim 174234287Sdim addRemove(loc, Offs, Len); 175234287Sdim addInsert(loc, Offs, text, false); 176234287Sdim return true; 177234287Sdim} 178234287Sdim 179234287Sdimvoid Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text, 180234287Sdim bool beforePreviousInsertions) { 181234287Sdim if (text.empty()) 182234287Sdim return; 183234287Sdim 184234287Sdim Edit data; 185234287Sdim data.Kind = Act_Insert; 186234287Sdim data.OrigLoc = OrigLoc; 187234287Sdim data.Offset = Offs; 188263508Sdim data.Text = copyString(text); 189234287Sdim data.BeforePrev = beforePreviousInsertions; 190234287Sdim CachedEdits.push_back(data); 191234287Sdim} 192234287Sdim 193234287Sdimvoid Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs, 194234287Sdim FileOffset RangeOffs, unsigned RangeLen, 195234287Sdim bool beforePreviousInsertions) { 196234287Sdim if (RangeLen == 0) 197234287Sdim return; 198234287Sdim 199234287Sdim Edit data; 200234287Sdim data.Kind = Act_InsertFromRange; 201234287Sdim data.OrigLoc = OrigLoc; 202234287Sdim data.Offset = Offs; 203234287Sdim data.InsertFromRangeOffs = RangeOffs; 204234287Sdim data.Length = RangeLen; 205234287Sdim data.BeforePrev = beforePreviousInsertions; 206234287Sdim CachedEdits.push_back(data); 207234287Sdim} 208234287Sdim 209234287Sdimvoid Commit::addRemove(SourceLocation OrigLoc, 210234287Sdim FileOffset Offs, unsigned Len) { 211234287Sdim if (Len == 0) 212234287Sdim return; 213234287Sdim 214234287Sdim Edit data; 215234287Sdim data.Kind = Act_Remove; 216234287Sdim data.OrigLoc = OrigLoc; 217234287Sdim data.Offset = Offs; 218234287Sdim data.Length = Len; 219234287Sdim CachedEdits.push_back(data); 220234287Sdim} 221234287Sdim 222234287Sdimbool Commit::canInsert(SourceLocation loc, FileOffset &offs) { 223234287Sdim if (loc.isInvalid()) 224234287Sdim return false; 225234287Sdim 226234287Sdim if (loc.isMacroID()) 227234287Sdim isAtStartOfMacroExpansion(loc, &loc); 228234287Sdim 229234287Sdim const SourceManager &SM = SourceMgr; 230234287Sdim while (SM.isMacroArgExpansion(loc)) 231234287Sdim loc = SM.getImmediateSpellingLoc(loc); 232234287Sdim 233234287Sdim if (loc.isMacroID()) 234234287Sdim if (!isAtStartOfMacroExpansion(loc, &loc)) 235234287Sdim return false; 236234287Sdim 237263508Sdim if (SM.isInSystemHeader(loc) && ForceCommitInSystemHeader) 238234287Sdim return false; 239234287Sdim 240234287Sdim std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); 241234287Sdim if (locInfo.first.isInvalid()) 242234287Sdim return false; 243234287Sdim offs = FileOffset(locInfo.first, locInfo.second); 244234287Sdim return canInsertInOffset(loc, offs); 245234287Sdim} 246234287Sdim 247234287Sdimbool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs, 248234287Sdim SourceLocation &AfterLoc) { 249234287Sdim if (loc.isInvalid()) 250234287Sdim 251234287Sdim return false; 252234287Sdim 253234287Sdim SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc); 254234287Sdim unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts); 255234287Sdim AfterLoc = loc.getLocWithOffset(tokLen); 256234287Sdim 257234287Sdim if (loc.isMacroID()) 258234287Sdim isAtEndOfMacroExpansion(loc, &loc); 259234287Sdim 260234287Sdim const SourceManager &SM = SourceMgr; 261234287Sdim while (SM.isMacroArgExpansion(loc)) 262234287Sdim loc = SM.getImmediateSpellingLoc(loc); 263234287Sdim 264234287Sdim if (loc.isMacroID()) 265234287Sdim if (!isAtEndOfMacroExpansion(loc, &loc)) 266234287Sdim return false; 267234287Sdim 268263508Sdim if (SM.isInSystemHeader(loc) && ForceCommitInSystemHeader) 269234287Sdim return false; 270234287Sdim 271234287Sdim loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts); 272234287Sdim if (loc.isInvalid()) 273234287Sdim return false; 274234287Sdim 275234287Sdim std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); 276234287Sdim if (locInfo.first.isInvalid()) 277234287Sdim return false; 278234287Sdim offs = FileOffset(locInfo.first, locInfo.second); 279234287Sdim return canInsertInOffset(loc, offs); 280234287Sdim} 281234287Sdim 282234287Sdimbool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { 283234287Sdim for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) { 284234287Sdim Edit &act = CachedEdits[i]; 285234287Sdim if (act.Kind == Act_Remove) { 286234287Sdim if (act.Offset.getFID() == Offs.getFID() && 287234287Sdim Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length)) 288234287Sdim return false; // position has been removed. 289234287Sdim } 290234287Sdim } 291234287Sdim 292234287Sdim if (!Editor) 293234287Sdim return true; 294234287Sdim return Editor->canInsertInOffset(OrigLoc, Offs); 295234287Sdim} 296234287Sdim 297234287Sdimbool Commit::canRemoveRange(CharSourceRange range, 298234287Sdim FileOffset &Offs, unsigned &Len) { 299234287Sdim const SourceManager &SM = SourceMgr; 300234287Sdim range = Lexer::makeFileCharRange(range, SM, LangOpts); 301234287Sdim if (range.isInvalid()) 302234287Sdim return false; 303234287Sdim 304234287Sdim if (range.getBegin().isMacroID() || range.getEnd().isMacroID()) 305234287Sdim return false; 306263508Sdim if ((SM.isInSystemHeader(range.getBegin()) || 307263508Sdim SM.isInSystemHeader(range.getEnd())) && ForceCommitInSystemHeader) 308234287Sdim return false; 309234287Sdim 310234287Sdim if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange())) 311234287Sdim return false; 312234287Sdim 313234287Sdim std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin()); 314234287Sdim std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd()); 315234287Sdim if (beginInfo.first != endInfo.first || 316234287Sdim beginInfo.second > endInfo.second) 317234287Sdim return false; 318234287Sdim 319234287Sdim Offs = FileOffset(beginInfo.first, beginInfo.second); 320234287Sdim Len = endInfo.second - beginInfo.second; 321234287Sdim return true; 322234287Sdim} 323234287Sdim 324234287Sdimbool Commit::canReplaceText(SourceLocation loc, StringRef text, 325234287Sdim FileOffset &Offs, unsigned &Len) { 326234287Sdim assert(!text.empty()); 327234287Sdim 328234287Sdim if (!canInsert(loc, Offs)) 329234287Sdim return false; 330234287Sdim 331234287Sdim // Try to load the file buffer. 332234287Sdim bool invalidTemp = false; 333234287Sdim StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp); 334234287Sdim if (invalidTemp) 335234287Sdim return false; 336234287Sdim 337239462Sdim Len = text.size(); 338234287Sdim return file.substr(Offs.getOffset()).startswith(text); 339234287Sdim} 340234287Sdim 341234287Sdimbool Commit::isAtStartOfMacroExpansion(SourceLocation loc, 342234287Sdim SourceLocation *MacroBegin) const { 343234287Sdim return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin); 344234287Sdim} 345234287Sdimbool Commit::isAtEndOfMacroExpansion(SourceLocation loc, 346234287Sdim SourceLocation *MacroEnd) const { 347234287Sdim return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd); 348234287Sdim} 349