Commit.cpp revision 6badc76787dc9480fd7c21d3eb75aab79d2df3f5
1//===----- Commit.cpp - A unit of edits -----------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9 10#include "clang/Edit/Commit.h" 11#include "clang/Basic/SourceManager.h" 12#include "clang/Edit/EditedSource.h" 13#include "clang/Lex/Lexer.h" 14#include "clang/Lex/PPConditionalDirectiveRecord.h" 15 16using namespace clang; 17using namespace edit; 18 19SourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const { 20 SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID()); 21 Loc = Loc.getLocWithOffset(Offset.getOffset()); 22 assert(Loc.isFileID()); 23 return Loc; 24} 25 26CharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const { 27 SourceLocation Loc = getFileLocation(SM); 28 return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); 29} 30 31CharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const { 32 SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID()); 33 Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset()); 34 assert(Loc.isFileID()); 35 return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); 36} 37 38Commit::Commit(EditedSource &Editor) 39 : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()), 40 PPRec(Editor.getPPCondDirectiveRecord()), 41 Editor(&Editor), 42 ForceCommitInSystemHeader(Editor.getForceCommitInSystemHeader()), 43 IsCommitable(true) { } 44 45bool Commit::insert(SourceLocation loc, StringRef text, 46 bool afterToken, bool beforePreviousInsertions) { 47 if (text.empty()) 48 return true; 49 50 FileOffset Offs; 51 if ((!afterToken && !canInsert(loc, Offs)) || 52 ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { 53 IsCommitable = false; 54 return false; 55 } 56 57 addInsert(loc, Offs, text, beforePreviousInsertions); 58 return true; 59} 60 61bool Commit::insertFromRange(SourceLocation loc, 62 CharSourceRange range, 63 bool afterToken, bool beforePreviousInsertions) { 64 FileOffset RangeOffs; 65 unsigned RangeLen; 66 if (!canRemoveRange(range, RangeOffs, RangeLen)) { 67 IsCommitable = false; 68 return false; 69 } 70 71 FileOffset Offs; 72 if ((!afterToken && !canInsert(loc, Offs)) || 73 ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { 74 IsCommitable = false; 75 return false; 76 } 77 78 if (PPRec && 79 PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) { 80 IsCommitable = false; 81 return false; 82 } 83 84 addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions); 85 return true; 86} 87 88bool Commit::remove(CharSourceRange range) { 89 FileOffset Offs; 90 unsigned Len; 91 if (!canRemoveRange(range, Offs, Len)) { 92 IsCommitable = false; 93 return false; 94 } 95 96 addRemove(range.getBegin(), Offs, Len); 97 return true; 98} 99 100bool Commit::insertWrap(StringRef before, CharSourceRange range, 101 StringRef after) { 102 bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false, 103 /*beforePreviousInsertions=*/true); 104 bool commitableAfter; 105 if (range.isTokenRange()) 106 commitableAfter = insertAfterToken(range.getEnd(), after); 107 else 108 commitableAfter = insert(range.getEnd(), after); 109 110 return commitableBefore && commitableAfter; 111} 112 113bool Commit::replace(CharSourceRange range, StringRef text) { 114 if (text.empty()) 115 return remove(range); 116 117 FileOffset Offs; 118 unsigned Len; 119 if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) { 120 IsCommitable = false; 121 return false; 122 } 123 124 addRemove(range.getBegin(), Offs, Len); 125 addInsert(range.getBegin(), Offs, text, false); 126 return true; 127} 128 129bool Commit::replaceWithInner(CharSourceRange range, 130 CharSourceRange replacementRange) { 131 FileOffset OuterBegin; 132 unsigned OuterLen; 133 if (!canRemoveRange(range, OuterBegin, OuterLen)) { 134 IsCommitable = false; 135 return false; 136 } 137 138 FileOffset InnerBegin; 139 unsigned InnerLen; 140 if (!canRemoveRange(replacementRange, InnerBegin, InnerLen)) { 141 IsCommitable = false; 142 return false; 143 } 144 145 FileOffset OuterEnd = OuterBegin.getWithOffset(OuterLen); 146 FileOffset InnerEnd = InnerBegin.getWithOffset(InnerLen); 147 if (OuterBegin.getFID() != InnerBegin.getFID() || 148 InnerBegin < OuterBegin || 149 InnerBegin > OuterEnd || 150 InnerEnd > OuterEnd) { 151 IsCommitable = false; 152 return false; 153 } 154 155 addRemove(range.getBegin(), 156 OuterBegin, InnerBegin.getOffset() - OuterBegin.getOffset()); 157 addRemove(replacementRange.getEnd(), 158 InnerEnd, OuterEnd.getOffset() - InnerEnd.getOffset()); 159 return true; 160} 161 162bool Commit::replaceText(SourceLocation loc, StringRef text, 163 StringRef replacementText) { 164 if (text.empty() || replacementText.empty()) 165 return true; 166 167 FileOffset Offs; 168 unsigned Len; 169 if (!canReplaceText(loc, replacementText, Offs, Len)) { 170 IsCommitable = false; 171 return false; 172 } 173 174 addRemove(loc, Offs, Len); 175 addInsert(loc, Offs, text, false); 176 return true; 177} 178 179void Commit::addInsert(SourceLocation OrigLoc, FileOffset Offs, StringRef text, 180 bool beforePreviousInsertions) { 181 if (text.empty()) 182 return; 183 184 Edit data; 185 data.Kind = Act_Insert; 186 data.OrigLoc = OrigLoc; 187 data.Offset = Offs; 188 data.Text = copyString(text); 189 data.BeforePrev = beforePreviousInsertions; 190 CachedEdits.push_back(data); 191} 192 193void Commit::addInsertFromRange(SourceLocation OrigLoc, FileOffset Offs, 194 FileOffset RangeOffs, unsigned RangeLen, 195 bool beforePreviousInsertions) { 196 if (RangeLen == 0) 197 return; 198 199 Edit data; 200 data.Kind = Act_InsertFromRange; 201 data.OrigLoc = OrigLoc; 202 data.Offset = Offs; 203 data.InsertFromRangeOffs = RangeOffs; 204 data.Length = RangeLen; 205 data.BeforePrev = beforePreviousInsertions; 206 CachedEdits.push_back(data); 207} 208 209void Commit::addRemove(SourceLocation OrigLoc, 210 FileOffset Offs, unsigned Len) { 211 if (Len == 0) 212 return; 213 214 Edit data; 215 data.Kind = Act_Remove; 216 data.OrigLoc = OrigLoc; 217 data.Offset = Offs; 218 data.Length = Len; 219 CachedEdits.push_back(data); 220} 221 222bool Commit::canInsert(SourceLocation loc, FileOffset &offs) { 223 if (loc.isInvalid()) 224 return false; 225 226 if (loc.isMacroID()) 227 isAtStartOfMacroExpansion(loc, &loc); 228 229 const SourceManager &SM = SourceMgr; 230 while (SM.isMacroArgExpansion(loc)) 231 loc = SM.getImmediateSpellingLoc(loc); 232 233 if (loc.isMacroID()) 234 if (!isAtStartOfMacroExpansion(loc, &loc)) 235 return false; 236 237 if (SM.isInSystemHeader(loc) && ForceCommitInSystemHeader) 238 return false; 239 240 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); 241 if (locInfo.first.isInvalid()) 242 return false; 243 offs = FileOffset(locInfo.first, locInfo.second); 244 return canInsertInOffset(loc, offs); 245} 246 247bool Commit::canInsertAfterToken(SourceLocation loc, FileOffset &offs, 248 SourceLocation &AfterLoc) { 249 if (loc.isInvalid()) 250 251 return false; 252 253 SourceLocation spellLoc = SourceMgr.getSpellingLoc(loc); 254 unsigned tokLen = Lexer::MeasureTokenLength(spellLoc, SourceMgr, LangOpts); 255 AfterLoc = loc.getLocWithOffset(tokLen); 256 257 if (loc.isMacroID()) 258 isAtEndOfMacroExpansion(loc, &loc); 259 260 const SourceManager &SM = SourceMgr; 261 while (SM.isMacroArgExpansion(loc)) 262 loc = SM.getImmediateSpellingLoc(loc); 263 264 if (loc.isMacroID()) 265 if (!isAtEndOfMacroExpansion(loc, &loc)) 266 return false; 267 268 if (SM.isInSystemHeader(loc) && ForceCommitInSystemHeader) 269 return false; 270 271 loc = Lexer::getLocForEndOfToken(loc, 0, SourceMgr, LangOpts); 272 if (loc.isInvalid()) 273 return false; 274 275 std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); 276 if (locInfo.first.isInvalid()) 277 return false; 278 offs = FileOffset(locInfo.first, locInfo.second); 279 return canInsertInOffset(loc, offs); 280} 281 282bool Commit::canInsertInOffset(SourceLocation OrigLoc, FileOffset Offs) { 283 for (unsigned i = 0, e = CachedEdits.size(); i != e; ++i) { 284 Edit &act = CachedEdits[i]; 285 if (act.Kind == Act_Remove) { 286 if (act.Offset.getFID() == Offs.getFID() && 287 Offs > act.Offset && Offs < act.Offset.getWithOffset(act.Length)) 288 return false; // position has been removed. 289 } 290 } 291 292 if (!Editor) 293 return true; 294 return Editor->canInsertInOffset(OrigLoc, Offs); 295} 296 297bool Commit::canRemoveRange(CharSourceRange range, 298 FileOffset &Offs, unsigned &Len) { 299 const SourceManager &SM = SourceMgr; 300 range = Lexer::makeFileCharRange(range, SM, LangOpts); 301 if (range.isInvalid()) 302 return false; 303 304 if (range.getBegin().isMacroID() || range.getEnd().isMacroID()) 305 return false; 306 if ((SM.isInSystemHeader(range.getBegin()) || 307 SM.isInSystemHeader(range.getEnd())) && ForceCommitInSystemHeader) 308 return false; 309 310 if (PPRec && PPRec->rangeIntersectsConditionalDirective(range.getAsRange())) 311 return false; 312 313 std::pair<FileID, unsigned> beginInfo = SM.getDecomposedLoc(range.getBegin()); 314 std::pair<FileID, unsigned> endInfo = SM.getDecomposedLoc(range.getEnd()); 315 if (beginInfo.first != endInfo.first || 316 beginInfo.second > endInfo.second) 317 return false; 318 319 Offs = FileOffset(beginInfo.first, beginInfo.second); 320 Len = endInfo.second - beginInfo.second; 321 return true; 322} 323 324bool Commit::canReplaceText(SourceLocation loc, StringRef text, 325 FileOffset &Offs, unsigned &Len) { 326 assert(!text.empty()); 327 328 if (!canInsert(loc, Offs)) 329 return false; 330 331 // Try to load the file buffer. 332 bool invalidTemp = false; 333 StringRef file = SourceMgr.getBufferData(Offs.getFID(), &invalidTemp); 334 if (invalidTemp) 335 return false; 336 337 Len = text.size(); 338 return file.substr(Offs.getOffset()).startswith(text); 339} 340 341bool Commit::isAtStartOfMacroExpansion(SourceLocation loc, 342 SourceLocation *MacroBegin) const { 343 return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, LangOpts, MacroBegin); 344} 345bool Commit::isAtEndOfMacroExpansion(SourceLocation loc, 346 SourceLocation *MacroEnd) const { 347 return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, LangOpts, MacroEnd); 348} 349