Commit.cpp revision 6badc76787dc9480fd7c21d3eb75aab79d2df3f5
196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//===----- Commit.cpp - A unit of edits -----------------------------------===// 257f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler// 396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// The LLVM Compiler Infrastructure 496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// 596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// This file is distributed under the University of Illinois Open Source 696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// License. See LICENSE.TXT for details. 796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project// 896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project//===----------------------------------------------------------------------===// 996c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Edit/Commit.h" 1196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Basic/SourceManager.h" 1296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Edit/EditedSource.h" 1396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Lex/Lexer.h" 1496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project#include "clang/Lex/PPConditionalDirectiveRecord.h" 1596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectusing namespace clang; 1757f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadlerusing namespace edit; 1896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 1957f125a01b5fbb5860b144b3057153a50d07ddd1Andrew StadlerSourceLocation Commit::Edit::getFileLocation(SourceManager &SM) const { 208ee17c7012105fd7a5bad644b455d3e894b557dcMakoto Onuki SourceLocation Loc = SM.getLocForStartOfFile(Offset.getFID()); 2196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Loc = Loc.getLocWithOffset(Offset.getOffset()); 2257f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler assert(Loc.isFileID()); 2357f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler return Loc; 2457f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler} 251a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki 2696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source ProjectCharSourceRange Commit::Edit::getFileRange(SourceManager &SM) const { 2796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project SourceLocation Loc = getFileLocation(SM); 2896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); 29f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank} 30f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 31f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc BlankCharSourceRange Commit::Edit::getInsertFromRange(SourceManager &SM) const { 32f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank SourceLocation Loc = SM.getLocForStartOfFile(InsertFromRangeOffs.getFID()); 33f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank Loc = Loc.getLocWithOffset(InsertFromRangeOffs.getOffset()); 34f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank assert(Loc.isFileID()); 35f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank return CharSourceRange::getCharRange(Loc, Loc.getLocWithOffset(Length)); 36560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy} 3796c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 381a0b0092e0deba07b7dcb543a837fbc116584389Makoto OnukiCommit::Commit(EditedSource &Editor) 391a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki : SourceMgr(Editor.getSourceManager()), LangOpts(Editor.getLangOpts()), 4096c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project PPRec(Editor.getPPCondDirectiveRecord()), 4196c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project Editor(&Editor), 427985b43ab7310f76c170266da346f993d2f86051Paul Westbrook ForceCommitInSystemHeader(Editor.getForceCommitInSystemHeader()), 437985b43ab7310f76c170266da346f993d2f86051Paul Westbrook IsCommitable(true) { } 447985b43ab7310f76c170266da346f993d2f86051Paul Westbrook 4596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Projectbool Commit::insert(SourceLocation loc, StringRef text, 4657f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler bool afterToken, bool beforePreviousInsertions) { 4757f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler if (text.empty()) 48f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank return true; 49560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy 5057f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler FileOffset Offs; 5157f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler if ((!afterToken && !canInsert(loc, Offs)) || 5296c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { 5357f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler IsCommitable = false; 5457f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler return false; 5596c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project } 565bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler 575bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler addInsert(loc, Offs, text, beforePreviousInsertions); 5891237e9dcb0a948f17488b464edabcea0f259d31Makoto Onuki return true; 595bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler} 605bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler 61e36b6ff4fa808afa84dc09fa82587dae106c8d3dAndrew Stadlerbool Commit::insertFromRange(SourceLocation loc, 62e36b6ff4fa808afa84dc09fa82587dae106c8d3dAndrew Stadler CharSourceRange range, 635bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler bool afterToken, bool beforePreviousInsertions) { 643a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank FileOffset RangeOffs; 65f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank unsigned RangeLen; 665bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler if (!canRemoveRange(range, RangeOffs, RangeLen)) { 675bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler IsCommitable = false; 685bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler return false; 695bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler } 703a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank 715bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler FileOffset Offs; 725bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler if ((!afterToken && !canInsert(loc, Offs)) || 733a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank ( afterToken && !canInsertAfterToken(loc, Offs, loc))) { 7457f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler IsCommitable = false; 752fbb3db5d86210d03175ce77ff08c989a96c5864Makoto Onuki return false; 761a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki } 775bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler 782fbb3db5d86210d03175ce77ff08c989a96c5864Makoto Onuki if (PPRec && 795bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler PPRec->areInDifferentConditionalDirectiveRegion(loc, range.getBegin())) { 805bbbe11596a38390320d6589cc5f69d5607ffe16Tony Mantler IsCommitable = false; 8119b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki return false; 8257f125a01b5fbb5860b144b3057153a50d07ddd1Andrew Stadler } 8396c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 8496c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project addInsertFromRange(loc, Offs, RangeOffs, RangeLen, beforePreviousInsertions); 851a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki return true; 8696c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project} 8776a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler 8876a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadlerbool Commit::remove(CharSourceRange range) { 893a5c1fb274a9ce72d708d88509bf2607cb018dddMarc Blank FileOffset Offs; 90f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank unsigned Len; 91f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank if (!canRemoveRange(range, Offs, Len)) { 9276a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler IsCommitable = false; 93f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank return false; 9476a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler } 95f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 9676a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler addRemove(range.getBegin(), Offs, Len); 97f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank return true; 9876a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler} 99f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank 10076a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadlerbool Commit::insertWrap(StringRef before, CharSourceRange range, 10119b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki StringRef after) { 10219b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki bool commitableBefore = insert(range.getBegin(), before, /*afterToken=*/false, 103f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank /*beforePreviousInsertions=*/true); 10419b2a7ebc9cc770baace1605ff5b44b3fcb46320Makoto Onuki bool commitableAfter; 10576a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler if (range.isTokenRange()) 10676a211e0d9ac6171ff20e1c3dda7a74692d35b6eAndrew Stadler commitableAfter = insertAfterToken(range.getEnd(), after); 107f419287f22ae44f25e1ba1f757ec33c7941bbfa8Marc Blank else 1088ee17c7012105fd7a5bad644b455d3e894b557dcMakoto Onuki commitableAfter = insert(range.getEnd(), after); 1091a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki 1101a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki return commitableBefore && commitableAfter; 1111a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki} 1121a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki 1131a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onukibool Commit::replace(CharSourceRange range, StringRef text) { 1141a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki if (text.empty()) 1151a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki return remove(range); 1161a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki 1171a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki FileOffset Offs; 1181a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki unsigned Len; 1191a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki if (!canInsert(range.getBegin(), Offs) || !canRemoveRange(range, Offs, Len)) { 1201a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki IsCommitable = false; 1211a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki return false; 1221a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki } 123560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy 1241a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki addRemove(range.getBegin(), Offs, Len); 1251a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki addInsert(range.getBegin(), Offs, text, false); 1261a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki return true; 1271a0b0092e0deba07b7dcb543a837fbc116584389Makoto Onuki} 12896c5af40d639d629267794f4f0338a267ff94ce5The Android Open Source Project 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