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